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