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