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