1*24df6592SIngo Weinhold /* 2*24df6592SIngo Weinhold * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3*24df6592SIngo Weinhold * Distributed under the terms of the MIT License. 4*24df6592SIngo Weinhold */ 5*24df6592SIngo Weinhold 6*24df6592SIngo Weinhold 7*24df6592SIngo Weinhold #include <UserTimer.h> 8*24df6592SIngo Weinhold 9*24df6592SIngo Weinhold #include <algorithm> 10*24df6592SIngo Weinhold 11*24df6592SIngo Weinhold #include <AutoDeleter.h> 12*24df6592SIngo Weinhold 13*24df6592SIngo Weinhold #include <debug.h> 14*24df6592SIngo Weinhold #include <kernel.h> 15*24df6592SIngo Weinhold #include <real_time_clock.h> 16*24df6592SIngo Weinhold #include <team.h> 17*24df6592SIngo Weinhold #include <thread_types.h> 18*24df6592SIngo Weinhold #include <UserEvent.h> 19*24df6592SIngo Weinhold #include <util/AutoLock.h> 20*24df6592SIngo Weinhold 21*24df6592SIngo Weinhold 22*24df6592SIngo Weinhold // Minimum interval length in microseconds for a periodic timer. This is not a 23*24df6592SIngo Weinhold // restriction on the user timer interval length itself, but the minimum time 24*24df6592SIngo Weinhold // span by which we advance the start time for kernel timers. A shorted user 25*24df6592SIngo Weinhold // timer interval will result in the overrun count to be increased every time 26*24df6592SIngo Weinhold // the kernel timer is rescheduled. 27*24df6592SIngo Weinhold static const bigtime_t kMinPeriodicTimerInterval = 100; 28*24df6592SIngo Weinhold 29*24df6592SIngo Weinhold static RealTimeUserTimerList sAbsoluteRealTimeTimers; 30*24df6592SIngo Weinhold static spinlock sAbsoluteRealTimeTimersLock = B_SPINLOCK_INITIALIZER; 31*24df6592SIngo Weinhold 32*24df6592SIngo Weinhold 33*24df6592SIngo Weinhold // #pragma mark - TimerLocker 34*24df6592SIngo Weinhold 35*24df6592SIngo Weinhold 36*24df6592SIngo Weinhold namespace { 37*24df6592SIngo Weinhold 38*24df6592SIngo Weinhold struct TimerLocker { 39*24df6592SIngo Weinhold Team* team; 40*24df6592SIngo Weinhold Thread* thread; 41*24df6592SIngo Weinhold 42*24df6592SIngo Weinhold TimerLocker() 43*24df6592SIngo Weinhold : 44*24df6592SIngo Weinhold team(NULL), 45*24df6592SIngo Weinhold thread(NULL) 46*24df6592SIngo Weinhold { 47*24df6592SIngo Weinhold } 48*24df6592SIngo Weinhold 49*24df6592SIngo Weinhold ~TimerLocker() 50*24df6592SIngo Weinhold { 51*24df6592SIngo Weinhold Unlock(); 52*24df6592SIngo Weinhold } 53*24df6592SIngo Weinhold 54*24df6592SIngo Weinhold void Lock(Team* team, Thread* thread) 55*24df6592SIngo Weinhold { 56*24df6592SIngo Weinhold this->team = team; 57*24df6592SIngo Weinhold team->Lock(); 58*24df6592SIngo Weinhold 59*24df6592SIngo Weinhold this->thread = thread; 60*24df6592SIngo Weinhold 61*24df6592SIngo Weinhold if (thread != NULL) { 62*24df6592SIngo Weinhold thread->AcquireReference(); 63*24df6592SIngo Weinhold thread->Lock(); 64*24df6592SIngo Weinhold } 65*24df6592SIngo Weinhold 66*24df6592SIngo Weinhold // We don't check thread->team != team here, since this method can be 67*24df6592SIngo Weinhold // called for new threads not added to the team yet. 68*24df6592SIngo Weinhold } 69*24df6592SIngo Weinhold 70*24df6592SIngo Weinhold status_t LockAndGetTimer(thread_id threadID, int32 timerID, 71*24df6592SIngo Weinhold UserTimer*& _timer) 72*24df6592SIngo Weinhold { 73*24df6592SIngo Weinhold team = thread_get_current_thread()->team; 74*24df6592SIngo Weinhold team->Lock(); 75*24df6592SIngo Weinhold 76*24df6592SIngo Weinhold if (threadID >= 0) { 77*24df6592SIngo Weinhold thread = Thread::GetAndLock(threadID); 78*24df6592SIngo Weinhold if (thread == NULL) 79*24df6592SIngo Weinhold return B_BAD_THREAD_ID; 80*24df6592SIngo Weinhold if (thread->team != team) 81*24df6592SIngo Weinhold return B_NOT_ALLOWED; 82*24df6592SIngo Weinhold } 83*24df6592SIngo Weinhold 84*24df6592SIngo Weinhold UserTimer* timer = thread != NULL 85*24df6592SIngo Weinhold ? thread->UserTimerFor(timerID) : team->UserTimerFor(timerID); 86*24df6592SIngo Weinhold if (timer == NULL) 87*24df6592SIngo Weinhold return B_BAD_VALUE; 88*24df6592SIngo Weinhold 89*24df6592SIngo Weinhold _timer = timer; 90*24df6592SIngo Weinhold return B_OK; 91*24df6592SIngo Weinhold } 92*24df6592SIngo Weinhold 93*24df6592SIngo Weinhold void Unlock() 94*24df6592SIngo Weinhold { 95*24df6592SIngo Weinhold if (thread != NULL) { 96*24df6592SIngo Weinhold thread->UnlockAndReleaseReference(); 97*24df6592SIngo Weinhold thread = NULL; 98*24df6592SIngo Weinhold } 99*24df6592SIngo Weinhold if (team != NULL) { 100*24df6592SIngo Weinhold team->Unlock(); 101*24df6592SIngo Weinhold team = NULL; 102*24df6592SIngo Weinhold } 103*24df6592SIngo Weinhold } 104*24df6592SIngo Weinhold }; 105*24df6592SIngo Weinhold 106*24df6592SIngo Weinhold } // unnamed namespace 107*24df6592SIngo Weinhold 108*24df6592SIngo Weinhold 109*24df6592SIngo Weinhold // #pragma mark - UserTimer 110*24df6592SIngo Weinhold 111*24df6592SIngo Weinhold 112*24df6592SIngo Weinhold UserTimer::UserTimer() 113*24df6592SIngo Weinhold : 114*24df6592SIngo Weinhold fID(-1), 115*24df6592SIngo Weinhold fEvent(NULL), 116*24df6592SIngo Weinhold fNextTime(0), 117*24df6592SIngo Weinhold fInterval(0), 118*24df6592SIngo Weinhold fOverrunCount(0), 119*24df6592SIngo Weinhold fScheduled(false) 120*24df6592SIngo Weinhold { 121*24df6592SIngo Weinhold // mark the timer unused 122*24df6592SIngo Weinhold fTimer.user_data = this; 123*24df6592SIngo Weinhold } 124*24df6592SIngo Weinhold 125*24df6592SIngo Weinhold 126*24df6592SIngo Weinhold UserTimer::~UserTimer() 127*24df6592SIngo Weinhold { 128*24df6592SIngo Weinhold delete fEvent; 129*24df6592SIngo Weinhold } 130*24df6592SIngo Weinhold 131*24df6592SIngo Weinhold 132*24df6592SIngo Weinhold /*! \fn UserTimer::Schedule(bigtime_t nextTime, bigtime_t interval, 133*24df6592SIngo Weinhold bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval) 134*24df6592SIngo Weinhold Cancels the timer, if it is already scheduled, and optionally schedules it 135*24df6592SIngo Weinhold with new parameters. 136*24df6592SIngo Weinhold 137*24df6592SIngo Weinhold The caller must not hold the scheduler lock. 138*24df6592SIngo Weinhold 139*24df6592SIngo Weinhold \param nextTime The time at which the timer should go off the next time. If 140*24df6592SIngo Weinhold \c B_INFINITE_TIMEOUT, the timer will not be scheduled. Whether the 141*24df6592SIngo Weinhold value is interpreted as absolute or relative time, depends on \c flags. 142*24df6592SIngo Weinhold \param interval If <tt> >0 </tt>, the timer will be scheduled to fire 143*24df6592SIngo Weinhold periodically every \a interval microseconds. Otherwise it will fire 144*24df6592SIngo Weinhold only once at \a nextTime. If \a nextTime is \c B_INFINITE_TIMEOUT, it 145*24df6592SIngo Weinhold will fire never in either case. 146*24df6592SIngo Weinhold \param flags Bitwise OR of flags. Currently \c B_ABSOLUTE_TIMEOUT and 147*24df6592SIngo Weinhold \c B_RELATIVE_TIMEOUT are supported, indicating whether \a nextTime is 148*24df6592SIngo Weinhold an absolute or relative time. 149*24df6592SIngo Weinhold \param _oldRemainingTime Return variable that will be set to the 150*24df6592SIngo Weinhold microseconds remaining to the time for which the timer was scheduled 151*24df6592SIngo Weinhold next before the call. If it wasn't scheduled, the variable is set to 152*24df6592SIngo Weinhold \c B_INFINITE_TIMEOUT. 153*24df6592SIngo Weinhold \param _oldInterval Return variable that will be set to the interval in 154*24df6592SIngo Weinhold microseconds the timer was to be scheduled periodically. If the timer 155*24df6592SIngo Weinhold wasn't periodic, the variable is set to \c 0. 156*24df6592SIngo Weinhold */ 157*24df6592SIngo Weinhold 158*24df6592SIngo Weinhold 159*24df6592SIngo Weinhold /*! Cancels the timer, if it is scheduled. 160*24df6592SIngo Weinhold 161*24df6592SIngo Weinhold The caller must not hold the scheduler lock. 162*24df6592SIngo Weinhold */ 163*24df6592SIngo Weinhold void 164*24df6592SIngo Weinhold UserTimer::Cancel() 165*24df6592SIngo Weinhold { 166*24df6592SIngo Weinhold bigtime_t oldNextTime; 167*24df6592SIngo Weinhold bigtime_t oldInterval; 168*24df6592SIngo Weinhold return Schedule(B_INFINITE_TIMEOUT, 0, 0, oldNextTime, oldInterval); 169*24df6592SIngo Weinhold } 170*24df6592SIngo Weinhold 171*24df6592SIngo Weinhold 172*24df6592SIngo Weinhold /*! \fn UserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval, 173*24df6592SIngo Weinhold uint32& _overrunCount) 174*24df6592SIngo Weinhold Return information on the current timer. 175*24df6592SIngo Weinhold 176*24df6592SIngo Weinhold The caller must not hold the scheduler lock. 177*24df6592SIngo Weinhold 178*24df6592SIngo Weinhold \param _remainingTime Return variable that will be set to the microseconds 179*24df6592SIngo Weinhold remaining to the time for which the timer was scheduled next before the 180*24df6592SIngo Weinhold call. If it wasn't scheduled, the variable is set to 181*24df6592SIngo Weinhold \c B_INFINITE_TIMEOUT. 182*24df6592SIngo Weinhold \param _interval Return variable that will be set to the interval in 183*24df6592SIngo Weinhold microseconds the timer is to be scheduled periodically. If the timer 184*24df6592SIngo Weinhold isn't periodic, the variable is set to \c 0. 185*24df6592SIngo Weinhold \param _overrunCount Return variable that will be set to the number of times 186*24df6592SIngo Weinhold the timer went off, but its event couldn't be delivered, since it's 187*24df6592SIngo Weinhold previous delivery hasn't been handled yet. 188*24df6592SIngo Weinhold */ 189*24df6592SIngo Weinhold 190*24df6592SIngo Weinhold 191*24df6592SIngo Weinhold /*static*/ int32 192*24df6592SIngo Weinhold UserTimer::HandleTimerHook(struct timer* timer) 193*24df6592SIngo Weinhold { 194*24df6592SIngo Weinhold ((UserTimer*)timer->user_data)->HandleTimer(); 195*24df6592SIngo Weinhold return B_HANDLED_INTERRUPT; 196*24df6592SIngo Weinhold } 197*24df6592SIngo Weinhold 198*24df6592SIngo Weinhold 199*24df6592SIngo Weinhold void 200*24df6592SIngo Weinhold UserTimer::HandleTimer() 201*24df6592SIngo Weinhold { 202*24df6592SIngo Weinhold if (fEvent != NULL) { 203*24df6592SIngo Weinhold // fire the event and update the overrun count, if necessary 204*24df6592SIngo Weinhold status_t error = fEvent->Fire(); 205*24df6592SIngo Weinhold if (error == B_BUSY) { 206*24df6592SIngo Weinhold if (fOverrunCount < MAX_USER_TIMER_OVERRUN_COUNT) 207*24df6592SIngo Weinhold fOverrunCount++; 208*24df6592SIngo Weinhold } 209*24df6592SIngo Weinhold } 210*24df6592SIngo Weinhold 211*24df6592SIngo Weinhold // Since we don't use periodic kernel timers, it isn't scheduled anymore. 212*24df6592SIngo Weinhold // If the timer is periodic, the derived class' version will schedule it 213*24df6592SIngo Weinhold // again. 214*24df6592SIngo Weinhold fScheduled = false; 215*24df6592SIngo Weinhold } 216*24df6592SIngo Weinhold 217*24df6592SIngo Weinhold 218*24df6592SIngo Weinhold /*! Updates the start time for a periodic timer after it expired, enforcing 219*24df6592SIngo Weinhold sanity limits and updating \c fOverrunCount, if necessary. 220*24df6592SIngo Weinhold 221*24df6592SIngo Weinhold The caller must not hold the scheduler lock. 222*24df6592SIngo Weinhold */ 223*24df6592SIngo Weinhold void 224*24df6592SIngo Weinhold UserTimer::UpdatePeriodicStartTime() 225*24df6592SIngo Weinhold { 226*24df6592SIngo Weinhold if (fInterval < kMinPeriodicTimerInterval) { 227*24df6592SIngo Weinhold bigtime_t skip = (kMinPeriodicTimerInterval + fInterval - 1) / fInterval; 228*24df6592SIngo Weinhold fNextTime += skip * fInterval; 229*24df6592SIngo Weinhold 230*24df6592SIngo Weinhold // One interval is the normal advance, so don't consider it skipped. 231*24df6592SIngo Weinhold skip--; 232*24df6592SIngo Weinhold 233*24df6592SIngo Weinhold if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT) 234*24df6592SIngo Weinhold fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT; 235*24df6592SIngo Weinhold else 236*24df6592SIngo Weinhold fOverrunCount += skip; 237*24df6592SIngo Weinhold } else 238*24df6592SIngo Weinhold fNextTime += fInterval; 239*24df6592SIngo Weinhold } 240*24df6592SIngo Weinhold 241*24df6592SIngo Weinhold 242*24df6592SIngo Weinhold /*! Checks whether the timer start time lies too much in the past and, if so, 243*24df6592SIngo Weinhold adjusts it and updates \c fOverrunCount. 244*24df6592SIngo Weinhold 245*24df6592SIngo Weinhold The caller must not hold the scheduler lock. 246*24df6592SIngo Weinhold 247*24df6592SIngo Weinhold \param now The current time. 248*24df6592SIngo Weinhold */ 249*24df6592SIngo Weinhold void 250*24df6592SIngo Weinhold UserTimer::CheckPeriodicOverrun(bigtime_t now) 251*24df6592SIngo Weinhold { 252*24df6592SIngo Weinhold if (fNextTime + fInterval > now) 253*24df6592SIngo Weinhold return; 254*24df6592SIngo Weinhold 255*24df6592SIngo Weinhold // The start time is a full interval or more in the past. Skip those 256*24df6592SIngo Weinhold // intervals. 257*24df6592SIngo Weinhold bigtime_t skip = (now - fNextTime) / fInterval; 258*24df6592SIngo Weinhold fNextTime += skip * fInterval; 259*24df6592SIngo Weinhold 260*24df6592SIngo Weinhold if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT) 261*24df6592SIngo Weinhold fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT; 262*24df6592SIngo Weinhold else 263*24df6592SIngo Weinhold fOverrunCount += skip; 264*24df6592SIngo Weinhold } 265*24df6592SIngo Weinhold 266*24df6592SIngo Weinhold 267*24df6592SIngo Weinhold // #pragma mark - SystemTimeUserTimer 268*24df6592SIngo Weinhold 269*24df6592SIngo Weinhold 270*24df6592SIngo Weinhold void 271*24df6592SIngo Weinhold SystemTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval, 272*24df6592SIngo Weinhold uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval) 273*24df6592SIngo Weinhold { 274*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 275*24df6592SIngo Weinhold 276*24df6592SIngo Weinhold // get the current time 277*24df6592SIngo Weinhold bigtime_t now = system_time(); 278*24df6592SIngo Weinhold 279*24df6592SIngo Weinhold // Cancel the old timer, if still scheduled, and get the previous values. 280*24df6592SIngo Weinhold if (fScheduled) { 281*24df6592SIngo Weinhold cancel_timer(&fTimer); 282*24df6592SIngo Weinhold 283*24df6592SIngo Weinhold _oldRemainingTime = fNextTime - now; 284*24df6592SIngo Weinhold _oldInterval = fInterval; 285*24df6592SIngo Weinhold 286*24df6592SIngo Weinhold fScheduled = false; 287*24df6592SIngo Weinhold } else { 288*24df6592SIngo Weinhold _oldRemainingTime = B_INFINITE_TIMEOUT; 289*24df6592SIngo Weinhold _oldInterval = 0; 290*24df6592SIngo Weinhold } 291*24df6592SIngo Weinhold 292*24df6592SIngo Weinhold // schedule the new timer 293*24df6592SIngo Weinhold fNextTime = nextTime; 294*24df6592SIngo Weinhold fInterval = interval; 295*24df6592SIngo Weinhold fOverrunCount = 0; 296*24df6592SIngo Weinhold 297*24df6592SIngo Weinhold if (nextTime == B_INFINITE_TIMEOUT) 298*24df6592SIngo Weinhold return; 299*24df6592SIngo Weinhold 300*24df6592SIngo Weinhold if ((flags & B_RELATIVE_TIMEOUT) != 0) 301*24df6592SIngo Weinhold fNextTime += now; 302*24df6592SIngo Weinhold 303*24df6592SIngo Weinhold ScheduleKernelTimer(now, fInterval > 0); 304*24df6592SIngo Weinhold } 305*24df6592SIngo Weinhold 306*24df6592SIngo Weinhold 307*24df6592SIngo Weinhold void 308*24df6592SIngo Weinhold SystemTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval, 309*24df6592SIngo Weinhold uint32& _overrunCount) 310*24df6592SIngo Weinhold { 311*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 312*24df6592SIngo Weinhold 313*24df6592SIngo Weinhold if (fScheduled) { 314*24df6592SIngo Weinhold _remainingTime = fNextTime - system_time(); 315*24df6592SIngo Weinhold _interval = fInterval; 316*24df6592SIngo Weinhold } else { 317*24df6592SIngo Weinhold _remainingTime = B_INFINITE_TIMEOUT; 318*24df6592SIngo Weinhold _interval = 0; 319*24df6592SIngo Weinhold } 320*24df6592SIngo Weinhold 321*24df6592SIngo Weinhold _overrunCount = fOverrunCount; 322*24df6592SIngo Weinhold } 323*24df6592SIngo Weinhold 324*24df6592SIngo Weinhold 325*24df6592SIngo Weinhold void 326*24df6592SIngo Weinhold SystemTimeUserTimer::HandleTimer() 327*24df6592SIngo Weinhold { 328*24df6592SIngo Weinhold UserTimer::HandleTimer(); 329*24df6592SIngo Weinhold 330*24df6592SIngo Weinhold // if periodic, reschedule the kernel timer 331*24df6592SIngo Weinhold if (fInterval > 0) { 332*24df6592SIngo Weinhold UpdatePeriodicStartTime(); 333*24df6592SIngo Weinhold ScheduleKernelTimer(system_time(), true); 334*24df6592SIngo Weinhold } 335*24df6592SIngo Weinhold } 336*24df6592SIngo Weinhold 337*24df6592SIngo Weinhold 338*24df6592SIngo Weinhold /*! Schedules the kernel timer. 339*24df6592SIngo Weinhold 340*24df6592SIngo Weinhold The caller must hold the scheduler lock. 341*24df6592SIngo Weinhold 342*24df6592SIngo Weinhold \param now The current system time to be used. 343*24df6592SIngo Weinhold \param checkPeriodicOverrun If \c true, calls CheckPeriodicOverrun() first, 344*24df6592SIngo Weinhold i.e. the start time will be adjusted to not lie too much in the past. 345*24df6592SIngo Weinhold */ 346*24df6592SIngo Weinhold void 347*24df6592SIngo Weinhold SystemTimeUserTimer::ScheduleKernelTimer(bigtime_t now, 348*24df6592SIngo Weinhold bool checkPeriodicOverrun) 349*24df6592SIngo Weinhold { 350*24df6592SIngo Weinhold // If periodic, check whether the start time is too far in the past. 351*24df6592SIngo Weinhold if (checkPeriodicOverrun) 352*24df6592SIngo Weinhold CheckPeriodicOverrun(now); 353*24df6592SIngo Weinhold 354*24df6592SIngo Weinhold uint32 timerFlags = B_ONE_SHOT_ABSOLUTE_TIMER 355*24df6592SIngo Weinhold | B_TIMER_USE_TIMER_STRUCT_TIMES | B_TIMER_ACQUIRE_SCHEDULER_LOCK; 356*24df6592SIngo Weinhold // We use B_TIMER_ACQUIRE_SCHEDULER_LOCK to avoid race conditions 357*24df6592SIngo Weinhold // between setting/canceling the timer and the event handler. 358*24df6592SIngo Weinhold 359*24df6592SIngo Weinhold fTimer.schedule_time = std::max(fNextTime, (bigtime_t)0); 360*24df6592SIngo Weinhold fTimer.period = 0; 361*24df6592SIngo Weinhold 362*24df6592SIngo Weinhold add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, timerFlags); 363*24df6592SIngo Weinhold 364*24df6592SIngo Weinhold fScheduled = true; 365*24df6592SIngo Weinhold } 366*24df6592SIngo Weinhold 367*24df6592SIngo Weinhold 368*24df6592SIngo Weinhold // #pragma mark - RealTimeUserTimer 369*24df6592SIngo Weinhold 370*24df6592SIngo Weinhold 371*24df6592SIngo Weinhold void 372*24df6592SIngo Weinhold RealTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval, 373*24df6592SIngo Weinhold uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval) 374*24df6592SIngo Weinhold { 375*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 376*24df6592SIngo Weinhold 377*24df6592SIngo Weinhold // get the current time 378*24df6592SIngo Weinhold bigtime_t now = system_time(); 379*24df6592SIngo Weinhold 380*24df6592SIngo Weinhold // Cancel the old timer, if still scheduled, and get the previous values. 381*24df6592SIngo Weinhold if (fScheduled) { 382*24df6592SIngo Weinhold cancel_timer(&fTimer); 383*24df6592SIngo Weinhold 384*24df6592SIngo Weinhold _oldRemainingTime = fNextTime - now; 385*24df6592SIngo Weinhold _oldInterval = fInterval; 386*24df6592SIngo Weinhold 387*24df6592SIngo Weinhold if (fAbsolute) { 388*24df6592SIngo Weinhold SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock); 389*24df6592SIngo Weinhold sAbsoluteRealTimeTimers.Remove(this); 390*24df6592SIngo Weinhold } 391*24df6592SIngo Weinhold 392*24df6592SIngo Weinhold fScheduled = false; 393*24df6592SIngo Weinhold } else { 394*24df6592SIngo Weinhold _oldRemainingTime = B_INFINITE_TIMEOUT; 395*24df6592SIngo Weinhold _oldInterval = 0; 396*24df6592SIngo Weinhold } 397*24df6592SIngo Weinhold 398*24df6592SIngo Weinhold // schedule the new timer 399*24df6592SIngo Weinhold fNextTime = nextTime; 400*24df6592SIngo Weinhold fInterval = interval; 401*24df6592SIngo Weinhold fOverrunCount = 0; 402*24df6592SIngo Weinhold 403*24df6592SIngo Weinhold if (nextTime == B_INFINITE_TIMEOUT) 404*24df6592SIngo Weinhold return; 405*24df6592SIngo Weinhold 406*24df6592SIngo Weinhold fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0; 407*24df6592SIngo Weinhold 408*24df6592SIngo Weinhold if (fAbsolute) { 409*24df6592SIngo Weinhold fRealTimeOffset = rtc_boot_time(); 410*24df6592SIngo Weinhold fNextTime -= fRealTimeOffset; 411*24df6592SIngo Weinhold 412*24df6592SIngo Weinhold // If periodic, check whether the start time is too far in the past. 413*24df6592SIngo Weinhold if (fInterval > 0) 414*24df6592SIngo Weinhold CheckPeriodicOverrun(now); 415*24df6592SIngo Weinhold 416*24df6592SIngo Weinhold // add the absolute timer to the global list 417*24df6592SIngo Weinhold SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock); 418*24df6592SIngo Weinhold sAbsoluteRealTimeTimers.Insert(this); 419*24df6592SIngo Weinhold } else 420*24df6592SIngo Weinhold fNextTime += now; 421*24df6592SIngo Weinhold 422*24df6592SIngo Weinhold ScheduleKernelTimer(now, false); 423*24df6592SIngo Weinhold } 424*24df6592SIngo Weinhold 425*24df6592SIngo Weinhold 426*24df6592SIngo Weinhold /*! Called when the real-time clock has been changed. 427*24df6592SIngo Weinhold 428*24df6592SIngo Weinhold The caller must hold the scheduler lock. Optionally the caller may also 429*24df6592SIngo Weinhold hold \c sAbsoluteRealTimeTimersLock. 430*24df6592SIngo Weinhold */ 431*24df6592SIngo Weinhold void 432*24df6592SIngo Weinhold RealTimeUserTimer::TimeWarped() 433*24df6592SIngo Weinhold { 434*24df6592SIngo Weinhold ASSERT(fScheduled && fAbsolute); 435*24df6592SIngo Weinhold 436*24df6592SIngo Weinhold // get the new real-time offset 437*24df6592SIngo Weinhold bigtime_t oldRealTimeOffset = fRealTimeOffset; 438*24df6592SIngo Weinhold fRealTimeOffset = rtc_boot_time(); 439*24df6592SIngo Weinhold if (fRealTimeOffset == oldRealTimeOffset) 440*24df6592SIngo Weinhold return; 441*24df6592SIngo Weinhold 442*24df6592SIngo Weinhold // cancel the kernel timer and reschedule it 443*24df6592SIngo Weinhold cancel_timer(&fTimer); 444*24df6592SIngo Weinhold 445*24df6592SIngo Weinhold fNextTime += oldRealTimeOffset - fRealTimeOffset; 446*24df6592SIngo Weinhold 447*24df6592SIngo Weinhold ScheduleKernelTimer(system_time(), fInterval > 0); 448*24df6592SIngo Weinhold } 449*24df6592SIngo Weinhold 450*24df6592SIngo Weinhold 451*24df6592SIngo Weinhold void 452*24df6592SIngo Weinhold RealTimeUserTimer::HandleTimer() 453*24df6592SIngo Weinhold { 454*24df6592SIngo Weinhold SystemTimeUserTimer::HandleTimer(); 455*24df6592SIngo Weinhold 456*24df6592SIngo Weinhold // remove from global list, if no longer scheduled 457*24df6592SIngo Weinhold if (!fScheduled && fAbsolute) { 458*24df6592SIngo Weinhold SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock); 459*24df6592SIngo Weinhold sAbsoluteRealTimeTimers.Remove(this); 460*24df6592SIngo Weinhold } 461*24df6592SIngo Weinhold } 462*24df6592SIngo Weinhold 463*24df6592SIngo Weinhold 464*24df6592SIngo Weinhold // #pragma mark - TeamTimeUserTimer 465*24df6592SIngo Weinhold 466*24df6592SIngo Weinhold 467*24df6592SIngo Weinhold TeamTimeUserTimer::TeamTimeUserTimer(team_id teamID) 468*24df6592SIngo Weinhold : 469*24df6592SIngo Weinhold fTeamID(teamID), 470*24df6592SIngo Weinhold fTeam(NULL) 471*24df6592SIngo Weinhold { 472*24df6592SIngo Weinhold } 473*24df6592SIngo Weinhold 474*24df6592SIngo Weinhold 475*24df6592SIngo Weinhold TeamTimeUserTimer::~TeamTimeUserTimer() 476*24df6592SIngo Weinhold { 477*24df6592SIngo Weinhold ASSERT(fTeam == NULL); 478*24df6592SIngo Weinhold } 479*24df6592SIngo Weinhold 480*24df6592SIngo Weinhold 481*24df6592SIngo Weinhold void 482*24df6592SIngo Weinhold TeamTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval, 483*24df6592SIngo Weinhold uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval) 484*24df6592SIngo Weinhold { 485*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 486*24df6592SIngo Weinhold 487*24df6592SIngo Weinhold // get the current time, but only if needed 488*24df6592SIngo Weinhold bool nowValid = fTeam != NULL; 489*24df6592SIngo Weinhold bigtime_t now = nowValid ? fTeam->CPUTime(false) : 0; 490*24df6592SIngo Weinhold 491*24df6592SIngo Weinhold // Cancel the old timer, if still scheduled, and get the previous values. 492*24df6592SIngo Weinhold if (fTeam != NULL) { 493*24df6592SIngo Weinhold if (fScheduled) { 494*24df6592SIngo Weinhold cancel_timer(&fTimer); 495*24df6592SIngo Weinhold fScheduled = false; 496*24df6592SIngo Weinhold } 497*24df6592SIngo Weinhold 498*24df6592SIngo Weinhold _oldRemainingTime = fNextTime - now; 499*24df6592SIngo Weinhold _oldInterval = fInterval; 500*24df6592SIngo Weinhold 501*24df6592SIngo Weinhold fTeam->UserTimerDeactivated(this); 502*24df6592SIngo Weinhold fTeam->ReleaseReference(); 503*24df6592SIngo Weinhold fTeam = NULL; 504*24df6592SIngo Weinhold } else { 505*24df6592SIngo Weinhold _oldRemainingTime = B_INFINITE_TIMEOUT; 506*24df6592SIngo Weinhold _oldInterval = 0; 507*24df6592SIngo Weinhold } 508*24df6592SIngo Weinhold 509*24df6592SIngo Weinhold // schedule the new timer 510*24df6592SIngo Weinhold fNextTime = nextTime; 511*24df6592SIngo Weinhold fInterval = interval; 512*24df6592SIngo Weinhold fOverrunCount = 0; 513*24df6592SIngo Weinhold 514*24df6592SIngo Weinhold if (fNextTime == B_INFINITE_TIMEOUT) 515*24df6592SIngo Weinhold return; 516*24df6592SIngo Weinhold 517*24df6592SIngo Weinhold // Get the team. If it doesn't exist anymore, just don't schedule the 518*24df6592SIngo Weinhold // timer anymore. 519*24df6592SIngo Weinhold fTeam = Team::Get(fTeamID); 520*24df6592SIngo Weinhold if (fTeam == NULL) 521*24df6592SIngo Weinhold return; 522*24df6592SIngo Weinhold 523*24df6592SIngo Weinhold fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0; 524*24df6592SIngo Weinhold 525*24df6592SIngo Weinhold // convert relative to absolute timeouts 526*24df6592SIngo Weinhold if (!fAbsolute) { 527*24df6592SIngo Weinhold if (!nowValid) 528*24df6592SIngo Weinhold now = fTeam->CPUTime(false); 529*24df6592SIngo Weinhold fNextTime += now; 530*24df6592SIngo Weinhold } 531*24df6592SIngo Weinhold 532*24df6592SIngo Weinhold fTeam->UserTimerActivated(this); 533*24df6592SIngo Weinhold 534*24df6592SIngo Weinhold // schedule/udpate the kernel timer 535*24df6592SIngo Weinhold Update(NULL); 536*24df6592SIngo Weinhold } 537*24df6592SIngo Weinhold 538*24df6592SIngo Weinhold 539*24df6592SIngo Weinhold void 540*24df6592SIngo Weinhold TeamTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval, 541*24df6592SIngo Weinhold uint32& _overrunCount) 542*24df6592SIngo Weinhold { 543*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 544*24df6592SIngo Weinhold 545*24df6592SIngo Weinhold if (fTeam != NULL) { 546*24df6592SIngo Weinhold _remainingTime = fNextTime - fTeam->CPUTime(false); 547*24df6592SIngo Weinhold _interval = fInterval; 548*24df6592SIngo Weinhold } else { 549*24df6592SIngo Weinhold _remainingTime = B_INFINITE_TIMEOUT; 550*24df6592SIngo Weinhold _interval = 0; 551*24df6592SIngo Weinhold } 552*24df6592SIngo Weinhold 553*24df6592SIngo Weinhold _overrunCount = fOverrunCount; 554*24df6592SIngo Weinhold } 555*24df6592SIngo Weinhold 556*24df6592SIngo Weinhold 557*24df6592SIngo Weinhold /*! Deactivates the timer, if it is activated. 558*24df6592SIngo Weinhold 559*24df6592SIngo Weinhold The caller must hold the scheduler lock. 560*24df6592SIngo Weinhold */ 561*24df6592SIngo Weinhold void 562*24df6592SIngo Weinhold TeamTimeUserTimer::Deactivate() 563*24df6592SIngo Weinhold { 564*24df6592SIngo Weinhold if (fTeam == NULL) 565*24df6592SIngo Weinhold return; 566*24df6592SIngo Weinhold 567*24df6592SIngo Weinhold // unschedule, if scheduled 568*24df6592SIngo Weinhold if (fScheduled) { 569*24df6592SIngo Weinhold cancel_timer(&fTimer); 570*24df6592SIngo Weinhold fScheduled = false; 571*24df6592SIngo Weinhold } 572*24df6592SIngo Weinhold 573*24df6592SIngo Weinhold // deactivate 574*24df6592SIngo Weinhold fTeam->UserTimerDeactivated(this); 575*24df6592SIngo Weinhold fTeam->ReleaseReference(); 576*24df6592SIngo Weinhold fTeam = NULL; 577*24df6592SIngo Weinhold } 578*24df6592SIngo Weinhold 579*24df6592SIngo Weinhold 580*24df6592SIngo Weinhold /*! Starts/stops the timer as necessary, if it is active. 581*24df6592SIngo Weinhold 582*24df6592SIngo Weinhold Called whenever threads of the team whose CPU time is referred to by the 583*24df6592SIngo Weinhold timer are scheduled or unscheduled (or leave the team), or when the timer 584*24df6592SIngo Weinhold was just set. Schedules a kernel timer for the remaining time, respectively 585*24df6592SIngo Weinhold cancels it. 586*24df6592SIngo Weinhold 587*24df6592SIngo Weinhold The caller must hold the scheduler lock. 588*24df6592SIngo Weinhold 589*24df6592SIngo Weinhold \param unscheduledThread If not \c NULL, this is the thread that is 590*24df6592SIngo Weinhold currently running and which is in the process of being unscheduled. 591*24df6592SIngo Weinhold */ 592*24df6592SIngo Weinhold void 593*24df6592SIngo Weinhold TeamTimeUserTimer::Update(Thread* unscheduledThread) 594*24df6592SIngo Weinhold { 595*24df6592SIngo Weinhold if (fTeam == NULL) 596*24df6592SIngo Weinhold return; 597*24df6592SIngo Weinhold 598*24df6592SIngo Weinhold // determine how many of the team's threads are currently running 599*24df6592SIngo Weinhold fRunningThreads = 0; 600*24df6592SIngo Weinhold int32 cpuCount = smp_get_num_cpus(); 601*24df6592SIngo Weinhold for (int32 i = 0; i < cpuCount; i++) { 602*24df6592SIngo Weinhold Thread* thread = gCPU[i].running_thread; 603*24df6592SIngo Weinhold if (thread != unscheduledThread && thread->team == fTeam) 604*24df6592SIngo Weinhold fRunningThreads++; 605*24df6592SIngo Weinhold } 606*24df6592SIngo Weinhold 607*24df6592SIngo Weinhold _Update(unscheduledThread != NULL); 608*24df6592SIngo Weinhold } 609*24df6592SIngo Weinhold 610*24df6592SIngo Weinhold 611*24df6592SIngo Weinhold /*! Called when the team's CPU time clock which this timer refers to has been 612*24df6592SIngo Weinhold set. 613*24df6592SIngo Weinhold 614*24df6592SIngo Weinhold The caller must hold the scheduler lock. 615*24df6592SIngo Weinhold 616*24df6592SIngo Weinhold \param changedBy The value by which the clock has changed. 617*24df6592SIngo Weinhold */ 618*24df6592SIngo Weinhold void 619*24df6592SIngo Weinhold TeamTimeUserTimer::TimeWarped(bigtime_t changedBy) 620*24df6592SIngo Weinhold { 621*24df6592SIngo Weinhold if (fTeam == NULL || changedBy == 0) 622*24df6592SIngo Weinhold return; 623*24df6592SIngo Weinhold 624*24df6592SIngo Weinhold // If this is a relative timer, adjust fNextTime by the value the clock has 625*24df6592SIngo Weinhold // changed. 626*24df6592SIngo Weinhold if (!fAbsolute) 627*24df6592SIngo Weinhold fNextTime += changedBy; 628*24df6592SIngo Weinhold 629*24df6592SIngo Weinhold // reschedule the kernel timer 630*24df6592SIngo Weinhold _Update(false); 631*24df6592SIngo Weinhold } 632*24df6592SIngo Weinhold 633*24df6592SIngo Weinhold 634*24df6592SIngo Weinhold void 635*24df6592SIngo Weinhold TeamTimeUserTimer::HandleTimer() 636*24df6592SIngo Weinhold { 637*24df6592SIngo Weinhold UserTimer::HandleTimer(); 638*24df6592SIngo Weinhold 639*24df6592SIngo Weinhold // If the timer is not periodic, it is no longer active. Otherwise 640*24df6592SIngo Weinhold // reschedule the kernel timer. 641*24df6592SIngo Weinhold if (fTeam != NULL) { 642*24df6592SIngo Weinhold if (fInterval == 0) { 643*24df6592SIngo Weinhold fTeam->UserTimerDeactivated(this); 644*24df6592SIngo Weinhold fTeam->ReleaseReference(); 645*24df6592SIngo Weinhold fTeam = NULL; 646*24df6592SIngo Weinhold } else { 647*24df6592SIngo Weinhold UpdatePeriodicStartTime(); 648*24df6592SIngo Weinhold _Update(false); 649*24df6592SIngo Weinhold } 650*24df6592SIngo Weinhold } 651*24df6592SIngo Weinhold } 652*24df6592SIngo Weinhold 653*24df6592SIngo Weinhold 654*24df6592SIngo Weinhold /*! Schedules/cancels the kernel timer as necessary. 655*24df6592SIngo Weinhold 656*24df6592SIngo Weinhold \c fRunningThreads must be up-to-date. 657*24df6592SIngo Weinhold The caller must hold the scheduler lock. 658*24df6592SIngo Weinhold 659*24df6592SIngo Weinhold \param unscheduling \c true, when the current thread is in the process of 660*24df6592SIngo Weinhold being unscheduled. 661*24df6592SIngo Weinhold */ 662*24df6592SIngo Weinhold void 663*24df6592SIngo Weinhold TeamTimeUserTimer::_Update(bool unscheduling) 664*24df6592SIngo Weinhold { 665*24df6592SIngo Weinhold // unschedule the kernel timer, if scheduled 666*24df6592SIngo Weinhold if (fScheduled) 667*24df6592SIngo Weinhold cancel_timer(&fTimer); 668*24df6592SIngo Weinhold 669*24df6592SIngo Weinhold // if no more threads are running, we're done 670*24df6592SIngo Weinhold if (fRunningThreads == 0) { 671*24df6592SIngo Weinhold fScheduled = false; 672*24df6592SIngo Weinhold return; 673*24df6592SIngo Weinhold } 674*24df6592SIngo Weinhold 675*24df6592SIngo Weinhold // There are still threads running. Reschedule the kernel timer. 676*24df6592SIngo Weinhold bigtime_t now = fTeam->CPUTime(unscheduling); 677*24df6592SIngo Weinhold 678*24df6592SIngo Weinhold // If periodic, check whether the start time is too far in the past. 679*24df6592SIngo Weinhold if (fInterval > 0) 680*24df6592SIngo Weinhold CheckPeriodicOverrun(now); 681*24df6592SIngo Weinhold 682*24df6592SIngo Weinhold if (fNextTime > now) { 683*24df6592SIngo Weinhold fTimer.schedule_time = system_time() 684*24df6592SIngo Weinhold + (fNextTime - now + fRunningThreads - 1) / fRunningThreads; 685*24df6592SIngo Weinhold // check for overflow 686*24df6592SIngo Weinhold if (fTimer.schedule_time < 0) 687*24df6592SIngo Weinhold fTimer.schedule_time = B_INFINITE_TIMEOUT; 688*24df6592SIngo Weinhold } else 689*24df6592SIngo Weinhold fTimer.schedule_time = 0; 690*24df6592SIngo Weinhold fTimer.period = 0; 691*24df6592SIngo Weinhold // We reschedule periodic timers manually in HandleTimer() to avoid 692*24df6592SIngo Weinhold // rounding errors. 693*24df6592SIngo Weinhold 694*24df6592SIngo Weinhold add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, 695*24df6592SIngo Weinhold B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES 696*24df6592SIngo Weinhold | B_TIMER_ACQUIRE_SCHEDULER_LOCK); 697*24df6592SIngo Weinhold // We use B_TIMER_ACQUIRE_SCHEDULER_LOCK to avoid race conditions 698*24df6592SIngo Weinhold // between setting/canceling the timer and the event handler. 699*24df6592SIngo Weinhold // We use B_TIMER_USE_TIMER_STRUCT_TIMES, so period remains 0, which 700*24df6592SIngo Weinhold // our base class expects. 701*24df6592SIngo Weinhold 702*24df6592SIngo Weinhold fScheduled = true; 703*24df6592SIngo Weinhold } 704*24df6592SIngo Weinhold 705*24df6592SIngo Weinhold 706*24df6592SIngo Weinhold // #pragma mark - TeamUserTimeUserTimer 707*24df6592SIngo Weinhold 708*24df6592SIngo Weinhold 709*24df6592SIngo Weinhold TeamUserTimeUserTimer::TeamUserTimeUserTimer(team_id teamID) 710*24df6592SIngo Weinhold : 711*24df6592SIngo Weinhold fTeamID(teamID), 712*24df6592SIngo Weinhold fTeam(NULL) 713*24df6592SIngo Weinhold { 714*24df6592SIngo Weinhold } 715*24df6592SIngo Weinhold 716*24df6592SIngo Weinhold 717*24df6592SIngo Weinhold TeamUserTimeUserTimer::~TeamUserTimeUserTimer() 718*24df6592SIngo Weinhold { 719*24df6592SIngo Weinhold ASSERT(fTeam == NULL); 720*24df6592SIngo Weinhold } 721*24df6592SIngo Weinhold 722*24df6592SIngo Weinhold 723*24df6592SIngo Weinhold void 724*24df6592SIngo Weinhold TeamUserTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval, 725*24df6592SIngo Weinhold uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval) 726*24df6592SIngo Weinhold { 727*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 728*24df6592SIngo Weinhold 729*24df6592SIngo Weinhold // get the current time, but only if needed 730*24df6592SIngo Weinhold bool nowValid = fTeam != NULL; 731*24df6592SIngo Weinhold bigtime_t now = nowValid ? fTeam->UserCPUTime() : 0; 732*24df6592SIngo Weinhold 733*24df6592SIngo Weinhold // Cancel the old timer, if still active, and get the previous values. 734*24df6592SIngo Weinhold if (fTeam != NULL) { 735*24df6592SIngo Weinhold _oldRemainingTime = fNextTime - now; 736*24df6592SIngo Weinhold _oldInterval = fInterval; 737*24df6592SIngo Weinhold 738*24df6592SIngo Weinhold fTeam->UserTimerDeactivated(this); 739*24df6592SIngo Weinhold fTeam->ReleaseReference(); 740*24df6592SIngo Weinhold fTeam = NULL; 741*24df6592SIngo Weinhold } else { 742*24df6592SIngo Weinhold _oldRemainingTime = B_INFINITE_TIMEOUT; 743*24df6592SIngo Weinhold _oldInterval = 0; 744*24df6592SIngo Weinhold } 745*24df6592SIngo Weinhold 746*24df6592SIngo Weinhold // schedule the new timer 747*24df6592SIngo Weinhold fNextTime = nextTime; 748*24df6592SIngo Weinhold fInterval = interval; 749*24df6592SIngo Weinhold fOverrunCount = 0; 750*24df6592SIngo Weinhold 751*24df6592SIngo Weinhold if (fNextTime == B_INFINITE_TIMEOUT) 752*24df6592SIngo Weinhold return; 753*24df6592SIngo Weinhold 754*24df6592SIngo Weinhold // Get the team. If it doesn't exist anymore, just don't schedule the 755*24df6592SIngo Weinhold // timer anymore. 756*24df6592SIngo Weinhold fTeam = Team::Get(fTeamID); 757*24df6592SIngo Weinhold if (fTeam == NULL) 758*24df6592SIngo Weinhold return; 759*24df6592SIngo Weinhold 760*24df6592SIngo Weinhold // convert relative to absolute timeouts 761*24df6592SIngo Weinhold if ((flags & B_RELATIVE_TIMEOUT) != 0) { 762*24df6592SIngo Weinhold if (!nowValid) 763*24df6592SIngo Weinhold now = fTeam->CPUTime(false); 764*24df6592SIngo Weinhold fNextTime += now; 765*24df6592SIngo Weinhold } 766*24df6592SIngo Weinhold 767*24df6592SIngo Weinhold fTeam->UserTimerActivated(this); 768*24df6592SIngo Weinhold 769*24df6592SIngo Weinhold // fire the event, if already timed out 770*24df6592SIngo Weinhold Check(); 771*24df6592SIngo Weinhold } 772*24df6592SIngo Weinhold 773*24df6592SIngo Weinhold 774*24df6592SIngo Weinhold void 775*24df6592SIngo Weinhold TeamUserTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval, 776*24df6592SIngo Weinhold uint32& _overrunCount) 777*24df6592SIngo Weinhold { 778*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 779*24df6592SIngo Weinhold 780*24df6592SIngo Weinhold if (fTeam != NULL) { 781*24df6592SIngo Weinhold _remainingTime = fNextTime - fTeam->UserCPUTime(); 782*24df6592SIngo Weinhold _interval = fInterval; 783*24df6592SIngo Weinhold } else { 784*24df6592SIngo Weinhold _remainingTime = B_INFINITE_TIMEOUT; 785*24df6592SIngo Weinhold _interval = 0; 786*24df6592SIngo Weinhold } 787*24df6592SIngo Weinhold 788*24df6592SIngo Weinhold _overrunCount = fOverrunCount; 789*24df6592SIngo Weinhold } 790*24df6592SIngo Weinhold 791*24df6592SIngo Weinhold 792*24df6592SIngo Weinhold /*! Deactivates the timer, if it is activated. 793*24df6592SIngo Weinhold 794*24df6592SIngo Weinhold The caller must hold the scheduler lock. 795*24df6592SIngo Weinhold */ 796*24df6592SIngo Weinhold void 797*24df6592SIngo Weinhold TeamUserTimeUserTimer::Deactivate() 798*24df6592SIngo Weinhold { 799*24df6592SIngo Weinhold if (fTeam == NULL) 800*24df6592SIngo Weinhold return; 801*24df6592SIngo Weinhold 802*24df6592SIngo Weinhold // deactivate 803*24df6592SIngo Weinhold fTeam->UserTimerDeactivated(this); 804*24df6592SIngo Weinhold fTeam->ReleaseReference(); 805*24df6592SIngo Weinhold fTeam = NULL; 806*24df6592SIngo Weinhold } 807*24df6592SIngo Weinhold 808*24df6592SIngo Weinhold 809*24df6592SIngo Weinhold /*! Checks whether the timer is up, firing an event, if so. 810*24df6592SIngo Weinhold 811*24df6592SIngo Weinhold The caller must hold the scheduler lock. 812*24df6592SIngo Weinhold */ 813*24df6592SIngo Weinhold void 814*24df6592SIngo Weinhold TeamUserTimeUserTimer::Check() 815*24df6592SIngo Weinhold { 816*24df6592SIngo Weinhold if (fTeam == NULL) 817*24df6592SIngo Weinhold return; 818*24df6592SIngo Weinhold 819*24df6592SIngo Weinhold // check whether we need to fire the event yet 820*24df6592SIngo Weinhold bigtime_t now = fTeam->UserCPUTime(); 821*24df6592SIngo Weinhold if (now < fNextTime) 822*24df6592SIngo Weinhold return; 823*24df6592SIngo Weinhold 824*24df6592SIngo Weinhold HandleTimer(); 825*24df6592SIngo Weinhold 826*24df6592SIngo Weinhold // If the timer is not periodic, it is no longer active. Otherwise compute 827*24df6592SIngo Weinhold // the event time. 828*24df6592SIngo Weinhold if (fInterval == 0) { 829*24df6592SIngo Weinhold fTeam->UserTimerDeactivated(this); 830*24df6592SIngo Weinhold fTeam->ReleaseReference(); 831*24df6592SIngo Weinhold fTeam = NULL; 832*24df6592SIngo Weinhold return; 833*24df6592SIngo Weinhold } 834*24df6592SIngo Weinhold 835*24df6592SIngo Weinhold // First validate fNextTime, then increment it, so that fNextTime is > now 836*24df6592SIngo Weinhold // (CheckPeriodicOverrun() only makes it > now - fInterval). 837*24df6592SIngo Weinhold CheckPeriodicOverrun(now); 838*24df6592SIngo Weinhold fNextTime += fInterval; 839*24df6592SIngo Weinhold fScheduled = true; 840*24df6592SIngo Weinhold } 841*24df6592SIngo Weinhold 842*24df6592SIngo Weinhold 843*24df6592SIngo Weinhold // #pragma mark - ThreadTimeUserTimer 844*24df6592SIngo Weinhold 845*24df6592SIngo Weinhold 846*24df6592SIngo Weinhold ThreadTimeUserTimer::ThreadTimeUserTimer(thread_id threadID) 847*24df6592SIngo Weinhold : 848*24df6592SIngo Weinhold fThreadID(threadID), 849*24df6592SIngo Weinhold fThread(NULL) 850*24df6592SIngo Weinhold { 851*24df6592SIngo Weinhold } 852*24df6592SIngo Weinhold 853*24df6592SIngo Weinhold 854*24df6592SIngo Weinhold ThreadTimeUserTimer::~ThreadTimeUserTimer() 855*24df6592SIngo Weinhold { 856*24df6592SIngo Weinhold ASSERT(fThread == NULL); 857*24df6592SIngo Weinhold } 858*24df6592SIngo Weinhold 859*24df6592SIngo Weinhold 860*24df6592SIngo Weinhold void 861*24df6592SIngo Weinhold ThreadTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval, 862*24df6592SIngo Weinhold uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval) 863*24df6592SIngo Weinhold { 864*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 865*24df6592SIngo Weinhold 866*24df6592SIngo Weinhold // get the current time, but only if needed 867*24df6592SIngo Weinhold bool nowValid = fThread != NULL; 868*24df6592SIngo Weinhold bigtime_t now = nowValid ? fThread->CPUTime(false) : 0; 869*24df6592SIngo Weinhold 870*24df6592SIngo Weinhold // Cancel the old timer, if still scheduled, and get the previous values. 871*24df6592SIngo Weinhold if (fThread != NULL) { 872*24df6592SIngo Weinhold if (fScheduled) { 873*24df6592SIngo Weinhold cancel_timer(&fTimer); 874*24df6592SIngo Weinhold fScheduled = false; 875*24df6592SIngo Weinhold } 876*24df6592SIngo Weinhold 877*24df6592SIngo Weinhold _oldRemainingTime = fNextTime - now; 878*24df6592SIngo Weinhold _oldInterval = fInterval; 879*24df6592SIngo Weinhold 880*24df6592SIngo Weinhold fThread->UserTimerDeactivated(this); 881*24df6592SIngo Weinhold fThread->ReleaseReference(); 882*24df6592SIngo Weinhold fThread = NULL; 883*24df6592SIngo Weinhold } else { 884*24df6592SIngo Weinhold _oldRemainingTime = B_INFINITE_TIMEOUT; 885*24df6592SIngo Weinhold _oldInterval = 0; 886*24df6592SIngo Weinhold } 887*24df6592SIngo Weinhold 888*24df6592SIngo Weinhold // schedule the new timer 889*24df6592SIngo Weinhold fNextTime = nextTime; 890*24df6592SIngo Weinhold fInterval = interval; 891*24df6592SIngo Weinhold fOverrunCount = 0; 892*24df6592SIngo Weinhold 893*24df6592SIngo Weinhold if (fNextTime == B_INFINITE_TIMEOUT) 894*24df6592SIngo Weinhold return; 895*24df6592SIngo Weinhold 896*24df6592SIngo Weinhold // Get the thread. If it doesn't exist anymore, just don't schedule the 897*24df6592SIngo Weinhold // timer anymore. 898*24df6592SIngo Weinhold fThread = Thread::Get(fThreadID); 899*24df6592SIngo Weinhold if (fThread == NULL) 900*24df6592SIngo Weinhold return; 901*24df6592SIngo Weinhold 902*24df6592SIngo Weinhold fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0; 903*24df6592SIngo Weinhold 904*24df6592SIngo Weinhold // convert relative to absolute timeouts 905*24df6592SIngo Weinhold if (!fAbsolute) { 906*24df6592SIngo Weinhold if (!nowValid) 907*24df6592SIngo Weinhold now = fThread->CPUTime(false); 908*24df6592SIngo Weinhold fNextTime += now; 909*24df6592SIngo Weinhold } 910*24df6592SIngo Weinhold 911*24df6592SIngo Weinhold fThread->UserTimerActivated(this); 912*24df6592SIngo Weinhold 913*24df6592SIngo Weinhold // If the thread is currently running, also schedule a kernel timer. 914*24df6592SIngo Weinhold if (fThread->cpu != NULL) 915*24df6592SIngo Weinhold Start(); 916*24df6592SIngo Weinhold } 917*24df6592SIngo Weinhold 918*24df6592SIngo Weinhold 919*24df6592SIngo Weinhold void 920*24df6592SIngo Weinhold ThreadTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval, 921*24df6592SIngo Weinhold uint32& _overrunCount) 922*24df6592SIngo Weinhold { 923*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 924*24df6592SIngo Weinhold 925*24df6592SIngo Weinhold if (fThread != NULL) { 926*24df6592SIngo Weinhold _remainingTime = fNextTime - fThread->CPUTime(false); 927*24df6592SIngo Weinhold _interval = fInterval; 928*24df6592SIngo Weinhold } else { 929*24df6592SIngo Weinhold _remainingTime = B_INFINITE_TIMEOUT; 930*24df6592SIngo Weinhold _interval = 0; 931*24df6592SIngo Weinhold } 932*24df6592SIngo Weinhold 933*24df6592SIngo Weinhold _overrunCount = fOverrunCount; 934*24df6592SIngo Weinhold } 935*24df6592SIngo Weinhold 936*24df6592SIngo Weinhold 937*24df6592SIngo Weinhold /*! Deactivates the timer, if it is activated. 938*24df6592SIngo Weinhold 939*24df6592SIngo Weinhold The caller must hold the scheduler lock. 940*24df6592SIngo Weinhold */ 941*24df6592SIngo Weinhold void 942*24df6592SIngo Weinhold ThreadTimeUserTimer::Deactivate() 943*24df6592SIngo Weinhold { 944*24df6592SIngo Weinhold if (fThread == NULL) 945*24df6592SIngo Weinhold return; 946*24df6592SIngo Weinhold 947*24df6592SIngo Weinhold // unschedule, if scheduled 948*24df6592SIngo Weinhold if (fScheduled) { 949*24df6592SIngo Weinhold cancel_timer(&fTimer); 950*24df6592SIngo Weinhold fScheduled = false; 951*24df6592SIngo Weinhold } 952*24df6592SIngo Weinhold 953*24df6592SIngo Weinhold // deactivate 954*24df6592SIngo Weinhold fThread->UserTimerDeactivated(this); 955*24df6592SIngo Weinhold fThread->ReleaseReference(); 956*24df6592SIngo Weinhold fThread = NULL; 957*24df6592SIngo Weinhold } 958*24df6592SIngo Weinhold 959*24df6592SIngo Weinhold 960*24df6592SIngo Weinhold /*! Starts the timer, if it is active. 961*24df6592SIngo Weinhold 962*24df6592SIngo Weinhold Called when the thread whose CPU time is referred to by the timer is 963*24df6592SIngo Weinhold scheduled, or, when the timer was just set and the thread is already 964*24df6592SIngo Weinhold running. Schedules a kernel timer for the remaining time. 965*24df6592SIngo Weinhold 966*24df6592SIngo Weinhold The caller must hold the scheduler lock. 967*24df6592SIngo Weinhold */ 968*24df6592SIngo Weinhold void 969*24df6592SIngo Weinhold ThreadTimeUserTimer::Start() 970*24df6592SIngo Weinhold { 971*24df6592SIngo Weinhold if (fThread == NULL) 972*24df6592SIngo Weinhold return; 973*24df6592SIngo Weinhold 974*24df6592SIngo Weinhold ASSERT(!fScheduled); 975*24df6592SIngo Weinhold 976*24df6592SIngo Weinhold // add the kernel timer 977*24df6592SIngo Weinhold bigtime_t now = fThread->CPUTime(false); 978*24df6592SIngo Weinhold 979*24df6592SIngo Weinhold // If periodic, check whether the start time is too far in the past. 980*24df6592SIngo Weinhold if (fInterval > 0) 981*24df6592SIngo Weinhold CheckPeriodicOverrun(now); 982*24df6592SIngo Weinhold 983*24df6592SIngo Weinhold if (fNextTime > now) { 984*24df6592SIngo Weinhold fTimer.schedule_time = system_time() + fNextTime - now; 985*24df6592SIngo Weinhold // check for overflow 986*24df6592SIngo Weinhold if (fTimer.schedule_time < 0) 987*24df6592SIngo Weinhold fTimer.schedule_time = B_INFINITE_TIMEOUT; 988*24df6592SIngo Weinhold } else 989*24df6592SIngo Weinhold fTimer.schedule_time = 0; 990*24df6592SIngo Weinhold fTimer.period = 0; 991*24df6592SIngo Weinhold 992*24df6592SIngo Weinhold uint32 flags = B_ONE_SHOT_ABSOLUTE_TIMER 993*24df6592SIngo Weinhold | B_TIMER_USE_TIMER_STRUCT_TIMES | B_TIMER_ACQUIRE_SCHEDULER_LOCK; 994*24df6592SIngo Weinhold // We use B_TIMER_ACQUIRE_SCHEDULER_LOCK to avoid race conditions 995*24df6592SIngo Weinhold // between setting/canceling the timer and the event handler. 996*24df6592SIngo Weinhold 997*24df6592SIngo Weinhold add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, flags); 998*24df6592SIngo Weinhold 999*24df6592SIngo Weinhold fScheduled = true; 1000*24df6592SIngo Weinhold } 1001*24df6592SIngo Weinhold 1002*24df6592SIngo Weinhold 1003*24df6592SIngo Weinhold /*! Stops the timer, if it is active. 1004*24df6592SIngo Weinhold 1005*24df6592SIngo Weinhold Called when the thread whose CPU time is referred to by the timer is 1006*24df6592SIngo Weinhold unscheduled, or, when the timer is canceled. 1007*24df6592SIngo Weinhold 1008*24df6592SIngo Weinhold The caller must hold the scheduler lock. 1009*24df6592SIngo Weinhold */ 1010*24df6592SIngo Weinhold void 1011*24df6592SIngo Weinhold ThreadTimeUserTimer::Stop() 1012*24df6592SIngo Weinhold { 1013*24df6592SIngo Weinhold if (fThread == NULL) 1014*24df6592SIngo Weinhold return; 1015*24df6592SIngo Weinhold 1016*24df6592SIngo Weinhold ASSERT(fScheduled); 1017*24df6592SIngo Weinhold 1018*24df6592SIngo Weinhold // cancel the kernel timer 1019*24df6592SIngo Weinhold cancel_timer(&fTimer); 1020*24df6592SIngo Weinhold fScheduled = false; 1021*24df6592SIngo Weinhold 1022*24df6592SIngo Weinhold // TODO: To avoid odd race conditions, we should check the current time of 1023*24df6592SIngo Weinhold // the thread (ignoring the time since last_time) and manually fire the 1024*24df6592SIngo Weinhold // user event, if necessary. 1025*24df6592SIngo Weinhold } 1026*24df6592SIngo Weinhold 1027*24df6592SIngo Weinhold 1028*24df6592SIngo Weinhold /*! Called when the team's CPU time clock which this timer refers to has been 1029*24df6592SIngo Weinhold set. 1030*24df6592SIngo Weinhold 1031*24df6592SIngo Weinhold The caller must hold the scheduler lock. 1032*24df6592SIngo Weinhold 1033*24df6592SIngo Weinhold \param changedBy The value by which the clock has changed. 1034*24df6592SIngo Weinhold */ 1035*24df6592SIngo Weinhold void 1036*24df6592SIngo Weinhold ThreadTimeUserTimer::TimeWarped(bigtime_t changedBy) 1037*24df6592SIngo Weinhold { 1038*24df6592SIngo Weinhold if (fThread == NULL || changedBy == 0) 1039*24df6592SIngo Weinhold return; 1040*24df6592SIngo Weinhold 1041*24df6592SIngo Weinhold // If this is a relative timer, adjust fNextTime by the value the clock has 1042*24df6592SIngo Weinhold // changed. 1043*24df6592SIngo Weinhold if (!fAbsolute) 1044*24df6592SIngo Weinhold fNextTime += changedBy; 1045*24df6592SIngo Weinhold 1046*24df6592SIngo Weinhold // reschedule the kernel timer 1047*24df6592SIngo Weinhold if (fScheduled) { 1048*24df6592SIngo Weinhold Stop(); 1049*24df6592SIngo Weinhold Start(); 1050*24df6592SIngo Weinhold } 1051*24df6592SIngo Weinhold } 1052*24df6592SIngo Weinhold 1053*24df6592SIngo Weinhold 1054*24df6592SIngo Weinhold void 1055*24df6592SIngo Weinhold ThreadTimeUserTimer::HandleTimer() 1056*24df6592SIngo Weinhold { 1057*24df6592SIngo Weinhold UserTimer::HandleTimer(); 1058*24df6592SIngo Weinhold 1059*24df6592SIngo Weinhold if (fThread != NULL) { 1060*24df6592SIngo Weinhold // If the timer is periodic, reschedule the kernel timer. Otherwise it 1061*24df6592SIngo Weinhold // is no longer active. 1062*24df6592SIngo Weinhold if (fInterval > 0) { 1063*24df6592SIngo Weinhold UpdatePeriodicStartTime(); 1064*24df6592SIngo Weinhold Start(); 1065*24df6592SIngo Weinhold } else { 1066*24df6592SIngo Weinhold fThread->UserTimerDeactivated(this); 1067*24df6592SIngo Weinhold fThread->ReleaseReference(); 1068*24df6592SIngo Weinhold fThread = NULL; 1069*24df6592SIngo Weinhold } 1070*24df6592SIngo Weinhold } 1071*24df6592SIngo Weinhold } 1072*24df6592SIngo Weinhold 1073*24df6592SIngo Weinhold 1074*24df6592SIngo Weinhold // #pragma mark - UserTimerList 1075*24df6592SIngo Weinhold 1076*24df6592SIngo Weinhold 1077*24df6592SIngo Weinhold UserTimerList::UserTimerList() 1078*24df6592SIngo Weinhold { 1079*24df6592SIngo Weinhold } 1080*24df6592SIngo Weinhold 1081*24df6592SIngo Weinhold 1082*24df6592SIngo Weinhold UserTimerList::~UserTimerList() 1083*24df6592SIngo Weinhold { 1084*24df6592SIngo Weinhold ASSERT(fTimers.IsEmpty()); 1085*24df6592SIngo Weinhold } 1086*24df6592SIngo Weinhold 1087*24df6592SIngo Weinhold 1088*24df6592SIngo Weinhold /*! Returns the user timer with the given ID. 1089*24df6592SIngo Weinhold 1090*24df6592SIngo Weinhold \param id The timer's ID 1091*24df6592SIngo Weinhold \return The user timer with the given ID or \c NULL, if there is no such 1092*24df6592SIngo Weinhold timer. 1093*24df6592SIngo Weinhold */ 1094*24df6592SIngo Weinhold UserTimer* 1095*24df6592SIngo Weinhold UserTimerList::TimerFor(int32 id) const 1096*24df6592SIngo Weinhold { 1097*24df6592SIngo Weinhold // TODO: Use a more efficient data structure. E.g. a sorted array. 1098*24df6592SIngo Weinhold for (TimerList::ConstIterator it = fTimers.GetIterator(); 1099*24df6592SIngo Weinhold UserTimer* timer = it.Next();) { 1100*24df6592SIngo Weinhold if (timer->ID() == id) 1101*24df6592SIngo Weinhold return timer; 1102*24df6592SIngo Weinhold } 1103*24df6592SIngo Weinhold 1104*24df6592SIngo Weinhold return NULL; 1105*24df6592SIngo Weinhold } 1106*24df6592SIngo Weinhold 1107*24df6592SIngo Weinhold 1108*24df6592SIngo Weinhold /*! Adds the given user timer and assigns it an ID. 1109*24df6592SIngo Weinhold 1110*24df6592SIngo Weinhold \param timer The timer to be added. 1111*24df6592SIngo Weinhold */ 1112*24df6592SIngo Weinhold void 1113*24df6592SIngo Weinhold UserTimerList::AddTimer(UserTimer* timer) 1114*24df6592SIngo Weinhold { 1115*24df6592SIngo Weinhold int32 id = timer->ID(); 1116*24df6592SIngo Weinhold if (id < 0) { 1117*24df6592SIngo Weinhold // user-defined timer -- find an usused ID 1118*24df6592SIngo Weinhold id = USER_TIMER_FIRST_USER_DEFINED_ID; 1119*24df6592SIngo Weinhold UserTimer* insertAfter = NULL; 1120*24df6592SIngo Weinhold for (TimerList::Iterator it = fTimers.GetIterator(); 1121*24df6592SIngo Weinhold UserTimer* other = it.Next();) { 1122*24df6592SIngo Weinhold if (other->ID() > id) 1123*24df6592SIngo Weinhold break; 1124*24df6592SIngo Weinhold if (other->ID() == id) 1125*24df6592SIngo Weinhold id++; 1126*24df6592SIngo Weinhold insertAfter = other; 1127*24df6592SIngo Weinhold } 1128*24df6592SIngo Weinhold 1129*24df6592SIngo Weinhold // insert the timer 1130*24df6592SIngo Weinhold timer->SetID(id); 1131*24df6592SIngo Weinhold fTimers.InsertAfter(insertAfter, timer); 1132*24df6592SIngo Weinhold } else { 1133*24df6592SIngo Weinhold // default timer -- find the insertion point 1134*24df6592SIngo Weinhold UserTimer* insertAfter = NULL; 1135*24df6592SIngo Weinhold for (TimerList::Iterator it = fTimers.GetIterator(); 1136*24df6592SIngo Weinhold UserTimer* other = it.Next();) { 1137*24df6592SIngo Weinhold if (other->ID() > id) 1138*24df6592SIngo Weinhold break; 1139*24df6592SIngo Weinhold if (other->ID() == id) { 1140*24df6592SIngo Weinhold panic("UserTimerList::AddTimer(): timer with ID %" B_PRId32 1141*24df6592SIngo Weinhold " already exists!", id); 1142*24df6592SIngo Weinhold } 1143*24df6592SIngo Weinhold insertAfter = other; 1144*24df6592SIngo Weinhold } 1145*24df6592SIngo Weinhold 1146*24df6592SIngo Weinhold // insert the timer 1147*24df6592SIngo Weinhold fTimers.InsertAfter(insertAfter, timer); 1148*24df6592SIngo Weinhold } 1149*24df6592SIngo Weinhold } 1150*24df6592SIngo Weinhold 1151*24df6592SIngo Weinhold 1152*24df6592SIngo Weinhold /*! Deletes all (or all user-defined) user timers. 1153*24df6592SIngo Weinhold 1154*24df6592SIngo Weinhold \param userDefinedOnly If \c true, only the user-defined timers are deleted, 1155*24df6592SIngo Weinhold otherwise all timers are deleted. 1156*24df6592SIngo Weinhold \return The number of user-defined timers that were removed and deleted. 1157*24df6592SIngo Weinhold */ 1158*24df6592SIngo Weinhold int32 1159*24df6592SIngo Weinhold UserTimerList::DeleteTimers(bool userDefinedOnly) 1160*24df6592SIngo Weinhold { 1161*24df6592SIngo Weinhold int32 userDefinedCount = 0; 1162*24df6592SIngo Weinhold 1163*24df6592SIngo Weinhold for (TimerList::Iterator it = fTimers.GetIterator(); 1164*24df6592SIngo Weinhold UserTimer* timer = it.Next();) { 1165*24df6592SIngo Weinhold if (timer->ID() < USER_TIMER_FIRST_USER_DEFINED_ID) { 1166*24df6592SIngo Weinhold if (userDefinedOnly) 1167*24df6592SIngo Weinhold continue; 1168*24df6592SIngo Weinhold } else 1169*24df6592SIngo Weinhold userDefinedCount++; 1170*24df6592SIngo Weinhold 1171*24df6592SIngo Weinhold // remove, cancel, and delete the timer 1172*24df6592SIngo Weinhold it.Remove(); 1173*24df6592SIngo Weinhold timer->Cancel(); 1174*24df6592SIngo Weinhold delete timer; 1175*24df6592SIngo Weinhold } 1176*24df6592SIngo Weinhold 1177*24df6592SIngo Weinhold return userDefinedCount; 1178*24df6592SIngo Weinhold } 1179*24df6592SIngo Weinhold 1180*24df6592SIngo Weinhold 1181*24df6592SIngo Weinhold // #pragma mark - private 1182*24df6592SIngo Weinhold 1183*24df6592SIngo Weinhold 1184*24df6592SIngo Weinhold static int32 1185*24df6592SIngo Weinhold create_timer(clockid_t clockID, int32 timerID, Team* team, Thread* thread, 1186*24df6592SIngo Weinhold uint32 flags, const struct sigevent& event, 1187*24df6592SIngo Weinhold ThreadCreationAttributes* threadAttributes, bool isDefaultEvent) 1188*24df6592SIngo Weinhold { 1189*24df6592SIngo Weinhold // create the timer object 1190*24df6592SIngo Weinhold UserTimer* timer; 1191*24df6592SIngo Weinhold switch (clockID) { 1192*24df6592SIngo Weinhold case CLOCK_MONOTONIC: 1193*24df6592SIngo Weinhold timer = new(std::nothrow) SystemTimeUserTimer; 1194*24df6592SIngo Weinhold break; 1195*24df6592SIngo Weinhold 1196*24df6592SIngo Weinhold case CLOCK_REALTIME: 1197*24df6592SIngo Weinhold timer = new(std::nothrow) RealTimeUserTimer; 1198*24df6592SIngo Weinhold break; 1199*24df6592SIngo Weinhold 1200*24df6592SIngo Weinhold case CLOCK_THREAD_CPUTIME_ID: 1201*24df6592SIngo Weinhold timer = new(std::nothrow) ThreadTimeUserTimer( 1202*24df6592SIngo Weinhold thread_get_current_thread()->id); 1203*24df6592SIngo Weinhold break; 1204*24df6592SIngo Weinhold 1205*24df6592SIngo Weinhold case CLOCK_PROCESS_CPUTIME_ID: 1206*24df6592SIngo Weinhold if (team == NULL) 1207*24df6592SIngo Weinhold return B_BAD_VALUE; 1208*24df6592SIngo Weinhold timer = new(std::nothrow) TeamTimeUserTimer(team->id); 1209*24df6592SIngo Weinhold break; 1210*24df6592SIngo Weinhold 1211*24df6592SIngo Weinhold case CLOCK_PROCESS_USER_CPUTIME_ID: 1212*24df6592SIngo Weinhold if (team == NULL) 1213*24df6592SIngo Weinhold return B_BAD_VALUE; 1214*24df6592SIngo Weinhold timer = new(std::nothrow) TeamUserTimeUserTimer(team->id); 1215*24df6592SIngo Weinhold break; 1216*24df6592SIngo Weinhold 1217*24df6592SIngo Weinhold default: 1218*24df6592SIngo Weinhold { 1219*24df6592SIngo Weinhold // The clock ID is a ID of the team whose CPU time the clock refers 1220*24df6592SIngo Weinhold // to. Check whether the team exists and we have permission to 1221*24df6592SIngo Weinhold // access its clock. 1222*24df6592SIngo Weinhold if (clockID <= 0) 1223*24df6592SIngo Weinhold return B_BAD_VALUE; 1224*24df6592SIngo Weinhold if (clockID == team_get_kernel_team_id()) 1225*24df6592SIngo Weinhold return B_NOT_ALLOWED; 1226*24df6592SIngo Weinhold 1227*24df6592SIngo Weinhold Team* timedTeam = Team::GetAndLock(clockID); 1228*24df6592SIngo Weinhold if (timedTeam == NULL) 1229*24df6592SIngo Weinhold return B_BAD_VALUE; 1230*24df6592SIngo Weinhold 1231*24df6592SIngo Weinhold uid_t uid = geteuid(); 1232*24df6592SIngo Weinhold uid_t teamUID = timedTeam->effective_uid; 1233*24df6592SIngo Weinhold 1234*24df6592SIngo Weinhold timedTeam->UnlockAndReleaseReference(); 1235*24df6592SIngo Weinhold 1236*24df6592SIngo Weinhold if (uid != 0 && uid != teamUID) 1237*24df6592SIngo Weinhold return B_NOT_ALLOWED; 1238*24df6592SIngo Weinhold 1239*24df6592SIngo Weinhold timer = new(std::nothrow) TeamTimeUserTimer(clockID); 1240*24df6592SIngo Weinhold break; 1241*24df6592SIngo Weinhold } 1242*24df6592SIngo Weinhold } 1243*24df6592SIngo Weinhold 1244*24df6592SIngo Weinhold if (timer == NULL) 1245*24df6592SIngo Weinhold return B_NO_MEMORY; 1246*24df6592SIngo Weinhold ObjectDeleter<UserTimer> timerDeleter(timer); 1247*24df6592SIngo Weinhold 1248*24df6592SIngo Weinhold if (timerID >= 0) 1249*24df6592SIngo Weinhold timer->SetID(timerID); 1250*24df6592SIngo Weinhold 1251*24df6592SIngo Weinhold SignalEvent* signalEvent = NULL; 1252*24df6592SIngo Weinhold 1253*24df6592SIngo Weinhold switch (event.sigev_notify) { 1254*24df6592SIngo Weinhold case SIGEV_NONE: 1255*24df6592SIngo Weinhold // the timer's event remains NULL 1256*24df6592SIngo Weinhold break; 1257*24df6592SIngo Weinhold 1258*24df6592SIngo Weinhold case SIGEV_SIGNAL: 1259*24df6592SIngo Weinhold { 1260*24df6592SIngo Weinhold if (event.sigev_signo <= 0 || event.sigev_signo > MAX_SIGNAL_NUMBER) 1261*24df6592SIngo Weinhold return B_BAD_VALUE; 1262*24df6592SIngo Weinhold 1263*24df6592SIngo Weinhold if (thread != NULL && (flags & USER_TIMER_SIGNAL_THREAD) != 0) { 1264*24df6592SIngo Weinhold // The signal shall be sent to the thread. 1265*24df6592SIngo Weinhold signalEvent = ThreadSignalEvent::Create(thread, 1266*24df6592SIngo Weinhold event.sigev_signo, SI_TIMER, 0, team->id); 1267*24df6592SIngo Weinhold } else { 1268*24df6592SIngo Weinhold // The signal shall be sent to the team. 1269*24df6592SIngo Weinhold signalEvent = TeamSignalEvent::Create(team, event.sigev_signo, 1270*24df6592SIngo Weinhold SI_TIMER, 0); 1271*24df6592SIngo Weinhold } 1272*24df6592SIngo Weinhold 1273*24df6592SIngo Weinhold if (signalEvent == NULL) 1274*24df6592SIngo Weinhold return B_NO_MEMORY; 1275*24df6592SIngo Weinhold 1276*24df6592SIngo Weinhold timer->SetEvent(signalEvent); 1277*24df6592SIngo Weinhold break; 1278*24df6592SIngo Weinhold } 1279*24df6592SIngo Weinhold 1280*24df6592SIngo Weinhold case SIGEV_THREAD: 1281*24df6592SIngo Weinhold { 1282*24df6592SIngo Weinhold if (threadAttributes == NULL) 1283*24df6592SIngo Weinhold return B_BAD_VALUE; 1284*24df6592SIngo Weinhold 1285*24df6592SIngo Weinhold CreateThreadEvent* event 1286*24df6592SIngo Weinhold = CreateThreadEvent::Create(*threadAttributes); 1287*24df6592SIngo Weinhold if (event == NULL) 1288*24df6592SIngo Weinhold return B_NO_MEMORY; 1289*24df6592SIngo Weinhold 1290*24df6592SIngo Weinhold timer->SetEvent(event); 1291*24df6592SIngo Weinhold break; 1292*24df6592SIngo Weinhold } 1293*24df6592SIngo Weinhold 1294*24df6592SIngo Weinhold default: 1295*24df6592SIngo Weinhold return B_BAD_VALUE; 1296*24df6592SIngo Weinhold } 1297*24df6592SIngo Weinhold 1298*24df6592SIngo Weinhold // add it to the team/thread 1299*24df6592SIngo Weinhold TimerLocker timerLocker; 1300*24df6592SIngo Weinhold timerLocker.Lock(team, thread); 1301*24df6592SIngo Weinhold 1302*24df6592SIngo Weinhold status_t error = thread != NULL 1303*24df6592SIngo Weinhold ? thread->AddUserTimer(timer) : team->AddUserTimer(timer); 1304*24df6592SIngo Weinhold if (error != B_OK) 1305*24df6592SIngo Weinhold return error; 1306*24df6592SIngo Weinhold 1307*24df6592SIngo Weinhold // set a signal event's user value 1308*24df6592SIngo Weinhold if (signalEvent != NULL) { 1309*24df6592SIngo Weinhold // If no sigevent structure was given, use the timer ID. 1310*24df6592SIngo Weinhold union sigval signalValue = event.sigev_value; 1311*24df6592SIngo Weinhold if (isDefaultEvent) 1312*24df6592SIngo Weinhold signalValue.sival_int = timer->ID(); 1313*24df6592SIngo Weinhold 1314*24df6592SIngo Weinhold signalEvent->SetUserValue(signalValue); 1315*24df6592SIngo Weinhold } 1316*24df6592SIngo Weinhold 1317*24df6592SIngo Weinhold return timerDeleter.Detach()->ID(); 1318*24df6592SIngo Weinhold } 1319*24df6592SIngo Weinhold 1320*24df6592SIngo Weinhold 1321*24df6592SIngo Weinhold /*! Called when the CPU time clock of the given thread has been set. 1322*24df6592SIngo Weinhold 1323*24df6592SIngo Weinhold The caller must hold the scheduler lock. 1324*24df6592SIngo Weinhold 1325*24df6592SIngo Weinhold \param thread The thread whose CPU time clock has been set. 1326*24df6592SIngo Weinhold \param changedBy The value by which the CPU time clock has changed 1327*24df6592SIngo Weinhold (new = old + changedBy). 1328*24df6592SIngo Weinhold */ 1329*24df6592SIngo Weinhold static void 1330*24df6592SIngo Weinhold thread_clock_changed(Thread* thread, bigtime_t changedBy) 1331*24df6592SIngo Weinhold { 1332*24df6592SIngo Weinhold for (ThreadTimeUserTimerList::ConstIterator it 1333*24df6592SIngo Weinhold = thread->CPUTimeUserTimerIterator(); 1334*24df6592SIngo Weinhold ThreadTimeUserTimer* timer = it.Next();) { 1335*24df6592SIngo Weinhold timer->TimeWarped(changedBy); 1336*24df6592SIngo Weinhold } 1337*24df6592SIngo Weinhold } 1338*24df6592SIngo Weinhold 1339*24df6592SIngo Weinhold 1340*24df6592SIngo Weinhold /*! Called when the CPU time clock of the given team has been set. 1341*24df6592SIngo Weinhold 1342*24df6592SIngo Weinhold The caller must hold the scheduler lock. 1343*24df6592SIngo Weinhold 1344*24df6592SIngo Weinhold \param team The team whose CPU time clock has been set. 1345*24df6592SIngo Weinhold \param changedBy The value by which the CPU time clock has changed 1346*24df6592SIngo Weinhold (new = old + changedBy). 1347*24df6592SIngo Weinhold */ 1348*24df6592SIngo Weinhold static void 1349*24df6592SIngo Weinhold team_clock_changed(Team* team, bigtime_t changedBy) 1350*24df6592SIngo Weinhold { 1351*24df6592SIngo Weinhold for (TeamTimeUserTimerList::ConstIterator it 1352*24df6592SIngo Weinhold = team->CPUTimeUserTimerIterator(); 1353*24df6592SIngo Weinhold TeamTimeUserTimer* timer = it.Next();) { 1354*24df6592SIngo Weinhold timer->TimeWarped(changedBy); 1355*24df6592SIngo Weinhold } 1356*24df6592SIngo Weinhold } 1357*24df6592SIngo Weinhold 1358*24df6592SIngo Weinhold 1359*24df6592SIngo Weinhold // #pragma mark - kernel private 1360*24df6592SIngo Weinhold 1361*24df6592SIngo Weinhold 1362*24df6592SIngo Weinhold /*! Creates the pre-defined user timers for the given thread. 1363*24df6592SIngo Weinhold The thread may not have been added to its team yet, hence the team must be 1364*24df6592SIngo Weinhold passed 1365*24df6592SIngo Weinhold 1366*24df6592SIngo Weinhold \param team The thread's (future) team. 1367*24df6592SIngo Weinhold \param thread The thread whose pre-defined timers shall be created. 1368*24df6592SIngo Weinhold \return \c B_OK, when everything when fine, another error code otherwise. 1369*24df6592SIngo Weinhold */ 1370*24df6592SIngo Weinhold status_t 1371*24df6592SIngo Weinhold user_timer_create_thread_timers(Team* team, Thread* thread) 1372*24df6592SIngo Weinhold { 1373*24df6592SIngo Weinhold // create a real time user timer 1374*24df6592SIngo Weinhold struct sigevent event; 1375*24df6592SIngo Weinhold event.sigev_notify = SIGEV_SIGNAL; 1376*24df6592SIngo Weinhold event.sigev_signo = SIGALRM; 1377*24df6592SIngo Weinhold 1378*24df6592SIngo Weinhold int32 timerID = create_timer(CLOCK_MONOTONIC, USER_TIMER_REAL_TIME_ID, 1379*24df6592SIngo Weinhold team, thread, USER_TIMER_SIGNAL_THREAD, event, NULL, true); 1380*24df6592SIngo Weinhold if (timerID < 0) 1381*24df6592SIngo Weinhold return timerID; 1382*24df6592SIngo Weinhold 1383*24df6592SIngo Weinhold return B_OK; 1384*24df6592SIngo Weinhold } 1385*24df6592SIngo Weinhold 1386*24df6592SIngo Weinhold 1387*24df6592SIngo Weinhold /*! Creates the pre-defined user timers for the given team. 1388*24df6592SIngo Weinhold 1389*24df6592SIngo Weinhold \param team The team whose pre-defined timers shall be created. 1390*24df6592SIngo Weinhold \return \c B_OK, when everything when fine, another error code otherwise. 1391*24df6592SIngo Weinhold */ 1392*24df6592SIngo Weinhold status_t 1393*24df6592SIngo Weinhold user_timer_create_team_timers(Team* team) 1394*24df6592SIngo Weinhold { 1395*24df6592SIngo Weinhold // create a real time user timer 1396*24df6592SIngo Weinhold struct sigevent event; 1397*24df6592SIngo Weinhold event.sigev_notify = SIGEV_SIGNAL; 1398*24df6592SIngo Weinhold event.sigev_signo = SIGALRM; 1399*24df6592SIngo Weinhold 1400*24df6592SIngo Weinhold int32 timerID = create_timer(CLOCK_MONOTONIC, USER_TIMER_REAL_TIME_ID, 1401*24df6592SIngo Weinhold team, NULL, 0, event, NULL, true); 1402*24df6592SIngo Weinhold if (timerID < 0) 1403*24df6592SIngo Weinhold return timerID; 1404*24df6592SIngo Weinhold 1405*24df6592SIngo Weinhold // create a total CPU time user timer 1406*24df6592SIngo Weinhold event.sigev_notify = SIGEV_SIGNAL; 1407*24df6592SIngo Weinhold event.sigev_signo = SIGPROF; 1408*24df6592SIngo Weinhold 1409*24df6592SIngo Weinhold timerID = create_timer(CLOCK_PROCESS_CPUTIME_ID, 1410*24df6592SIngo Weinhold USER_TIMER_TEAM_TOTAL_TIME_ID, team, NULL, 0, event, NULL, true); 1411*24df6592SIngo Weinhold if (timerID < 0) 1412*24df6592SIngo Weinhold return timerID; 1413*24df6592SIngo Weinhold 1414*24df6592SIngo Weinhold // create a user CPU time user timer 1415*24df6592SIngo Weinhold event.sigev_notify = SIGEV_SIGNAL; 1416*24df6592SIngo Weinhold event.sigev_signo = SIGVTALRM; 1417*24df6592SIngo Weinhold 1418*24df6592SIngo Weinhold timerID = create_timer(CLOCK_PROCESS_USER_CPUTIME_ID, 1419*24df6592SIngo Weinhold USER_TIMER_TEAM_USER_TIME_ID, team, NULL, 0, event, NULL, true); 1420*24df6592SIngo Weinhold if (timerID < 0) 1421*24df6592SIngo Weinhold return timerID; 1422*24df6592SIngo Weinhold 1423*24df6592SIngo Weinhold return B_OK; 1424*24df6592SIngo Weinhold } 1425*24df6592SIngo Weinhold 1426*24df6592SIngo Weinhold 1427*24df6592SIngo Weinhold status_t 1428*24df6592SIngo Weinhold user_timer_get_clock(clockid_t clockID, bigtime_t& _time) 1429*24df6592SIngo Weinhold { 1430*24df6592SIngo Weinhold switch (clockID) { 1431*24df6592SIngo Weinhold case CLOCK_MONOTONIC: 1432*24df6592SIngo Weinhold _time = system_time(); 1433*24df6592SIngo Weinhold return B_OK; 1434*24df6592SIngo Weinhold 1435*24df6592SIngo Weinhold case CLOCK_REALTIME: 1436*24df6592SIngo Weinhold _time = real_time_clock_usecs(); 1437*24df6592SIngo Weinhold return B_OK; 1438*24df6592SIngo Weinhold 1439*24df6592SIngo Weinhold case CLOCK_THREAD_CPUTIME_ID: 1440*24df6592SIngo Weinhold { 1441*24df6592SIngo Weinhold Thread* thread = thread_get_current_thread(); 1442*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 1443*24df6592SIngo Weinhold _time = thread->CPUTime(false); 1444*24df6592SIngo Weinhold return B_OK; 1445*24df6592SIngo Weinhold } 1446*24df6592SIngo Weinhold 1447*24df6592SIngo Weinhold case CLOCK_PROCESS_USER_CPUTIME_ID: 1448*24df6592SIngo Weinhold { 1449*24df6592SIngo Weinhold Team* team = thread_get_current_thread()->team; 1450*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 1451*24df6592SIngo Weinhold _time = team->UserCPUTime(); 1452*24df6592SIngo Weinhold return B_OK; 1453*24df6592SIngo Weinhold } 1454*24df6592SIngo Weinhold 1455*24df6592SIngo Weinhold case CLOCK_PROCESS_CPUTIME_ID: 1456*24df6592SIngo Weinhold default: 1457*24df6592SIngo Weinhold { 1458*24df6592SIngo Weinhold // get the ID of the target team (or the respective placeholder) 1459*24df6592SIngo Weinhold team_id teamID; 1460*24df6592SIngo Weinhold if (clockID == CLOCK_PROCESS_CPUTIME_ID) { 1461*24df6592SIngo Weinhold teamID = B_CURRENT_TEAM; 1462*24df6592SIngo Weinhold } else { 1463*24df6592SIngo Weinhold if (clockID < 0) 1464*24df6592SIngo Weinhold return B_BAD_VALUE; 1465*24df6592SIngo Weinhold if (clockID == team_get_kernel_team_id()) 1466*24df6592SIngo Weinhold return B_NOT_ALLOWED; 1467*24df6592SIngo Weinhold 1468*24df6592SIngo Weinhold teamID = clockID; 1469*24df6592SIngo Weinhold } 1470*24df6592SIngo Weinhold 1471*24df6592SIngo Weinhold // get the team 1472*24df6592SIngo Weinhold Team* team = Team::Get(teamID); 1473*24df6592SIngo Weinhold if (team == NULL) 1474*24df6592SIngo Weinhold return B_BAD_VALUE; 1475*24df6592SIngo Weinhold BReference<Team> teamReference(team, true); 1476*24df6592SIngo Weinhold 1477*24df6592SIngo Weinhold // get the time 1478*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 1479*24df6592SIngo Weinhold _time = team->CPUTime(false); 1480*24df6592SIngo Weinhold 1481*24df6592SIngo Weinhold return B_OK; 1482*24df6592SIngo Weinhold } 1483*24df6592SIngo Weinhold } 1484*24df6592SIngo Weinhold } 1485*24df6592SIngo Weinhold 1486*24df6592SIngo Weinhold 1487*24df6592SIngo Weinhold void 1488*24df6592SIngo Weinhold user_timer_real_time_clock_changed() 1489*24df6592SIngo Weinhold { 1490*24df6592SIngo Weinhold // we need to update all absolute real-time timers 1491*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 1492*24df6592SIngo Weinhold SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock); 1493*24df6592SIngo Weinhold 1494*24df6592SIngo Weinhold for (RealTimeUserTimerList::Iterator it 1495*24df6592SIngo Weinhold = sAbsoluteRealTimeTimers.GetIterator(); 1496*24df6592SIngo Weinhold RealTimeUserTimer* timer = it.Next();) { 1497*24df6592SIngo Weinhold timer->TimeWarped(); 1498*24df6592SIngo Weinhold } 1499*24df6592SIngo Weinhold } 1500*24df6592SIngo Weinhold 1501*24df6592SIngo Weinhold 1502*24df6592SIngo Weinhold void 1503*24df6592SIngo Weinhold user_timer_stop_cpu_timers(Thread* thread, Thread* nextThread) 1504*24df6592SIngo Weinhold { 1505*24df6592SIngo Weinhold // stop thread timers 1506*24df6592SIngo Weinhold for (ThreadTimeUserTimerList::ConstIterator it 1507*24df6592SIngo Weinhold = thread->CPUTimeUserTimerIterator(); 1508*24df6592SIngo Weinhold ThreadTimeUserTimer* timer = it.Next();) { 1509*24df6592SIngo Weinhold timer->Stop(); 1510*24df6592SIngo Weinhold } 1511*24df6592SIngo Weinhold 1512*24df6592SIngo Weinhold // update team timers 1513*24df6592SIngo Weinhold if (nextThread == NULL || nextThread->team != thread->team) { 1514*24df6592SIngo Weinhold for (TeamTimeUserTimerList::ConstIterator it 1515*24df6592SIngo Weinhold = thread->team->CPUTimeUserTimerIterator(); 1516*24df6592SIngo Weinhold TeamTimeUserTimer* timer = it.Next();) { 1517*24df6592SIngo Weinhold timer->Update(thread); 1518*24df6592SIngo Weinhold } 1519*24df6592SIngo Weinhold } 1520*24df6592SIngo Weinhold } 1521*24df6592SIngo Weinhold 1522*24df6592SIngo Weinhold 1523*24df6592SIngo Weinhold void 1524*24df6592SIngo Weinhold user_timer_continue_cpu_timers(Thread* thread, Thread* previousThread) 1525*24df6592SIngo Weinhold { 1526*24df6592SIngo Weinhold // update team timers 1527*24df6592SIngo Weinhold if (previousThread == NULL || previousThread->team != thread->team) { 1528*24df6592SIngo Weinhold for (TeamTimeUserTimerList::ConstIterator it 1529*24df6592SIngo Weinhold = thread->team->CPUTimeUserTimerIterator(); 1530*24df6592SIngo Weinhold TeamTimeUserTimer* timer = it.Next();) { 1531*24df6592SIngo Weinhold timer->Update(NULL); 1532*24df6592SIngo Weinhold } 1533*24df6592SIngo Weinhold } 1534*24df6592SIngo Weinhold 1535*24df6592SIngo Weinhold // start thread timers 1536*24df6592SIngo Weinhold for (ThreadTimeUserTimerList::ConstIterator it 1537*24df6592SIngo Weinhold = thread->CPUTimeUserTimerIterator(); 1538*24df6592SIngo Weinhold ThreadTimeUserTimer* timer = it.Next();) { 1539*24df6592SIngo Weinhold timer->Start(); 1540*24df6592SIngo Weinhold } 1541*24df6592SIngo Weinhold } 1542*24df6592SIngo Weinhold 1543*24df6592SIngo Weinhold 1544*24df6592SIngo Weinhold void 1545*24df6592SIngo Weinhold user_timer_check_team_user_timers(Team* team) 1546*24df6592SIngo Weinhold { 1547*24df6592SIngo Weinhold for (TeamUserTimeUserTimerList::ConstIterator it 1548*24df6592SIngo Weinhold = team->UserTimeUserTimerIterator(); 1549*24df6592SIngo Weinhold TeamUserTimeUserTimer* timer = it.Next();) { 1550*24df6592SIngo Weinhold timer->Check(); 1551*24df6592SIngo Weinhold } 1552*24df6592SIngo Weinhold } 1553*24df6592SIngo Weinhold 1554*24df6592SIngo Weinhold 1555*24df6592SIngo Weinhold // #pragma mark - syscalls 1556*24df6592SIngo Weinhold 1557*24df6592SIngo Weinhold 1558*24df6592SIngo Weinhold status_t 1559*24df6592SIngo Weinhold _user_get_clock(clockid_t clockID, bigtime_t* userTime) 1560*24df6592SIngo Weinhold { 1561*24df6592SIngo Weinhold // get the time 1562*24df6592SIngo Weinhold bigtime_t time; 1563*24df6592SIngo Weinhold status_t error = user_timer_get_clock(clockID, time); 1564*24df6592SIngo Weinhold if (error != B_OK) 1565*24df6592SIngo Weinhold return error; 1566*24df6592SIngo Weinhold 1567*24df6592SIngo Weinhold // copy the value back to userland 1568*24df6592SIngo Weinhold if (userTime == NULL || !IS_USER_ADDRESS(userTime) 1569*24df6592SIngo Weinhold || user_memcpy(userTime, &time, sizeof(time)) != B_OK) { 1570*24df6592SIngo Weinhold return B_BAD_ADDRESS; 1571*24df6592SIngo Weinhold } 1572*24df6592SIngo Weinhold 1573*24df6592SIngo Weinhold return B_OK; 1574*24df6592SIngo Weinhold } 1575*24df6592SIngo Weinhold 1576*24df6592SIngo Weinhold 1577*24df6592SIngo Weinhold status_t 1578*24df6592SIngo Weinhold _user_set_clock(clockid_t clockID, bigtime_t time) 1579*24df6592SIngo Weinhold { 1580*24df6592SIngo Weinhold switch (clockID) { 1581*24df6592SIngo Weinhold case CLOCK_MONOTONIC: 1582*24df6592SIngo Weinhold return B_BAD_VALUE; 1583*24df6592SIngo Weinhold 1584*24df6592SIngo Weinhold case CLOCK_REALTIME: 1585*24df6592SIngo Weinhold // only root may set the time 1586*24df6592SIngo Weinhold if (geteuid() != 0) 1587*24df6592SIngo Weinhold return B_NOT_ALLOWED; 1588*24df6592SIngo Weinhold 1589*24df6592SIngo Weinhold set_real_time_clock_usecs(time); 1590*24df6592SIngo Weinhold return B_OK; 1591*24df6592SIngo Weinhold 1592*24df6592SIngo Weinhold case CLOCK_THREAD_CPUTIME_ID: 1593*24df6592SIngo Weinhold { 1594*24df6592SIngo Weinhold Thread* thread = thread_get_current_thread(); 1595*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 1596*24df6592SIngo Weinhold bigtime_t diff = time - thread->CPUTime(false); 1597*24df6592SIngo Weinhold thread->cpu_clock_offset += diff; 1598*24df6592SIngo Weinhold 1599*24df6592SIngo Weinhold thread_clock_changed(thread, diff); 1600*24df6592SIngo Weinhold return B_OK; 1601*24df6592SIngo Weinhold } 1602*24df6592SIngo Weinhold 1603*24df6592SIngo Weinhold case CLOCK_PROCESS_USER_CPUTIME_ID: 1604*24df6592SIngo Weinhold // not supported -- this clock is an Haiku-internal extension 1605*24df6592SIngo Weinhold return B_BAD_VALUE; 1606*24df6592SIngo Weinhold 1607*24df6592SIngo Weinhold case CLOCK_PROCESS_CPUTIME_ID: 1608*24df6592SIngo Weinhold default: 1609*24df6592SIngo Weinhold { 1610*24df6592SIngo Weinhold // get the ID of the target team (or the respective placeholder) 1611*24df6592SIngo Weinhold team_id teamID; 1612*24df6592SIngo Weinhold if (clockID == CLOCK_PROCESS_CPUTIME_ID) { 1613*24df6592SIngo Weinhold teamID = B_CURRENT_TEAM; 1614*24df6592SIngo Weinhold } else { 1615*24df6592SIngo Weinhold if (clockID < 0) 1616*24df6592SIngo Weinhold return B_BAD_VALUE; 1617*24df6592SIngo Weinhold if (clockID == team_get_kernel_team_id()) 1618*24df6592SIngo Weinhold return B_NOT_ALLOWED; 1619*24df6592SIngo Weinhold 1620*24df6592SIngo Weinhold teamID = clockID; 1621*24df6592SIngo Weinhold } 1622*24df6592SIngo Weinhold 1623*24df6592SIngo Weinhold // get the team 1624*24df6592SIngo Weinhold Team* team = Team::Get(teamID); 1625*24df6592SIngo Weinhold if (team == NULL) 1626*24df6592SIngo Weinhold return B_BAD_VALUE; 1627*24df6592SIngo Weinhold BReference<Team> teamReference(team, true); 1628*24df6592SIngo Weinhold 1629*24df6592SIngo Weinhold // set the time offset 1630*24df6592SIngo Weinhold InterruptsSpinLocker schedulerLocker(gSchedulerLock); 1631*24df6592SIngo Weinhold bigtime_t diff = time - team->CPUTime(false); 1632*24df6592SIngo Weinhold team->cpu_clock_offset += diff; 1633*24df6592SIngo Weinhold 1634*24df6592SIngo Weinhold team_clock_changed(team, diff); 1635*24df6592SIngo Weinhold return B_OK; 1636*24df6592SIngo Weinhold } 1637*24df6592SIngo Weinhold } 1638*24df6592SIngo Weinhold 1639*24df6592SIngo Weinhold return B_OK; 1640*24df6592SIngo Weinhold } 1641*24df6592SIngo Weinhold 1642*24df6592SIngo Weinhold 1643*24df6592SIngo Weinhold int32 1644*24df6592SIngo Weinhold _user_create_timer(clockid_t clockID, thread_id threadID, uint32 flags, 1645*24df6592SIngo Weinhold const struct sigevent* userEvent, 1646*24df6592SIngo Weinhold const thread_creation_attributes* userThreadAttributes) 1647*24df6592SIngo Weinhold { 1648*24df6592SIngo Weinhold // copy the sigevent structure from userland 1649*24df6592SIngo Weinhold struct sigevent event; 1650*24df6592SIngo Weinhold if (userEvent != NULL) { 1651*24df6592SIngo Weinhold if (!IS_USER_ADDRESS(userEvent) 1652*24df6592SIngo Weinhold || user_memcpy(&event, userEvent, sizeof(event)) != B_OK) { 1653*24df6592SIngo Weinhold return B_BAD_ADDRESS; 1654*24df6592SIngo Weinhold } 1655*24df6592SIngo Weinhold } else { 1656*24df6592SIngo Weinhold // none given -- use defaults 1657*24df6592SIngo Weinhold event.sigev_notify = SIGEV_SIGNAL; 1658*24df6592SIngo Weinhold event.sigev_signo = SIGALRM; 1659*24df6592SIngo Weinhold } 1660*24df6592SIngo Weinhold 1661*24df6592SIngo Weinhold // copy thread creation attributes from userland, if specified 1662*24df6592SIngo Weinhold char nameBuffer[B_OS_NAME_LENGTH]; 1663*24df6592SIngo Weinhold ThreadCreationAttributes threadAttributes; 1664*24df6592SIngo Weinhold if (event.sigev_notify == SIGEV_THREAD) { 1665*24df6592SIngo Weinhold status_t error = threadAttributes.InitFromUserAttributes( 1666*24df6592SIngo Weinhold userThreadAttributes, nameBuffer); 1667*24df6592SIngo Weinhold if (error != B_OK) 1668*24df6592SIngo Weinhold return error; 1669*24df6592SIngo Weinhold } 1670*24df6592SIngo Weinhold 1671*24df6592SIngo Weinhold // get team and thread 1672*24df6592SIngo Weinhold Team* team = thread_get_current_thread()->team; 1673*24df6592SIngo Weinhold Thread* thread = NULL; 1674*24df6592SIngo Weinhold if (threadID >= 0) { 1675*24df6592SIngo Weinhold thread = Thread::GetAndLock(threadID); 1676*24df6592SIngo Weinhold if (thread == NULL) 1677*24df6592SIngo Weinhold return B_BAD_THREAD_ID; 1678*24df6592SIngo Weinhold 1679*24df6592SIngo Weinhold thread->Unlock(); 1680*24df6592SIngo Weinhold } 1681*24df6592SIngo Weinhold BReference<Thread> threadReference(thread, true); 1682*24df6592SIngo Weinhold 1683*24df6592SIngo Weinhold // create the timer 1684*24df6592SIngo Weinhold return create_timer(clockID, -1, team, thread, flags, event, 1685*24df6592SIngo Weinhold userThreadAttributes != NULL ? &threadAttributes : NULL, 1686*24df6592SIngo Weinhold userEvent == NULL); 1687*24df6592SIngo Weinhold } 1688*24df6592SIngo Weinhold 1689*24df6592SIngo Weinhold 1690*24df6592SIngo Weinhold status_t 1691*24df6592SIngo Weinhold _user_delete_timer(int32 timerID, thread_id threadID) 1692*24df6592SIngo Weinhold { 1693*24df6592SIngo Weinhold // can only delete user-defined timers 1694*24df6592SIngo Weinhold if (timerID < USER_TIMER_FIRST_USER_DEFINED_ID) 1695*24df6592SIngo Weinhold return B_BAD_VALUE; 1696*24df6592SIngo Weinhold 1697*24df6592SIngo Weinhold // get the timer 1698*24df6592SIngo Weinhold TimerLocker timerLocker; 1699*24df6592SIngo Weinhold UserTimer* timer; 1700*24df6592SIngo Weinhold status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer); 1701*24df6592SIngo Weinhold if (error != B_OK) 1702*24df6592SIngo Weinhold return error; 1703*24df6592SIngo Weinhold 1704*24df6592SIngo Weinhold // cancel, remove, and delete it 1705*24df6592SIngo Weinhold timer->Cancel(); 1706*24df6592SIngo Weinhold 1707*24df6592SIngo Weinhold if (threadID >= 0) 1708*24df6592SIngo Weinhold timerLocker.thread->RemoveUserTimer(timer); 1709*24df6592SIngo Weinhold else 1710*24df6592SIngo Weinhold timerLocker.team->RemoveUserTimer(timer); 1711*24df6592SIngo Weinhold 1712*24df6592SIngo Weinhold delete timer; 1713*24df6592SIngo Weinhold 1714*24df6592SIngo Weinhold return B_OK; 1715*24df6592SIngo Weinhold } 1716*24df6592SIngo Weinhold 1717*24df6592SIngo Weinhold 1718*24df6592SIngo Weinhold status_t 1719*24df6592SIngo Weinhold _user_get_timer(int32 timerID, thread_id threadID, 1720*24df6592SIngo Weinhold struct user_timer_info* userInfo) 1721*24df6592SIngo Weinhold { 1722*24df6592SIngo Weinhold // get the timer 1723*24df6592SIngo Weinhold TimerLocker timerLocker; 1724*24df6592SIngo Weinhold UserTimer* timer; 1725*24df6592SIngo Weinhold status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer); 1726*24df6592SIngo Weinhold if (error != B_OK) 1727*24df6592SIngo Weinhold return error; 1728*24df6592SIngo Weinhold 1729*24df6592SIngo Weinhold // get the info 1730*24df6592SIngo Weinhold user_timer_info info; 1731*24df6592SIngo Weinhold timer->GetInfo(info.remaining_time, info.interval, info.overrun_count); 1732*24df6592SIngo Weinhold 1733*24df6592SIngo Weinhold // Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid 1734*24df6592SIngo Weinhold // value. 1735*24df6592SIngo Weinhold if (info.remaining_time <= 0) 1736*24df6592SIngo Weinhold info.remaining_time = 1; 1737*24df6592SIngo Weinhold 1738*24df6592SIngo Weinhold timerLocker.Unlock(); 1739*24df6592SIngo Weinhold 1740*24df6592SIngo Weinhold // copy it back to userland 1741*24df6592SIngo Weinhold if (userInfo != NULL 1742*24df6592SIngo Weinhold && (!IS_USER_ADDRESS(userInfo) 1743*24df6592SIngo Weinhold || user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) { 1744*24df6592SIngo Weinhold return B_BAD_ADDRESS; 1745*24df6592SIngo Weinhold } 1746*24df6592SIngo Weinhold 1747*24df6592SIngo Weinhold return B_OK; 1748*24df6592SIngo Weinhold } 1749*24df6592SIngo Weinhold 1750*24df6592SIngo Weinhold 1751*24df6592SIngo Weinhold status_t 1752*24df6592SIngo Weinhold _user_set_timer(int32 timerID, thread_id threadID, bigtime_t startTime, 1753*24df6592SIngo Weinhold bigtime_t interval, uint32 flags, struct user_timer_info* userOldInfo) 1754*24df6592SIngo Weinhold { 1755*24df6592SIngo Weinhold // check the values 1756*24df6592SIngo Weinhold if (startTime < 0 || interval < 0) 1757*24df6592SIngo Weinhold return B_BAD_VALUE; 1758*24df6592SIngo Weinhold 1759*24df6592SIngo Weinhold // get the timer 1760*24df6592SIngo Weinhold TimerLocker timerLocker; 1761*24df6592SIngo Weinhold UserTimer* timer; 1762*24df6592SIngo Weinhold status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer); 1763*24df6592SIngo Weinhold if (error != B_OK) 1764*24df6592SIngo Weinhold return error; 1765*24df6592SIngo Weinhold 1766*24df6592SIngo Weinhold // schedule the timer 1767*24df6592SIngo Weinhold user_timer_info oldInfo; 1768*24df6592SIngo Weinhold timer->Schedule(startTime, interval, flags, oldInfo.remaining_time, 1769*24df6592SIngo Weinhold oldInfo.interval); 1770*24df6592SIngo Weinhold 1771*24df6592SIngo Weinhold // Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid 1772*24df6592SIngo Weinhold // value. 1773*24df6592SIngo Weinhold if (oldInfo.remaining_time <= 0) 1774*24df6592SIngo Weinhold oldInfo.remaining_time = 1; 1775*24df6592SIngo Weinhold 1776*24df6592SIngo Weinhold timerLocker.Unlock(); 1777*24df6592SIngo Weinhold 1778*24df6592SIngo Weinhold // copy back the old info 1779*24df6592SIngo Weinhold if (userOldInfo != NULL 1780*24df6592SIngo Weinhold && (!IS_USER_ADDRESS(userOldInfo) 1781*24df6592SIngo Weinhold || user_memcpy(userOldInfo, &oldInfo, sizeof(oldInfo)) != B_OK)) { 1782*24df6592SIngo Weinhold return B_BAD_ADDRESS; 1783*24df6592SIngo Weinhold } 1784*24df6592SIngo Weinhold 1785*24df6592SIngo Weinhold return B_OK; 1786*24df6592SIngo Weinhold } 1787