xref: /haiku/src/system/kernel/UserTimer.cpp (revision 24df65921befcd0ad0c5c7866118f922da61cb96)
1*24df6592SIngo Weinhold /*
2*24df6592SIngo Weinhold  * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3*24df6592SIngo Weinhold  * Distributed under the terms of the MIT License.
4*24df6592SIngo Weinhold  */
5*24df6592SIngo Weinhold 
6*24df6592SIngo Weinhold 
7*24df6592SIngo Weinhold #include <UserTimer.h>
8*24df6592SIngo Weinhold 
9*24df6592SIngo Weinhold #include <algorithm>
10*24df6592SIngo Weinhold 
11*24df6592SIngo Weinhold #include <AutoDeleter.h>
12*24df6592SIngo Weinhold 
13*24df6592SIngo Weinhold #include <debug.h>
14*24df6592SIngo Weinhold #include <kernel.h>
15*24df6592SIngo Weinhold #include <real_time_clock.h>
16*24df6592SIngo Weinhold #include <team.h>
17*24df6592SIngo Weinhold #include <thread_types.h>
18*24df6592SIngo Weinhold #include <UserEvent.h>
19*24df6592SIngo Weinhold #include <util/AutoLock.h>
20*24df6592SIngo Weinhold 
21*24df6592SIngo Weinhold 
22*24df6592SIngo Weinhold // Minimum interval length in microseconds for a periodic timer. This is not a
23*24df6592SIngo Weinhold // restriction on the user timer interval length itself, but the minimum time
24*24df6592SIngo Weinhold // span by which we advance the start time for kernel timers. A shorted user
25*24df6592SIngo Weinhold // timer interval will result in the overrun count to be increased every time
26*24df6592SIngo Weinhold // the kernel timer is rescheduled.
27*24df6592SIngo Weinhold static const bigtime_t kMinPeriodicTimerInterval = 100;
28*24df6592SIngo Weinhold 
29*24df6592SIngo Weinhold static RealTimeUserTimerList sAbsoluteRealTimeTimers;
30*24df6592SIngo Weinhold static spinlock sAbsoluteRealTimeTimersLock = B_SPINLOCK_INITIALIZER;
31*24df6592SIngo Weinhold 
32*24df6592SIngo Weinhold 
33*24df6592SIngo Weinhold // #pragma mark - TimerLocker
34*24df6592SIngo Weinhold 
35*24df6592SIngo Weinhold 
36*24df6592SIngo Weinhold namespace {
37*24df6592SIngo Weinhold 
38*24df6592SIngo Weinhold struct TimerLocker {
39*24df6592SIngo Weinhold 	Team*	team;
40*24df6592SIngo Weinhold 	Thread*	thread;
41*24df6592SIngo Weinhold 
42*24df6592SIngo Weinhold 	TimerLocker()
43*24df6592SIngo Weinhold 		:
44*24df6592SIngo Weinhold 		team(NULL),
45*24df6592SIngo Weinhold 		thread(NULL)
46*24df6592SIngo Weinhold 	{
47*24df6592SIngo Weinhold 	}
48*24df6592SIngo Weinhold 
49*24df6592SIngo Weinhold 	~TimerLocker()
50*24df6592SIngo Weinhold 	{
51*24df6592SIngo Weinhold 		Unlock();
52*24df6592SIngo Weinhold 	}
53*24df6592SIngo Weinhold 
54*24df6592SIngo Weinhold 	void Lock(Team* team, Thread* thread)
55*24df6592SIngo Weinhold 	{
56*24df6592SIngo Weinhold 		this->team = team;
57*24df6592SIngo Weinhold 		team->Lock();
58*24df6592SIngo Weinhold 
59*24df6592SIngo Weinhold 		this->thread = thread;
60*24df6592SIngo Weinhold 
61*24df6592SIngo Weinhold 		if (thread != NULL) {
62*24df6592SIngo Weinhold 			thread->AcquireReference();
63*24df6592SIngo Weinhold 			thread->Lock();
64*24df6592SIngo Weinhold 		}
65*24df6592SIngo Weinhold 
66*24df6592SIngo Weinhold 		// We don't check thread->team != team here, since this method can be
67*24df6592SIngo Weinhold 		// called for new threads not added to the team yet.
68*24df6592SIngo Weinhold 	}
69*24df6592SIngo Weinhold 
70*24df6592SIngo Weinhold 	status_t LockAndGetTimer(thread_id threadID, int32 timerID,
71*24df6592SIngo Weinhold 		UserTimer*& _timer)
72*24df6592SIngo Weinhold 	{
73*24df6592SIngo Weinhold 		team = thread_get_current_thread()->team;
74*24df6592SIngo Weinhold 		team->Lock();
75*24df6592SIngo Weinhold 
76*24df6592SIngo Weinhold 		if (threadID >= 0) {
77*24df6592SIngo Weinhold 			thread = Thread::GetAndLock(threadID);
78*24df6592SIngo Weinhold 			if (thread == NULL)
79*24df6592SIngo Weinhold 				return B_BAD_THREAD_ID;
80*24df6592SIngo Weinhold 			if (thread->team != team)
81*24df6592SIngo Weinhold 				return B_NOT_ALLOWED;
82*24df6592SIngo Weinhold 		}
83*24df6592SIngo Weinhold 
84*24df6592SIngo Weinhold 		UserTimer* timer = thread != NULL
85*24df6592SIngo Weinhold 			? thread->UserTimerFor(timerID) : team->UserTimerFor(timerID);
86*24df6592SIngo Weinhold 		if (timer == NULL)
87*24df6592SIngo Weinhold 			return B_BAD_VALUE;
88*24df6592SIngo Weinhold 
89*24df6592SIngo Weinhold 		_timer = timer;
90*24df6592SIngo Weinhold 		return B_OK;
91*24df6592SIngo Weinhold 	}
92*24df6592SIngo Weinhold 
93*24df6592SIngo Weinhold 	void Unlock()
94*24df6592SIngo Weinhold 	{
95*24df6592SIngo Weinhold 		if (thread != NULL) {
96*24df6592SIngo Weinhold 			thread->UnlockAndReleaseReference();
97*24df6592SIngo Weinhold 			thread = NULL;
98*24df6592SIngo Weinhold 		}
99*24df6592SIngo Weinhold 		if (team != NULL) {
100*24df6592SIngo Weinhold 			team->Unlock();
101*24df6592SIngo Weinhold 			team = NULL;
102*24df6592SIngo Weinhold 		}
103*24df6592SIngo Weinhold 	}
104*24df6592SIngo Weinhold };
105*24df6592SIngo Weinhold 
106*24df6592SIngo Weinhold }	// unnamed namespace
107*24df6592SIngo Weinhold 
108*24df6592SIngo Weinhold 
109*24df6592SIngo Weinhold // #pragma mark - UserTimer
110*24df6592SIngo Weinhold 
111*24df6592SIngo Weinhold 
112*24df6592SIngo Weinhold UserTimer::UserTimer()
113*24df6592SIngo Weinhold 	:
114*24df6592SIngo Weinhold 	fID(-1),
115*24df6592SIngo Weinhold 	fEvent(NULL),
116*24df6592SIngo Weinhold 	fNextTime(0),
117*24df6592SIngo Weinhold 	fInterval(0),
118*24df6592SIngo Weinhold 	fOverrunCount(0),
119*24df6592SIngo Weinhold 	fScheduled(false)
120*24df6592SIngo Weinhold {
121*24df6592SIngo Weinhold 	// mark the timer unused
122*24df6592SIngo Weinhold 	fTimer.user_data = this;
123*24df6592SIngo Weinhold }
124*24df6592SIngo Weinhold 
125*24df6592SIngo Weinhold 
126*24df6592SIngo Weinhold UserTimer::~UserTimer()
127*24df6592SIngo Weinhold {
128*24df6592SIngo Weinhold 	delete fEvent;
129*24df6592SIngo Weinhold }
130*24df6592SIngo Weinhold 
131*24df6592SIngo Weinhold 
132*24df6592SIngo Weinhold /*!	\fn UserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
133*24df6592SIngo Weinhold 		bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
134*24df6592SIngo Weinhold 	Cancels the timer, if it is already scheduled, and optionally schedules it
135*24df6592SIngo Weinhold 	with new parameters.
136*24df6592SIngo Weinhold 
137*24df6592SIngo Weinhold 	The caller must not hold the scheduler lock.
138*24df6592SIngo Weinhold 
139*24df6592SIngo Weinhold 	\param nextTime The time at which the timer should go off the next time. If
140*24df6592SIngo Weinhold 		\c B_INFINITE_TIMEOUT, the timer will not be scheduled. Whether the
141*24df6592SIngo Weinhold 		value is interpreted as absolute or relative time, depends on \c flags.
142*24df6592SIngo Weinhold 	\param interval If <tt> >0 </tt>, the timer will be scheduled to fire
143*24df6592SIngo Weinhold 		periodically every \a interval microseconds. Otherwise it will fire
144*24df6592SIngo Weinhold 		only once at \a nextTime. If \a nextTime is \c B_INFINITE_TIMEOUT, it
145*24df6592SIngo Weinhold 		will fire never in either case.
146*24df6592SIngo Weinhold 	\param flags Bitwise OR of flags. Currently \c B_ABSOLUTE_TIMEOUT and
147*24df6592SIngo Weinhold 		\c B_RELATIVE_TIMEOUT are supported, indicating whether \a nextTime is
148*24df6592SIngo Weinhold 		an absolute or relative time.
149*24df6592SIngo Weinhold 	\param _oldRemainingTime Return variable that will be set to the
150*24df6592SIngo Weinhold 		microseconds remaining to the time for which the timer was scheduled
151*24df6592SIngo Weinhold 		next before the call. If it wasn't scheduled, the variable is set to
152*24df6592SIngo Weinhold 		\c B_INFINITE_TIMEOUT.
153*24df6592SIngo Weinhold 	\param _oldInterval Return variable that will be set to the interval in
154*24df6592SIngo Weinhold 		microseconds the timer was to be scheduled periodically. If the timer
155*24df6592SIngo Weinhold 		wasn't periodic, the variable is set to \c 0.
156*24df6592SIngo Weinhold */
157*24df6592SIngo Weinhold 
158*24df6592SIngo Weinhold 
159*24df6592SIngo Weinhold /*!	Cancels the timer, if it is scheduled.
160*24df6592SIngo Weinhold 
161*24df6592SIngo Weinhold 	The caller must not hold the scheduler lock.
162*24df6592SIngo Weinhold */
163*24df6592SIngo Weinhold void
164*24df6592SIngo Weinhold UserTimer::Cancel()
165*24df6592SIngo Weinhold {
166*24df6592SIngo Weinhold 	bigtime_t oldNextTime;
167*24df6592SIngo Weinhold 	bigtime_t oldInterval;
168*24df6592SIngo Weinhold 	return Schedule(B_INFINITE_TIMEOUT, 0, 0, oldNextTime, oldInterval);
169*24df6592SIngo Weinhold }
170*24df6592SIngo Weinhold 
171*24df6592SIngo Weinhold 
172*24df6592SIngo Weinhold /*!	\fn UserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
173*24df6592SIngo Weinhold 		uint32& _overrunCount)
174*24df6592SIngo Weinhold 	Return information on the current timer.
175*24df6592SIngo Weinhold 
176*24df6592SIngo Weinhold 	The caller must not hold the scheduler lock.
177*24df6592SIngo Weinhold 
178*24df6592SIngo Weinhold 	\param _remainingTime Return variable that will be set to the microseconds
179*24df6592SIngo Weinhold 		remaining to the time for which the timer was scheduled next before the
180*24df6592SIngo Weinhold 		call. If it wasn't scheduled, the variable is set to
181*24df6592SIngo Weinhold 		\c B_INFINITE_TIMEOUT.
182*24df6592SIngo Weinhold 	\param _interval Return variable that will be set to the interval in
183*24df6592SIngo Weinhold 		microseconds the timer is to be scheduled periodically. If the timer
184*24df6592SIngo Weinhold 		isn't periodic, the variable is set to \c 0.
185*24df6592SIngo Weinhold 	\param _overrunCount Return variable that will be set to the number of times
186*24df6592SIngo Weinhold 		the timer went off, but its event couldn't be delivered, since it's
187*24df6592SIngo Weinhold 		previous delivery hasn't been handled yet.
188*24df6592SIngo Weinhold */
189*24df6592SIngo Weinhold 
190*24df6592SIngo Weinhold 
191*24df6592SIngo Weinhold /*static*/ int32
192*24df6592SIngo Weinhold UserTimer::HandleTimerHook(struct timer* timer)
193*24df6592SIngo Weinhold {
194*24df6592SIngo Weinhold 	((UserTimer*)timer->user_data)->HandleTimer();
195*24df6592SIngo Weinhold 	return B_HANDLED_INTERRUPT;
196*24df6592SIngo Weinhold }
197*24df6592SIngo Weinhold 
198*24df6592SIngo Weinhold 
199*24df6592SIngo Weinhold void
200*24df6592SIngo Weinhold UserTimer::HandleTimer()
201*24df6592SIngo Weinhold {
202*24df6592SIngo Weinhold 	if (fEvent != NULL) {
203*24df6592SIngo Weinhold 		// fire the event and update the overrun count, if necessary
204*24df6592SIngo Weinhold 		status_t error = fEvent->Fire();
205*24df6592SIngo Weinhold 		if (error == B_BUSY) {
206*24df6592SIngo Weinhold 			if (fOverrunCount < MAX_USER_TIMER_OVERRUN_COUNT)
207*24df6592SIngo Weinhold 				fOverrunCount++;
208*24df6592SIngo Weinhold 		}
209*24df6592SIngo Weinhold 	}
210*24df6592SIngo Weinhold 
211*24df6592SIngo Weinhold 	// Since we don't use periodic kernel timers, it isn't scheduled anymore.
212*24df6592SIngo Weinhold 	// If the timer is periodic, the derived class' version will schedule it
213*24df6592SIngo Weinhold 	// again.
214*24df6592SIngo Weinhold 	fScheduled = false;
215*24df6592SIngo Weinhold }
216*24df6592SIngo Weinhold 
217*24df6592SIngo Weinhold 
218*24df6592SIngo Weinhold /*!	Updates the start time for a periodic timer after it expired, enforcing
219*24df6592SIngo Weinhold 	sanity limits and updating \c fOverrunCount, if necessary.
220*24df6592SIngo Weinhold 
221*24df6592SIngo Weinhold 	The caller must not hold the scheduler lock.
222*24df6592SIngo Weinhold */
223*24df6592SIngo Weinhold void
224*24df6592SIngo Weinhold UserTimer::UpdatePeriodicStartTime()
225*24df6592SIngo Weinhold {
226*24df6592SIngo Weinhold 	if (fInterval < kMinPeriodicTimerInterval) {
227*24df6592SIngo Weinhold 		bigtime_t skip = (kMinPeriodicTimerInterval + fInterval - 1) / fInterval;
228*24df6592SIngo Weinhold 		fNextTime += skip * fInterval;
229*24df6592SIngo Weinhold 
230*24df6592SIngo Weinhold 		// One interval is the normal advance, so don't consider it skipped.
231*24df6592SIngo Weinhold 		skip--;
232*24df6592SIngo Weinhold 
233*24df6592SIngo Weinhold 		if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT)
234*24df6592SIngo Weinhold 			fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT;
235*24df6592SIngo Weinhold 		else
236*24df6592SIngo Weinhold 			fOverrunCount += skip;
237*24df6592SIngo Weinhold 	} else
238*24df6592SIngo Weinhold 		fNextTime += fInterval;
239*24df6592SIngo Weinhold }
240*24df6592SIngo Weinhold 
241*24df6592SIngo Weinhold 
242*24df6592SIngo Weinhold /*!	Checks whether the timer start time lies too much in the past and, if so,
243*24df6592SIngo Weinhold 	adjusts it and updates \c fOverrunCount.
244*24df6592SIngo Weinhold 
245*24df6592SIngo Weinhold 	The caller must not hold the scheduler lock.
246*24df6592SIngo Weinhold 
247*24df6592SIngo Weinhold 	\param now The current time.
248*24df6592SIngo Weinhold */
249*24df6592SIngo Weinhold void
250*24df6592SIngo Weinhold UserTimer::CheckPeriodicOverrun(bigtime_t now)
251*24df6592SIngo Weinhold {
252*24df6592SIngo Weinhold 	if (fNextTime + fInterval > now)
253*24df6592SIngo Weinhold 		return;
254*24df6592SIngo Weinhold 
255*24df6592SIngo Weinhold 	// The start time is a full interval or more in the past. Skip those
256*24df6592SIngo Weinhold 	// intervals.
257*24df6592SIngo Weinhold 	bigtime_t skip = (now - fNextTime) / fInterval;
258*24df6592SIngo Weinhold 	fNextTime += skip * fInterval;
259*24df6592SIngo Weinhold 
260*24df6592SIngo Weinhold 	if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT)
261*24df6592SIngo Weinhold 		fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT;
262*24df6592SIngo Weinhold 	else
263*24df6592SIngo Weinhold 		fOverrunCount += skip;
264*24df6592SIngo Weinhold }
265*24df6592SIngo Weinhold 
266*24df6592SIngo Weinhold 
267*24df6592SIngo Weinhold // #pragma mark - SystemTimeUserTimer
268*24df6592SIngo Weinhold 
269*24df6592SIngo Weinhold 
270*24df6592SIngo Weinhold void
271*24df6592SIngo Weinhold SystemTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
272*24df6592SIngo Weinhold 	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
273*24df6592SIngo Weinhold {
274*24df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
275*24df6592SIngo Weinhold 
276*24df6592SIngo Weinhold 	// get the current time
277*24df6592SIngo Weinhold 	bigtime_t now = system_time();
278*24df6592SIngo Weinhold 
279*24df6592SIngo Weinhold 	// Cancel the old timer, if still scheduled, and get the previous values.
280*24df6592SIngo Weinhold 	if (fScheduled) {
281*24df6592SIngo Weinhold 		cancel_timer(&fTimer);
282*24df6592SIngo Weinhold 
283*24df6592SIngo Weinhold 		_oldRemainingTime = fNextTime - now;
284*24df6592SIngo Weinhold 		_oldInterval = fInterval;
285*24df6592SIngo Weinhold 
286*24df6592SIngo Weinhold 		fScheduled = false;
287*24df6592SIngo Weinhold 	} else {
288*24df6592SIngo Weinhold 		_oldRemainingTime = B_INFINITE_TIMEOUT;
289*24df6592SIngo Weinhold 		_oldInterval = 0;
290*24df6592SIngo Weinhold 	}
291*24df6592SIngo Weinhold 
292*24df6592SIngo Weinhold 	// schedule the new timer
293*24df6592SIngo Weinhold 	fNextTime = nextTime;
294*24df6592SIngo Weinhold 	fInterval = interval;
295*24df6592SIngo Weinhold 	fOverrunCount = 0;
296*24df6592SIngo Weinhold 
297*24df6592SIngo Weinhold 	if (nextTime == B_INFINITE_TIMEOUT)
298*24df6592SIngo Weinhold 		return;
299*24df6592SIngo Weinhold 
300*24df6592SIngo Weinhold 	if ((flags & B_RELATIVE_TIMEOUT) != 0)
301*24df6592SIngo Weinhold 		fNextTime += now;
302*24df6592SIngo Weinhold 
303*24df6592SIngo Weinhold 	ScheduleKernelTimer(now, fInterval > 0);
304*24df6592SIngo Weinhold }
305*24df6592SIngo Weinhold 
306*24df6592SIngo Weinhold 
307*24df6592SIngo Weinhold void
308*24df6592SIngo Weinhold SystemTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
309*24df6592SIngo Weinhold 	uint32& _overrunCount)
310*24df6592SIngo Weinhold {
311*24df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
312*24df6592SIngo Weinhold 
313*24df6592SIngo Weinhold 	if (fScheduled) {
314*24df6592SIngo Weinhold 		_remainingTime = fNextTime - system_time();
315*24df6592SIngo Weinhold 		_interval = fInterval;
316*24df6592SIngo Weinhold 	} else {
317*24df6592SIngo Weinhold 		_remainingTime = B_INFINITE_TIMEOUT;
318*24df6592SIngo Weinhold 		_interval = 0;
319*24df6592SIngo Weinhold 	}
320*24df6592SIngo Weinhold 
321*24df6592SIngo Weinhold 	_overrunCount = fOverrunCount;
322*24df6592SIngo Weinhold }
323*24df6592SIngo Weinhold 
324*24df6592SIngo Weinhold 
325*24df6592SIngo Weinhold void
326*24df6592SIngo Weinhold SystemTimeUserTimer::HandleTimer()
327*24df6592SIngo Weinhold {
328*24df6592SIngo Weinhold 	UserTimer::HandleTimer();
329*24df6592SIngo Weinhold 
330*24df6592SIngo Weinhold 	// if periodic, reschedule the kernel timer
331*24df6592SIngo Weinhold 	if (fInterval > 0) {
332*24df6592SIngo Weinhold 		UpdatePeriodicStartTime();
333*24df6592SIngo Weinhold 		ScheduleKernelTimer(system_time(), true);
334*24df6592SIngo Weinhold 	}
335*24df6592SIngo Weinhold }
336*24df6592SIngo Weinhold 
337*24df6592SIngo Weinhold 
338*24df6592SIngo Weinhold /*!	Schedules the kernel timer.
339*24df6592SIngo Weinhold 
340*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
341*24df6592SIngo Weinhold 
342*24df6592SIngo Weinhold 	\param now The current system time to be used.
343*24df6592SIngo Weinhold 	\param checkPeriodicOverrun If \c true, calls CheckPeriodicOverrun() first,
344*24df6592SIngo Weinhold 		i.e. the start time will be adjusted to not lie too much in the past.
345*24df6592SIngo Weinhold */
346*24df6592SIngo Weinhold void
347*24df6592SIngo Weinhold SystemTimeUserTimer::ScheduleKernelTimer(bigtime_t now,
348*24df6592SIngo Weinhold 	bool checkPeriodicOverrun)
349*24df6592SIngo Weinhold {
350*24df6592SIngo Weinhold 	// If periodic, check whether the start time is too far in the past.
351*24df6592SIngo Weinhold 	if (checkPeriodicOverrun)
352*24df6592SIngo Weinhold 		CheckPeriodicOverrun(now);
353*24df6592SIngo Weinhold 
354*24df6592SIngo Weinhold 	uint32 timerFlags = B_ONE_SHOT_ABSOLUTE_TIMER
355*24df6592SIngo Weinhold 			| B_TIMER_USE_TIMER_STRUCT_TIMES | B_TIMER_ACQUIRE_SCHEDULER_LOCK;
356*24df6592SIngo Weinhold 		// We use B_TIMER_ACQUIRE_SCHEDULER_LOCK to avoid race conditions
357*24df6592SIngo Weinhold 		// between setting/canceling the timer and the event handler.
358*24df6592SIngo Weinhold 
359*24df6592SIngo Weinhold 	fTimer.schedule_time = std::max(fNextTime, (bigtime_t)0);
360*24df6592SIngo Weinhold 	fTimer.period = 0;
361*24df6592SIngo Weinhold 
362*24df6592SIngo Weinhold 	add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, timerFlags);
363*24df6592SIngo Weinhold 
364*24df6592SIngo Weinhold 	fScheduled = true;
365*24df6592SIngo Weinhold }
366*24df6592SIngo Weinhold 
367*24df6592SIngo Weinhold 
368*24df6592SIngo Weinhold // #pragma mark - RealTimeUserTimer
369*24df6592SIngo Weinhold 
370*24df6592SIngo Weinhold 
371*24df6592SIngo Weinhold void
372*24df6592SIngo Weinhold RealTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
373*24df6592SIngo Weinhold 	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
374*24df6592SIngo Weinhold {
375*24df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
376*24df6592SIngo Weinhold 
377*24df6592SIngo Weinhold 	// get the current time
378*24df6592SIngo Weinhold 	bigtime_t now = system_time();
379*24df6592SIngo Weinhold 
380*24df6592SIngo Weinhold 	// Cancel the old timer, if still scheduled, and get the previous values.
381*24df6592SIngo Weinhold 	if (fScheduled) {
382*24df6592SIngo Weinhold 		cancel_timer(&fTimer);
383*24df6592SIngo Weinhold 
384*24df6592SIngo Weinhold 		_oldRemainingTime = fNextTime - now;
385*24df6592SIngo Weinhold 		_oldInterval = fInterval;
386*24df6592SIngo Weinhold 
387*24df6592SIngo Weinhold 		if (fAbsolute) {
388*24df6592SIngo Weinhold 			SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
389*24df6592SIngo Weinhold 			sAbsoluteRealTimeTimers.Remove(this);
390*24df6592SIngo Weinhold 		}
391*24df6592SIngo Weinhold 
392*24df6592SIngo Weinhold 		fScheduled = false;
393*24df6592SIngo Weinhold 	} else {
394*24df6592SIngo Weinhold 		_oldRemainingTime = B_INFINITE_TIMEOUT;
395*24df6592SIngo Weinhold 		_oldInterval = 0;
396*24df6592SIngo Weinhold 	}
397*24df6592SIngo Weinhold 
398*24df6592SIngo Weinhold 	// schedule the new timer
399*24df6592SIngo Weinhold 	fNextTime = nextTime;
400*24df6592SIngo Weinhold 	fInterval = interval;
401*24df6592SIngo Weinhold 	fOverrunCount = 0;
402*24df6592SIngo Weinhold 
403*24df6592SIngo Weinhold 	if (nextTime == B_INFINITE_TIMEOUT)
404*24df6592SIngo Weinhold 		return;
405*24df6592SIngo Weinhold 
406*24df6592SIngo Weinhold 	fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
407*24df6592SIngo Weinhold 
408*24df6592SIngo Weinhold 	if (fAbsolute) {
409*24df6592SIngo Weinhold 		fRealTimeOffset = rtc_boot_time();
410*24df6592SIngo Weinhold 		fNextTime -= fRealTimeOffset;
411*24df6592SIngo Weinhold 
412*24df6592SIngo Weinhold 		// If periodic, check whether the start time is too far in the past.
413*24df6592SIngo Weinhold 		if (fInterval > 0)
414*24df6592SIngo Weinhold 			CheckPeriodicOverrun(now);
415*24df6592SIngo Weinhold 
416*24df6592SIngo Weinhold 		// add the absolute timer to the global list
417*24df6592SIngo Weinhold 		SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
418*24df6592SIngo Weinhold 		sAbsoluteRealTimeTimers.Insert(this);
419*24df6592SIngo Weinhold 	} else
420*24df6592SIngo Weinhold 		fNextTime += now;
421*24df6592SIngo Weinhold 
422*24df6592SIngo Weinhold 	ScheduleKernelTimer(now, false);
423*24df6592SIngo Weinhold }
424*24df6592SIngo Weinhold 
425*24df6592SIngo Weinhold 
426*24df6592SIngo Weinhold /*!	Called when the real-time clock has been changed.
427*24df6592SIngo Weinhold 
428*24df6592SIngo Weinhold 	The caller must hold the scheduler lock. Optionally the caller may also
429*24df6592SIngo Weinhold 	hold \c sAbsoluteRealTimeTimersLock.
430*24df6592SIngo Weinhold */
431*24df6592SIngo Weinhold void
432*24df6592SIngo Weinhold RealTimeUserTimer::TimeWarped()
433*24df6592SIngo Weinhold {
434*24df6592SIngo Weinhold 	ASSERT(fScheduled && fAbsolute);
435*24df6592SIngo Weinhold 
436*24df6592SIngo Weinhold 	// get the new real-time offset
437*24df6592SIngo Weinhold 	bigtime_t oldRealTimeOffset = fRealTimeOffset;
438*24df6592SIngo Weinhold 	fRealTimeOffset = rtc_boot_time();
439*24df6592SIngo Weinhold 	if (fRealTimeOffset == oldRealTimeOffset)
440*24df6592SIngo Weinhold 		return;
441*24df6592SIngo Weinhold 
442*24df6592SIngo Weinhold 	// cancel the kernel timer and reschedule it
443*24df6592SIngo Weinhold 	cancel_timer(&fTimer);
444*24df6592SIngo Weinhold 
445*24df6592SIngo Weinhold 	fNextTime += oldRealTimeOffset - fRealTimeOffset;
446*24df6592SIngo Weinhold 
447*24df6592SIngo Weinhold 	ScheduleKernelTimer(system_time(), fInterval > 0);
448*24df6592SIngo Weinhold }
449*24df6592SIngo Weinhold 
450*24df6592SIngo Weinhold 
451*24df6592SIngo Weinhold void
452*24df6592SIngo Weinhold RealTimeUserTimer::HandleTimer()
453*24df6592SIngo Weinhold {
454*24df6592SIngo Weinhold 	SystemTimeUserTimer::HandleTimer();
455*24df6592SIngo Weinhold 
456*24df6592SIngo Weinhold 	// remove from global list, if no longer scheduled
457*24df6592SIngo Weinhold 	if (!fScheduled && fAbsolute) {
458*24df6592SIngo Weinhold 		SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
459*24df6592SIngo Weinhold 		sAbsoluteRealTimeTimers.Remove(this);
460*24df6592SIngo Weinhold 	}
461*24df6592SIngo Weinhold }
462*24df6592SIngo Weinhold 
463*24df6592SIngo Weinhold 
464*24df6592SIngo Weinhold // #pragma mark - TeamTimeUserTimer
465*24df6592SIngo Weinhold 
466*24df6592SIngo Weinhold 
467*24df6592SIngo Weinhold TeamTimeUserTimer::TeamTimeUserTimer(team_id teamID)
468*24df6592SIngo Weinhold 	:
469*24df6592SIngo Weinhold 	fTeamID(teamID),
470*24df6592SIngo Weinhold 	fTeam(NULL)
471*24df6592SIngo Weinhold {
472*24df6592SIngo Weinhold }
473*24df6592SIngo Weinhold 
474*24df6592SIngo Weinhold 
475*24df6592SIngo Weinhold TeamTimeUserTimer::~TeamTimeUserTimer()
476*24df6592SIngo Weinhold {
477*24df6592SIngo Weinhold 	ASSERT(fTeam == NULL);
478*24df6592SIngo Weinhold }
479*24df6592SIngo Weinhold 
480*24df6592SIngo Weinhold 
481*24df6592SIngo Weinhold void
482*24df6592SIngo Weinhold TeamTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
483*24df6592SIngo Weinhold 	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
484*24df6592SIngo Weinhold {
485*24df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
486*24df6592SIngo Weinhold 
487*24df6592SIngo Weinhold 	// get the current time, but only if needed
488*24df6592SIngo Weinhold 	bool nowValid = fTeam != NULL;
489*24df6592SIngo Weinhold 	bigtime_t now = nowValid ? fTeam->CPUTime(false) : 0;
490*24df6592SIngo Weinhold 
491*24df6592SIngo Weinhold 	// Cancel the old timer, if still scheduled, and get the previous values.
492*24df6592SIngo Weinhold 	if (fTeam != NULL) {
493*24df6592SIngo Weinhold 		if (fScheduled) {
494*24df6592SIngo Weinhold 			cancel_timer(&fTimer);
495*24df6592SIngo Weinhold 			fScheduled = false;
496*24df6592SIngo Weinhold 		}
497*24df6592SIngo Weinhold 
498*24df6592SIngo Weinhold 		_oldRemainingTime = fNextTime - now;
499*24df6592SIngo Weinhold 		_oldInterval = fInterval;
500*24df6592SIngo Weinhold 
501*24df6592SIngo Weinhold 		fTeam->UserTimerDeactivated(this);
502*24df6592SIngo Weinhold 		fTeam->ReleaseReference();
503*24df6592SIngo Weinhold 		fTeam = NULL;
504*24df6592SIngo Weinhold 	} else {
505*24df6592SIngo Weinhold 		_oldRemainingTime = B_INFINITE_TIMEOUT;
506*24df6592SIngo Weinhold 		_oldInterval = 0;
507*24df6592SIngo Weinhold 	}
508*24df6592SIngo Weinhold 
509*24df6592SIngo Weinhold 	// schedule the new timer
510*24df6592SIngo Weinhold 	fNextTime = nextTime;
511*24df6592SIngo Weinhold 	fInterval = interval;
512*24df6592SIngo Weinhold 	fOverrunCount = 0;
513*24df6592SIngo Weinhold 
514*24df6592SIngo Weinhold 	if (fNextTime == B_INFINITE_TIMEOUT)
515*24df6592SIngo Weinhold 		return;
516*24df6592SIngo Weinhold 
517*24df6592SIngo Weinhold 	// Get the team. If it doesn't exist anymore, just don't schedule the
518*24df6592SIngo Weinhold 	// timer anymore.
519*24df6592SIngo Weinhold 	fTeam = Team::Get(fTeamID);
520*24df6592SIngo Weinhold 	if (fTeam == NULL)
521*24df6592SIngo Weinhold 		return;
522*24df6592SIngo Weinhold 
523*24df6592SIngo Weinhold 	fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
524*24df6592SIngo Weinhold 
525*24df6592SIngo Weinhold 	// convert relative to absolute timeouts
526*24df6592SIngo Weinhold 	if (!fAbsolute) {
527*24df6592SIngo Weinhold 		if (!nowValid)
528*24df6592SIngo Weinhold 			now = fTeam->CPUTime(false);
529*24df6592SIngo Weinhold 		fNextTime += now;
530*24df6592SIngo Weinhold 	}
531*24df6592SIngo Weinhold 
532*24df6592SIngo Weinhold 	fTeam->UserTimerActivated(this);
533*24df6592SIngo Weinhold 
534*24df6592SIngo Weinhold 	// schedule/udpate the kernel timer
535*24df6592SIngo Weinhold 	Update(NULL);
536*24df6592SIngo Weinhold }
537*24df6592SIngo Weinhold 
538*24df6592SIngo Weinhold 
539*24df6592SIngo Weinhold void
540*24df6592SIngo Weinhold TeamTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
541*24df6592SIngo Weinhold 	uint32& _overrunCount)
542*24df6592SIngo Weinhold {
543*24df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
544*24df6592SIngo Weinhold 
545*24df6592SIngo Weinhold 	if (fTeam != NULL) {
546*24df6592SIngo Weinhold 		_remainingTime = fNextTime - fTeam->CPUTime(false);
547*24df6592SIngo Weinhold 		_interval = fInterval;
548*24df6592SIngo Weinhold 	} else {
549*24df6592SIngo Weinhold 		_remainingTime = B_INFINITE_TIMEOUT;
550*24df6592SIngo Weinhold 		_interval = 0;
551*24df6592SIngo Weinhold 	}
552*24df6592SIngo Weinhold 
553*24df6592SIngo Weinhold 	_overrunCount = fOverrunCount;
554*24df6592SIngo Weinhold }
555*24df6592SIngo Weinhold 
556*24df6592SIngo Weinhold 
557*24df6592SIngo Weinhold /*!	Deactivates the timer, if it is activated.
558*24df6592SIngo Weinhold 
559*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
560*24df6592SIngo Weinhold */
561*24df6592SIngo Weinhold void
562*24df6592SIngo Weinhold TeamTimeUserTimer::Deactivate()
563*24df6592SIngo Weinhold {
564*24df6592SIngo Weinhold 	if (fTeam == NULL)
565*24df6592SIngo Weinhold 		return;
566*24df6592SIngo Weinhold 
567*24df6592SIngo Weinhold 	// unschedule, if scheduled
568*24df6592SIngo Weinhold 	if (fScheduled) {
569*24df6592SIngo Weinhold 		cancel_timer(&fTimer);
570*24df6592SIngo Weinhold 		fScheduled = false;
571*24df6592SIngo Weinhold 	}
572*24df6592SIngo Weinhold 
573*24df6592SIngo Weinhold 	// deactivate
574*24df6592SIngo Weinhold 	fTeam->UserTimerDeactivated(this);
575*24df6592SIngo Weinhold 	fTeam->ReleaseReference();
576*24df6592SIngo Weinhold 	fTeam = NULL;
577*24df6592SIngo Weinhold }
578*24df6592SIngo Weinhold 
579*24df6592SIngo Weinhold 
580*24df6592SIngo Weinhold /*!	Starts/stops the timer as necessary, if it is active.
581*24df6592SIngo Weinhold 
582*24df6592SIngo Weinhold 	Called whenever threads of the team whose CPU time is referred to by the
583*24df6592SIngo Weinhold 	timer are scheduled or unscheduled (or leave the team), or when the timer
584*24df6592SIngo Weinhold 	was just set. Schedules a kernel timer for the remaining time, respectively
585*24df6592SIngo Weinhold 	cancels it.
586*24df6592SIngo Weinhold 
587*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
588*24df6592SIngo Weinhold 
589*24df6592SIngo Weinhold 	\param unscheduledThread If not \c NULL, this is the thread that is
590*24df6592SIngo Weinhold 		currently running and which is in the process of being unscheduled.
591*24df6592SIngo Weinhold */
592*24df6592SIngo Weinhold void
593*24df6592SIngo Weinhold TeamTimeUserTimer::Update(Thread* unscheduledThread)
594*24df6592SIngo Weinhold {
595*24df6592SIngo Weinhold 	if (fTeam == NULL)
596*24df6592SIngo Weinhold 		return;
597*24df6592SIngo Weinhold 
598*24df6592SIngo Weinhold 	// determine how many of the team's threads are currently running
599*24df6592SIngo Weinhold 	fRunningThreads = 0;
600*24df6592SIngo Weinhold 	int32 cpuCount = smp_get_num_cpus();
601*24df6592SIngo Weinhold 	for (int32 i = 0; i < cpuCount; i++) {
602*24df6592SIngo Weinhold 		Thread* thread = gCPU[i].running_thread;
603*24df6592SIngo Weinhold 		if (thread != unscheduledThread && thread->team == fTeam)
604*24df6592SIngo Weinhold 			fRunningThreads++;
605*24df6592SIngo Weinhold 	}
606*24df6592SIngo Weinhold 
607*24df6592SIngo Weinhold 	_Update(unscheduledThread != NULL);
608*24df6592SIngo Weinhold }
609*24df6592SIngo Weinhold 
610*24df6592SIngo Weinhold 
611*24df6592SIngo Weinhold /*!	Called when the team's CPU time clock which this timer refers to has been
612*24df6592SIngo Weinhold 	set.
613*24df6592SIngo Weinhold 
614*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
615*24df6592SIngo Weinhold 
616*24df6592SIngo Weinhold 	\param changedBy The value by which the clock has changed.
617*24df6592SIngo Weinhold */
618*24df6592SIngo Weinhold void
619*24df6592SIngo Weinhold TeamTimeUserTimer::TimeWarped(bigtime_t changedBy)
620*24df6592SIngo Weinhold {
621*24df6592SIngo Weinhold 	if (fTeam == NULL || changedBy == 0)
622*24df6592SIngo Weinhold 		return;
623*24df6592SIngo Weinhold 
624*24df6592SIngo Weinhold 	// If this is a relative timer, adjust fNextTime by the value the clock has
625*24df6592SIngo Weinhold 	// changed.
626*24df6592SIngo Weinhold 	if (!fAbsolute)
627*24df6592SIngo Weinhold 		fNextTime += changedBy;
628*24df6592SIngo Weinhold 
629*24df6592SIngo Weinhold 	// reschedule the kernel timer
630*24df6592SIngo Weinhold 	_Update(false);
631*24df6592SIngo Weinhold }
632*24df6592SIngo Weinhold 
633*24df6592SIngo Weinhold 
634*24df6592SIngo Weinhold void
635*24df6592SIngo Weinhold TeamTimeUserTimer::HandleTimer()
636*24df6592SIngo Weinhold {
637*24df6592SIngo Weinhold 	UserTimer::HandleTimer();
638*24df6592SIngo Weinhold 
639*24df6592SIngo Weinhold 	// If the timer is not periodic, it is no longer active. Otherwise
640*24df6592SIngo Weinhold 	// reschedule the kernel timer.
641*24df6592SIngo Weinhold 	if (fTeam != NULL) {
642*24df6592SIngo Weinhold 		if (fInterval == 0) {
643*24df6592SIngo Weinhold 			fTeam->UserTimerDeactivated(this);
644*24df6592SIngo Weinhold 			fTeam->ReleaseReference();
645*24df6592SIngo Weinhold 			fTeam = NULL;
646*24df6592SIngo Weinhold 		} else {
647*24df6592SIngo Weinhold 			UpdatePeriodicStartTime();
648*24df6592SIngo Weinhold 			_Update(false);
649*24df6592SIngo Weinhold 		}
650*24df6592SIngo Weinhold 	}
651*24df6592SIngo Weinhold }
652*24df6592SIngo Weinhold 
653*24df6592SIngo Weinhold 
654*24df6592SIngo Weinhold /*!	Schedules/cancels the kernel timer as necessary.
655*24df6592SIngo Weinhold 
656*24df6592SIngo Weinhold 	\c fRunningThreads must be up-to-date.
657*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
658*24df6592SIngo Weinhold 
659*24df6592SIngo Weinhold 	\param unscheduling \c true, when the current thread is in the process of
660*24df6592SIngo Weinhold 		being unscheduled.
661*24df6592SIngo Weinhold */
662*24df6592SIngo Weinhold void
663*24df6592SIngo Weinhold TeamTimeUserTimer::_Update(bool unscheduling)
664*24df6592SIngo Weinhold {
665*24df6592SIngo Weinhold 	// unschedule the kernel timer, if scheduled
666*24df6592SIngo Weinhold 	if (fScheduled)
667*24df6592SIngo Weinhold 		cancel_timer(&fTimer);
668*24df6592SIngo Weinhold 
669*24df6592SIngo Weinhold 	// if no more threads are running, we're done
670*24df6592SIngo Weinhold 	if (fRunningThreads == 0) {
671*24df6592SIngo Weinhold 		fScheduled = false;
672*24df6592SIngo Weinhold 		return;
673*24df6592SIngo Weinhold 	}
674*24df6592SIngo Weinhold 
675*24df6592SIngo Weinhold 	// There are still threads running. Reschedule the kernel timer.
676*24df6592SIngo Weinhold 	bigtime_t now = fTeam->CPUTime(unscheduling);
677*24df6592SIngo Weinhold 
678*24df6592SIngo Weinhold 	// If periodic, check whether the start time is too far in the past.
679*24df6592SIngo Weinhold 	if (fInterval > 0)
680*24df6592SIngo Weinhold 		CheckPeriodicOverrun(now);
681*24df6592SIngo Weinhold 
682*24df6592SIngo Weinhold 	if (fNextTime > now) {
683*24df6592SIngo Weinhold 		fTimer.schedule_time = system_time()
684*24df6592SIngo Weinhold 			+ (fNextTime - now + fRunningThreads - 1) / fRunningThreads;
685*24df6592SIngo Weinhold 		// check for overflow
686*24df6592SIngo Weinhold 		if (fTimer.schedule_time < 0)
687*24df6592SIngo Weinhold 			fTimer.schedule_time = B_INFINITE_TIMEOUT;
688*24df6592SIngo Weinhold 	} else
689*24df6592SIngo Weinhold 		fTimer.schedule_time = 0;
690*24df6592SIngo Weinhold 	fTimer.period = 0;
691*24df6592SIngo Weinhold 		// We reschedule periodic timers manually in HandleTimer() to avoid
692*24df6592SIngo Weinhold 		// rounding errors.
693*24df6592SIngo Weinhold 
694*24df6592SIngo Weinhold 	add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time,
695*24df6592SIngo Weinhold 		B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES
696*24df6592SIngo Weinhold 			| B_TIMER_ACQUIRE_SCHEDULER_LOCK);
697*24df6592SIngo Weinhold 		// We use B_TIMER_ACQUIRE_SCHEDULER_LOCK to avoid race conditions
698*24df6592SIngo Weinhold 		// between setting/canceling the timer and the event handler.
699*24df6592SIngo Weinhold 		// We use B_TIMER_USE_TIMER_STRUCT_TIMES, so period remains 0, which
700*24df6592SIngo Weinhold 		// our base class expects.
701*24df6592SIngo Weinhold 
702*24df6592SIngo Weinhold 	fScheduled = true;
703*24df6592SIngo Weinhold }
704*24df6592SIngo Weinhold 
705*24df6592SIngo Weinhold 
706*24df6592SIngo Weinhold // #pragma mark - TeamUserTimeUserTimer
707*24df6592SIngo Weinhold 
708*24df6592SIngo Weinhold 
709*24df6592SIngo Weinhold TeamUserTimeUserTimer::TeamUserTimeUserTimer(team_id teamID)
710*24df6592SIngo Weinhold 	:
711*24df6592SIngo Weinhold 	fTeamID(teamID),
712*24df6592SIngo Weinhold 	fTeam(NULL)
713*24df6592SIngo Weinhold {
714*24df6592SIngo Weinhold }
715*24df6592SIngo Weinhold 
716*24df6592SIngo Weinhold 
717*24df6592SIngo Weinhold TeamUserTimeUserTimer::~TeamUserTimeUserTimer()
718*24df6592SIngo Weinhold {
719*24df6592SIngo Weinhold 	ASSERT(fTeam == NULL);
720*24df6592SIngo Weinhold }
721*24df6592SIngo Weinhold 
722*24df6592SIngo Weinhold 
723*24df6592SIngo Weinhold void
724*24df6592SIngo Weinhold TeamUserTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
725*24df6592SIngo Weinhold 	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
726*24df6592SIngo Weinhold {
727*24df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
728*24df6592SIngo Weinhold 
729*24df6592SIngo Weinhold 	// get the current time, but only if needed
730*24df6592SIngo Weinhold 	bool nowValid = fTeam != NULL;
731*24df6592SIngo Weinhold 	bigtime_t now = nowValid ? fTeam->UserCPUTime() : 0;
732*24df6592SIngo Weinhold 
733*24df6592SIngo Weinhold 	// Cancel the old timer, if still active, and get the previous values.
734*24df6592SIngo Weinhold 	if (fTeam != NULL) {
735*24df6592SIngo Weinhold 		_oldRemainingTime = fNextTime - now;
736*24df6592SIngo Weinhold 		_oldInterval = fInterval;
737*24df6592SIngo Weinhold 
738*24df6592SIngo Weinhold 		fTeam->UserTimerDeactivated(this);
739*24df6592SIngo Weinhold 		fTeam->ReleaseReference();
740*24df6592SIngo Weinhold 		fTeam = NULL;
741*24df6592SIngo Weinhold 	} else {
742*24df6592SIngo Weinhold 		_oldRemainingTime = B_INFINITE_TIMEOUT;
743*24df6592SIngo Weinhold 		_oldInterval = 0;
744*24df6592SIngo Weinhold 	}
745*24df6592SIngo Weinhold 
746*24df6592SIngo Weinhold 	// schedule the new timer
747*24df6592SIngo Weinhold 	fNextTime = nextTime;
748*24df6592SIngo Weinhold 	fInterval = interval;
749*24df6592SIngo Weinhold 	fOverrunCount = 0;
750*24df6592SIngo Weinhold 
751*24df6592SIngo Weinhold 	if (fNextTime == B_INFINITE_TIMEOUT)
752*24df6592SIngo Weinhold 		return;
753*24df6592SIngo Weinhold 
754*24df6592SIngo Weinhold 	// Get the team. If it doesn't exist anymore, just don't schedule the
755*24df6592SIngo Weinhold 	// timer anymore.
756*24df6592SIngo Weinhold 	fTeam = Team::Get(fTeamID);
757*24df6592SIngo Weinhold 	if (fTeam == NULL)
758*24df6592SIngo Weinhold 		return;
759*24df6592SIngo Weinhold 
760*24df6592SIngo Weinhold 	// convert relative to absolute timeouts
761*24df6592SIngo Weinhold 	if ((flags & B_RELATIVE_TIMEOUT) != 0) {
762*24df6592SIngo Weinhold 		if (!nowValid)
763*24df6592SIngo Weinhold 			now = fTeam->CPUTime(false);
764*24df6592SIngo Weinhold 		fNextTime += now;
765*24df6592SIngo Weinhold 	}
766*24df6592SIngo Weinhold 
767*24df6592SIngo Weinhold 	fTeam->UserTimerActivated(this);
768*24df6592SIngo Weinhold 
769*24df6592SIngo Weinhold 	// fire the event, if already timed out
770*24df6592SIngo Weinhold 	Check();
771*24df6592SIngo Weinhold }
772*24df6592SIngo Weinhold 
773*24df6592SIngo Weinhold 
774*24df6592SIngo Weinhold void
775*24df6592SIngo Weinhold TeamUserTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
776*24df6592SIngo Weinhold 	uint32& _overrunCount)
777*24df6592SIngo Weinhold {
778*24df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
779*24df6592SIngo Weinhold 
780*24df6592SIngo Weinhold 	if (fTeam != NULL) {
781*24df6592SIngo Weinhold 		_remainingTime = fNextTime - fTeam->UserCPUTime();
782*24df6592SIngo Weinhold 		_interval = fInterval;
783*24df6592SIngo Weinhold 	} else {
784*24df6592SIngo Weinhold 		_remainingTime = B_INFINITE_TIMEOUT;
785*24df6592SIngo Weinhold 		_interval = 0;
786*24df6592SIngo Weinhold 	}
787*24df6592SIngo Weinhold 
788*24df6592SIngo Weinhold 	_overrunCount = fOverrunCount;
789*24df6592SIngo Weinhold }
790*24df6592SIngo Weinhold 
791*24df6592SIngo Weinhold 
792*24df6592SIngo Weinhold /*!	Deactivates the timer, if it is activated.
793*24df6592SIngo Weinhold 
794*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
795*24df6592SIngo Weinhold */
796*24df6592SIngo Weinhold void
797*24df6592SIngo Weinhold TeamUserTimeUserTimer::Deactivate()
798*24df6592SIngo Weinhold {
799*24df6592SIngo Weinhold 	if (fTeam == NULL)
800*24df6592SIngo Weinhold 		return;
801*24df6592SIngo Weinhold 
802*24df6592SIngo Weinhold 	// deactivate
803*24df6592SIngo Weinhold 	fTeam->UserTimerDeactivated(this);
804*24df6592SIngo Weinhold 	fTeam->ReleaseReference();
805*24df6592SIngo Weinhold 	fTeam = NULL;
806*24df6592SIngo Weinhold }
807*24df6592SIngo Weinhold 
808*24df6592SIngo Weinhold 
809*24df6592SIngo Weinhold /*!	Checks whether the timer is up, firing an event, if so.
810*24df6592SIngo Weinhold 
811*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
812*24df6592SIngo Weinhold */
813*24df6592SIngo Weinhold void
814*24df6592SIngo Weinhold TeamUserTimeUserTimer::Check()
815*24df6592SIngo Weinhold {
816*24df6592SIngo Weinhold 	if (fTeam == NULL)
817*24df6592SIngo Weinhold 		return;
818*24df6592SIngo Weinhold 
819*24df6592SIngo Weinhold 	// check whether we need to fire the event yet
820*24df6592SIngo Weinhold 	bigtime_t now = fTeam->UserCPUTime();
821*24df6592SIngo Weinhold 	if (now < fNextTime)
822*24df6592SIngo Weinhold 		return;
823*24df6592SIngo Weinhold 
824*24df6592SIngo Weinhold 	HandleTimer();
825*24df6592SIngo Weinhold 
826*24df6592SIngo Weinhold 	// If the timer is not periodic, it is no longer active. Otherwise compute
827*24df6592SIngo Weinhold 	// the event time.
828*24df6592SIngo Weinhold 	if (fInterval == 0) {
829*24df6592SIngo Weinhold 		fTeam->UserTimerDeactivated(this);
830*24df6592SIngo Weinhold 		fTeam->ReleaseReference();
831*24df6592SIngo Weinhold 		fTeam = NULL;
832*24df6592SIngo Weinhold 		return;
833*24df6592SIngo Weinhold 	}
834*24df6592SIngo Weinhold 
835*24df6592SIngo Weinhold 	// First validate fNextTime, then increment it, so that fNextTime is > now
836*24df6592SIngo Weinhold 	// (CheckPeriodicOverrun() only makes it > now - fInterval).
837*24df6592SIngo Weinhold 	CheckPeriodicOverrun(now);
838*24df6592SIngo Weinhold 	fNextTime += fInterval;
839*24df6592SIngo Weinhold 	fScheduled = true;
840*24df6592SIngo Weinhold }
841*24df6592SIngo Weinhold 
842*24df6592SIngo Weinhold 
843*24df6592SIngo Weinhold // #pragma mark - ThreadTimeUserTimer
844*24df6592SIngo Weinhold 
845*24df6592SIngo Weinhold 
846*24df6592SIngo Weinhold ThreadTimeUserTimer::ThreadTimeUserTimer(thread_id threadID)
847*24df6592SIngo Weinhold 	:
848*24df6592SIngo Weinhold 	fThreadID(threadID),
849*24df6592SIngo Weinhold 	fThread(NULL)
850*24df6592SIngo Weinhold {
851*24df6592SIngo Weinhold }
852*24df6592SIngo Weinhold 
853*24df6592SIngo Weinhold 
854*24df6592SIngo Weinhold ThreadTimeUserTimer::~ThreadTimeUserTimer()
855*24df6592SIngo Weinhold {
856*24df6592SIngo Weinhold 	ASSERT(fThread == NULL);
857*24df6592SIngo Weinhold }
858*24df6592SIngo Weinhold 
859*24df6592SIngo Weinhold 
860*24df6592SIngo Weinhold void
861*24df6592SIngo Weinhold ThreadTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
862*24df6592SIngo Weinhold 	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
863*24df6592SIngo Weinhold {
864*24df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
865*24df6592SIngo Weinhold 
866*24df6592SIngo Weinhold 	// get the current time, but only if needed
867*24df6592SIngo Weinhold 	bool nowValid = fThread != NULL;
868*24df6592SIngo Weinhold 	bigtime_t now = nowValid ? fThread->CPUTime(false) : 0;
869*24df6592SIngo Weinhold 
870*24df6592SIngo Weinhold 	// Cancel the old timer, if still scheduled, and get the previous values.
871*24df6592SIngo Weinhold 	if (fThread != NULL) {
872*24df6592SIngo Weinhold 		if (fScheduled) {
873*24df6592SIngo Weinhold 			cancel_timer(&fTimer);
874*24df6592SIngo Weinhold 			fScheduled = false;
875*24df6592SIngo Weinhold 		}
876*24df6592SIngo Weinhold 
877*24df6592SIngo Weinhold 		_oldRemainingTime = fNextTime - now;
878*24df6592SIngo Weinhold 		_oldInterval = fInterval;
879*24df6592SIngo Weinhold 
880*24df6592SIngo Weinhold 		fThread->UserTimerDeactivated(this);
881*24df6592SIngo Weinhold 		fThread->ReleaseReference();
882*24df6592SIngo Weinhold 		fThread = NULL;
883*24df6592SIngo Weinhold 	} else {
884*24df6592SIngo Weinhold 		_oldRemainingTime = B_INFINITE_TIMEOUT;
885*24df6592SIngo Weinhold 		_oldInterval = 0;
886*24df6592SIngo Weinhold 	}
887*24df6592SIngo Weinhold 
888*24df6592SIngo Weinhold 	// schedule the new timer
889*24df6592SIngo Weinhold 	fNextTime = nextTime;
890*24df6592SIngo Weinhold 	fInterval = interval;
891*24df6592SIngo Weinhold 	fOverrunCount = 0;
892*24df6592SIngo Weinhold 
893*24df6592SIngo Weinhold 	if (fNextTime == B_INFINITE_TIMEOUT)
894*24df6592SIngo Weinhold 		return;
895*24df6592SIngo Weinhold 
896*24df6592SIngo Weinhold 	// Get the thread. If it doesn't exist anymore, just don't schedule the
897*24df6592SIngo Weinhold 	// timer anymore.
898*24df6592SIngo Weinhold 	fThread = Thread::Get(fThreadID);
899*24df6592SIngo Weinhold 	if (fThread == NULL)
900*24df6592SIngo Weinhold 		return;
901*24df6592SIngo Weinhold 
902*24df6592SIngo Weinhold 	fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
903*24df6592SIngo Weinhold 
904*24df6592SIngo Weinhold 	// convert relative to absolute timeouts
905*24df6592SIngo Weinhold 	if (!fAbsolute) {
906*24df6592SIngo Weinhold 		if (!nowValid)
907*24df6592SIngo Weinhold 			now = fThread->CPUTime(false);
908*24df6592SIngo Weinhold 		fNextTime += now;
909*24df6592SIngo Weinhold 	}
910*24df6592SIngo Weinhold 
911*24df6592SIngo Weinhold 	fThread->UserTimerActivated(this);
912*24df6592SIngo Weinhold 
913*24df6592SIngo Weinhold 	// If the thread is currently running, also schedule a kernel timer.
914*24df6592SIngo Weinhold 	if (fThread->cpu != NULL)
915*24df6592SIngo Weinhold 		Start();
916*24df6592SIngo Weinhold }
917*24df6592SIngo Weinhold 
918*24df6592SIngo Weinhold 
919*24df6592SIngo Weinhold void
920*24df6592SIngo Weinhold ThreadTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
921*24df6592SIngo Weinhold 	uint32& _overrunCount)
922*24df6592SIngo Weinhold {
923*24df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
924*24df6592SIngo Weinhold 
925*24df6592SIngo Weinhold 	if (fThread != NULL) {
926*24df6592SIngo Weinhold 		_remainingTime = fNextTime - fThread->CPUTime(false);
927*24df6592SIngo Weinhold 		_interval = fInterval;
928*24df6592SIngo Weinhold 	} else {
929*24df6592SIngo Weinhold 		_remainingTime = B_INFINITE_TIMEOUT;
930*24df6592SIngo Weinhold 		_interval = 0;
931*24df6592SIngo Weinhold 	}
932*24df6592SIngo Weinhold 
933*24df6592SIngo Weinhold 	_overrunCount = fOverrunCount;
934*24df6592SIngo Weinhold }
935*24df6592SIngo Weinhold 
936*24df6592SIngo Weinhold 
937*24df6592SIngo Weinhold /*!	Deactivates the timer, if it is activated.
938*24df6592SIngo Weinhold 
939*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
940*24df6592SIngo Weinhold */
941*24df6592SIngo Weinhold void
942*24df6592SIngo Weinhold ThreadTimeUserTimer::Deactivate()
943*24df6592SIngo Weinhold {
944*24df6592SIngo Weinhold 	if (fThread == NULL)
945*24df6592SIngo Weinhold 		return;
946*24df6592SIngo Weinhold 
947*24df6592SIngo Weinhold 	// unschedule, if scheduled
948*24df6592SIngo Weinhold 	if (fScheduled) {
949*24df6592SIngo Weinhold 		cancel_timer(&fTimer);
950*24df6592SIngo Weinhold 		fScheduled = false;
951*24df6592SIngo Weinhold 	}
952*24df6592SIngo Weinhold 
953*24df6592SIngo Weinhold 	// deactivate
954*24df6592SIngo Weinhold 	fThread->UserTimerDeactivated(this);
955*24df6592SIngo Weinhold 	fThread->ReleaseReference();
956*24df6592SIngo Weinhold 	fThread = NULL;
957*24df6592SIngo Weinhold }
958*24df6592SIngo Weinhold 
959*24df6592SIngo Weinhold 
960*24df6592SIngo Weinhold /*!	Starts the timer, if it is active.
961*24df6592SIngo Weinhold 
962*24df6592SIngo Weinhold 	Called when the thread whose CPU time is referred to by the timer is
963*24df6592SIngo Weinhold 	scheduled, or, when the timer was just set and the thread is already
964*24df6592SIngo Weinhold 	running. Schedules a kernel timer for the remaining time.
965*24df6592SIngo Weinhold 
966*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
967*24df6592SIngo Weinhold */
968*24df6592SIngo Weinhold void
969*24df6592SIngo Weinhold ThreadTimeUserTimer::Start()
970*24df6592SIngo Weinhold {
971*24df6592SIngo Weinhold 	if (fThread == NULL)
972*24df6592SIngo Weinhold 		return;
973*24df6592SIngo Weinhold 
974*24df6592SIngo Weinhold 	ASSERT(!fScheduled);
975*24df6592SIngo Weinhold 
976*24df6592SIngo Weinhold 	// add the kernel timer
977*24df6592SIngo Weinhold 	bigtime_t now = fThread->CPUTime(false);
978*24df6592SIngo Weinhold 
979*24df6592SIngo Weinhold 	// If periodic, check whether the start time is too far in the past.
980*24df6592SIngo Weinhold 	if (fInterval > 0)
981*24df6592SIngo Weinhold 		CheckPeriodicOverrun(now);
982*24df6592SIngo Weinhold 
983*24df6592SIngo Weinhold 	if (fNextTime > now) {
984*24df6592SIngo Weinhold 		fTimer.schedule_time = system_time() + fNextTime - now;
985*24df6592SIngo Weinhold 		// check for overflow
986*24df6592SIngo Weinhold 		if (fTimer.schedule_time < 0)
987*24df6592SIngo Weinhold 			fTimer.schedule_time = B_INFINITE_TIMEOUT;
988*24df6592SIngo Weinhold 	} else
989*24df6592SIngo Weinhold 		fTimer.schedule_time = 0;
990*24df6592SIngo Weinhold 	fTimer.period = 0;
991*24df6592SIngo Weinhold 
992*24df6592SIngo Weinhold 	uint32 flags = B_ONE_SHOT_ABSOLUTE_TIMER
993*24df6592SIngo Weinhold 		| B_TIMER_USE_TIMER_STRUCT_TIMES | B_TIMER_ACQUIRE_SCHEDULER_LOCK;
994*24df6592SIngo Weinhold 		// We use B_TIMER_ACQUIRE_SCHEDULER_LOCK to avoid race conditions
995*24df6592SIngo Weinhold 		// between setting/canceling the timer and the event handler.
996*24df6592SIngo Weinhold 
997*24df6592SIngo Weinhold 	add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, flags);
998*24df6592SIngo Weinhold 
999*24df6592SIngo Weinhold 	fScheduled = true;
1000*24df6592SIngo Weinhold }
1001*24df6592SIngo Weinhold 
1002*24df6592SIngo Weinhold 
1003*24df6592SIngo Weinhold /*!	Stops the timer, if it is active.
1004*24df6592SIngo Weinhold 
1005*24df6592SIngo Weinhold 	Called when the thread whose CPU time is referred to by the timer is
1006*24df6592SIngo Weinhold 	unscheduled, or, when the timer is canceled.
1007*24df6592SIngo Weinhold 
1008*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
1009*24df6592SIngo Weinhold */
1010*24df6592SIngo Weinhold void
1011*24df6592SIngo Weinhold ThreadTimeUserTimer::Stop()
1012*24df6592SIngo Weinhold {
1013*24df6592SIngo Weinhold 	if (fThread == NULL)
1014*24df6592SIngo Weinhold 		return;
1015*24df6592SIngo Weinhold 
1016*24df6592SIngo Weinhold 	ASSERT(fScheduled);
1017*24df6592SIngo Weinhold 
1018*24df6592SIngo Weinhold 	// cancel the kernel timer
1019*24df6592SIngo Weinhold 	cancel_timer(&fTimer);
1020*24df6592SIngo Weinhold 	fScheduled = false;
1021*24df6592SIngo Weinhold 
1022*24df6592SIngo Weinhold 	// TODO: To avoid odd race conditions, we should check the current time of
1023*24df6592SIngo Weinhold 	// the thread (ignoring the time since last_time) and manually fire the
1024*24df6592SIngo Weinhold 	// user event, if necessary.
1025*24df6592SIngo Weinhold }
1026*24df6592SIngo Weinhold 
1027*24df6592SIngo Weinhold 
1028*24df6592SIngo Weinhold /*!	Called when the team's CPU time clock which this timer refers to has been
1029*24df6592SIngo Weinhold 	set.
1030*24df6592SIngo Weinhold 
1031*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
1032*24df6592SIngo Weinhold 
1033*24df6592SIngo Weinhold 	\param changedBy The value by which the clock has changed.
1034*24df6592SIngo Weinhold */
1035*24df6592SIngo Weinhold void
1036*24df6592SIngo Weinhold ThreadTimeUserTimer::TimeWarped(bigtime_t changedBy)
1037*24df6592SIngo Weinhold {
1038*24df6592SIngo Weinhold 	if (fThread == NULL || changedBy == 0)
1039*24df6592SIngo Weinhold 		return;
1040*24df6592SIngo Weinhold 
1041*24df6592SIngo Weinhold 	// If this is a relative timer, adjust fNextTime by the value the clock has
1042*24df6592SIngo Weinhold 	// changed.
1043*24df6592SIngo Weinhold 	if (!fAbsolute)
1044*24df6592SIngo Weinhold 		fNextTime += changedBy;
1045*24df6592SIngo Weinhold 
1046*24df6592SIngo Weinhold 	// reschedule the kernel timer
1047*24df6592SIngo Weinhold 	if (fScheduled) {
1048*24df6592SIngo Weinhold 		Stop();
1049*24df6592SIngo Weinhold 		Start();
1050*24df6592SIngo Weinhold 	}
1051*24df6592SIngo Weinhold }
1052*24df6592SIngo Weinhold 
1053*24df6592SIngo Weinhold 
1054*24df6592SIngo Weinhold void
1055*24df6592SIngo Weinhold ThreadTimeUserTimer::HandleTimer()
1056*24df6592SIngo Weinhold {
1057*24df6592SIngo Weinhold 	UserTimer::HandleTimer();
1058*24df6592SIngo Weinhold 
1059*24df6592SIngo Weinhold 	if (fThread != NULL) {
1060*24df6592SIngo Weinhold 		// If the timer is periodic, reschedule the kernel timer. Otherwise it
1061*24df6592SIngo Weinhold 		// is no longer active.
1062*24df6592SIngo Weinhold 		if (fInterval > 0) {
1063*24df6592SIngo Weinhold 			UpdatePeriodicStartTime();
1064*24df6592SIngo Weinhold 			Start();
1065*24df6592SIngo Weinhold 		} else {
1066*24df6592SIngo Weinhold 			fThread->UserTimerDeactivated(this);
1067*24df6592SIngo Weinhold 			fThread->ReleaseReference();
1068*24df6592SIngo Weinhold 			fThread = NULL;
1069*24df6592SIngo Weinhold 		}
1070*24df6592SIngo Weinhold 	}
1071*24df6592SIngo Weinhold }
1072*24df6592SIngo Weinhold 
1073*24df6592SIngo Weinhold 
1074*24df6592SIngo Weinhold // #pragma mark - UserTimerList
1075*24df6592SIngo Weinhold 
1076*24df6592SIngo Weinhold 
1077*24df6592SIngo Weinhold UserTimerList::UserTimerList()
1078*24df6592SIngo Weinhold {
1079*24df6592SIngo Weinhold }
1080*24df6592SIngo Weinhold 
1081*24df6592SIngo Weinhold 
1082*24df6592SIngo Weinhold UserTimerList::~UserTimerList()
1083*24df6592SIngo Weinhold {
1084*24df6592SIngo Weinhold 	ASSERT(fTimers.IsEmpty());
1085*24df6592SIngo Weinhold }
1086*24df6592SIngo Weinhold 
1087*24df6592SIngo Weinhold 
1088*24df6592SIngo Weinhold /*!	Returns the user timer with the given ID.
1089*24df6592SIngo Weinhold 
1090*24df6592SIngo Weinhold 	\param id The timer's ID
1091*24df6592SIngo Weinhold 	\return The user timer with the given ID or \c NULL, if there is no such
1092*24df6592SIngo Weinhold 		timer.
1093*24df6592SIngo Weinhold */
1094*24df6592SIngo Weinhold UserTimer*
1095*24df6592SIngo Weinhold UserTimerList::TimerFor(int32 id) const
1096*24df6592SIngo Weinhold {
1097*24df6592SIngo Weinhold 	// TODO: Use a more efficient data structure. E.g. a sorted array.
1098*24df6592SIngo Weinhold 	for (TimerList::ConstIterator it = fTimers.GetIterator();
1099*24df6592SIngo Weinhold 			UserTimer* timer = it.Next();) {
1100*24df6592SIngo Weinhold 		if (timer->ID() == id)
1101*24df6592SIngo Weinhold 			return timer;
1102*24df6592SIngo Weinhold 	}
1103*24df6592SIngo Weinhold 
1104*24df6592SIngo Weinhold 	return NULL;
1105*24df6592SIngo Weinhold }
1106*24df6592SIngo Weinhold 
1107*24df6592SIngo Weinhold 
1108*24df6592SIngo Weinhold /*!	Adds the given user timer and assigns it an ID.
1109*24df6592SIngo Weinhold 
1110*24df6592SIngo Weinhold 	\param timer The timer to be added.
1111*24df6592SIngo Weinhold */
1112*24df6592SIngo Weinhold void
1113*24df6592SIngo Weinhold UserTimerList::AddTimer(UserTimer* timer)
1114*24df6592SIngo Weinhold {
1115*24df6592SIngo Weinhold 	int32 id = timer->ID();
1116*24df6592SIngo Weinhold 	if (id < 0) {
1117*24df6592SIngo Weinhold 		// user-defined timer -- find an usused ID
1118*24df6592SIngo Weinhold 		id = USER_TIMER_FIRST_USER_DEFINED_ID;
1119*24df6592SIngo Weinhold 		UserTimer* insertAfter = NULL;
1120*24df6592SIngo Weinhold 		for (TimerList::Iterator it = fTimers.GetIterator();
1121*24df6592SIngo Weinhold 				UserTimer* other = it.Next();) {
1122*24df6592SIngo Weinhold 			if (other->ID() > id)
1123*24df6592SIngo Weinhold 				break;
1124*24df6592SIngo Weinhold 			if (other->ID() == id)
1125*24df6592SIngo Weinhold 				id++;
1126*24df6592SIngo Weinhold 			insertAfter = other;
1127*24df6592SIngo Weinhold 		}
1128*24df6592SIngo Weinhold 
1129*24df6592SIngo Weinhold 		// insert the timer
1130*24df6592SIngo Weinhold 		timer->SetID(id);
1131*24df6592SIngo Weinhold 		fTimers.InsertAfter(insertAfter, timer);
1132*24df6592SIngo Weinhold 	} else {
1133*24df6592SIngo Weinhold 		// default timer -- find the insertion point
1134*24df6592SIngo Weinhold 		UserTimer* insertAfter = NULL;
1135*24df6592SIngo Weinhold 		for (TimerList::Iterator it = fTimers.GetIterator();
1136*24df6592SIngo Weinhold 				UserTimer* other = it.Next();) {
1137*24df6592SIngo Weinhold 			if (other->ID() > id)
1138*24df6592SIngo Weinhold 				break;
1139*24df6592SIngo Weinhold 			if (other->ID() == id) {
1140*24df6592SIngo Weinhold 				panic("UserTimerList::AddTimer(): timer with ID %" B_PRId32
1141*24df6592SIngo Weinhold 					" already exists!", id);
1142*24df6592SIngo Weinhold 			}
1143*24df6592SIngo Weinhold 			insertAfter = other;
1144*24df6592SIngo Weinhold 		}
1145*24df6592SIngo Weinhold 
1146*24df6592SIngo Weinhold 		// insert the timer
1147*24df6592SIngo Weinhold 		fTimers.InsertAfter(insertAfter, timer);
1148*24df6592SIngo Weinhold 	}
1149*24df6592SIngo Weinhold }
1150*24df6592SIngo Weinhold 
1151*24df6592SIngo Weinhold 
1152*24df6592SIngo Weinhold /*!	Deletes all (or all user-defined) user timers.
1153*24df6592SIngo Weinhold 
1154*24df6592SIngo Weinhold 	\param userDefinedOnly If \c true, only the user-defined timers are deleted,
1155*24df6592SIngo Weinhold 		otherwise all timers are deleted.
1156*24df6592SIngo Weinhold 	\return The number of user-defined timers that were removed and deleted.
1157*24df6592SIngo Weinhold */
1158*24df6592SIngo Weinhold int32
1159*24df6592SIngo Weinhold UserTimerList::DeleteTimers(bool userDefinedOnly)
1160*24df6592SIngo Weinhold {
1161*24df6592SIngo Weinhold 	int32 userDefinedCount = 0;
1162*24df6592SIngo Weinhold 
1163*24df6592SIngo Weinhold 	for (TimerList::Iterator it = fTimers.GetIterator();
1164*24df6592SIngo Weinhold 			UserTimer* timer = it.Next();) {
1165*24df6592SIngo Weinhold 		if (timer->ID() < USER_TIMER_FIRST_USER_DEFINED_ID) {
1166*24df6592SIngo Weinhold 			if (userDefinedOnly)
1167*24df6592SIngo Weinhold 				continue;
1168*24df6592SIngo Weinhold 		} else
1169*24df6592SIngo Weinhold 			userDefinedCount++;
1170*24df6592SIngo Weinhold 
1171*24df6592SIngo Weinhold 		// remove, cancel, and delete the timer
1172*24df6592SIngo Weinhold 		it.Remove();
1173*24df6592SIngo Weinhold 		timer->Cancel();
1174*24df6592SIngo Weinhold 		delete timer;
1175*24df6592SIngo Weinhold 	}
1176*24df6592SIngo Weinhold 
1177*24df6592SIngo Weinhold 	return userDefinedCount;
1178*24df6592SIngo Weinhold }
1179*24df6592SIngo Weinhold 
1180*24df6592SIngo Weinhold 
1181*24df6592SIngo Weinhold // #pragma mark - private
1182*24df6592SIngo Weinhold 
1183*24df6592SIngo Weinhold 
1184*24df6592SIngo Weinhold static int32
1185*24df6592SIngo Weinhold create_timer(clockid_t clockID, int32 timerID, Team* team, Thread* thread,
1186*24df6592SIngo Weinhold 	uint32 flags, const struct sigevent& event,
1187*24df6592SIngo Weinhold 	ThreadCreationAttributes* threadAttributes, bool isDefaultEvent)
1188*24df6592SIngo Weinhold {
1189*24df6592SIngo Weinhold 	// create the timer object
1190*24df6592SIngo Weinhold 	UserTimer* timer;
1191*24df6592SIngo Weinhold 	switch (clockID) {
1192*24df6592SIngo Weinhold 		case CLOCK_MONOTONIC:
1193*24df6592SIngo Weinhold 			timer = new(std::nothrow) SystemTimeUserTimer;
1194*24df6592SIngo Weinhold 			break;
1195*24df6592SIngo Weinhold 
1196*24df6592SIngo Weinhold 		case CLOCK_REALTIME:
1197*24df6592SIngo Weinhold 			timer = new(std::nothrow) RealTimeUserTimer;
1198*24df6592SIngo Weinhold 			break;
1199*24df6592SIngo Weinhold 
1200*24df6592SIngo Weinhold 		case CLOCK_THREAD_CPUTIME_ID:
1201*24df6592SIngo Weinhold 			timer = new(std::nothrow) ThreadTimeUserTimer(
1202*24df6592SIngo Weinhold 				thread_get_current_thread()->id);
1203*24df6592SIngo Weinhold 			break;
1204*24df6592SIngo Weinhold 
1205*24df6592SIngo Weinhold 		case CLOCK_PROCESS_CPUTIME_ID:
1206*24df6592SIngo Weinhold 			if (team == NULL)
1207*24df6592SIngo Weinhold 				return B_BAD_VALUE;
1208*24df6592SIngo Weinhold 			timer = new(std::nothrow) TeamTimeUserTimer(team->id);
1209*24df6592SIngo Weinhold 			break;
1210*24df6592SIngo Weinhold 
1211*24df6592SIngo Weinhold 		case CLOCK_PROCESS_USER_CPUTIME_ID:
1212*24df6592SIngo Weinhold 			if (team == NULL)
1213*24df6592SIngo Weinhold 				return B_BAD_VALUE;
1214*24df6592SIngo Weinhold 			timer = new(std::nothrow) TeamUserTimeUserTimer(team->id);
1215*24df6592SIngo Weinhold 			break;
1216*24df6592SIngo Weinhold 
1217*24df6592SIngo Weinhold 		default:
1218*24df6592SIngo Weinhold 		{
1219*24df6592SIngo Weinhold 			// The clock ID is a ID of the team whose CPU time the clock refers
1220*24df6592SIngo Weinhold 			// to. Check whether the team exists and we have permission to
1221*24df6592SIngo Weinhold 			// access its clock.
1222*24df6592SIngo Weinhold 			if (clockID <= 0)
1223*24df6592SIngo Weinhold 				return B_BAD_VALUE;
1224*24df6592SIngo Weinhold 			if (clockID == team_get_kernel_team_id())
1225*24df6592SIngo Weinhold 				return B_NOT_ALLOWED;
1226*24df6592SIngo Weinhold 
1227*24df6592SIngo Weinhold 			Team* timedTeam = Team::GetAndLock(clockID);
1228*24df6592SIngo Weinhold 			if (timedTeam == NULL)
1229*24df6592SIngo Weinhold 				return B_BAD_VALUE;
1230*24df6592SIngo Weinhold 
1231*24df6592SIngo Weinhold 			uid_t uid = geteuid();
1232*24df6592SIngo Weinhold 			uid_t teamUID = timedTeam->effective_uid;
1233*24df6592SIngo Weinhold 
1234*24df6592SIngo Weinhold 			timedTeam->UnlockAndReleaseReference();
1235*24df6592SIngo Weinhold 
1236*24df6592SIngo Weinhold 			if (uid != 0 && uid != teamUID)
1237*24df6592SIngo Weinhold 				return B_NOT_ALLOWED;
1238*24df6592SIngo Weinhold 
1239*24df6592SIngo Weinhold 			timer = new(std::nothrow) TeamTimeUserTimer(clockID);
1240*24df6592SIngo Weinhold 			break;
1241*24df6592SIngo Weinhold 		}
1242*24df6592SIngo Weinhold 	}
1243*24df6592SIngo Weinhold 
1244*24df6592SIngo Weinhold 	if (timer == NULL)
1245*24df6592SIngo Weinhold 		return B_NO_MEMORY;
1246*24df6592SIngo Weinhold 	ObjectDeleter<UserTimer> timerDeleter(timer);
1247*24df6592SIngo Weinhold 
1248*24df6592SIngo Weinhold 	if (timerID >= 0)
1249*24df6592SIngo Weinhold 		timer->SetID(timerID);
1250*24df6592SIngo Weinhold 
1251*24df6592SIngo Weinhold 	SignalEvent* signalEvent = NULL;
1252*24df6592SIngo Weinhold 
1253*24df6592SIngo Weinhold 	switch (event.sigev_notify) {
1254*24df6592SIngo Weinhold 		case SIGEV_NONE:
1255*24df6592SIngo Weinhold 			// the timer's event remains NULL
1256*24df6592SIngo Weinhold 			break;
1257*24df6592SIngo Weinhold 
1258*24df6592SIngo Weinhold 		case SIGEV_SIGNAL:
1259*24df6592SIngo Weinhold 		{
1260*24df6592SIngo Weinhold 			if (event.sigev_signo <= 0 || event.sigev_signo > MAX_SIGNAL_NUMBER)
1261*24df6592SIngo Weinhold 				return B_BAD_VALUE;
1262*24df6592SIngo Weinhold 
1263*24df6592SIngo Weinhold 			if (thread != NULL && (flags & USER_TIMER_SIGNAL_THREAD) != 0) {
1264*24df6592SIngo Weinhold 				// The signal shall be sent to the thread.
1265*24df6592SIngo Weinhold 				signalEvent = ThreadSignalEvent::Create(thread,
1266*24df6592SIngo Weinhold 					event.sigev_signo, SI_TIMER, 0, team->id);
1267*24df6592SIngo Weinhold 			} else {
1268*24df6592SIngo Weinhold 				// The signal shall be sent to the team.
1269*24df6592SIngo Weinhold 				signalEvent = TeamSignalEvent::Create(team, event.sigev_signo,
1270*24df6592SIngo Weinhold 					SI_TIMER, 0);
1271*24df6592SIngo Weinhold 			}
1272*24df6592SIngo Weinhold 
1273*24df6592SIngo Weinhold 			if (signalEvent == NULL)
1274*24df6592SIngo Weinhold 				return B_NO_MEMORY;
1275*24df6592SIngo Weinhold 
1276*24df6592SIngo Weinhold 			timer->SetEvent(signalEvent);
1277*24df6592SIngo Weinhold 			break;
1278*24df6592SIngo Weinhold 		}
1279*24df6592SIngo Weinhold 
1280*24df6592SIngo Weinhold 		case SIGEV_THREAD:
1281*24df6592SIngo Weinhold 		{
1282*24df6592SIngo Weinhold 			if (threadAttributes == NULL)
1283*24df6592SIngo Weinhold 				return B_BAD_VALUE;
1284*24df6592SIngo Weinhold 
1285*24df6592SIngo Weinhold 			CreateThreadEvent* event
1286*24df6592SIngo Weinhold 				= CreateThreadEvent::Create(*threadAttributes);
1287*24df6592SIngo Weinhold 			if (event == NULL)
1288*24df6592SIngo Weinhold 				return B_NO_MEMORY;
1289*24df6592SIngo Weinhold 
1290*24df6592SIngo Weinhold 			timer->SetEvent(event);
1291*24df6592SIngo Weinhold 			break;
1292*24df6592SIngo Weinhold 		}
1293*24df6592SIngo Weinhold 
1294*24df6592SIngo Weinhold 		default:
1295*24df6592SIngo Weinhold 			return B_BAD_VALUE;
1296*24df6592SIngo Weinhold 	}
1297*24df6592SIngo Weinhold 
1298*24df6592SIngo Weinhold 	// add it to the team/thread
1299*24df6592SIngo Weinhold 	TimerLocker timerLocker;
1300*24df6592SIngo Weinhold 	timerLocker.Lock(team, thread);
1301*24df6592SIngo Weinhold 
1302*24df6592SIngo Weinhold 	status_t error = thread != NULL
1303*24df6592SIngo Weinhold 		? thread->AddUserTimer(timer) : team->AddUserTimer(timer);
1304*24df6592SIngo Weinhold 	if (error != B_OK)
1305*24df6592SIngo Weinhold 		return error;
1306*24df6592SIngo Weinhold 
1307*24df6592SIngo Weinhold 	// set a signal event's user value
1308*24df6592SIngo Weinhold 	if (signalEvent != NULL) {
1309*24df6592SIngo Weinhold 		// If no sigevent structure was given, use the timer ID.
1310*24df6592SIngo Weinhold 		union sigval signalValue = event.sigev_value;
1311*24df6592SIngo Weinhold 		if (isDefaultEvent)
1312*24df6592SIngo Weinhold 			signalValue.sival_int = timer->ID();
1313*24df6592SIngo Weinhold 
1314*24df6592SIngo Weinhold 		signalEvent->SetUserValue(signalValue);
1315*24df6592SIngo Weinhold 	}
1316*24df6592SIngo Weinhold 
1317*24df6592SIngo Weinhold 	return timerDeleter.Detach()->ID();
1318*24df6592SIngo Weinhold }
1319*24df6592SIngo Weinhold 
1320*24df6592SIngo Weinhold 
1321*24df6592SIngo Weinhold /*!	Called when the CPU time clock of the given thread has been set.
1322*24df6592SIngo Weinhold 
1323*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
1324*24df6592SIngo Weinhold 
1325*24df6592SIngo Weinhold 	\param thread The thread whose CPU time clock has been set.
1326*24df6592SIngo Weinhold 	\param changedBy The value by which the CPU time clock has changed
1327*24df6592SIngo Weinhold 		(new = old + changedBy).
1328*24df6592SIngo Weinhold */
1329*24df6592SIngo Weinhold static void
1330*24df6592SIngo Weinhold thread_clock_changed(Thread* thread, bigtime_t changedBy)
1331*24df6592SIngo Weinhold {
1332*24df6592SIngo Weinhold 	for (ThreadTimeUserTimerList::ConstIterator it
1333*24df6592SIngo Weinhold 				= thread->CPUTimeUserTimerIterator();
1334*24df6592SIngo Weinhold 			ThreadTimeUserTimer* timer = it.Next();) {
1335*24df6592SIngo Weinhold 		timer->TimeWarped(changedBy);
1336*24df6592SIngo Weinhold 	}
1337*24df6592SIngo Weinhold }
1338*24df6592SIngo Weinhold 
1339*24df6592SIngo Weinhold 
1340*24df6592SIngo Weinhold /*!	Called when the CPU time clock of the given team has been set.
1341*24df6592SIngo Weinhold 
1342*24df6592SIngo Weinhold 	The caller must hold the scheduler lock.
1343*24df6592SIngo Weinhold 
1344*24df6592SIngo Weinhold 	\param team The team whose CPU time clock has been set.
1345*24df6592SIngo Weinhold 	\param changedBy The value by which the CPU time clock has changed
1346*24df6592SIngo Weinhold 		(new = old + changedBy).
1347*24df6592SIngo Weinhold */
1348*24df6592SIngo Weinhold static void
1349*24df6592SIngo Weinhold team_clock_changed(Team* team, bigtime_t changedBy)
1350*24df6592SIngo Weinhold {
1351*24df6592SIngo Weinhold 	for (TeamTimeUserTimerList::ConstIterator it
1352*24df6592SIngo Weinhold 				= team->CPUTimeUserTimerIterator();
1353*24df6592SIngo Weinhold 			TeamTimeUserTimer* timer = it.Next();) {
1354*24df6592SIngo Weinhold 		timer->TimeWarped(changedBy);
1355*24df6592SIngo Weinhold 	}
1356*24df6592SIngo Weinhold }
1357*24df6592SIngo Weinhold 
1358*24df6592SIngo Weinhold 
1359*24df6592SIngo Weinhold // #pragma mark - kernel private
1360*24df6592SIngo Weinhold 
1361*24df6592SIngo Weinhold 
1362*24df6592SIngo Weinhold /*!	Creates the pre-defined user timers for the given thread.
1363*24df6592SIngo Weinhold 	The thread may not have been added to its team yet, hence the team must be
1364*24df6592SIngo Weinhold 	passed
1365*24df6592SIngo Weinhold 
1366*24df6592SIngo Weinhold 	\param team The thread's (future) team.
1367*24df6592SIngo Weinhold 	\param thread The thread whose pre-defined timers shall be created.
1368*24df6592SIngo Weinhold 	\return \c B_OK, when everything when fine, another error code otherwise.
1369*24df6592SIngo Weinhold */
1370*24df6592SIngo Weinhold status_t
1371*24df6592SIngo Weinhold user_timer_create_thread_timers(Team* team, Thread* thread)
1372*24df6592SIngo Weinhold {
1373*24df6592SIngo Weinhold 	// create a real time user timer
1374*24df6592SIngo Weinhold 	struct sigevent event;
1375*24df6592SIngo Weinhold 	event.sigev_notify = SIGEV_SIGNAL;
1376*24df6592SIngo Weinhold 	event.sigev_signo = SIGALRM;
1377*24df6592SIngo Weinhold 
1378*24df6592SIngo Weinhold 	int32 timerID = create_timer(CLOCK_MONOTONIC, USER_TIMER_REAL_TIME_ID,
1379*24df6592SIngo Weinhold 		team, thread, USER_TIMER_SIGNAL_THREAD, event, NULL, true);
1380*24df6592SIngo Weinhold 	if (timerID < 0)
1381*24df6592SIngo Weinhold 		return timerID;
1382*24df6592SIngo Weinhold 
1383*24df6592SIngo Weinhold 	return B_OK;
1384*24df6592SIngo Weinhold }
1385*24df6592SIngo Weinhold 
1386*24df6592SIngo Weinhold 
1387*24df6592SIngo Weinhold /*!	Creates the pre-defined user timers for the given team.
1388*24df6592SIngo Weinhold 
1389*24df6592SIngo Weinhold 	\param team The team whose pre-defined timers shall be created.
1390*24df6592SIngo Weinhold 	\return \c B_OK, when everything when fine, another error code otherwise.
1391*24df6592SIngo Weinhold */
1392*24df6592SIngo Weinhold status_t
1393*24df6592SIngo Weinhold user_timer_create_team_timers(Team* team)
1394*24df6592SIngo Weinhold {
1395*24df6592SIngo Weinhold 	// create a real time user timer
1396*24df6592SIngo Weinhold 	struct sigevent event;
1397*24df6592SIngo Weinhold 	event.sigev_notify = SIGEV_SIGNAL;
1398*24df6592SIngo Weinhold 	event.sigev_signo = SIGALRM;
1399*24df6592SIngo Weinhold 
1400*24df6592SIngo Weinhold 	int32 timerID = create_timer(CLOCK_MONOTONIC, USER_TIMER_REAL_TIME_ID,
1401*24df6592SIngo Weinhold 		team, NULL, 0, event, NULL, true);
1402*24df6592SIngo Weinhold 	if (timerID < 0)
1403*24df6592SIngo Weinhold 		return timerID;
1404*24df6592SIngo Weinhold 
1405*24df6592SIngo Weinhold 	// create a total CPU time user timer
1406*24df6592SIngo Weinhold 	event.sigev_notify = SIGEV_SIGNAL;
1407*24df6592SIngo Weinhold 	event.sigev_signo = SIGPROF;
1408*24df6592SIngo Weinhold 
1409*24df6592SIngo Weinhold 	timerID = create_timer(CLOCK_PROCESS_CPUTIME_ID,
1410*24df6592SIngo Weinhold 		USER_TIMER_TEAM_TOTAL_TIME_ID, team, NULL, 0, event, NULL, true);
1411*24df6592SIngo Weinhold 	if (timerID < 0)
1412*24df6592SIngo Weinhold 		return timerID;
1413*24df6592SIngo Weinhold 
1414*24df6592SIngo Weinhold 	// create a user CPU time user timer
1415*24df6592SIngo Weinhold 	event.sigev_notify = SIGEV_SIGNAL;
1416*24df6592SIngo Weinhold 	event.sigev_signo = SIGVTALRM;
1417*24df6592SIngo Weinhold 
1418*24df6592SIngo Weinhold 	timerID = create_timer(CLOCK_PROCESS_USER_CPUTIME_ID,
1419*24df6592SIngo Weinhold 		USER_TIMER_TEAM_USER_TIME_ID, team, NULL, 0, event, NULL, true);
1420*24df6592SIngo Weinhold 	if (timerID < 0)
1421*24df6592SIngo Weinhold 		return timerID;
1422*24df6592SIngo Weinhold 
1423*24df6592SIngo Weinhold 	return B_OK;
1424*24df6592SIngo Weinhold }
1425*24df6592SIngo Weinhold 
1426*24df6592SIngo Weinhold 
1427*24df6592SIngo Weinhold status_t
1428*24df6592SIngo Weinhold user_timer_get_clock(clockid_t clockID, bigtime_t& _time)
1429*24df6592SIngo Weinhold {
1430*24df6592SIngo Weinhold 	switch (clockID) {
1431*24df6592SIngo Weinhold 		case CLOCK_MONOTONIC:
1432*24df6592SIngo Weinhold 			_time = system_time();
1433*24df6592SIngo Weinhold 			return B_OK;
1434*24df6592SIngo Weinhold 
1435*24df6592SIngo Weinhold 		case CLOCK_REALTIME:
1436*24df6592SIngo Weinhold 			_time = real_time_clock_usecs();
1437*24df6592SIngo Weinhold 			return B_OK;
1438*24df6592SIngo Weinhold 
1439*24df6592SIngo Weinhold 		case CLOCK_THREAD_CPUTIME_ID:
1440*24df6592SIngo Weinhold 		{
1441*24df6592SIngo Weinhold 			Thread* thread = thread_get_current_thread();
1442*24df6592SIngo Weinhold 			InterruptsSpinLocker schedulerLocker(gSchedulerLock);
1443*24df6592SIngo Weinhold 			_time = thread->CPUTime(false);
1444*24df6592SIngo Weinhold 			return B_OK;
1445*24df6592SIngo Weinhold 		}
1446*24df6592SIngo Weinhold 
1447*24df6592SIngo Weinhold 		case CLOCK_PROCESS_USER_CPUTIME_ID:
1448*24df6592SIngo Weinhold 		{
1449*24df6592SIngo Weinhold 			Team* team = thread_get_current_thread()->team;
1450*24df6592SIngo Weinhold 			InterruptsSpinLocker schedulerLocker(gSchedulerLock);
1451*24df6592SIngo Weinhold 			_time = team->UserCPUTime();
1452*24df6592SIngo Weinhold 			return B_OK;
1453*24df6592SIngo Weinhold 		}
1454*24df6592SIngo Weinhold 
1455*24df6592SIngo Weinhold 		case CLOCK_PROCESS_CPUTIME_ID:
1456*24df6592SIngo Weinhold 		default:
1457*24df6592SIngo Weinhold 		{
1458*24df6592SIngo Weinhold 			// get the ID of the target team (or the respective placeholder)
1459*24df6592SIngo Weinhold 			team_id teamID;
1460*24df6592SIngo Weinhold 			if (clockID == CLOCK_PROCESS_CPUTIME_ID) {
1461*24df6592SIngo Weinhold 				teamID = B_CURRENT_TEAM;
1462*24df6592SIngo Weinhold 			} else {
1463*24df6592SIngo Weinhold 				if (clockID < 0)
1464*24df6592SIngo Weinhold 					return B_BAD_VALUE;
1465*24df6592SIngo Weinhold 				if (clockID == team_get_kernel_team_id())
1466*24df6592SIngo Weinhold 					return B_NOT_ALLOWED;
1467*24df6592SIngo Weinhold 
1468*24df6592SIngo Weinhold 				teamID = clockID;
1469*24df6592SIngo Weinhold 			}
1470*24df6592SIngo Weinhold 
1471*24df6592SIngo Weinhold 			// get the team
1472*24df6592SIngo Weinhold 			Team* team = Team::Get(teamID);
1473*24df6592SIngo Weinhold 			if (team == NULL)
1474*24df6592SIngo Weinhold 				return B_BAD_VALUE;
1475*24df6592SIngo Weinhold 			BReference<Team> teamReference(team, true);
1476*24df6592SIngo Weinhold 
1477*24df6592SIngo Weinhold 			// get the time
1478*24df6592SIngo Weinhold 			InterruptsSpinLocker schedulerLocker(gSchedulerLock);
1479*24df6592SIngo Weinhold 			_time = team->CPUTime(false);
1480*24df6592SIngo Weinhold 
1481*24df6592SIngo Weinhold 			return B_OK;
1482*24df6592SIngo Weinhold 		}
1483*24df6592SIngo Weinhold 	}
1484*24df6592SIngo Weinhold }
1485*24df6592SIngo Weinhold 
1486*24df6592SIngo Weinhold 
1487*24df6592SIngo Weinhold void
1488*24df6592SIngo Weinhold user_timer_real_time_clock_changed()
1489*24df6592SIngo Weinhold {
1490*24df6592SIngo Weinhold 	// we need to update all absolute real-time timers
1491*24df6592SIngo Weinhold 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
1492*24df6592SIngo Weinhold 	SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
1493*24df6592SIngo Weinhold 
1494*24df6592SIngo Weinhold 	for (RealTimeUserTimerList::Iterator it
1495*24df6592SIngo Weinhold 				= sAbsoluteRealTimeTimers.GetIterator();
1496*24df6592SIngo Weinhold 			RealTimeUserTimer* timer = it.Next();) {
1497*24df6592SIngo Weinhold 		timer->TimeWarped();
1498*24df6592SIngo Weinhold 	}
1499*24df6592SIngo Weinhold }
1500*24df6592SIngo Weinhold 
1501*24df6592SIngo Weinhold 
1502*24df6592SIngo Weinhold void
1503*24df6592SIngo Weinhold user_timer_stop_cpu_timers(Thread* thread, Thread* nextThread)
1504*24df6592SIngo Weinhold {
1505*24df6592SIngo Weinhold 	// stop thread timers
1506*24df6592SIngo Weinhold 	for (ThreadTimeUserTimerList::ConstIterator it
1507*24df6592SIngo Weinhold 				= thread->CPUTimeUserTimerIterator();
1508*24df6592SIngo Weinhold 			ThreadTimeUserTimer* timer = it.Next();) {
1509*24df6592SIngo Weinhold 		timer->Stop();
1510*24df6592SIngo Weinhold 	}
1511*24df6592SIngo Weinhold 
1512*24df6592SIngo Weinhold 	// update team timers
1513*24df6592SIngo Weinhold 	if (nextThread == NULL || nextThread->team != thread->team) {
1514*24df6592SIngo Weinhold 		for (TeamTimeUserTimerList::ConstIterator it
1515*24df6592SIngo Weinhold 					= thread->team->CPUTimeUserTimerIterator();
1516*24df6592SIngo Weinhold 				TeamTimeUserTimer* timer = it.Next();) {
1517*24df6592SIngo Weinhold 			timer->Update(thread);
1518*24df6592SIngo Weinhold 		}
1519*24df6592SIngo Weinhold 	}
1520*24df6592SIngo Weinhold }
1521*24df6592SIngo Weinhold 
1522*24df6592SIngo Weinhold 
1523*24df6592SIngo Weinhold void
1524*24df6592SIngo Weinhold user_timer_continue_cpu_timers(Thread* thread, Thread* previousThread)
1525*24df6592SIngo Weinhold {
1526*24df6592SIngo Weinhold 	// update team timers
1527*24df6592SIngo Weinhold 	if (previousThread == NULL || previousThread->team != thread->team) {
1528*24df6592SIngo Weinhold 		for (TeamTimeUserTimerList::ConstIterator it
1529*24df6592SIngo Weinhold 					= thread->team->CPUTimeUserTimerIterator();
1530*24df6592SIngo Weinhold 				TeamTimeUserTimer* timer = it.Next();) {
1531*24df6592SIngo Weinhold 			timer->Update(NULL);
1532*24df6592SIngo Weinhold 		}
1533*24df6592SIngo Weinhold 	}
1534*24df6592SIngo Weinhold 
1535*24df6592SIngo Weinhold 	// start thread timers
1536*24df6592SIngo Weinhold 	for (ThreadTimeUserTimerList::ConstIterator it
1537*24df6592SIngo Weinhold 				= thread->CPUTimeUserTimerIterator();
1538*24df6592SIngo Weinhold 			ThreadTimeUserTimer* timer = it.Next();) {
1539*24df6592SIngo Weinhold 		timer->Start();
1540*24df6592SIngo Weinhold 	}
1541*24df6592SIngo Weinhold }
1542*24df6592SIngo Weinhold 
1543*24df6592SIngo Weinhold 
1544*24df6592SIngo Weinhold void
1545*24df6592SIngo Weinhold user_timer_check_team_user_timers(Team* team)
1546*24df6592SIngo Weinhold {
1547*24df6592SIngo Weinhold 	for (TeamUserTimeUserTimerList::ConstIterator it
1548*24df6592SIngo Weinhold 				= team->UserTimeUserTimerIterator();
1549*24df6592SIngo Weinhold 			TeamUserTimeUserTimer* timer = it.Next();) {
1550*24df6592SIngo Weinhold 		timer->Check();
1551*24df6592SIngo Weinhold 	}
1552*24df6592SIngo Weinhold }
1553*24df6592SIngo Weinhold 
1554*24df6592SIngo Weinhold 
1555*24df6592SIngo Weinhold // #pragma mark - syscalls
1556*24df6592SIngo Weinhold 
1557*24df6592SIngo Weinhold 
1558*24df6592SIngo Weinhold status_t
1559*24df6592SIngo Weinhold _user_get_clock(clockid_t clockID, bigtime_t* userTime)
1560*24df6592SIngo Weinhold {
1561*24df6592SIngo Weinhold 	// get the time
1562*24df6592SIngo Weinhold 	bigtime_t time;
1563*24df6592SIngo Weinhold 	status_t error = user_timer_get_clock(clockID, time);
1564*24df6592SIngo Weinhold 	if (error != B_OK)
1565*24df6592SIngo Weinhold 		return error;
1566*24df6592SIngo Weinhold 
1567*24df6592SIngo Weinhold 	// copy the value back to userland
1568*24df6592SIngo Weinhold 	if (userTime == NULL || !IS_USER_ADDRESS(userTime)
1569*24df6592SIngo Weinhold 		|| user_memcpy(userTime, &time, sizeof(time)) != B_OK) {
1570*24df6592SIngo Weinhold 		return B_BAD_ADDRESS;
1571*24df6592SIngo Weinhold 	}
1572*24df6592SIngo Weinhold 
1573*24df6592SIngo Weinhold 	return B_OK;
1574*24df6592SIngo Weinhold }
1575*24df6592SIngo Weinhold 
1576*24df6592SIngo Weinhold 
1577*24df6592SIngo Weinhold status_t
1578*24df6592SIngo Weinhold _user_set_clock(clockid_t clockID, bigtime_t time)
1579*24df6592SIngo Weinhold {
1580*24df6592SIngo Weinhold 	switch (clockID) {
1581*24df6592SIngo Weinhold 		case CLOCK_MONOTONIC:
1582*24df6592SIngo Weinhold 			return B_BAD_VALUE;
1583*24df6592SIngo Weinhold 
1584*24df6592SIngo Weinhold 		case CLOCK_REALTIME:
1585*24df6592SIngo Weinhold 			// only root may set the time
1586*24df6592SIngo Weinhold 			if (geteuid() != 0)
1587*24df6592SIngo Weinhold 				return B_NOT_ALLOWED;
1588*24df6592SIngo Weinhold 
1589*24df6592SIngo Weinhold 			set_real_time_clock_usecs(time);
1590*24df6592SIngo Weinhold 			return B_OK;
1591*24df6592SIngo Weinhold 
1592*24df6592SIngo Weinhold 		case CLOCK_THREAD_CPUTIME_ID:
1593*24df6592SIngo Weinhold 		{
1594*24df6592SIngo Weinhold 			Thread* thread = thread_get_current_thread();
1595*24df6592SIngo Weinhold 			InterruptsSpinLocker schedulerLocker(gSchedulerLock);
1596*24df6592SIngo Weinhold 			bigtime_t diff = time - thread->CPUTime(false);
1597*24df6592SIngo Weinhold 			thread->cpu_clock_offset += diff;
1598*24df6592SIngo Weinhold 
1599*24df6592SIngo Weinhold 			thread_clock_changed(thread, diff);
1600*24df6592SIngo Weinhold 			return B_OK;
1601*24df6592SIngo Weinhold 		}
1602*24df6592SIngo Weinhold 
1603*24df6592SIngo Weinhold 		case CLOCK_PROCESS_USER_CPUTIME_ID:
1604*24df6592SIngo Weinhold 			// not supported -- this clock is an Haiku-internal extension
1605*24df6592SIngo Weinhold 			return B_BAD_VALUE;
1606*24df6592SIngo Weinhold 
1607*24df6592SIngo Weinhold 		case CLOCK_PROCESS_CPUTIME_ID:
1608*24df6592SIngo Weinhold 		default:
1609*24df6592SIngo Weinhold 		{
1610*24df6592SIngo Weinhold 			// get the ID of the target team (or the respective placeholder)
1611*24df6592SIngo Weinhold 			team_id teamID;
1612*24df6592SIngo Weinhold 			if (clockID == CLOCK_PROCESS_CPUTIME_ID) {
1613*24df6592SIngo Weinhold 				teamID = B_CURRENT_TEAM;
1614*24df6592SIngo Weinhold 			} else {
1615*24df6592SIngo Weinhold 				if (clockID < 0)
1616*24df6592SIngo Weinhold 					return B_BAD_VALUE;
1617*24df6592SIngo Weinhold 				if (clockID == team_get_kernel_team_id())
1618*24df6592SIngo Weinhold 					return B_NOT_ALLOWED;
1619*24df6592SIngo Weinhold 
1620*24df6592SIngo Weinhold 				teamID = clockID;
1621*24df6592SIngo Weinhold 			}
1622*24df6592SIngo Weinhold 
1623*24df6592SIngo Weinhold 			// get the team
1624*24df6592SIngo Weinhold 			Team* team = Team::Get(teamID);
1625*24df6592SIngo Weinhold 			if (team == NULL)
1626*24df6592SIngo Weinhold 				return B_BAD_VALUE;
1627*24df6592SIngo Weinhold 			BReference<Team> teamReference(team, true);
1628*24df6592SIngo Weinhold 
1629*24df6592SIngo Weinhold 			// set the time offset
1630*24df6592SIngo Weinhold 			InterruptsSpinLocker schedulerLocker(gSchedulerLock);
1631*24df6592SIngo Weinhold 			bigtime_t diff = time - team->CPUTime(false);
1632*24df6592SIngo Weinhold 			team->cpu_clock_offset += diff;
1633*24df6592SIngo Weinhold 
1634*24df6592SIngo Weinhold 			team_clock_changed(team, diff);
1635*24df6592SIngo Weinhold 			return B_OK;
1636*24df6592SIngo Weinhold 		}
1637*24df6592SIngo Weinhold 	}
1638*24df6592SIngo Weinhold 
1639*24df6592SIngo Weinhold 	return B_OK;
1640*24df6592SIngo Weinhold }
1641*24df6592SIngo Weinhold 
1642*24df6592SIngo Weinhold 
1643*24df6592SIngo Weinhold int32
1644*24df6592SIngo Weinhold _user_create_timer(clockid_t clockID, thread_id threadID, uint32 flags,
1645*24df6592SIngo Weinhold 	const struct sigevent* userEvent,
1646*24df6592SIngo Weinhold 	const thread_creation_attributes* userThreadAttributes)
1647*24df6592SIngo Weinhold {
1648*24df6592SIngo Weinhold 	// copy the sigevent structure from userland
1649*24df6592SIngo Weinhold 	struct sigevent event;
1650*24df6592SIngo Weinhold 	if (userEvent != NULL) {
1651*24df6592SIngo Weinhold 		if (!IS_USER_ADDRESS(userEvent)
1652*24df6592SIngo Weinhold 			|| user_memcpy(&event, userEvent, sizeof(event)) != B_OK) {
1653*24df6592SIngo Weinhold 			return B_BAD_ADDRESS;
1654*24df6592SIngo Weinhold 		}
1655*24df6592SIngo Weinhold 	} else {
1656*24df6592SIngo Weinhold 		// none given -- use defaults
1657*24df6592SIngo Weinhold 		event.sigev_notify = SIGEV_SIGNAL;
1658*24df6592SIngo Weinhold 		event.sigev_signo = SIGALRM;
1659*24df6592SIngo Weinhold 	}
1660*24df6592SIngo Weinhold 
1661*24df6592SIngo Weinhold 	// copy thread creation attributes from userland, if specified
1662*24df6592SIngo Weinhold 	char nameBuffer[B_OS_NAME_LENGTH];
1663*24df6592SIngo Weinhold 	ThreadCreationAttributes threadAttributes;
1664*24df6592SIngo Weinhold 	if (event.sigev_notify == SIGEV_THREAD) {
1665*24df6592SIngo Weinhold 		status_t error = threadAttributes.InitFromUserAttributes(
1666*24df6592SIngo Weinhold 			userThreadAttributes, nameBuffer);
1667*24df6592SIngo Weinhold 		if (error != B_OK)
1668*24df6592SIngo Weinhold 			return error;
1669*24df6592SIngo Weinhold 	}
1670*24df6592SIngo Weinhold 
1671*24df6592SIngo Weinhold 	// get team and thread
1672*24df6592SIngo Weinhold 	Team* team = thread_get_current_thread()->team;
1673*24df6592SIngo Weinhold 	Thread* thread = NULL;
1674*24df6592SIngo Weinhold 	if (threadID >= 0) {
1675*24df6592SIngo Weinhold 		thread = Thread::GetAndLock(threadID);
1676*24df6592SIngo Weinhold 		if (thread == NULL)
1677*24df6592SIngo Weinhold 			return B_BAD_THREAD_ID;
1678*24df6592SIngo Weinhold 
1679*24df6592SIngo Weinhold 		thread->Unlock();
1680*24df6592SIngo Weinhold 	}
1681*24df6592SIngo Weinhold 	BReference<Thread> threadReference(thread, true);
1682*24df6592SIngo Weinhold 
1683*24df6592SIngo Weinhold 	// create the timer
1684*24df6592SIngo Weinhold 	return create_timer(clockID, -1, team, thread, flags, event,
1685*24df6592SIngo Weinhold 		userThreadAttributes != NULL ? &threadAttributes : NULL,
1686*24df6592SIngo Weinhold 		userEvent == NULL);
1687*24df6592SIngo Weinhold }
1688*24df6592SIngo Weinhold 
1689*24df6592SIngo Weinhold 
1690*24df6592SIngo Weinhold status_t
1691*24df6592SIngo Weinhold _user_delete_timer(int32 timerID, thread_id threadID)
1692*24df6592SIngo Weinhold {
1693*24df6592SIngo Weinhold 	// can only delete user-defined timers
1694*24df6592SIngo Weinhold 	if (timerID < USER_TIMER_FIRST_USER_DEFINED_ID)
1695*24df6592SIngo Weinhold 		return B_BAD_VALUE;
1696*24df6592SIngo Weinhold 
1697*24df6592SIngo Weinhold 	// get the timer
1698*24df6592SIngo Weinhold 	TimerLocker timerLocker;
1699*24df6592SIngo Weinhold 	UserTimer* timer;
1700*24df6592SIngo Weinhold 	status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
1701*24df6592SIngo Weinhold 	if (error != B_OK)
1702*24df6592SIngo Weinhold 		return error;
1703*24df6592SIngo Weinhold 
1704*24df6592SIngo Weinhold 	// cancel, remove, and delete it
1705*24df6592SIngo Weinhold 	timer->Cancel();
1706*24df6592SIngo Weinhold 
1707*24df6592SIngo Weinhold 	if (threadID >= 0)
1708*24df6592SIngo Weinhold 		timerLocker.thread->RemoveUserTimer(timer);
1709*24df6592SIngo Weinhold 	else
1710*24df6592SIngo Weinhold 		timerLocker.team->RemoveUserTimer(timer);
1711*24df6592SIngo Weinhold 
1712*24df6592SIngo Weinhold 	delete timer;
1713*24df6592SIngo Weinhold 
1714*24df6592SIngo Weinhold 	return B_OK;
1715*24df6592SIngo Weinhold }
1716*24df6592SIngo Weinhold 
1717*24df6592SIngo Weinhold 
1718*24df6592SIngo Weinhold status_t
1719*24df6592SIngo Weinhold _user_get_timer(int32 timerID, thread_id threadID,
1720*24df6592SIngo Weinhold 	struct user_timer_info* userInfo)
1721*24df6592SIngo Weinhold {
1722*24df6592SIngo Weinhold 	// get the timer
1723*24df6592SIngo Weinhold 	TimerLocker timerLocker;
1724*24df6592SIngo Weinhold 	UserTimer* timer;
1725*24df6592SIngo Weinhold 	status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
1726*24df6592SIngo Weinhold 	if (error != B_OK)
1727*24df6592SIngo Weinhold 		return error;
1728*24df6592SIngo Weinhold 
1729*24df6592SIngo Weinhold 	// get the info
1730*24df6592SIngo Weinhold 	user_timer_info info;
1731*24df6592SIngo Weinhold 	timer->GetInfo(info.remaining_time, info.interval, info.overrun_count);
1732*24df6592SIngo Weinhold 
1733*24df6592SIngo Weinhold 	// Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid
1734*24df6592SIngo Weinhold 	// value.
1735*24df6592SIngo Weinhold 	if (info.remaining_time <= 0)
1736*24df6592SIngo Weinhold 		info.remaining_time = 1;
1737*24df6592SIngo Weinhold 
1738*24df6592SIngo Weinhold 	timerLocker.Unlock();
1739*24df6592SIngo Weinhold 
1740*24df6592SIngo Weinhold 	// copy it back to userland
1741*24df6592SIngo Weinhold 	if (userInfo != NULL
1742*24df6592SIngo Weinhold 		&& (!IS_USER_ADDRESS(userInfo)
1743*24df6592SIngo Weinhold 			|| user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) {
1744*24df6592SIngo Weinhold 		return B_BAD_ADDRESS;
1745*24df6592SIngo Weinhold 	}
1746*24df6592SIngo Weinhold 
1747*24df6592SIngo Weinhold 	return B_OK;
1748*24df6592SIngo Weinhold }
1749*24df6592SIngo Weinhold 
1750*24df6592SIngo Weinhold 
1751*24df6592SIngo Weinhold status_t
1752*24df6592SIngo Weinhold _user_set_timer(int32 timerID, thread_id threadID, bigtime_t startTime,
1753*24df6592SIngo Weinhold 	bigtime_t interval, uint32 flags, struct user_timer_info* userOldInfo)
1754*24df6592SIngo Weinhold {
1755*24df6592SIngo Weinhold 	// check the values
1756*24df6592SIngo Weinhold 	if (startTime < 0 || interval < 0)
1757*24df6592SIngo Weinhold 		return B_BAD_VALUE;
1758*24df6592SIngo Weinhold 
1759*24df6592SIngo Weinhold 	// get the timer
1760*24df6592SIngo Weinhold 	TimerLocker timerLocker;
1761*24df6592SIngo Weinhold 	UserTimer* timer;
1762*24df6592SIngo Weinhold 	status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
1763*24df6592SIngo Weinhold 	if (error != B_OK)
1764*24df6592SIngo Weinhold 		return error;
1765*24df6592SIngo Weinhold 
1766*24df6592SIngo Weinhold 	// schedule the timer
1767*24df6592SIngo Weinhold 	user_timer_info oldInfo;
1768*24df6592SIngo Weinhold 	timer->Schedule(startTime, interval, flags, oldInfo.remaining_time,
1769*24df6592SIngo Weinhold 		oldInfo.interval);
1770*24df6592SIngo Weinhold 
1771*24df6592SIngo Weinhold 	// Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid
1772*24df6592SIngo Weinhold 	// value.
1773*24df6592SIngo Weinhold 	if (oldInfo.remaining_time <= 0)
1774*24df6592SIngo Weinhold 		oldInfo.remaining_time = 1;
1775*24df6592SIngo Weinhold 
1776*24df6592SIngo Weinhold 	timerLocker.Unlock();
1777*24df6592SIngo Weinhold 
1778*24df6592SIngo Weinhold 	// copy back the old info
1779*24df6592SIngo Weinhold 	if (userOldInfo != NULL
1780*24df6592SIngo Weinhold 		&& (!IS_USER_ADDRESS(userOldInfo)
1781*24df6592SIngo Weinhold 			|| user_memcpy(userOldInfo, &oldInfo, sizeof(oldInfo)) != B_OK)) {
1782*24df6592SIngo Weinhold 		return B_BAD_ADDRESS;
1783*24df6592SIngo Weinhold 	}
1784*24df6592SIngo Weinhold 
1785*24df6592SIngo Weinhold 	return B_OK;
1786*24df6592SIngo Weinhold }
1787