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 Weinholdstatic const bigtime_t kMinPeriodicTimerInterval = 100;
2924df6592SIngo Weinhold
3024df6592SIngo Weinholdstatic RealTimeUserTimerList sAbsoluteRealTimeTimers;
3124df6592SIngo Weinholdstatic spinlock sAbsoluteRealTimeTimersLock = B_SPINLOCK_INITIALIZER;
3224df6592SIngo Weinhold
33f4b088a9SPawel Dziepakstatic seqlock sUserTimerLock = B_SEQLOCK_INITIALIZER;
34f4b088a9SPawel Dziepak
3524df6592SIngo Weinhold
3624df6592SIngo Weinhold// #pragma mark - TimerLocker
3724df6592SIngo Weinhold
3824df6592SIngo Weinhold
3924df6592SIngo Weinholdnamespace {
4024df6592SIngo Weinhold
4124df6592SIngo Weinholdstruct TimerLocker {
4224df6592SIngo Weinhold	Team*	team;
4324df6592SIngo Weinhold	Thread*	thread;
4424df6592SIngo Weinhold
4524df6592SIngo Weinhold	TimerLocker()
4624df6592SIngo Weinhold		:
4724df6592SIngo Weinhold		team(NULL),
4824df6592SIngo Weinhold		thread(NULL)
4924df6592SIngo Weinhold	{
5024df6592SIngo Weinhold	}
5124df6592SIngo Weinhold
5224df6592SIngo Weinhold	~TimerLocker()
5324df6592SIngo Weinhold	{
5424df6592SIngo Weinhold		Unlock();
5524df6592SIngo Weinhold	}
5624df6592SIngo Weinhold
5724df6592SIngo 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
7324df6592SIngo 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
9624df6592SIngo 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
11524df6592SIngo WeinholdUserTimer::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
13024df6592SIngo WeinholdUserTimer::~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 Weinholdvoid
16524df6592SIngo WeinholdUserTimer::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
19124df6592SIngo WeinholdUserTimer::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 Weinholdvoid
21424df6592SIngo WeinholdUserTimer::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 Weinholdvoid
23824df6592SIngo WeinholdUserTimer::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 Weinholdvoid
26424df6592SIngo WeinholdUserTimer::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 Dziepakvoid
282f4b088a9SPawel DziepakUserTimer::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 Weinholdvoid
29624df6592SIngo WeinholdSystemTimeUserTimer::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 Weinholdvoid
33324df6592SIngo WeinholdSystemTimeUserTimer::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
340f4b088a9SPawel Dziepak		if (fScheduled) {
341f4b088a9SPawel Dziepak			_remainingTime = fNextTime - system_time();
342f4b088a9SPawel Dziepak			_interval = fInterval;
343f4b088a9SPawel Dziepak		} else {
344f4b088a9SPawel Dziepak			_remainingTime = B_INFINITE_TIMEOUT;
345f4b088a9SPawel Dziepak			_interval = 0;
346f4b088a9SPawel Dziepak		}
34724df6592SIngo Weinhold
348f4b088a9SPawel Dziepak		_overrunCount = fOverrunCount;
349f4b088a9SPawel Dziepak	} while (!release_read_seqlock(&sUserTimerLock, count));
35024df6592SIngo Weinhold}
35124df6592SIngo Weinhold
35224df6592SIngo Weinhold
35324df6592SIngo Weinholdvoid
35424df6592SIngo WeinholdSystemTimeUserTimer::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 Weinholdvoid
37524df6592SIngo WeinholdSystemTimeUserTimer::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 Weinholdvoid
39824df6592SIngo WeinholdRealTimeUserTimer::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;