xref: /haiku/src/system/kernel/UserTimer.cpp (revision 7db89e8dc395db73368479fd9817b2b67899f3f6)
124df6592SIngo Weinhold /*
224df6592SIngo Weinhold  * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
324df6592SIngo Weinhold  * Distributed under the terms of the MIT License.
424df6592SIngo Weinhold  */
524df6592SIngo Weinhold 
624df6592SIngo Weinhold 
724df6592SIngo Weinhold #include <UserTimer.h>
824df6592SIngo Weinhold 
924df6592SIngo Weinhold #include <algorithm>
1024df6592SIngo Weinhold 
1124df6592SIngo Weinhold #include <AutoDeleter.h>
1224df6592SIngo Weinhold 
1324df6592SIngo Weinhold #include <debug.h>
1424df6592SIngo Weinhold #include <kernel.h>
1524df6592SIngo Weinhold #include <real_time_clock.h>
1624df6592SIngo Weinhold #include <team.h>
1724df6592SIngo Weinhold #include <thread_types.h>
1824df6592SIngo Weinhold #include <UserEvent.h>
1924df6592SIngo Weinhold #include <util/AutoLock.h>
2024df6592SIngo Weinhold 
2124df6592SIngo Weinhold 
2224df6592SIngo Weinhold // Minimum interval length in microseconds for a periodic timer. This is not a
2324df6592SIngo Weinhold // restriction on the user timer interval length itself, but the minimum time
2424df6592SIngo Weinhold // span by which we advance the start time for kernel timers. A shorted user
2524df6592SIngo Weinhold // timer interval will result in the overrun count to be increased every time
2624df6592SIngo Weinhold // the kernel timer is rescheduled.
2724df6592SIngo Weinhold static const bigtime_t kMinPeriodicTimerInterval = 100;
2824df6592SIngo Weinhold 
2924df6592SIngo Weinhold static RealTimeUserTimerList sAbsoluteRealTimeTimers;
3024df6592SIngo Weinhold static spinlock sAbsoluteRealTimeTimersLock = B_SPINLOCK_INITIALIZER;
3124df6592SIngo Weinhold 
32f4b088a9SPawel Dziepak static seqlock sUserTimerLock = B_SEQLOCK_INITIALIZER;
33f4b088a9SPawel Dziepak 
3424df6592SIngo Weinhold 
3524df6592SIngo Weinhold // #pragma mark - TimerLocker
3624df6592SIngo Weinhold 
3724df6592SIngo Weinhold 
3824df6592SIngo Weinhold namespace {
3924df6592SIngo Weinhold 
4024df6592SIngo Weinhold struct TimerLocker {
4124df6592SIngo Weinhold 	Team*	team;
4224df6592SIngo Weinhold 	Thread*	thread;
4324df6592SIngo Weinhold 
4424df6592SIngo Weinhold 	TimerLocker()
4524df6592SIngo Weinhold 		:
4624df6592SIngo Weinhold 		team(NULL),
4724df6592SIngo Weinhold 		thread(NULL)
4824df6592SIngo Weinhold 	{
4924df6592SIngo Weinhold 	}
5024df6592SIngo Weinhold 
5124df6592SIngo Weinhold 	~TimerLocker()
5224df6592SIngo Weinhold 	{
5324df6592SIngo Weinhold 		Unlock();
5424df6592SIngo Weinhold 	}
5524df6592SIngo Weinhold 
5624df6592SIngo Weinhold 	void Lock(Team* team, Thread* thread)
5724df6592SIngo Weinhold 	{
5824df6592SIngo Weinhold 		this->team = team;
5924df6592SIngo Weinhold 		team->Lock();
6024df6592SIngo Weinhold 
6124df6592SIngo Weinhold 		this->thread = thread;
6224df6592SIngo Weinhold 
6324df6592SIngo Weinhold 		if (thread != NULL) {
6424df6592SIngo Weinhold 			thread->AcquireReference();
6524df6592SIngo Weinhold 			thread->Lock();
6624df6592SIngo Weinhold 		}
6724df6592SIngo Weinhold 
6824df6592SIngo Weinhold 		// We don't check thread->team != team here, since this method can be
6924df6592SIngo Weinhold 		// called for new threads not added to the team yet.
7024df6592SIngo Weinhold 	}
7124df6592SIngo Weinhold 
7224df6592SIngo Weinhold 	status_t LockAndGetTimer(thread_id threadID, int32 timerID,
7324df6592SIngo Weinhold 		UserTimer*& _timer)
7424df6592SIngo Weinhold 	{
7524df6592SIngo Weinhold 		team = thread_get_current_thread()->team;
7624df6592SIngo Weinhold 		team->Lock();
7724df6592SIngo Weinhold 
7824df6592SIngo Weinhold 		if (threadID >= 0) {
7924df6592SIngo Weinhold 			thread = Thread::GetAndLock(threadID);
8024df6592SIngo Weinhold 			if (thread == NULL)
8124df6592SIngo Weinhold 				return B_BAD_THREAD_ID;
8224df6592SIngo Weinhold 			if (thread->team != team)
8324df6592SIngo Weinhold 				return B_NOT_ALLOWED;
8424df6592SIngo Weinhold 		}
8524df6592SIngo Weinhold 
8624df6592SIngo Weinhold 		UserTimer* timer = thread != NULL
8724df6592SIngo Weinhold 			? thread->UserTimerFor(timerID) : team->UserTimerFor(timerID);
8824df6592SIngo Weinhold 		if (timer == NULL)
8924df6592SIngo Weinhold 			return B_BAD_VALUE;
9024df6592SIngo Weinhold 
9124df6592SIngo Weinhold 		_timer = timer;
9224df6592SIngo Weinhold 		return B_OK;
9324df6592SIngo Weinhold 	}
9424df6592SIngo Weinhold 
9524df6592SIngo Weinhold 	void Unlock()
9624df6592SIngo Weinhold 	{
9724df6592SIngo Weinhold 		if (thread != NULL) {
9824df6592SIngo Weinhold 			thread->UnlockAndReleaseReference();
9924df6592SIngo Weinhold 			thread = NULL;
10024df6592SIngo Weinhold 		}
10124df6592SIngo Weinhold 		if (team != NULL) {
10224df6592SIngo Weinhold 			team->Unlock();
10324df6592SIngo Weinhold 			team = NULL;
10424df6592SIngo Weinhold 		}
10524df6592SIngo Weinhold 	}
10624df6592SIngo Weinhold };
10724df6592SIngo Weinhold 
10824df6592SIngo Weinhold }	// unnamed namespace
10924df6592SIngo Weinhold 
11024df6592SIngo Weinhold 
11124df6592SIngo Weinhold // #pragma mark - UserTimer
11224df6592SIngo Weinhold 
11324df6592SIngo Weinhold 
11424df6592SIngo Weinhold UserTimer::UserTimer()
11524df6592SIngo Weinhold 	:
11624df6592SIngo Weinhold 	fID(-1),
11724df6592SIngo Weinhold 	fEvent(NULL),
11824df6592SIngo Weinhold 	fNextTime(0),
11924df6592SIngo Weinhold 	fInterval(0),
12024df6592SIngo Weinhold 	fOverrunCount(0),
121f4b088a9SPawel Dziepak 	fScheduled(false),
122f4b088a9SPawel Dziepak 	fSkip(0)
12324df6592SIngo Weinhold {
12424df6592SIngo Weinhold 	// mark the timer unused
12524df6592SIngo Weinhold 	fTimer.user_data = this;
12624df6592SIngo Weinhold }
12724df6592SIngo Weinhold 
12824df6592SIngo Weinhold 
12924df6592SIngo Weinhold UserTimer::~UserTimer()
13024df6592SIngo Weinhold {
13124df6592SIngo Weinhold 	delete fEvent;
13224df6592SIngo Weinhold }
13324df6592SIngo Weinhold 
13424df6592SIngo Weinhold 
13524df6592SIngo Weinhold /*!	\fn UserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
13624df6592SIngo Weinhold 		bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
13724df6592SIngo Weinhold 	Cancels the timer, if it is already scheduled, and optionally schedules it
13824df6592SIngo Weinhold 	with new parameters.
13924df6592SIngo Weinhold 
14024df6592SIngo Weinhold 	\param nextTime The time at which the timer should go off the next time. If
14124df6592SIngo Weinhold 		\c B_INFINITE_TIMEOUT, the timer will not be scheduled. Whether the
14224df6592SIngo Weinhold 		value is interpreted as absolute or relative time, depends on \c flags.
14324df6592SIngo Weinhold 	\param interval If <tt> >0 </tt>, the timer will be scheduled to fire
14424df6592SIngo Weinhold 		periodically every \a interval microseconds. Otherwise it will fire
14524df6592SIngo Weinhold 		only once at \a nextTime. If \a nextTime is \c B_INFINITE_TIMEOUT, it
14624df6592SIngo Weinhold 		will fire never in either case.
14724df6592SIngo Weinhold 	\param flags Bitwise OR of flags. Currently \c B_ABSOLUTE_TIMEOUT and
14824df6592SIngo Weinhold 		\c B_RELATIVE_TIMEOUT are supported, indicating whether \a nextTime is
14924df6592SIngo Weinhold 		an absolute or relative time.
15024df6592SIngo Weinhold 	\param _oldRemainingTime Return variable that will be set to the
15124df6592SIngo Weinhold 		microseconds remaining to the time for which the timer was scheduled
15224df6592SIngo Weinhold 		next before the call. If it wasn't scheduled, the variable is set to
15324df6592SIngo Weinhold 		\c B_INFINITE_TIMEOUT.
15424df6592SIngo Weinhold 	\param _oldInterval Return variable that will be set to the interval in
15524df6592SIngo Weinhold 		microseconds the timer was to be scheduled periodically. If the timer
15624df6592SIngo Weinhold 		wasn't periodic, the variable is set to \c 0.
15724df6592SIngo Weinhold */
15824df6592SIngo Weinhold 
15924df6592SIngo Weinhold 
16024df6592SIngo Weinhold /*!	Cancels the timer, if it is scheduled.
16124df6592SIngo Weinhold */
16224df6592SIngo Weinhold void
16324df6592SIngo Weinhold UserTimer::Cancel()
16424df6592SIngo Weinhold {
16524df6592SIngo Weinhold 	bigtime_t oldNextTime;
16624df6592SIngo Weinhold 	bigtime_t oldInterval;
16724df6592SIngo Weinhold 	return Schedule(B_INFINITE_TIMEOUT, 0, 0, oldNextTime, oldInterval);
16824df6592SIngo Weinhold }
16924df6592SIngo Weinhold 
17024df6592SIngo Weinhold 
17124df6592SIngo Weinhold /*!	\fn UserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
17224df6592SIngo Weinhold 		uint32& _overrunCount)
17324df6592SIngo Weinhold 	Return information on the current timer.
17424df6592SIngo Weinhold 
17524df6592SIngo Weinhold 	\param _remainingTime Return variable that will be set to the microseconds
17624df6592SIngo Weinhold 		remaining to the time for which the timer was scheduled next before the
17724df6592SIngo Weinhold 		call. If it wasn't scheduled, the variable is set to
17824df6592SIngo Weinhold 		\c B_INFINITE_TIMEOUT.
17924df6592SIngo Weinhold 	\param _interval Return variable that will be set to the interval in
18024df6592SIngo Weinhold 		microseconds the timer is to be scheduled periodically. If the timer
18124df6592SIngo Weinhold 		isn't periodic, the variable is set to \c 0.
18224df6592SIngo Weinhold 	\param _overrunCount Return variable that will be set to the number of times
18324df6592SIngo Weinhold 		the timer went off, but its event couldn't be delivered, since it's
18424df6592SIngo Weinhold 		previous delivery hasn't been handled yet.
18524df6592SIngo Weinhold */
18624df6592SIngo Weinhold 
18724df6592SIngo Weinhold 
18824df6592SIngo Weinhold /*static*/ int32
18924df6592SIngo Weinhold UserTimer::HandleTimerHook(struct timer* timer)
19024df6592SIngo Weinhold {
191f4b088a9SPawel Dziepak 	UserTimer* userTimer = reinterpret_cast<UserTimer*>(timer->user_data);
192f4b088a9SPawel Dziepak 
193f4b088a9SPawel Dziepak 	InterruptsLocker _;
194f4b088a9SPawel Dziepak 
195f4b088a9SPawel Dziepak 	bool locked = false;
196f4b088a9SPawel Dziepak 	while (!locked && atomic_get(&userTimer->fSkip) == 0) {
197f4b088a9SPawel Dziepak 		locked = try_acquire_write_seqlock(&sUserTimerLock);
198f4b088a9SPawel Dziepak 		if (!locked)
199*7db89e8dSPawel Dziepak 			cpu_pause();
200f4b088a9SPawel Dziepak 	}
201f4b088a9SPawel Dziepak 
202f4b088a9SPawel Dziepak 	if (locked) {
203f4b088a9SPawel Dziepak 		userTimer->HandleTimer();
204f4b088a9SPawel Dziepak 		release_write_seqlock(&sUserTimerLock);
205f4b088a9SPawel Dziepak 	}
206f4b088a9SPawel Dziepak 
20724df6592SIngo Weinhold 	return B_HANDLED_INTERRUPT;
20824df6592SIngo Weinhold }
20924df6592SIngo Weinhold 
21024df6592SIngo Weinhold 
21124df6592SIngo Weinhold void
21224df6592SIngo Weinhold UserTimer::HandleTimer()
21324df6592SIngo Weinhold {
21424df6592SIngo Weinhold 	if (fEvent != NULL) {
21524df6592SIngo Weinhold 		// fire the event and update the overrun count, if necessary
21624df6592SIngo Weinhold 		status_t error = fEvent->Fire();
21724df6592SIngo Weinhold 		if (error == B_BUSY) {
21824df6592SIngo Weinhold 			if (fOverrunCount < MAX_USER_TIMER_OVERRUN_COUNT)
21924df6592SIngo Weinhold 				fOverrunCount++;
22024df6592SIngo Weinhold 		}
22124df6592SIngo Weinhold 	}
22224df6592SIngo Weinhold 
22324df6592SIngo Weinhold 	// Since we don't use periodic kernel timers, it isn't scheduled anymore.
22424df6592SIngo Weinhold 	// If the timer is periodic, the derived class' version will schedule it
22524df6592SIngo Weinhold 	// again.
22624df6592SIngo Weinhold 	fScheduled = false;
22724df6592SIngo Weinhold }
22824df6592SIngo Weinhold 
22924df6592SIngo Weinhold 
23024df6592SIngo Weinhold /*!	Updates the start time for a periodic timer after it expired, enforcing
23124df6592SIngo Weinhold 	sanity limits and updating \c fOverrunCount, if necessary.
23224df6592SIngo Weinhold 
233f4b088a9SPawel Dziepak 	The caller must not hold \c sUserTimerLock.
23424df6592SIngo Weinhold */
23524df6592SIngo Weinhold void
23624df6592SIngo Weinhold UserTimer::UpdatePeriodicStartTime()
23724df6592SIngo Weinhold {
23824df6592SIngo Weinhold 	if (fInterval < kMinPeriodicTimerInterval) {
23924df6592SIngo Weinhold 		bigtime_t skip = (kMinPeriodicTimerInterval + fInterval - 1) / fInterval;
24024df6592SIngo Weinhold 		fNextTime += skip * fInterval;
24124df6592SIngo Weinhold 
24224df6592SIngo Weinhold 		// One interval is the normal advance, so don't consider it skipped.
24324df6592SIngo Weinhold 		skip--;
24424df6592SIngo Weinhold 
24524df6592SIngo Weinhold 		if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT)
24624df6592SIngo Weinhold 			fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT;
24724df6592SIngo Weinhold 		else
24824df6592SIngo Weinhold 			fOverrunCount += skip;
24924df6592SIngo Weinhold 	} else
25024df6592SIngo Weinhold 		fNextTime += fInterval;
25124df6592SIngo Weinhold }
25224df6592SIngo Weinhold 
25324df6592SIngo Weinhold 
25424df6592SIngo Weinhold /*!	Checks whether the timer start time lies too much in the past and, if so,
25524df6592SIngo Weinhold 	adjusts it and updates \c fOverrunCount.
25624df6592SIngo Weinhold 
257f4b088a9SPawel Dziepak 	The caller must not hold \c sUserTimerLock.
25824df6592SIngo Weinhold 
25924df6592SIngo Weinhold 	\param now The current time.
26024df6592SIngo Weinhold */
26124df6592SIngo Weinhold void
26224df6592SIngo Weinhold UserTimer::CheckPeriodicOverrun(bigtime_t now)
26324df6592SIngo Weinhold {
26424df6592SIngo Weinhold 	if (fNextTime + fInterval > now)
26524df6592SIngo Weinhold 		return;
26624df6592SIngo Weinhold 
26724df6592SIngo Weinhold 	// The start time is a full interval or more in the past. Skip those
26824df6592SIngo Weinhold 	// intervals.
26924df6592SIngo Weinhold 	bigtime_t skip = (now - fNextTime) / fInterval;
27024df6592SIngo Weinhold 	fNextTime += skip * fInterval;
27124df6592SIngo Weinhold 
27224df6592SIngo Weinhold 	if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT)
27324df6592SIngo Weinhold 		fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT;
27424df6592SIngo Weinhold 	else
27524df6592SIngo Weinhold 		fOverrunCount += skip;
27624df6592SIngo Weinhold }
27724df6592SIngo Weinhold 
27824df6592SIngo Weinhold 
279f4b088a9SPawel Dziepak void
280f4b088a9SPawel Dziepak UserTimer::CancelTimer()
281f4b088a9SPawel Dziepak {
282f4b088a9SPawel Dziepak 	ASSERT(fScheduled);
283f4b088a9SPawel Dziepak 
284e7dba861SPawel Dziepak 	atomic_set(&fSkip, 1);
285f4b088a9SPawel Dziepak 	cancel_timer(&fTimer);
286e7dba861SPawel Dziepak 	atomic_set(&fSkip, 0);
287f4b088a9SPawel Dziepak }
288f4b088a9SPawel Dziepak 
289f4b088a9SPawel Dziepak 
29024df6592SIngo Weinhold // #pragma mark - SystemTimeUserTimer
29124df6592SIngo Weinhold 
29224df6592SIngo Weinhold 
29324df6592SIngo Weinhold void
29424df6592SIngo Weinhold SystemTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
29524df6592SIngo Weinhold 	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
29624df6592SIngo Weinhold {
297f4b088a9SPawel Dziepak 	InterruptsWriteSequentialLocker locker(sUserTimerLock);
29824df6592SIngo Weinhold 
29924df6592SIngo Weinhold 	// get the current time
30024df6592SIngo Weinhold 	bigtime_t now = system_time();
30124df6592SIngo Weinhold 
30224df6592SIngo Weinhold 	// Cancel the old timer, if still scheduled, and get the previous values.
30324df6592SIngo Weinhold 	if (fScheduled) {
304f4b088a9SPawel Dziepak 		CancelTimer();
30524df6592SIngo Weinhold 
30624df6592SIngo Weinhold 		_oldRemainingTime = fNextTime - now;
30724df6592SIngo Weinhold 		_oldInterval = fInterval;
30824df6592SIngo Weinhold 
30924df6592SIngo Weinhold 		fScheduled = false;
31024df6592SIngo Weinhold 	} else {
31124df6592SIngo Weinhold 		_oldRemainingTime = B_INFINITE_TIMEOUT;
31224df6592SIngo Weinhold 		_oldInterval = 0;
31324df6592SIngo Weinhold 	}
31424df6592SIngo Weinhold 
31524df6592SIngo Weinhold 	// schedule the new timer
31624df6592SIngo Weinhold 	fNextTime = nextTime;
31724df6592SIngo Weinhold 	fInterval = interval;
31824df6592SIngo Weinhold 	fOverrunCount = 0;
31924df6592SIngo Weinhold 
32024df6592SIngo Weinhold 	if (nextTime == B_INFINITE_TIMEOUT)
32124df6592SIngo Weinhold 		return;
32224df6592SIngo Weinhold 
32324df6592SIngo Weinhold 	if ((flags & B_RELATIVE_TIMEOUT) != 0)
32424df6592SIngo Weinhold 		fNextTime += now;
32524df6592SIngo Weinhold 
32624df6592SIngo Weinhold 	ScheduleKernelTimer(now, fInterval > 0);
32724df6592SIngo Weinhold }
32824df6592SIngo Weinhold 
32924df6592SIngo Weinhold 
33024df6592SIngo Weinhold void
33124df6592SIngo Weinhold SystemTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
33224df6592SIngo Weinhold 	uint32& _overrunCount)
33324df6592SIngo Weinhold {
334f4b088a9SPawel Dziepak 	uint32 count;
335f4b088a9SPawel Dziepak 	do {
336f4b088a9SPawel Dziepak 		count = acquire_read_seqlock(&sUserTimerLock);
33724df6592SIngo Weinhold 
33824df6592SIngo Weinhold 		if (fScheduled) {
33924df6592SIngo Weinhold 			_remainingTime = fNextTime - system_time();
34024df6592SIngo Weinhold 			_interval = fInterval;
34124df6592SIngo Weinhold 		} else {
34224df6592SIngo Weinhold 			_remainingTime = B_INFINITE_TIMEOUT;
34324df6592SIngo Weinhold 			_interval = 0;
34424df6592SIngo Weinhold 		}
34524df6592SIngo Weinhold 
34624df6592SIngo Weinhold 		_overrunCount = fOverrunCount;
347f4b088a9SPawel Dziepak 	} while (!release_read_seqlock(&sUserTimerLock, count));
34824df6592SIngo Weinhold }
34924df6592SIngo Weinhold 
35024df6592SIngo Weinhold 
35124df6592SIngo Weinhold void
35224df6592SIngo Weinhold SystemTimeUserTimer::HandleTimer()
35324df6592SIngo Weinhold {
35424df6592SIngo Weinhold 	UserTimer::HandleTimer();
35524df6592SIngo Weinhold 
35624df6592SIngo Weinhold 	// if periodic, reschedule the kernel timer
35724df6592SIngo Weinhold 	if (fInterval > 0) {
35824df6592SIngo Weinhold 		UpdatePeriodicStartTime();
35924df6592SIngo Weinhold 		ScheduleKernelTimer(system_time(), true);
36024df6592SIngo Weinhold 	}
36124df6592SIngo Weinhold }
36224df6592SIngo Weinhold 
36324df6592SIngo Weinhold 
36424df6592SIngo Weinhold /*!	Schedules the kernel timer.
36524df6592SIngo Weinhold 
366f4b088a9SPawel Dziepak 	The caller must hold \c sUserTimerLock.
36724df6592SIngo Weinhold 
36824df6592SIngo Weinhold 	\param now The current system time to be used.
36924df6592SIngo Weinhold 	\param checkPeriodicOverrun If \c true, calls CheckPeriodicOverrun() first,
37024df6592SIngo Weinhold 		i.e. the start time will be adjusted to not lie too much in the past.
37124df6592SIngo Weinhold */
37224df6592SIngo Weinhold void
37324df6592SIngo Weinhold SystemTimeUserTimer::ScheduleKernelTimer(bigtime_t now,
37424df6592SIngo Weinhold 	bool checkPeriodicOverrun)
37524df6592SIngo Weinhold {
37624df6592SIngo Weinhold 	// If periodic, check whether the start time is too far in the past.
37724df6592SIngo Weinhold 	if (checkPeriodicOverrun)
37824df6592SIngo Weinhold 		CheckPeriodicOverrun(now);
37924df6592SIngo Weinhold 
38024df6592SIngo Weinhold 	uint32 timerFlags = B_ONE_SHOT_ABSOLUTE_TIMER
381d8fcc8a8SPawel Dziepak 			| B_TIMER_USE_TIMER_STRUCT_TIMES;
38224df6592SIngo Weinhold 
38324df6592SIngo Weinhold 	fTimer.schedule_time = std::max(fNextTime, (bigtime_t)0);
38424df6592SIngo Weinhold 	fTimer.period = 0;
38524df6592SIngo Weinhold 
38624df6592SIngo Weinhold 	add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, timerFlags);
38724df6592SIngo Weinhold 
38824df6592SIngo Weinhold 	fScheduled = true;
38924df6592SIngo Weinhold }
39024df6592SIngo Weinhold 
39124df6592SIngo Weinhold 
39224df6592SIngo Weinhold // #pragma mark - RealTimeUserTimer
39324df6592SIngo Weinhold 
39424df6592SIngo Weinhold 
39524df6592SIngo Weinhold void
39624df6592SIngo Weinhold RealTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
39724df6592SIngo Weinhold 	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
39824df6592SIngo Weinhold {
399f4b088a9SPawel Dziepak 	InterruptsWriteSequentialLocker locker(sUserTimerLock);
40024df6592SIngo Weinhold 
40124df6592SIngo Weinhold 	// get the current time
40224df6592SIngo Weinhold 	bigtime_t now = system_time();
40324df6592SIngo Weinhold 
40424df6592SIngo Weinhold 	// Cancel the old timer, if still scheduled, and get the previous values.
40524df6592SIngo Weinhold 	if (fScheduled) {
406f4b088a9SPawel Dziepak 		CancelTimer();
40724df6592SIngo Weinhold 
40824df6592SIngo Weinhold 		_oldRemainingTime = fNextTime - now;
40924df6592SIngo Weinhold 		_oldInterval = fInterval;
41024df6592SIngo Weinhold 
41124df6592SIngo Weinhold 		if (fAbsolute) {
41224df6592SIngo Weinhold 			SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
41324df6592SIngo Weinhold 			sAbsoluteRealTimeTimers.Remove(this);
41424df6592SIngo Weinhold 		}
41524df6592SIngo Weinhold 
41624df6592SIngo Weinhold 		fScheduled = false;
41724df6592SIngo Weinhold 	} else {
41824df6592SIngo Weinhold 		_oldRemainingTime = B_INFINITE_TIMEOUT;
41924df6592SIngo Weinhold 		_oldInterval = 0;
42024df6592SIngo Weinhold 	}
42124df6592SIngo Weinhold 
42224df6592SIngo Weinhold 	// schedule the new timer
42324df6592SIngo Weinhold 	fNextTime = nextTime;
42424df6592SIngo Weinhold 	fInterval = interval;
42524df6592SIngo Weinhold 	fOverrunCount = 0;
42624df6592SIngo Weinhold 
42724df6592SIngo Weinhold 	if (nextTime == B_INFINITE_TIMEOUT)
42824df6592SIngo Weinhold 		return;
42924df6592SIngo Weinhold 
43024df6592SIngo Weinhold 	fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
43124df6592SIngo Weinhold 
43224df6592SIngo Weinhold 	if (fAbsolute) {
43324df6592SIngo Weinhold 		fRealTimeOffset = rtc_boot_time();
43424df6592SIngo Weinhold 		fNextTime -= fRealTimeOffset;
43524df6592SIngo Weinhold 
43624df6592SIngo Weinhold 		// If periodic, check whether the start time is too far in the past.
43724df6592SIngo Weinhold 		if (fInterval > 0)
43824df6592SIngo Weinhold 			CheckPeriodicOverrun(now);
43924df6592SIngo Weinhold 
44024df6592SIngo Weinhold 		// add the absolute timer to the global list
44124df6592SIngo Weinhold 		SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
44224df6592SIngo Weinhold 		sAbsoluteRealTimeTimers.Insert(this);
44324df6592SIngo Weinhold 	} else
44424df6592SIngo Weinhold 		fNextTime += now;
44524df6592SIngo Weinhold 
44624df6592SIngo Weinhold 	ScheduleKernelTimer(now, false);
44724df6592SIngo Weinhold }
44824df6592SIngo Weinhold 
44924df6592SIngo Weinhold 
45024df6592SIngo Weinhold /*!	Called when the real-time clock has been changed.
45124df6592SIngo Weinhold 
452f4b088a9SPawel Dziepak 	The caller must hold \c sUserTimerLock. Optionally the caller may also
45324df6592SIngo Weinhold 	hold \c sAbsoluteRealTimeTimersLock.
45424df6592SIngo Weinhold */
45524df6592SIngo Weinhold void
45624df6592SIngo Weinhold RealTimeUserTimer::TimeWarped()
45724df6592SIngo Weinhold {
45824df6592SIngo Weinhold 	ASSERT(fScheduled && fAbsolute);
45924df6592SIngo Weinhold 
46024df6592SIngo Weinhold 	// get the new real-time offset
46124df6592SIngo Weinhold 	bigtime_t oldRealTimeOffset = fRealTimeOffset;
46224df6592SIngo Weinhold 	fRealTimeOffset = rtc_boot_time();
46324df6592SIngo Weinhold 	if (fRealTimeOffset == oldRealTimeOffset)
46424df6592SIngo Weinhold 		return;
46524df6592SIngo Weinhold 
46624df6592SIngo Weinhold 	// cancel the kernel timer and reschedule it
467f4b088a9SPawel Dziepak 	CancelTimer();
46824df6592SIngo Weinhold 
46924df6592SIngo Weinhold 	fNextTime += oldRealTimeOffset - fRealTimeOffset;
47024df6592SIngo Weinhold 
47124df6592SIngo Weinhold 	ScheduleKernelTimer(system_time(), fInterval > 0);
47224df6592SIngo Weinhold }
47324df6592SIngo Weinhold 
47424df6592SIngo Weinhold 
47524df6592SIngo Weinhold void
47624df6592SIngo Weinhold RealTimeUserTimer::HandleTimer()
47724df6592SIngo Weinhold {
47824df6592SIngo Weinhold 	SystemTimeUserTimer::HandleTimer();
47924df6592SIngo Weinhold 
48024df6592SIngo Weinhold 	// remove from global list, if no longer scheduled
48124df6592SIngo Weinhold 	if (!fScheduled && fAbsolute) {
48224df6592SIngo Weinhold 		SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
48324df6592SIngo Weinhold 		sAbsoluteRealTimeTimers.Remove(this);
48424df6592SIngo Weinhold 	}
48524df6592SIngo Weinhold }
48624df6592SIngo Weinhold 
48724df6592SIngo Weinhold 
48824df6592SIngo Weinhold // #pragma mark - TeamTimeUserTimer
48924df6592SIngo Weinhold 
49024df6592SIngo Weinhold 
49124df6592SIngo Weinhold TeamTimeUserTimer::TeamTimeUserTimer(team_id teamID)
49224df6592SIngo Weinhold 	:
49324df6592SIngo Weinhold 	fTeamID(teamID),
49424df6592SIngo Weinhold 	fTeam(NULL)
49524df6592SIngo Weinhold {
49624df6592SIngo Weinhold }
49724df6592SIngo Weinhold 
49824df6592SIngo Weinhold 
49924df6592SIngo Weinhold TeamTimeUserTimer::~TeamTimeUserTimer()
50024df6592SIngo Weinhold {
50124df6592SIngo Weinhold 	ASSERT(fTeam == NULL);
50224df6592SIngo Weinhold }
50324df6592SIngo Weinhold 
50424df6592SIngo Weinhold 
50524df6592SIngo Weinhold void
50624df6592SIngo Weinhold TeamTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
50724df6592SIngo Weinhold 	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
50824df6592SIngo Weinhold {
509f4b088a9SPawel Dziepak 	InterruptsWriteSequentialLocker locker(sUserTimerLock);
51072addc62SPawel Dziepak 	SpinLocker timeLocker(fTeam != NULL ? &fTeam->time_lock : NULL);
51124df6592SIngo Weinhold 
51224df6592SIngo Weinhold 	// get the current time, but only if needed
51324df6592SIngo Weinhold 	bool nowValid = fTeam != NULL;
51424df6592SIngo Weinhold 	bigtime_t now = nowValid ? fTeam->CPUTime(false) : 0;
51524df6592SIngo Weinhold 
51624df6592SIngo Weinhold 	// Cancel the old timer, if still scheduled, and get the previous values.
51724df6592SIngo Weinhold 	if (fTeam != NULL) {
51824df6592SIngo Weinhold 		if (fScheduled) {
519f4b088a9SPawel Dziepak 			CancelTimer();
52024df6592SIngo Weinhold 			fScheduled = false;
52124df6592SIngo Weinhold 		}
52224df6592SIngo Weinhold 
52324df6592SIngo Weinhold 		_oldRemainingTime = fNextTime - now;
52424df6592SIngo Weinhold 		_oldInterval = fInterval;
52524df6592SIngo Weinhold 
52624df6592SIngo Weinhold 		fTeam->UserTimerDeactivated(this);
52724df6592SIngo Weinhold 		fTeam->ReleaseReference();
52824df6592SIngo Weinhold 		fTeam = NULL;
52924df6592SIngo Weinhold 	} else {
53024df6592SIngo Weinhold 		_oldRemainingTime = B_INFINITE_TIMEOUT;
53124df6592SIngo Weinhold 		_oldInterval = 0;
53224df6592SIngo Weinhold 	}
53324df6592SIngo Weinhold 
53424df6592SIngo Weinhold 	// schedule the new timer
53524df6592SIngo Weinhold 	fNextTime = nextTime;
53624df6592SIngo Weinhold 	fInterval = interval;
53724df6592SIngo Weinhold 	fOverrunCount = 0;
53824df6592SIngo Weinhold 
53924df6592SIngo Weinhold 	if (fNextTime == B_INFINITE_TIMEOUT)
54024df6592SIngo Weinhold 		return;
54124df6592SIngo Weinhold 
54224df6592SIngo Weinhold 	// Get the team. If it doesn't exist anymore, just don't schedule the
54324df6592SIngo Weinhold 	// timer anymore.
54472addc62SPawel Dziepak 	Team* newTeam = Team::Get(fTeamID);
54572addc62SPawel Dziepak 	if (newTeam == NULL) {
54672addc62SPawel Dziepak 		fTeam = NULL;
54724df6592SIngo Weinhold 		return;
54872addc62SPawel Dziepak 	} else if (fTeam == NULL)
54972addc62SPawel Dziepak 		timeLocker.SetTo(newTeam->time_lock, false);
55072addc62SPawel Dziepak 	fTeam = newTeam;
55124df6592SIngo Weinhold 
55224df6592SIngo Weinhold 	fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
55324df6592SIngo Weinhold 
55424df6592SIngo Weinhold 	// convert relative to absolute timeouts
55524df6592SIngo Weinhold 	if (!fAbsolute) {
55624df6592SIngo Weinhold 		if (!nowValid)
55724df6592SIngo Weinhold 			now = fTeam->CPUTime(false);
55824df6592SIngo Weinhold 		fNextTime += now;
55924df6592SIngo Weinhold 	}
56024df6592SIngo Weinhold 
56124df6592SIngo Weinhold 	fTeam->UserTimerActivated(this);
56224df6592SIngo Weinhold 
56324df6592SIngo Weinhold 	// schedule/udpate the kernel timer
56424df6592SIngo Weinhold 	Update(NULL);
56524df6592SIngo Weinhold }
56624df6592SIngo Weinhold 
56724df6592SIngo Weinhold 
56824df6592SIngo Weinhold void
56924df6592SIngo Weinhold TeamTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
57024df6592SIngo Weinhold 	uint32& _overrunCount)
57124df6592SIngo Weinhold {
572f4b088a9SPawel Dziepak 	uint32 count;
573f4b088a9SPawel Dziepak 	do {
574f4b088a9SPawel Dziepak 		count = acquire_read_seqlock(&sUserTimerLock);
57524df6592SIngo Weinhold 
57624df6592SIngo Weinhold 		if (fTeam != NULL) {
57772addc62SPawel Dziepak 			InterruptsSpinLocker timeLocker(fTeam->time_lock);
57824df6592SIngo Weinhold 			_remainingTime = fNextTime - fTeam->CPUTime(false);
57924df6592SIngo Weinhold 			_interval = fInterval;
58024df6592SIngo Weinhold 		} else {
58124df6592SIngo Weinhold 			_remainingTime = B_INFINITE_TIMEOUT;
58224df6592SIngo Weinhold 			_interval = 0;
58324df6592SIngo Weinhold 		}
58424df6592SIngo Weinhold 
58524df6592SIngo Weinhold 		_overrunCount = fOverrunCount;
586f4b088a9SPawel Dziepak 	} while (!release_read_seqlock(&sUserTimerLock, count));
58724df6592SIngo Weinhold }
58824df6592SIngo Weinhold 
58924df6592SIngo Weinhold 
59024df6592SIngo Weinhold /*!	Deactivates the timer, if it is activated.
59124df6592SIngo Weinhold 
59272addc62SPawel Dziepak 	The caller must hold \c time_lock and \c sUserTimerLock.
59324df6592SIngo Weinhold */
59424df6592SIngo Weinhold void
59524df6592SIngo Weinhold TeamTimeUserTimer::Deactivate()
59624df6592SIngo Weinhold {
59724df6592SIngo Weinhold 	if (fTeam == NULL)
59824df6592SIngo Weinhold 		return;
59924df6592SIngo Weinhold 
60024df6592SIngo Weinhold 	// unschedule, if scheduled
60124df6592SIngo Weinhold 	if (fScheduled) {
602f4b088a9SPawel Dziepak 		CancelTimer();
60324df6592SIngo Weinhold 		fScheduled = false;
60424df6592SIngo Weinhold 	}
60524df6592SIngo Weinhold 
60624df6592SIngo Weinhold 	// deactivate
60724df6592SIngo Weinhold 	fTeam->UserTimerDeactivated(this);
60824df6592SIngo Weinhold 	fTeam->ReleaseReference();
60924df6592SIngo Weinhold 	fTeam = NULL;
61024df6592SIngo Weinhold }
61124df6592SIngo Weinhold 
61224df6592SIngo Weinhold 
61324df6592SIngo Weinhold /*!	Starts/stops the timer as necessary, if it is active.
61424df6592SIngo Weinhold 
61524df6592SIngo Weinhold 	Called whenever threads of the team whose CPU time is referred to by the
61624df6592SIngo Weinhold 	timer are scheduled or unscheduled (or leave the team), or when the timer
61724df6592SIngo Weinhold 	was just set. Schedules a kernel timer for the remaining time, respectively
61824df6592SIngo Weinhold 	cancels it.
61924df6592SIngo Weinhold 
62072addc62SPawel Dziepak 	The caller must hold \c time_lock and \c sUserTimerLock.
62124df6592SIngo Weinhold 
62224df6592SIngo Weinhold 	\param unscheduledThread If not \c NULL, this is the thread that is
62324df6592SIngo Weinhold 		currently running and which is in the process of being unscheduled.
62424df6592SIngo Weinhold */
62524df6592SIngo Weinhold void
62624df6592SIngo Weinhold TeamTimeUserTimer::Update(Thread* unscheduledThread)
62724df6592SIngo Weinhold {
62824df6592SIngo Weinhold 	if (fTeam == NULL)
62924df6592SIngo Weinhold 		return;
63024df6592SIngo Weinhold 
63124df6592SIngo Weinhold 	// determine how many of the team's threads are currently running
63224df6592SIngo Weinhold 	fRunningThreads = 0;
63324df6592SIngo Weinhold 	int32 cpuCount = smp_get_num_cpus();
63424df6592SIngo Weinhold 	for (int32 i = 0; i < cpuCount; i++) {
63524df6592SIngo Weinhold 		Thread* thread = gCPU[i].running_thread;
63624df6592SIngo Weinhold 		if (thread != unscheduledThread && thread->team == fTeam)
63724df6592SIngo Weinhold 			fRunningThreads++;
63824df6592SIngo Weinhold 	}
63924df6592SIngo Weinhold 
64024df6592SIngo Weinhold 	_Update(unscheduledThread != NULL);
64124df6592SIngo Weinhold }
64224df6592SIngo Weinhold 
64324df6592SIngo Weinhold 
64424df6592SIngo Weinhold /*!	Called when the team's CPU time clock which this timer refers to has been
64524df6592SIngo Weinhold 	set.
64624df6592SIngo Weinhold 
64772addc62SPawel Dziepak 	The caller must hold \c time_lock and \c sUserTimerLock.
64824df6592SIngo Weinhold 
64924df6592SIngo Weinhold 	\param changedBy The value by which the clock has changed.
65024df6592SIngo Weinhold */
65124df6592SIngo Weinhold void
65224df6592SIngo Weinhold TeamTimeUserTimer::TimeWarped(bigtime_t changedBy)
65324df6592SIngo Weinhold {
65424df6592SIngo Weinhold 	if (fTeam == NULL || changedBy == 0)
65524df6592SIngo Weinhold 		return;
65624df6592SIngo Weinhold 
65724df6592SIngo Weinhold 	// If this is a relative timer, adjust fNextTime by the value the clock has
65824df6592SIngo Weinhold 	// changed.
65924df6592SIngo Weinhold 	if (!fAbsolute)
66024df6592SIngo Weinhold 		fNextTime += changedBy;
66124df6592SIngo Weinhold 
66224df6592SIngo Weinhold 	// reschedule the kernel timer
66324df6592SIngo Weinhold 	_Update(false);
66424df6592SIngo Weinhold }
66524df6592SIngo Weinhold 
66624df6592SIngo Weinhold 
66724df6592SIngo Weinhold void
66824df6592SIngo Weinhold TeamTimeUserTimer::HandleTimer()
66924df6592SIngo Weinhold {
67024df6592SIngo Weinhold 	UserTimer::HandleTimer();
67124df6592SIngo Weinhold 
67224df6592SIngo Weinhold 	// If the timer is not periodic, it is no longer active. Otherwise
67324df6592SIngo Weinhold 	// reschedule the kernel timer.
67424df6592SIngo Weinhold 	if (fTeam != NULL) {
67524df6592SIngo Weinhold 		if (fInterval == 0) {
67624df6592SIngo Weinhold 			fTeam->UserTimerDeactivated(this);
67724df6592SIngo Weinhold 			fTeam->ReleaseReference();
67824df6592SIngo Weinhold 			fTeam = NULL;
67924df6592SIngo Weinhold 		} else {
68024df6592SIngo Weinhold 			UpdatePeriodicStartTime();
68124df6592SIngo Weinhold 			_Update(false);
68224df6592SIngo Weinhold 		}
68324df6592SIngo Weinhold 	}
68424df6592SIngo Weinhold }
68524df6592SIngo Weinhold 
68624df6592SIngo Weinhold 
68724df6592SIngo Weinhold /*!	Schedules/cancels the kernel timer as necessary.
68824df6592SIngo Weinhold 
68924df6592SIngo Weinhold 	\c fRunningThreads must be up-to-date.
69072addc62SPawel Dziepak 	The caller must hold \c time_lock and \c sUserTimerLock.
69124df6592SIngo Weinhold 
69224df6592SIngo Weinhold 	\param unscheduling \c true, when the current thread is in the process of
69324df6592SIngo Weinhold 		being unscheduled.
69424df6592SIngo Weinhold */
69524df6592SIngo Weinhold void
69624df6592SIngo Weinhold TeamTimeUserTimer::_Update(bool unscheduling)
69724df6592SIngo Weinhold {
69824df6592SIngo Weinhold 	// unschedule the kernel timer, if scheduled
69924df6592SIngo Weinhold 	if (fScheduled)
700f4b088a9SPawel Dziepak 		CancelTimer();
70124df6592SIngo Weinhold 
70224df6592SIngo Weinhold 	// if no more threads are running, we're done
70324df6592SIngo Weinhold 	if (fRunningThreads == 0) {
70424df6592SIngo Weinhold 		fScheduled = false;
70524df6592SIngo Weinhold 		return;
70624df6592SIngo Weinhold 	}
70724df6592SIngo Weinhold 
70824df6592SIngo Weinhold 	// There are still threads running. Reschedule the kernel timer.
70924df6592SIngo Weinhold 	bigtime_t now = fTeam->CPUTime(unscheduling);
71024df6592SIngo Weinhold 
71124df6592SIngo Weinhold 	// If periodic, check whether the start time is too far in the past.
71224df6592SIngo Weinhold 	if (fInterval > 0)
71324df6592SIngo Weinhold 		CheckPeriodicOverrun(now);
71424df6592SIngo Weinhold 
71524df6592SIngo Weinhold 	if (fNextTime > now) {
71624df6592SIngo Weinhold 		fTimer.schedule_time = system_time()
71724df6592SIngo Weinhold 			+ (fNextTime - now + fRunningThreads - 1) / fRunningThreads;
71824df6592SIngo Weinhold 		// check for overflow
71924df6592SIngo Weinhold 		if (fTimer.schedule_time < 0)
72024df6592SIngo Weinhold 			fTimer.schedule_time = B_INFINITE_TIMEOUT;
72124df6592SIngo Weinhold 	} else
72224df6592SIngo Weinhold 		fTimer.schedule_time = 0;
72324df6592SIngo Weinhold 	fTimer.period = 0;
72424df6592SIngo Weinhold 		// We reschedule periodic timers manually in HandleTimer() to avoid
72524df6592SIngo Weinhold 		// rounding errors.
72624df6592SIngo Weinhold 
72724df6592SIngo Weinhold 	add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time,
728d8fcc8a8SPawel Dziepak 		B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES);
72924df6592SIngo Weinhold 		// We use B_TIMER_USE_TIMER_STRUCT_TIMES, so period remains 0, which
73024df6592SIngo Weinhold 		// our base class expects.
73124df6592SIngo Weinhold 
73224df6592SIngo Weinhold 	fScheduled = true;
73324df6592SIngo Weinhold }
73424df6592SIngo Weinhold 
73524df6592SIngo Weinhold 
73624df6592SIngo Weinhold // #pragma mark - TeamUserTimeUserTimer
73724df6592SIngo Weinhold 
73824df6592SIngo Weinhold 
73924df6592SIngo Weinhold TeamUserTimeUserTimer::TeamUserTimeUserTimer(team_id teamID)
74024df6592SIngo Weinhold 	:
74124df6592SIngo Weinhold 	fTeamID(teamID),
74224df6592SIngo Weinhold 	fTeam(NULL)
74324df6592SIngo Weinhold {
74424df6592SIngo Weinhold }
74524df6592SIngo Weinhold 
74624df6592SIngo Weinhold 
74724df6592SIngo Weinhold TeamUserTimeUserTimer::~TeamUserTimeUserTimer()
74824df6592SIngo Weinhold {
74924df6592SIngo Weinhold 	ASSERT(fTeam == NULL);
75024df6592SIngo Weinhold }
75124df6592SIngo Weinhold 
75224df6592SIngo Weinhold 
75324df6592SIngo Weinhold void
75424df6592SIngo Weinhold TeamUserTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
75524df6592SIngo Weinhold 	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
75624df6592SIngo Weinhold {
757f4b088a9SPawel Dziepak 	InterruptsWriteSequentialLocker locker(sUserTimerLock);
75872addc62SPawel Dziepak 	SpinLocker timeLocker(fTeam != NULL ? &fTeam->time_lock : NULL);
75924df6592SIngo Weinhold 
76024df6592SIngo Weinhold 	// get the current time, but only if needed
76124df6592SIngo Weinhold 	bool nowValid = fTeam != NULL;
76224df6592SIngo Weinhold 	bigtime_t now = nowValid ? fTeam->UserCPUTime() : 0;
76324df6592SIngo Weinhold 
76424df6592SIngo Weinhold 	// Cancel the old timer, if still active, and get the previous values.
76524df6592SIngo Weinhold 	if (fTeam != NULL) {
76624df6592SIngo Weinhold 		_oldRemainingTime = fNextTime - now;
76724df6592SIngo Weinhold 		_oldInterval = fInterval;
76824df6592SIngo Weinhold 
76924df6592SIngo Weinhold 		fTeam->UserTimerDeactivated(this);
77024df6592SIngo Weinhold 		fTeam->ReleaseReference();
77124df6592SIngo Weinhold 		fTeam = NULL;
77224df6592SIngo Weinhold 	} else {
77324df6592SIngo Weinhold 		_oldRemainingTime = B_INFINITE_TIMEOUT;
77424df6592SIngo Weinhold 		_oldInterval = 0;
77524df6592SIngo Weinhold 	}
77624df6592SIngo Weinhold 
77724df6592SIngo Weinhold 	// schedule the new timer
77824df6592SIngo Weinhold 	fNextTime = nextTime;
77924df6592SIngo Weinhold 	fInterval = interval;
78024df6592SIngo Weinhold 	fOverrunCount = 0;
78124df6592SIngo Weinhold 
78224df6592SIngo Weinhold 	if (fNextTime == B_INFINITE_TIMEOUT)
78324df6592SIngo Weinhold 		return;
78424df6592SIngo Weinhold 
78524df6592SIngo Weinhold 	// Get the team. If it doesn't exist anymore, just don't schedule the
78624df6592SIngo Weinhold 	// timer anymore.
78772addc62SPawel Dziepak 	Team* newTeam = Team::Get(fTeamID);
78872addc62SPawel Dziepak 	if (newTeam == NULL) {
78972addc62SPawel Dziepak 		fTeam = NULL;
79024df6592SIngo Weinhold 		return;
79172addc62SPawel Dziepak 	} else if (fTeam == NULL)
79272addc62SPawel Dziepak 		timeLocker.SetTo(newTeam->time_lock, false);
79372addc62SPawel Dziepak 	fTeam = newTeam;
79424df6592SIngo Weinhold 
79524df6592SIngo Weinhold 	// convert relative to absolute timeouts
79624df6592SIngo Weinhold 	if ((flags & B_RELATIVE_TIMEOUT) != 0) {
79724df6592SIngo Weinhold 		if (!nowValid)
79824df6592SIngo Weinhold 			now = fTeam->CPUTime(false);
79924df6592SIngo Weinhold 		fNextTime += now;
80024df6592SIngo Weinhold 	}
80124df6592SIngo Weinhold 
80224df6592SIngo Weinhold 	fTeam->UserTimerActivated(this);
80324df6592SIngo Weinhold 
80424df6592SIngo Weinhold 	// fire the event, if already timed out
80524df6592SIngo Weinhold 	Check();
80624df6592SIngo Weinhold }
80724df6592SIngo Weinhold 
80824df6592SIngo Weinhold 
80924df6592SIngo Weinhold void
81024df6592SIngo Weinhold TeamUserTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
81124df6592SIngo Weinhold 	uint32& _overrunCount)
81224df6592SIngo Weinhold {
813f4b088a9SPawel Dziepak 	uint32 count;
814f4b088a9SPawel Dziepak 	do {
815f4b088a9SPawel Dziepak 		count = acquire_read_seqlock(&sUserTimerLock);
81624df6592SIngo Weinhold 
81724df6592SIngo Weinhold 		if (fTeam != NULL) {
81872addc62SPawel Dziepak 			InterruptsSpinLocker timeLocker(fTeam->time_lock);
81924df6592SIngo Weinhold 			_remainingTime = fNextTime - fTeam->UserCPUTime();
82024df6592SIngo Weinhold 			_interval = fInterval;
82124df6592SIngo Weinhold 		} else {
82224df6592SIngo Weinhold 			_remainingTime = B_INFINITE_TIMEOUT;
82324df6592SIngo Weinhold 			_interval = 0;
82424df6592SIngo Weinhold 		}
82524df6592SIngo Weinhold 
82624df6592SIngo Weinhold 		_overrunCount = fOverrunCount;
827f4b088a9SPawel Dziepak 	} while (!release_read_seqlock(&sUserTimerLock, count));
82824df6592SIngo Weinhold }
82924df6592SIngo Weinhold 
83024df6592SIngo Weinhold 
83124df6592SIngo Weinhold /*!	Deactivates the timer, if it is activated.
83224df6592SIngo Weinhold 
83372addc62SPawel Dziepak 	The caller must hold \c time_lock and \c sUserTimerLock.
83424df6592SIngo Weinhold */
83524df6592SIngo Weinhold void
83624df6592SIngo Weinhold TeamUserTimeUserTimer::Deactivate()
83724df6592SIngo Weinhold {
83824df6592SIngo Weinhold 	if (fTeam == NULL)
83924df6592SIngo Weinhold 		return;
84024df6592SIngo Weinhold 
84124df6592SIngo Weinhold 	// deactivate
84224df6592SIngo Weinhold 	fTeam->UserTimerDeactivated(this);
84324df6592SIngo Weinhold 	fTeam->ReleaseReference();
84424df6592SIngo Weinhold 	fTeam = NULL;
84524df6592SIngo Weinhold }
84624df6592SIngo Weinhold 
84724df6592SIngo Weinhold 
84824df6592SIngo Weinhold /*!	Checks whether the timer is up, firing an event, if so.
84924df6592SIngo Weinhold 
85072addc62SPawel Dziepak 	The caller must hold \c time_lock and \c sUserTimerLock.
85124df6592SIngo Weinhold */
85224df6592SIngo Weinhold void
85324df6592SIngo Weinhold TeamUserTimeUserTimer::Check()
85424df6592SIngo Weinhold {
85524df6592SIngo Weinhold 	if (fTeam == NULL)
85624df6592SIngo Weinhold 		return;
85724df6592SIngo Weinhold 
85824df6592SIngo Weinhold 	// check whether we need to fire the event yet
85924df6592SIngo Weinhold 	bigtime_t now = fTeam->UserCPUTime();
86024df6592SIngo Weinhold 	if (now < fNextTime)
86124df6592SIngo Weinhold 		return;
86224df6592SIngo Weinhold 
86324df6592SIngo Weinhold 	HandleTimer();
86424df6592SIngo Weinhold 
86524df6592SIngo Weinhold 	// If the timer is not periodic, it is no longer active. Otherwise compute
86624df6592SIngo Weinhold 	// the event time.
86724df6592SIngo Weinhold 	if (fInterval == 0) {
86824df6592SIngo Weinhold 		fTeam->UserTimerDeactivated(this);
86924df6592SIngo Weinhold 		fTeam->ReleaseReference();
87024df6592SIngo Weinhold 		fTeam = NULL;
87124df6592SIngo Weinhold 		return;
87224df6592SIngo Weinhold 	}
87324df6592SIngo Weinhold 
87424df6592SIngo Weinhold 	// First validate fNextTime, then increment it, so that fNextTime is > now
87524df6592SIngo Weinhold 	// (CheckPeriodicOverrun() only makes it > now - fInterval).
87624df6592SIngo Weinhold 	CheckPeriodicOverrun(now);
87724df6592SIngo Weinhold 	fNextTime += fInterval;
87824df6592SIngo Weinhold 	fScheduled = true;
87924df6592SIngo Weinhold }
88024df6592SIngo Weinhold 
88124df6592SIngo Weinhold 
88224df6592SIngo Weinhold // #pragma mark - ThreadTimeUserTimer
88324df6592SIngo Weinhold 
88424df6592SIngo Weinhold 
88524df6592SIngo Weinhold ThreadTimeUserTimer::ThreadTimeUserTimer(thread_id threadID)
88624df6592SIngo Weinhold 	:
88724df6592SIngo Weinhold 	fThreadID(threadID),
88824df6592SIngo Weinhold 	fThread(NULL)
88924df6592SIngo Weinhold {
89024df6592SIngo Weinhold }
89124df6592SIngo Weinhold 
89224df6592SIngo Weinhold 
89324df6592SIngo Weinhold ThreadTimeUserTimer::~ThreadTimeUserTimer()
89424df6592SIngo Weinhold {
89524df6592SIngo Weinhold 	ASSERT(fThread == NULL);
89624df6592SIngo Weinhold }
89724df6592SIngo Weinhold 
89824df6592SIngo Weinhold 
89924df6592SIngo Weinhold void
90024df6592SIngo Weinhold ThreadTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
90124df6592SIngo Weinhold 	uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
90224df6592SIngo Weinhold {
903f4b088a9SPawel Dziepak 	InterruptsWriteSequentialLocker locker(sUserTimerLock);
90472addc62SPawel Dziepak 	SpinLocker timeLocker(fThread->time_lock);
90524df6592SIngo Weinhold 
90624df6592SIngo Weinhold 	// get the current time, but only if needed
90724df6592SIngo Weinhold 	bool nowValid = fThread != NULL;
90824df6592SIngo Weinhold 	bigtime_t now = nowValid ? fThread->CPUTime(false) : 0;
90924df6592SIngo Weinhold 
91024df6592SIngo Weinhold 	// Cancel the old timer, if still scheduled, and get the previous values.
91124df6592SIngo Weinhold 	if (fThread != NULL) {
91224df6592SIngo Weinhold 		if (fScheduled) {
913f4b088a9SPawel Dziepak 			CancelTimer();
91424df6592SIngo Weinhold 			fScheduled = false;
91524df6592SIngo Weinhold 		}
91624df6592SIngo Weinhold 
91724df6592SIngo Weinhold 		_oldRemainingTime = fNextTime - now;
91824df6592SIngo Weinhold 		_oldInterval = fInterval;
91924df6592SIngo Weinhold 
92024df6592SIngo Weinhold 		fThread->UserTimerDeactivated(this);
92124df6592SIngo Weinhold 		fThread->ReleaseReference();
92224df6592SIngo Weinhold 		fThread = NULL;
92324df6592SIngo Weinhold 	} else {
92424df6592SIngo Weinhold 		_oldRemainingTime = B_INFINITE_TIMEOUT;
92524df6592SIngo Weinhold 		_oldInterval = 0;
92624df6592SIngo Weinhold 	}
92724df6592SIngo Weinhold 
92824df6592SIngo Weinhold 	// schedule the new timer
92924df6592SIngo Weinhold 	fNextTime = nextTime;
93024df6592SIngo Weinhold 	fInterval = interval;
93124df6592SIngo Weinhold 	fOverrunCount = 0;
93224df6592SIngo Weinhold 
93324df6592SIngo Weinhold 	if (fNextTime == B_INFINITE_TIMEOUT)
93424df6592SIngo Weinhold 		return;
93524df6592SIngo Weinhold 
93624df6592SIngo Weinhold 	// Get the thread. If it doesn't exist anymore, just don't schedule the
93724df6592SIngo Weinhold 	// timer anymore.
93872addc62SPawel Dziepak 	Thread* newThread = Thread::Get(fThreadID);
93972addc62SPawel Dziepak 	if (newThread == NULL) {
94072addc62SPawel Dziepak 		fThread = NULL;
94124df6592SIngo Weinhold 		return;
94272addc62SPawel Dziepak 	} else if (fThread == NULL)
94372addc62SPawel Dziepak 		timeLocker.SetTo(newThread->time_lock, false);
94472addc62SPawel Dziepak 	fThread = newThread;
94524df6592SIngo Weinhold 
94624df6592SIngo Weinhold 	fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
94724df6592SIngo Weinhold 
94824df6592SIngo Weinhold 	// convert relative to absolute timeouts
94924df6592SIngo Weinhold 	if (!fAbsolute) {
95024df6592SIngo Weinhold 		if (!nowValid)
95124df6592SIngo Weinhold 			now = fThread->CPUTime(false);
95224df6592SIngo Weinhold 		fNextTime += now;
95324df6592SIngo Weinhold 	}
95424df6592SIngo Weinhold 
95524df6592SIngo Weinhold 	fThread->UserTimerActivated(this);
95624df6592SIngo Weinhold 
95724df6592SIngo Weinhold 	// If the thread is currently running, also schedule a kernel timer.
95824df6592SIngo Weinhold 	if (fThread->cpu != NULL)
95924df6592SIngo Weinhold 		Start();
96024df6592SIngo Weinhold }
96124df6592SIngo Weinhold 
96224df6592SIngo Weinhold 
96324df6592SIngo Weinhold void
96424df6592SIngo Weinhold ThreadTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
96524df6592SIngo Weinhold 	uint32& _overrunCount)
96624df6592SIngo Weinhold {
967f4b088a9SPawel Dziepak 	uint32 count;
968f4b088a9SPawel Dziepak 	do {
969f4b088a9SPawel Dziepak 		count = acquire_read_seqlock(&sUserTimerLock);
97024df6592SIngo Weinhold 
97124df6592SIngo Weinhold 		if (fThread != NULL) {
97272addc62SPawel Dziepak 			SpinLocker timeLocker(fThread->time_lock);
97324df6592SIngo Weinhold 			_remainingTime = fNextTime - fThread->CPUTime(false);
97424df6592SIngo Weinhold 			_interval = fInterval;
97524df6592SIngo Weinhold 		} else {
97624df6592SIngo Weinhold 			_remainingTime = B_INFINITE_TIMEOUT;
97724df6592SIngo Weinhold 			_interval = 0;
97824df6592SIngo Weinhold 		}
97924df6592SIngo Weinhold 
98024df6592SIngo Weinhold 		_overrunCount = fOverrunCount;
981f4b088a9SPawel Dziepak 	} while (!release_read_seqlock(&sUserTimerLock, count));
98224df6592SIngo Weinhold }
98324df6592SIngo Weinhold 
98424df6592SIngo Weinhold 
98524df6592SIngo Weinhold /*!	Deactivates the timer, if it is activated.
98624df6592SIngo Weinhold 
98772addc62SPawel Dziepak 	The caller must hold \c time_lock and \c sUserTimerLock.
98824df6592SIngo Weinhold */
98924df6592SIngo Weinhold void
99024df6592SIngo Weinhold ThreadTimeUserTimer::Deactivate()
99124df6592SIngo Weinhold {
99224df6592SIngo Weinhold 	if (fThread == NULL)
99324df6592SIngo Weinhold 		return;
99424df6592SIngo Weinhold 
99524df6592SIngo Weinhold 	// unschedule, if scheduled
99624df6592SIngo Weinhold 	if (fScheduled) {
997f4b088a9SPawel Dziepak 		CancelTimer();
99824df6592SIngo Weinhold 		fScheduled = false;
99924df6592SIngo Weinhold 	}
100024df6592SIngo Weinhold 
100124df6592SIngo Weinhold 	// deactivate
100224df6592SIngo Weinhold 	fThread->UserTimerDeactivated(this);
100324df6592SIngo Weinhold 	fThread->ReleaseReference();
100424df6592SIngo Weinhold 	fThread = NULL;
100524df6592SIngo Weinhold }
100624df6592SIngo Weinhold 
100724df6592SIngo Weinhold 
100824df6592SIngo Weinhold /*!	Starts the timer, if it is active.
100924df6592SIngo Weinhold 
101024df6592SIngo Weinhold 	Called when the thread whose CPU time is referred to by the timer is
101124df6592SIngo Weinhold 	scheduled, or, when the timer was just set and the thread is already
101224df6592SIngo Weinhold 	running. Schedules a kernel timer for the remaining time.
101324df6592SIngo Weinhold 
101472addc62SPawel Dziepak 	The caller must hold \c time_lock and \c sUserTimerLock.
101524df6592SIngo Weinhold */
101624df6592SIngo Weinhold void
101724df6592SIngo Weinhold ThreadTimeUserTimer::Start()
101824df6592SIngo Weinhold {
101924df6592SIngo Weinhold 	if (fThread == NULL)
102024df6592SIngo Weinhold 		return;
102124df6592SIngo Weinhold 
102224df6592SIngo Weinhold 	ASSERT(!fScheduled);
102324df6592SIngo Weinhold 
102424df6592SIngo Weinhold 	// add the kernel timer
102524df6592SIngo Weinhold 	bigtime_t now = fThread->CPUTime(false);
102624df6592SIngo Weinhold 
102724df6592SIngo Weinhold 	// If periodic, check whether the start time is too far in the past.
102824df6592SIngo Weinhold 	if (fInterval > 0)
102924df6592SIngo Weinhold 		CheckPeriodicOverrun(now);
103024df6592SIngo Weinhold 
103124df6592SIngo Weinhold 	if (fNextTime > now) {
103224df6592SIngo Weinhold 		fTimer.schedule_time = system_time() + fNextTime - now;
103324df6592SIngo Weinhold 		// check for overflow
103424df6592SIngo Weinhold 		if (fTimer.schedule_time < 0)
103524df6592SIngo Weinhold 			fTimer.schedule_time = B_INFINITE_TIMEOUT;
103624df6592SIngo Weinhold 	} else
103724df6592SIngo Weinhold 		fTimer.schedule_time = 0;
103824df6592SIngo Weinhold 	fTimer.period = 0;
103924df6592SIngo Weinhold 
1040d8fcc8a8SPawel Dziepak 	uint32 flags = B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES;
104124df6592SIngo Weinhold 	add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, flags);
104224df6592SIngo Weinhold 
104324df6592SIngo Weinhold 	fScheduled = true;
104424df6592SIngo Weinhold }
104524df6592SIngo Weinhold 
104624df6592SIngo Weinhold 
104724df6592SIngo Weinhold /*!	Stops the timer, if it is active.
104824df6592SIngo Weinhold 
104924df6592SIngo Weinhold 	Called when the thread whose CPU time is referred to by the timer is
105024df6592SIngo Weinhold 	unscheduled, or, when the timer is canceled.
105124df6592SIngo Weinhold 
1052f4b088a9SPawel Dziepak 	The caller must hold \c sUserTimerLock.
105324df6592SIngo Weinhold */
105424df6592SIngo Weinhold void
105524df6592SIngo Weinhold ThreadTimeUserTimer::Stop()
105624df6592SIngo Weinhold {
105724df6592SIngo Weinhold 	if (fThread == NULL)
105824df6592SIngo Weinhold 		return;
105924df6592SIngo Weinhold 
106024df6592SIngo Weinhold 	ASSERT(fScheduled);
106124df6592SIngo Weinhold 
106224df6592SIngo Weinhold 	// cancel the kernel timer
1063f4b088a9SPawel Dziepak 	CancelTimer();
106424df6592SIngo Weinhold 	fScheduled = false;
106524df6592SIngo Weinhold 
106624df6592SIngo Weinhold 	// TODO: To avoid odd race conditions, we should check the current time of
106724df6592SIngo Weinhold 	// the thread (ignoring the time since last_time) and manually fire the
106824df6592SIngo Weinhold 	// user event, if necessary.
106924df6592SIngo Weinhold }
107024df6592SIngo Weinhold 
107124df6592SIngo Weinhold 
107224df6592SIngo Weinhold /*!	Called when the team's CPU time clock which this timer refers to has been
107324df6592SIngo Weinhold 	set.
107424df6592SIngo Weinhold 
107572addc62SPawel Dziepak 	The caller must hold \c time_lock and \c sUserTimerLock.
107624df6592SIngo Weinhold 
107724df6592SIngo Weinhold 	\param changedBy The value by which the clock has changed.
107824df6592SIngo Weinhold */
107924df6592SIngo Weinhold void
108024df6592SIngo Weinhold ThreadTimeUserTimer::TimeWarped(bigtime_t changedBy)
108124df6592SIngo Weinhold {
108224df6592SIngo Weinhold 	if (fThread == NULL || changedBy == 0)
108324df6592SIngo Weinhold 		return;
108424df6592SIngo Weinhold 
108524df6592SIngo Weinhold 	// If this is a relative timer, adjust fNextTime by the value the clock has
108624df6592SIngo Weinhold 	// changed.
108724df6592SIngo Weinhold 	if (!fAbsolute)
108824df6592SIngo Weinhold 		fNextTime += changedBy;
108924df6592SIngo Weinhold 
109024df6592SIngo Weinhold 	// reschedule the kernel timer
109124df6592SIngo Weinhold 	if (fScheduled) {
109224df6592SIngo Weinhold 		Stop();
109324df6592SIngo Weinhold 		Start();
109424df6592SIngo Weinhold 	}
109524df6592SIngo Weinhold }
109624df6592SIngo Weinhold 
109724df6592SIngo Weinhold 
109824df6592SIngo Weinhold void
109924df6592SIngo Weinhold ThreadTimeUserTimer::HandleTimer()
110024df6592SIngo Weinhold {
110124df6592SIngo Weinhold 	UserTimer::HandleTimer();
110224df6592SIngo Weinhold 
110324df6592SIngo Weinhold 	if (fThread != NULL) {
110424df6592SIngo Weinhold 		// If the timer is periodic, reschedule the kernel timer. Otherwise it
110524df6592SIngo Weinhold 		// is no longer active.
110624df6592SIngo Weinhold 		if (fInterval > 0) {
110724df6592SIngo Weinhold 			UpdatePeriodicStartTime();
110824df6592SIngo Weinhold 			Start();
110924df6592SIngo Weinhold 		} else {
111024df6592SIngo Weinhold 			fThread->UserTimerDeactivated(this);
111124df6592SIngo Weinhold 			fThread->ReleaseReference();
111224df6592SIngo Weinhold 			fThread = NULL;
111324df6592SIngo Weinhold 		}
111424df6592SIngo Weinhold 	}
111524df6592SIngo Weinhold }
111624df6592SIngo Weinhold 
111724df6592SIngo Weinhold 
111824df6592SIngo Weinhold // #pragma mark - UserTimerList
111924df6592SIngo Weinhold 
112024df6592SIngo Weinhold 
112124df6592SIngo Weinhold UserTimerList::UserTimerList()
112224df6592SIngo Weinhold {
112324df6592SIngo Weinhold }
112424df6592SIngo Weinhold 
112524df6592SIngo Weinhold 
112624df6592SIngo Weinhold UserTimerList::~UserTimerList()
112724df6592SIngo Weinhold {
112824df6592SIngo Weinhold 	ASSERT(fTimers.IsEmpty());
112924df6592SIngo Weinhold }
113024df6592SIngo Weinhold 
113124df6592SIngo Weinhold 
113224df6592SIngo Weinhold /*!	Returns the user timer with the given ID.
113324df6592SIngo Weinhold 
113424df6592SIngo Weinhold 	\param id The timer's ID
113524df6592SIngo Weinhold 	\return The user timer with the given ID or \c NULL, if there is no such
113624df6592SIngo Weinhold 		timer.
113724df6592SIngo Weinhold */
113824df6592SIngo Weinhold UserTimer*
113924df6592SIngo Weinhold UserTimerList::TimerFor(int32 id) const
114024df6592SIngo Weinhold {
114124df6592SIngo Weinhold 	// TODO: Use a more efficient data structure. E.g. a sorted array.
114224df6592SIngo Weinhold 	for (TimerList::ConstIterator it = fTimers.GetIterator();
114324df6592SIngo Weinhold 			UserTimer* timer = it.Next();) {
114424df6592SIngo Weinhold 		if (timer->ID() == id)
114524df6592SIngo Weinhold 			return timer;
114624df6592SIngo Weinhold 	}
114724df6592SIngo Weinhold 
114824df6592SIngo Weinhold 	return NULL;
114924df6592SIngo Weinhold }
115024df6592SIngo Weinhold 
115124df6592SIngo Weinhold 
115224df6592SIngo Weinhold /*!	Adds the given user timer and assigns it an ID.
115324df6592SIngo Weinhold 
115424df6592SIngo Weinhold 	\param timer The timer to be added.
115524df6592SIngo Weinhold */
115624df6592SIngo Weinhold void
115724df6592SIngo Weinhold UserTimerList::AddTimer(UserTimer* timer)
115824df6592SIngo Weinhold {
115924df6592SIngo Weinhold 	int32 id = timer->ID();
116024df6592SIngo Weinhold 	if (id < 0) {
116124df6592SIngo Weinhold 		// user-defined timer -- find an usused ID
116224df6592SIngo Weinhold 		id = USER_TIMER_FIRST_USER_DEFINED_ID;
116324df6592SIngo Weinhold 		UserTimer* insertAfter = NULL;
116424df6592SIngo Weinhold 		for (TimerList::Iterator it = fTimers.GetIterator();
116524df6592SIngo Weinhold 				UserTimer* other = it.Next();) {
116624df6592SIngo Weinhold 			if (other->ID() > id)
116724df6592SIngo Weinhold 				break;
116824df6592SIngo Weinhold 			if (other->ID() == id)
116924df6592SIngo Weinhold 				id++;
117024df6592SIngo Weinhold 			insertAfter = other;
117124df6592SIngo Weinhold 		}
117224df6592SIngo Weinhold 
117324df6592SIngo Weinhold 		// insert the timer
117424df6592SIngo Weinhold 		timer->SetID(id);
117524df6592SIngo Weinhold 		fTimers.InsertAfter(insertAfter, timer);
117624df6592SIngo Weinhold 	} else {
117724df6592SIngo Weinhold 		// default timer -- find the insertion point
117824df6592SIngo Weinhold 		UserTimer* insertAfter = NULL;
117924df6592SIngo Weinhold 		for (TimerList::Iterator it = fTimers.GetIterator();
118024df6592SIngo Weinhold 				UserTimer* other = it.Next();) {
118124df6592SIngo Weinhold 			if (other->ID() > id)
118224df6592SIngo Weinhold 				break;
118324df6592SIngo Weinhold 			if (other->ID() == id) {
118424df6592SIngo Weinhold 				panic("UserTimerList::AddTimer(): timer with ID %" B_PRId32
118524df6592SIngo Weinhold 					" already exists!", id);
118624df6592SIngo Weinhold 			}
118724df6592SIngo Weinhold 			insertAfter = other;
118824df6592SIngo Weinhold 		}
118924df6592SIngo Weinhold 
119024df6592SIngo Weinhold 		// insert the timer
119124df6592SIngo Weinhold 		fTimers.InsertAfter(insertAfter, timer);
119224df6592SIngo Weinhold 	}
119324df6592SIngo Weinhold }
119424df6592SIngo Weinhold 
119524df6592SIngo Weinhold 
119624df6592SIngo Weinhold /*!	Deletes all (or all user-defined) user timers.
119724df6592SIngo Weinhold 
119824df6592SIngo Weinhold 	\param userDefinedOnly If \c true, only the user-defined timers are deleted,
119924df6592SIngo Weinhold 		otherwise all timers are deleted.
120024df6592SIngo Weinhold 	\return The number of user-defined timers that were removed and deleted.
120124df6592SIngo Weinhold */
120224df6592SIngo Weinhold int32
120324df6592SIngo Weinhold UserTimerList::DeleteTimers(bool userDefinedOnly)
120424df6592SIngo Weinhold {
120524df6592SIngo Weinhold 	int32 userDefinedCount = 0;
120624df6592SIngo Weinhold 
120724df6592SIngo Weinhold 	for (TimerList::Iterator it = fTimers.GetIterator();
120824df6592SIngo Weinhold 			UserTimer* timer = it.Next();) {
120924df6592SIngo Weinhold 		if (timer->ID() < USER_TIMER_FIRST_USER_DEFINED_ID) {
121024df6592SIngo Weinhold 			if (userDefinedOnly)
121124df6592SIngo Weinhold 				continue;
121224df6592SIngo Weinhold 		} else
121324df6592SIngo Weinhold 			userDefinedCount++;
121424df6592SIngo Weinhold 
121524df6592SIngo Weinhold 		// remove, cancel, and delete the timer
121624df6592SIngo Weinhold 		it.Remove();
121724df6592SIngo Weinhold 		timer->Cancel();
121824df6592SIngo Weinhold 		delete timer;
121924df6592SIngo Weinhold 	}
122024df6592SIngo Weinhold 
122124df6592SIngo Weinhold 	return userDefinedCount;
122224df6592SIngo Weinhold }
122324df6592SIngo Weinhold 
122424df6592SIngo Weinhold 
122524df6592SIngo Weinhold // #pragma mark - private
122624df6592SIngo Weinhold 
122724df6592SIngo Weinhold 
122824df6592SIngo Weinhold static int32
122924df6592SIngo Weinhold create_timer(clockid_t clockID, int32 timerID, Team* team, Thread* thread,
123024df6592SIngo Weinhold 	uint32 flags, const struct sigevent& event,
123124df6592SIngo Weinhold 	ThreadCreationAttributes* threadAttributes, bool isDefaultEvent)
123224df6592SIngo Weinhold {
123324df6592SIngo Weinhold 	// create the timer object
123424df6592SIngo Weinhold 	UserTimer* timer;
123524df6592SIngo Weinhold 	switch (clockID) {
123624df6592SIngo Weinhold 		case CLOCK_MONOTONIC:
123724df6592SIngo Weinhold 			timer = new(std::nothrow) SystemTimeUserTimer;
123824df6592SIngo Weinhold 			break;
123924df6592SIngo Weinhold 
124024df6592SIngo Weinhold 		case CLOCK_REALTIME:
124124df6592SIngo Weinhold 			timer = new(std::nothrow) RealTimeUserTimer;
124224df6592SIngo Weinhold 			break;
124324df6592SIngo Weinhold 
124424df6592SIngo Weinhold 		case CLOCK_THREAD_CPUTIME_ID:
124524df6592SIngo Weinhold 			timer = new(std::nothrow) ThreadTimeUserTimer(
124624df6592SIngo Weinhold 				thread_get_current_thread()->id);
124724df6592SIngo Weinhold 			break;
124824df6592SIngo Weinhold 
124924df6592SIngo Weinhold 		case CLOCK_PROCESS_CPUTIME_ID:
125024df6592SIngo Weinhold 			if (team == NULL)
125124df6592SIngo Weinhold 				return B_BAD_VALUE;
125224df6592SIngo Weinhold 			timer = new(std::nothrow) TeamTimeUserTimer(team->id);
125324df6592SIngo Weinhold 			break;
125424df6592SIngo Weinhold 
125524df6592SIngo Weinhold 		case CLOCK_PROCESS_USER_CPUTIME_ID:
125624df6592SIngo Weinhold 			if (team == NULL)
125724df6592SIngo Weinhold 				return B_BAD_VALUE;
125824df6592SIngo Weinhold 			timer = new(std::nothrow) TeamUserTimeUserTimer(team->id);
125924df6592SIngo Weinhold 			break;
126024df6592SIngo Weinhold 
126124df6592SIngo Weinhold 		default:
126224df6592SIngo Weinhold 		{
126324df6592SIngo Weinhold 			// The clock ID is a ID of the team whose CPU time the clock refers
126424df6592SIngo Weinhold 			// to. Check whether the team exists and we have permission to
126524df6592SIngo Weinhold 			// access its clock.
126624df6592SIngo Weinhold 			if (clockID <= 0)
126724df6592SIngo Weinhold 				return B_BAD_VALUE;
126824df6592SIngo Weinhold 			if (clockID == team_get_kernel_team_id())
126924df6592SIngo Weinhold 				return B_NOT_ALLOWED;
127024df6592SIngo Weinhold 
127124df6592SIngo Weinhold 			Team* timedTeam = Team::GetAndLock(clockID);
127224df6592SIngo Weinhold 			if (timedTeam == NULL)
127324df6592SIngo Weinhold 				return B_BAD_VALUE;
127424df6592SIngo Weinhold 
127524df6592SIngo Weinhold 			uid_t uid = geteuid();
127624df6592SIngo Weinhold 			uid_t teamUID = timedTeam->effective_uid;
127724df6592SIngo Weinhold 
127824df6592SIngo Weinhold 			timedTeam->UnlockAndReleaseReference();
127924df6592SIngo Weinhold 
128024df6592SIngo Weinhold 			if (uid != 0 && uid != teamUID)
128124df6592SIngo Weinhold 				return B_NOT_ALLOWED;
128224df6592SIngo Weinhold 
128324df6592SIngo Weinhold 			timer = new(std::nothrow) TeamTimeUserTimer(clockID);
128424df6592SIngo Weinhold 			break;
128524df6592SIngo Weinhold 		}
128624df6592SIngo Weinhold 	}
128724df6592SIngo Weinhold 
128824df6592SIngo Weinhold 	if (timer == NULL)
128924df6592SIngo Weinhold 		return B_NO_MEMORY;
129024df6592SIngo Weinhold 	ObjectDeleter<UserTimer> timerDeleter(timer);
129124df6592SIngo Weinhold 
129224df6592SIngo Weinhold 	if (timerID >= 0)
129324df6592SIngo Weinhold 		timer->SetID(timerID);
129424df6592SIngo Weinhold 
129524df6592SIngo Weinhold 	SignalEvent* signalEvent = NULL;
129624df6592SIngo Weinhold 
129724df6592SIngo Weinhold 	switch (event.sigev_notify) {
129824df6592SIngo Weinhold 		case SIGEV_NONE:
129924df6592SIngo Weinhold 			// the timer's event remains NULL
130024df6592SIngo Weinhold 			break;
130124df6592SIngo Weinhold 
130224df6592SIngo Weinhold 		case SIGEV_SIGNAL:
130324df6592SIngo Weinhold 		{
130424df6592SIngo Weinhold 			if (event.sigev_signo <= 0 || event.sigev_signo > MAX_SIGNAL_NUMBER)
130524df6592SIngo Weinhold 				return B_BAD_VALUE;
130624df6592SIngo Weinhold 
130724df6592SIngo Weinhold 			if (thread != NULL && (flags & USER_TIMER_SIGNAL_THREAD) != 0) {
130824df6592SIngo Weinhold 				// The signal shall be sent to the thread.
130924df6592SIngo Weinhold 				signalEvent = ThreadSignalEvent::Create(thread,
131024df6592SIngo Weinhold 					event.sigev_signo, SI_TIMER, 0, team->id);
131124df6592SIngo Weinhold 			} else {
131224df6592SIngo Weinhold 				// The signal shall be sent to the team.
131324df6592SIngo Weinhold 				signalEvent = TeamSignalEvent::Create(team, event.sigev_signo,
131424df6592SIngo Weinhold 					SI_TIMER, 0);
131524df6592SIngo Weinhold 			}
131624df6592SIngo Weinhold 
131724df6592SIngo Weinhold 			if (signalEvent == NULL)
131824df6592SIngo Weinhold 				return B_NO_MEMORY;
131924df6592SIngo Weinhold 
132024df6592SIngo Weinhold 			timer->SetEvent(signalEvent);
132124df6592SIngo Weinhold 			break;
132224df6592SIngo Weinhold 		}
132324df6592SIngo Weinhold 
132424df6592SIngo Weinhold 		case SIGEV_THREAD:
132524df6592SIngo Weinhold 		{
132624df6592SIngo Weinhold 			if (threadAttributes == NULL)
132724df6592SIngo Weinhold 				return B_BAD_VALUE;
132824df6592SIngo Weinhold 
132924df6592SIngo Weinhold 			CreateThreadEvent* event
133024df6592SIngo Weinhold 				= CreateThreadEvent::Create(*threadAttributes);
133124df6592SIngo Weinhold 			if (event == NULL)
133224df6592SIngo Weinhold 				return B_NO_MEMORY;
133324df6592SIngo Weinhold 
133424df6592SIngo Weinhold 			timer->SetEvent(event);
133524df6592SIngo Weinhold 			break;
133624df6592SIngo Weinhold 		}
133724df6592SIngo Weinhold 
133824df6592SIngo Weinhold 		default:
133924df6592SIngo Weinhold 			return B_BAD_VALUE;
134024df6592SIngo Weinhold 	}
134124df6592SIngo Weinhold 
134224df6592SIngo Weinhold 	// add it to the team/thread
134324df6592SIngo Weinhold 	TimerLocker timerLocker;
134424df6592SIngo Weinhold 	timerLocker.Lock(team, thread);
134524df6592SIngo Weinhold 
134624df6592SIngo Weinhold 	status_t error = thread != NULL
134724df6592SIngo Weinhold 		? thread->AddUserTimer(timer) : team->AddUserTimer(timer);
134824df6592SIngo Weinhold 	if (error != B_OK)
134924df6592SIngo Weinhold 		return error;
135024df6592SIngo Weinhold 
135124df6592SIngo Weinhold 	// set a signal event's user value
135224df6592SIngo Weinhold 	if (signalEvent != NULL) {
135324df6592SIngo Weinhold 		// If no sigevent structure was given, use the timer ID.
135424df6592SIngo Weinhold 		union sigval signalValue = event.sigev_value;
135524df6592SIngo Weinhold 		if (isDefaultEvent)
135624df6592SIngo Weinhold 			signalValue.sival_int = timer->ID();
135724df6592SIngo Weinhold 
135824df6592SIngo Weinhold 		signalEvent->SetUserValue(signalValue);
135924df6592SIngo Weinhold 	}
136024df6592SIngo Weinhold 
136124df6592SIngo Weinhold 	return timerDeleter.Detach()->ID();
136224df6592SIngo Weinhold }
136324df6592SIngo Weinhold 
136424df6592SIngo Weinhold 
136524df6592SIngo Weinhold /*!	Called when the CPU time clock of the given thread has been set.
136624df6592SIngo Weinhold 
136772addc62SPawel Dziepak 	The caller must hold \c time_lock.
136824df6592SIngo Weinhold 
136924df6592SIngo Weinhold 	\param thread The thread whose CPU time clock has been set.
137024df6592SIngo Weinhold 	\param changedBy The value by which the CPU time clock has changed
137124df6592SIngo Weinhold 		(new = old + changedBy).
137224df6592SIngo Weinhold */
137324df6592SIngo Weinhold static void
137424df6592SIngo Weinhold thread_clock_changed(Thread* thread, bigtime_t changedBy)
137524df6592SIngo Weinhold {
137624df6592SIngo Weinhold 	for (ThreadTimeUserTimerList::ConstIterator it
137724df6592SIngo Weinhold 				= thread->CPUTimeUserTimerIterator();
137824df6592SIngo Weinhold 			ThreadTimeUserTimer* timer = it.Next();) {
137924df6592SIngo Weinhold 		timer->TimeWarped(changedBy);
138024df6592SIngo Weinhold 	}
138124df6592SIngo Weinhold }
138224df6592SIngo Weinhold 
138324df6592SIngo Weinhold 
138424df6592SIngo Weinhold /*!	Called when the CPU time clock of the given team has been set.
138524df6592SIngo Weinhold 
138672addc62SPawel Dziepak 	The caller must hold \c time_lock.
138724df6592SIngo Weinhold 
138824df6592SIngo Weinhold 	\param team The team whose CPU time clock has been set.
138924df6592SIngo Weinhold 	\param changedBy The value by which the CPU time clock has changed
139024df6592SIngo Weinhold 		(new = old + changedBy).
139124df6592SIngo Weinhold */
139224df6592SIngo Weinhold static void
139324df6592SIngo Weinhold team_clock_changed(Team* team, bigtime_t changedBy)
139424df6592SIngo Weinhold {
139524df6592SIngo Weinhold 	for (TeamTimeUserTimerList::ConstIterator it
139624df6592SIngo Weinhold 				= team->CPUTimeUserTimerIterator();
139724df6592SIngo Weinhold 			TeamTimeUserTimer* timer = it.Next();) {
139824df6592SIngo Weinhold 		timer->TimeWarped(changedBy);
139924df6592SIngo Weinhold 	}
140024df6592SIngo Weinhold }
140124df6592SIngo Weinhold 
140224df6592SIngo Weinhold 
140324df6592SIngo Weinhold // #pragma mark - kernel private
140424df6592SIngo Weinhold 
140524df6592SIngo Weinhold 
140624df6592SIngo Weinhold /*!	Creates the pre-defined user timers for the given thread.
140724df6592SIngo Weinhold 	The thread may not have been added to its team yet, hence the team must be
140824df6592SIngo Weinhold 	passed
140924df6592SIngo Weinhold 
141024df6592SIngo Weinhold 	\param team The thread's (future) team.
141124df6592SIngo Weinhold 	\param thread The thread whose pre-defined timers shall be created.
141224df6592SIngo Weinhold 	\return \c B_OK, when everything when fine, another error code otherwise.
141324df6592SIngo Weinhold */
141424df6592SIngo Weinhold status_t
141524df6592SIngo Weinhold user_timer_create_thread_timers(Team* team, Thread* thread)
141624df6592SIngo Weinhold {
141724df6592SIngo Weinhold 	// create a real time user timer
141824df6592SIngo Weinhold 	struct sigevent event;
141924df6592SIngo Weinhold 	event.sigev_notify = SIGEV_SIGNAL;
142024df6592SIngo Weinhold 	event.sigev_signo = SIGALRM;
142124df6592SIngo Weinhold 
142224df6592SIngo Weinhold 	int32 timerID = create_timer(CLOCK_MONOTONIC, USER_TIMER_REAL_TIME_ID,
142324df6592SIngo Weinhold 		team, thread, USER_TIMER_SIGNAL_THREAD, event, NULL, true);
142424df6592SIngo Weinhold 	if (timerID < 0)
142524df6592SIngo Weinhold 		return timerID;
142624df6592SIngo Weinhold 
142724df6592SIngo Weinhold 	return B_OK;
142824df6592SIngo Weinhold }
142924df6592SIngo Weinhold 
143024df6592SIngo Weinhold 
143124df6592SIngo Weinhold /*!	Creates the pre-defined user timers for the given team.
143224df6592SIngo Weinhold 
143324df6592SIngo Weinhold 	\param team The team whose pre-defined timers shall be created.
143424df6592SIngo Weinhold 	\return \c B_OK, when everything when fine, another error code otherwise.
143524df6592SIngo Weinhold */
143624df6592SIngo Weinhold status_t
143724df6592SIngo Weinhold user_timer_create_team_timers(Team* team)
143824df6592SIngo Weinhold {
143924df6592SIngo Weinhold 	// create a real time user timer
144024df6592SIngo Weinhold 	struct sigevent event;
144124df6592SIngo Weinhold 	event.sigev_notify = SIGEV_SIGNAL;
144224df6592SIngo Weinhold 	event.sigev_signo = SIGALRM;
144324df6592SIngo Weinhold 
144424df6592SIngo Weinhold 	int32 timerID = create_timer(CLOCK_MONOTONIC, USER_TIMER_REAL_TIME_ID,
144524df6592SIngo Weinhold 		team, NULL, 0, event, NULL, true);
144624df6592SIngo Weinhold 	if (timerID < 0)
144724df6592SIngo Weinhold 		return timerID;
144824df6592SIngo Weinhold 
144924df6592SIngo Weinhold 	// create a total CPU time user timer
145024df6592SIngo Weinhold 	event.sigev_notify = SIGEV_SIGNAL;
145124df6592SIngo Weinhold 	event.sigev_signo = SIGPROF;
145224df6592SIngo Weinhold 
145324df6592SIngo Weinhold 	timerID = create_timer(CLOCK_PROCESS_CPUTIME_ID,
145424df6592SIngo Weinhold 		USER_TIMER_TEAM_TOTAL_TIME_ID, team, NULL, 0, event, NULL, true);
145524df6592SIngo Weinhold 	if (timerID < 0)
145624df6592SIngo Weinhold 		return timerID;
145724df6592SIngo Weinhold 
145824df6592SIngo Weinhold 	// create a user CPU time user timer
145924df6592SIngo Weinhold 	event.sigev_notify = SIGEV_SIGNAL;
146024df6592SIngo Weinhold 	event.sigev_signo = SIGVTALRM;
146124df6592SIngo Weinhold 
146224df6592SIngo Weinhold 	timerID = create_timer(CLOCK_PROCESS_USER_CPUTIME_ID,
146324df6592SIngo Weinhold 		USER_TIMER_TEAM_USER_TIME_ID, team, NULL, 0, event, NULL, true);
146424df6592SIngo Weinhold 	if (timerID < 0)
146524df6592SIngo Weinhold 		return timerID;
146624df6592SIngo Weinhold 
146724df6592SIngo Weinhold 	return B_OK;
146824df6592SIngo Weinhold }
146924df6592SIngo Weinhold 
147024df6592SIngo Weinhold 
147124df6592SIngo Weinhold status_t
147224df6592SIngo Weinhold user_timer_get_clock(clockid_t clockID, bigtime_t& _time)
147324df6592SIngo Weinhold {
147424df6592SIngo Weinhold 	switch (clockID) {
147524df6592SIngo Weinhold 		case CLOCK_MONOTONIC:
147624df6592SIngo Weinhold 			_time = system_time();
147724df6592SIngo Weinhold 			return B_OK;
147824df6592SIngo Weinhold 
147924df6592SIngo Weinhold 		case CLOCK_REALTIME:
148024df6592SIngo Weinhold 			_time = real_time_clock_usecs();
148124df6592SIngo Weinhold 			return B_OK;
148224df6592SIngo Weinhold 
148324df6592SIngo Weinhold 		case CLOCK_THREAD_CPUTIME_ID:
148424df6592SIngo Weinhold 		{
148524df6592SIngo Weinhold 			Thread* thread = thread_get_current_thread();
148672addc62SPawel Dziepak 			InterruptsSpinLocker timeLocker(thread->time_lock);
148724df6592SIngo Weinhold 			_time = thread->CPUTime(false);
148824df6592SIngo Weinhold 			return B_OK;
148924df6592SIngo Weinhold 		}
149024df6592SIngo Weinhold 
149124df6592SIngo Weinhold 		case CLOCK_PROCESS_USER_CPUTIME_ID:
149224df6592SIngo Weinhold 		{
149324df6592SIngo Weinhold 			Team* team = thread_get_current_thread()->team;
149472addc62SPawel Dziepak 			InterruptsSpinLocker timeLocker(team->time_lock);
149524df6592SIngo Weinhold 			_time = team->UserCPUTime();
149624df6592SIngo Weinhold 			return B_OK;
149724df6592SIngo Weinhold 		}
149824df6592SIngo Weinhold 
149924df6592SIngo Weinhold 		case CLOCK_PROCESS_CPUTIME_ID:
150024df6592SIngo Weinhold 		default:
150124df6592SIngo Weinhold 		{
150224df6592SIngo Weinhold 			// get the ID of the target team (or the respective placeholder)
150324df6592SIngo Weinhold 			team_id teamID;
150424df6592SIngo Weinhold 			if (clockID == CLOCK_PROCESS_CPUTIME_ID) {
150524df6592SIngo Weinhold 				teamID = B_CURRENT_TEAM;
150624df6592SIngo Weinhold 			} else {
150724df6592SIngo Weinhold 				if (clockID < 0)
150824df6592SIngo Weinhold 					return B_BAD_VALUE;
150924df6592SIngo Weinhold 				if (clockID == team_get_kernel_team_id())
151024df6592SIngo Weinhold 					return B_NOT_ALLOWED;
151124df6592SIngo Weinhold 
151224df6592SIngo Weinhold 				teamID = clockID;
151324df6592SIngo Weinhold 			}
151424df6592SIngo Weinhold 
151524df6592SIngo Weinhold 			// get the team
151624df6592SIngo Weinhold 			Team* team = Team::Get(teamID);
151724df6592SIngo Weinhold 			if (team == NULL)
151824df6592SIngo Weinhold 				return B_BAD_VALUE;
151924df6592SIngo Weinhold 			BReference<Team> teamReference(team, true);
152024df6592SIngo Weinhold 
152124df6592SIngo Weinhold 			// get the time
152272addc62SPawel Dziepak 			InterruptsSpinLocker timeLocker(team->time_lock);
152324df6592SIngo Weinhold 			_time = team->CPUTime(false);
152424df6592SIngo Weinhold 
152524df6592SIngo Weinhold 			return B_OK;
152624df6592SIngo Weinhold 		}
152724df6592SIngo Weinhold 	}
152824df6592SIngo Weinhold }
152924df6592SIngo Weinhold 
153024df6592SIngo Weinhold 
153124df6592SIngo Weinhold void
153224df6592SIngo Weinhold user_timer_real_time_clock_changed()
153324df6592SIngo Weinhold {
153424df6592SIngo Weinhold 	// we need to update all absolute real-time timers
1535f4b088a9SPawel Dziepak 	InterruptsWriteSequentialLocker locker(sUserTimerLock);
153624df6592SIngo Weinhold 	SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
153724df6592SIngo Weinhold 
153824df6592SIngo Weinhold 	for (RealTimeUserTimerList::Iterator it
153924df6592SIngo Weinhold 				= sAbsoluteRealTimeTimers.GetIterator();
154024df6592SIngo Weinhold 			RealTimeUserTimer* timer = it.Next();) {
154124df6592SIngo Weinhold 		timer->TimeWarped();
154224df6592SIngo Weinhold 	}
154324df6592SIngo Weinhold }
154424df6592SIngo Weinhold 
154524df6592SIngo Weinhold 
154624df6592SIngo Weinhold void
154724df6592SIngo Weinhold user_timer_stop_cpu_timers(Thread* thread, Thread* nextThread)
154824df6592SIngo Weinhold {
154924df6592SIngo Weinhold 	// stop thread timers
155024df6592SIngo Weinhold 	for (ThreadTimeUserTimerList::ConstIterator it
155124df6592SIngo Weinhold 				= thread->CPUTimeUserTimerIterator();
155224df6592SIngo Weinhold 			ThreadTimeUserTimer* timer = it.Next();) {
155324df6592SIngo Weinhold 		timer->Stop();
155424df6592SIngo Weinhold 	}
155524df6592SIngo Weinhold 
155624df6592SIngo Weinhold 	// update team timers
155724df6592SIngo Weinhold 	if (nextThread == NULL || nextThread->team != thread->team) {
155824df6592SIngo Weinhold 		for (TeamTimeUserTimerList::ConstIterator it
155924df6592SIngo Weinhold 					= thread->team->CPUTimeUserTimerIterator();
156024df6592SIngo Weinhold 				TeamTimeUserTimer* timer = it.Next();) {
156124df6592SIngo Weinhold 			timer->Update(thread);
156224df6592SIngo Weinhold 		}
156324df6592SIngo Weinhold 	}
156424df6592SIngo Weinhold }
156524df6592SIngo Weinhold 
156624df6592SIngo Weinhold 
156724df6592SIngo Weinhold void
156824df6592SIngo Weinhold user_timer_continue_cpu_timers(Thread* thread, Thread* previousThread)
156924df6592SIngo Weinhold {
157024df6592SIngo Weinhold 	// update team timers
157124df6592SIngo Weinhold 	if (previousThread == NULL || previousThread->team != thread->team) {
157224df6592SIngo Weinhold 		for (TeamTimeUserTimerList::ConstIterator it
157324df6592SIngo Weinhold 					= thread->team->CPUTimeUserTimerIterator();
157424df6592SIngo Weinhold 				TeamTimeUserTimer* timer = it.Next();) {
157524df6592SIngo Weinhold 			timer->Update(NULL);
157624df6592SIngo Weinhold 		}
157724df6592SIngo Weinhold 	}
157824df6592SIngo Weinhold 
157924df6592SIngo Weinhold 	// start thread timers
158024df6592SIngo Weinhold 	for (ThreadTimeUserTimerList::ConstIterator it
158124df6592SIngo Weinhold 				= thread->CPUTimeUserTimerIterator();
158224df6592SIngo Weinhold 			ThreadTimeUserTimer* timer = it.Next();) {
158324df6592SIngo Weinhold 		timer->Start();
158424df6592SIngo Weinhold 	}
158524df6592SIngo Weinhold }
158624df6592SIngo Weinhold 
158724df6592SIngo Weinhold 
158824df6592SIngo Weinhold void
158924df6592SIngo Weinhold user_timer_check_team_user_timers(Team* team)
159024df6592SIngo Weinhold {
159124df6592SIngo Weinhold 	for (TeamUserTimeUserTimerList::ConstIterator it
159224df6592SIngo Weinhold 				= team->UserTimeUserTimerIterator();
159324df6592SIngo Weinhold 			TeamUserTimeUserTimer* timer = it.Next();) {
159424df6592SIngo Weinhold 		timer->Check();
159524df6592SIngo Weinhold 	}
159624df6592SIngo Weinhold }
159724df6592SIngo Weinhold 
159824df6592SIngo Weinhold 
159924df6592SIngo Weinhold // #pragma mark - syscalls
160024df6592SIngo Weinhold 
160124df6592SIngo Weinhold 
160224df6592SIngo Weinhold status_t
160324df6592SIngo Weinhold _user_get_clock(clockid_t clockID, bigtime_t* userTime)
160424df6592SIngo Weinhold {
160524df6592SIngo Weinhold 	// get the time
160624df6592SIngo Weinhold 	bigtime_t time;
160724df6592SIngo Weinhold 	status_t error = user_timer_get_clock(clockID, time);
160824df6592SIngo Weinhold 	if (error != B_OK)
160924df6592SIngo Weinhold 		return error;
161024df6592SIngo Weinhold 
161124df6592SIngo Weinhold 	// copy the value back to userland
161224df6592SIngo Weinhold 	if (userTime == NULL || !IS_USER_ADDRESS(userTime)
161324df6592SIngo Weinhold 		|| user_memcpy(userTime, &time, sizeof(time)) != B_OK) {
161424df6592SIngo Weinhold 		return B_BAD_ADDRESS;
161524df6592SIngo Weinhold 	}
161624df6592SIngo Weinhold 
161724df6592SIngo Weinhold 	return B_OK;
161824df6592SIngo Weinhold }
161924df6592SIngo Weinhold 
162024df6592SIngo Weinhold 
162124df6592SIngo Weinhold status_t
162224df6592SIngo Weinhold _user_set_clock(clockid_t clockID, bigtime_t time)
162324df6592SIngo Weinhold {
162424df6592SIngo Weinhold 	switch (clockID) {
162524df6592SIngo Weinhold 		case CLOCK_MONOTONIC:
162624df6592SIngo Weinhold 			return B_BAD_VALUE;
162724df6592SIngo Weinhold 
162824df6592SIngo Weinhold 		case CLOCK_REALTIME:
162924df6592SIngo Weinhold 			// only root may set the time
163024df6592SIngo Weinhold 			if (geteuid() != 0)
163124df6592SIngo Weinhold 				return B_NOT_ALLOWED;
163224df6592SIngo Weinhold 
163324df6592SIngo Weinhold 			set_real_time_clock_usecs(time);
163424df6592SIngo Weinhold 			return B_OK;
163524df6592SIngo Weinhold 
163624df6592SIngo Weinhold 		case CLOCK_THREAD_CPUTIME_ID:
163724df6592SIngo Weinhold 		{
163824df6592SIngo Weinhold 			Thread* thread = thread_get_current_thread();
163972addc62SPawel Dziepak 			InterruptsSpinLocker timeLocker(thread->time_lock);
164024df6592SIngo Weinhold 			bigtime_t diff = time - thread->CPUTime(false);
164124df6592SIngo Weinhold 			thread->cpu_clock_offset += diff;
164224df6592SIngo Weinhold 
164324df6592SIngo Weinhold 			thread_clock_changed(thread, diff);
164424df6592SIngo Weinhold 			return B_OK;
164524df6592SIngo Weinhold 		}
164624df6592SIngo Weinhold 
164724df6592SIngo Weinhold 		case CLOCK_PROCESS_USER_CPUTIME_ID:
164824df6592SIngo Weinhold 			// not supported -- this clock is an Haiku-internal extension
164924df6592SIngo Weinhold 			return B_BAD_VALUE;
165024df6592SIngo Weinhold 
165124df6592SIngo Weinhold 		case CLOCK_PROCESS_CPUTIME_ID:
165224df6592SIngo Weinhold 		default:
165324df6592SIngo Weinhold 		{
165424df6592SIngo Weinhold 			// get the ID of the target team (or the respective placeholder)
165524df6592SIngo Weinhold 			team_id teamID;
165624df6592SIngo Weinhold 			if (clockID == CLOCK_PROCESS_CPUTIME_ID) {
165724df6592SIngo Weinhold 				teamID = B_CURRENT_TEAM;
165824df6592SIngo Weinhold 			} else {
165924df6592SIngo Weinhold 				if (clockID < 0)
166024df6592SIngo Weinhold 					return B_BAD_VALUE;
166124df6592SIngo Weinhold 				if (clockID == team_get_kernel_team_id())
166224df6592SIngo Weinhold 					return B_NOT_ALLOWED;
166324df6592SIngo Weinhold 
166424df6592SIngo Weinhold 				teamID = clockID;
166524df6592SIngo Weinhold 			}
166624df6592SIngo Weinhold 
166724df6592SIngo Weinhold 			// get the team
166824df6592SIngo Weinhold 			Team* team = Team::Get(teamID);
166924df6592SIngo Weinhold 			if (team == NULL)
167024df6592SIngo Weinhold 				return B_BAD_VALUE;
167124df6592SIngo Weinhold 			BReference<Team> teamReference(team, true);
167224df6592SIngo Weinhold 
167324df6592SIngo Weinhold 			// set the time offset
167472addc62SPawel Dziepak 			InterruptsSpinLocker timeLocker(team->time_lock);
167524df6592SIngo Weinhold 			bigtime_t diff = time - team->CPUTime(false);
167624df6592SIngo Weinhold 			team->cpu_clock_offset += diff;
167724df6592SIngo Weinhold 
167824df6592SIngo Weinhold 			team_clock_changed(team, diff);
167924df6592SIngo Weinhold 			return B_OK;
168024df6592SIngo Weinhold 		}
168124df6592SIngo Weinhold 	}
168224df6592SIngo Weinhold 
168324df6592SIngo Weinhold 	return B_OK;
168424df6592SIngo Weinhold }
168524df6592SIngo Weinhold 
168624df6592SIngo Weinhold 
168724df6592SIngo Weinhold int32
168824df6592SIngo Weinhold _user_create_timer(clockid_t clockID, thread_id threadID, uint32 flags,
168924df6592SIngo Weinhold 	const struct sigevent* userEvent,
169024df6592SIngo Weinhold 	const thread_creation_attributes* userThreadAttributes)
169124df6592SIngo Weinhold {
169224df6592SIngo Weinhold 	// copy the sigevent structure from userland
169324df6592SIngo Weinhold 	struct sigevent event;
169424df6592SIngo Weinhold 	if (userEvent != NULL) {
169524df6592SIngo Weinhold 		if (!IS_USER_ADDRESS(userEvent)
169624df6592SIngo Weinhold 			|| user_memcpy(&event, userEvent, sizeof(event)) != B_OK) {
169724df6592SIngo Weinhold 			return B_BAD_ADDRESS;
169824df6592SIngo Weinhold 		}
169924df6592SIngo Weinhold 	} else {
170024df6592SIngo Weinhold 		// none given -- use defaults
170124df6592SIngo Weinhold 		event.sigev_notify = SIGEV_SIGNAL;
170224df6592SIngo Weinhold 		event.sigev_signo = SIGALRM;
170324df6592SIngo Weinhold 	}
170424df6592SIngo Weinhold 
170524df6592SIngo Weinhold 	// copy thread creation attributes from userland, if specified
170624df6592SIngo Weinhold 	char nameBuffer[B_OS_NAME_LENGTH];
170724df6592SIngo Weinhold 	ThreadCreationAttributes threadAttributes;
170824df6592SIngo Weinhold 	if (event.sigev_notify == SIGEV_THREAD) {
170924df6592SIngo Weinhold 		status_t error = threadAttributes.InitFromUserAttributes(
171024df6592SIngo Weinhold 			userThreadAttributes, nameBuffer);
171124df6592SIngo Weinhold 		if (error != B_OK)
171224df6592SIngo Weinhold 			return error;
171324df6592SIngo Weinhold 	}
171424df6592SIngo Weinhold 
171524df6592SIngo Weinhold 	// get team and thread
171624df6592SIngo Weinhold 	Team* team = thread_get_current_thread()->team;
171724df6592SIngo Weinhold 	Thread* thread = NULL;
171824df6592SIngo Weinhold 	if (threadID >= 0) {
171924df6592SIngo Weinhold 		thread = Thread::GetAndLock(threadID);
172024df6592SIngo Weinhold 		if (thread == NULL)
172124df6592SIngo Weinhold 			return B_BAD_THREAD_ID;
172224df6592SIngo Weinhold 
172324df6592SIngo Weinhold 		thread->Unlock();
172424df6592SIngo Weinhold 	}
172524df6592SIngo Weinhold 	BReference<Thread> threadReference(thread, true);
172624df6592SIngo Weinhold 
172724df6592SIngo Weinhold 	// create the timer
172824df6592SIngo Weinhold 	return create_timer(clockID, -1, team, thread, flags, event,
172924df6592SIngo Weinhold 		userThreadAttributes != NULL ? &threadAttributes : NULL,
173024df6592SIngo Weinhold 		userEvent == NULL);
173124df6592SIngo Weinhold }
173224df6592SIngo Weinhold 
173324df6592SIngo Weinhold 
173424df6592SIngo Weinhold status_t
173524df6592SIngo Weinhold _user_delete_timer(int32 timerID, thread_id threadID)
173624df6592SIngo Weinhold {
173724df6592SIngo Weinhold 	// can only delete user-defined timers
173824df6592SIngo Weinhold 	if (timerID < USER_TIMER_FIRST_USER_DEFINED_ID)
173924df6592SIngo Weinhold 		return B_BAD_VALUE;
174024df6592SIngo Weinhold 
174124df6592SIngo Weinhold 	// get the timer
174224df6592SIngo Weinhold 	TimerLocker timerLocker;
174324df6592SIngo Weinhold 	UserTimer* timer;
174424df6592SIngo Weinhold 	status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
174524df6592SIngo Weinhold 	if (error != B_OK)
174624df6592SIngo Weinhold 		return error;
174724df6592SIngo Weinhold 
174824df6592SIngo Weinhold 	// cancel, remove, and delete it
174924df6592SIngo Weinhold 	timer->Cancel();
175024df6592SIngo Weinhold 
175124df6592SIngo Weinhold 	if (threadID >= 0)
175224df6592SIngo Weinhold 		timerLocker.thread->RemoveUserTimer(timer);
175324df6592SIngo Weinhold 	else
175424df6592SIngo Weinhold 		timerLocker.team->RemoveUserTimer(timer);
175524df6592SIngo Weinhold 
175624df6592SIngo Weinhold 	delete timer;
175724df6592SIngo Weinhold 
175824df6592SIngo Weinhold 	return B_OK;
175924df6592SIngo Weinhold }
176024df6592SIngo Weinhold 
176124df6592SIngo Weinhold 
176224df6592SIngo Weinhold status_t
176324df6592SIngo Weinhold _user_get_timer(int32 timerID, thread_id threadID,
176424df6592SIngo Weinhold 	struct user_timer_info* userInfo)
176524df6592SIngo Weinhold {
176624df6592SIngo Weinhold 	// get the timer
176724df6592SIngo Weinhold 	TimerLocker timerLocker;
176824df6592SIngo Weinhold 	UserTimer* timer;
176924df6592SIngo Weinhold 	status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
177024df6592SIngo Weinhold 	if (error != B_OK)
177124df6592SIngo Weinhold 		return error;
177224df6592SIngo Weinhold 
177324df6592SIngo Weinhold 	// get the info
177424df6592SIngo Weinhold 	user_timer_info info;
177524df6592SIngo Weinhold 	timer->GetInfo(info.remaining_time, info.interval, info.overrun_count);
177624df6592SIngo Weinhold 
177724df6592SIngo Weinhold 	// Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid
177824df6592SIngo Weinhold 	// value.
177924df6592SIngo Weinhold 	if (info.remaining_time <= 0)
178024df6592SIngo Weinhold 		info.remaining_time = 1;
178124df6592SIngo Weinhold 
178224df6592SIngo Weinhold 	timerLocker.Unlock();
178324df6592SIngo Weinhold 
178424df6592SIngo Weinhold 	// copy it back to userland
178524df6592SIngo Weinhold 	if (userInfo != NULL
178624df6592SIngo Weinhold 		&& (!IS_USER_ADDRESS(userInfo)
178724df6592SIngo Weinhold 			|| user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) {
178824df6592SIngo Weinhold 		return B_BAD_ADDRESS;
178924df6592SIngo Weinhold 	}
179024df6592SIngo Weinhold 
179124df6592SIngo Weinhold 	return B_OK;
179224df6592SIngo Weinhold }
179324df6592SIngo Weinhold 
179424df6592SIngo Weinhold 
179524df6592SIngo Weinhold status_t
179624df6592SIngo Weinhold _user_set_timer(int32 timerID, thread_id threadID, bigtime_t startTime,
179724df6592SIngo Weinhold 	bigtime_t interval, uint32 flags, struct user_timer_info* userOldInfo)
179824df6592SIngo Weinhold {
179924df6592SIngo Weinhold 	// check the values
180024df6592SIngo Weinhold 	if (startTime < 0 || interval < 0)
180124df6592SIngo Weinhold 		return B_BAD_VALUE;
180224df6592SIngo Weinhold 
180324df6592SIngo Weinhold 	// get the timer
180424df6592SIngo Weinhold 	TimerLocker timerLocker;
180524df6592SIngo Weinhold 	UserTimer* timer;
180624df6592SIngo Weinhold 	status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
180724df6592SIngo Weinhold 	if (error != B_OK)
180824df6592SIngo Weinhold 		return error;
180924df6592SIngo Weinhold 
181024df6592SIngo Weinhold 	// schedule the timer
181124df6592SIngo Weinhold 	user_timer_info oldInfo;
181224df6592SIngo Weinhold 	timer->Schedule(startTime, interval, flags, oldInfo.remaining_time,
181324df6592SIngo Weinhold 		oldInfo.interval);
181424df6592SIngo Weinhold 
181524df6592SIngo Weinhold 	// Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid
181624df6592SIngo Weinhold 	// value.
181724df6592SIngo Weinhold 	if (oldInfo.remaining_time <= 0)
181824df6592SIngo Weinhold 		oldInfo.remaining_time = 1;
181924df6592SIngo Weinhold 
182024df6592SIngo Weinhold 	timerLocker.Unlock();
182124df6592SIngo Weinhold 
182224df6592SIngo Weinhold 	// copy back the old info
182324df6592SIngo Weinhold 	if (userOldInfo != NULL
182424df6592SIngo Weinhold 		&& (!IS_USER_ADDRESS(userOldInfo)
182524df6592SIngo Weinhold 			|| user_memcpy(userOldInfo, &oldInfo, sizeof(oldInfo)) != B_OK)) {
182624df6592SIngo Weinhold 		return B_BAD_ADDRESS;
182724df6592SIngo Weinhold 	}
182824df6592SIngo Weinhold 
182924df6592SIngo Weinhold 	return B_OK;
183024df6592SIngo Weinhold }
1831