2 * Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org.
3 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
12 #include <AutoDeleter.h>
16 #include <real_time_clock.h>
18 #include <thread_types.h>
19 #include <UserEvent.h>
20 #include <util/AutoLock.h>
23 // Minimum interval length in microseconds for a periodic timer. This is not a
24 // restriction on the user timer interval length itself, but the minimum time
25 // span by which we advance the start time for kernel timers. A shorted user
26 // timer interval will result in the overrun count to be increased every time
27 // the kernel timer is rescheduled.
28 static const bigtime_t kMinPeriodicTimerInterval
= 100;
30 static RealTimeUserTimerList sAbsoluteRealTimeTimers
;
31 static spinlock sAbsoluteRealTimeTimersLock
= B_SPINLOCK_INITIALIZER
;
33 static seqlock sUserTimerLock
= B_SEQLOCK_INITIALIZER
;
36 // #pragma mark - TimerLocker
57 void Lock(Team
* team
, Thread
* thread
)
62 this->thread
= thread
;
65 thread
->AcquireReference();
69 // We don't check thread->team != team here, since this method can be
70 // called for new threads not added to the team yet.
73 status_t
LockAndGetTimer(thread_id threadID
, int32 timerID
,
76 team
= thread_get_current_thread()->team
;
80 thread
= Thread::GetAndLock(threadID
);
82 return B_BAD_THREAD_ID
;
83 if (thread
->team
!= team
)
87 UserTimer
* timer
= thread
!= NULL
88 ? thread
->UserTimerFor(timerID
) : team
->UserTimerFor(timerID
);
99 thread
->UnlockAndReleaseReference();
109 } // unnamed namespace
112 // #pragma mark - UserTimer
115 UserTimer::UserTimer()
125 // mark the timer unused
126 fTimer
.user_data
= this;
130 UserTimer::~UserTimer()
133 fEvent
->ReleaseReference();
137 /*! \fn UserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
138 bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
139 Cancels the timer, if it is already scheduled, and optionally schedules it
142 \param nextTime The time at which the timer should go off the next time. If
143 \c B_INFINITE_TIMEOUT, the timer will not be scheduled. Whether the
144 value is interpreted as absolute or relative time, depends on \c flags.
145 \param interval If <tt> >0 </tt>, the timer will be scheduled to fire
146 periodically every \a interval microseconds. Otherwise it will fire
147 only once at \a nextTime. If \a nextTime is \c B_INFINITE_TIMEOUT, it
148 will fire never in either case.
149 \param flags Bitwise OR of flags. Currently \c B_ABSOLUTE_TIMEOUT and
150 \c B_RELATIVE_TIMEOUT are supported, indicating whether \a nextTime is
151 an absolute or relative time.
152 \param _oldRemainingTime Return variable that will be set to the
153 microseconds remaining to the time for which the timer was scheduled
154 next before the call. If it wasn't scheduled, the variable is set to
155 \c B_INFINITE_TIMEOUT.
156 \param _oldInterval Return variable that will be set to the interval in
157 microseconds the timer was to be scheduled periodically. If the timer
158 wasn't periodic, the variable is set to \c 0.
162 /*! Cancels the timer, if it is scheduled.
167 bigtime_t oldNextTime
;
168 bigtime_t oldInterval
;
169 return Schedule(B_INFINITE_TIMEOUT
, 0, 0, oldNextTime
, oldInterval
);
173 /*! \fn UserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
174 uint32& _overrunCount)
175 Return information on the current timer.
177 \param _remainingTime Return variable that will be set to the microseconds
178 remaining to the time for which the timer was scheduled next before the
179 call. If it wasn't scheduled, the variable is set to
180 \c B_INFINITE_TIMEOUT.
181 \param _interval Return variable that will be set to the interval in
182 microseconds the timer is to be scheduled periodically. If the timer
183 isn't periodic, the variable is set to \c 0.
184 \param _overrunCount Return variable that will be set to the number of times
185 the timer went off, but its event couldn't be delivered, since it's
186 previous delivery hasn't been handled yet.
191 UserTimer::HandleTimerHook(struct timer
* timer
)
193 UserTimer
* userTimer
= reinterpret_cast<UserTimer
*>(timer
->user_data
);
198 while (!locked
&& atomic_get(&userTimer
->fSkip
) == 0) {
199 locked
= try_acquire_write_seqlock(&sUserTimerLock
);
205 userTimer
->HandleTimer();
206 release_write_seqlock(&sUserTimerLock
);
209 return B_HANDLED_INTERRUPT
;
214 UserTimer::HandleTimer()
216 if (fEvent
!= NULL
) {
217 // fire the event and update the overrun count, if necessary
218 status_t error
= fEvent
->Fire();
219 if (error
== B_BUSY
) {
220 if (fOverrunCount
< MAX_USER_TIMER_OVERRUN_COUNT
)
225 // Since we don't use periodic kernel timers, it isn't scheduled anymore.
226 // If the timer is periodic, the derived class' version will schedule it
232 /*! Updates the start time for a periodic timer after it expired, enforcing
233 sanity limits and updating \c fOverrunCount, if necessary.
235 The caller must not hold \c sUserTimerLock.
238 UserTimer::UpdatePeriodicStartTime()
240 if (fInterval
< kMinPeriodicTimerInterval
) {
241 bigtime_t skip
= (kMinPeriodicTimerInterval
+ fInterval
- 1) / fInterval
;
242 fNextTime
+= skip
* fInterval
;
244 // One interval is the normal advance, so don't consider it skipped.
247 if (skip
+ fOverrunCount
> MAX_USER_TIMER_OVERRUN_COUNT
)
248 fOverrunCount
= MAX_USER_TIMER_OVERRUN_COUNT
;
250 fOverrunCount
+= skip
;
252 fNextTime
+= fInterval
;
256 /*! Checks whether the timer start time lies too much in the past and, if so,
257 adjusts it and updates \c fOverrunCount.
259 The caller must not hold \c sUserTimerLock.
261 \param now The current time.
264 UserTimer::CheckPeriodicOverrun(bigtime_t now
)
266 if (fNextTime
+ fInterval
> now
)
269 // The start time is a full interval or more in the past. Skip those
271 bigtime_t skip
= (now
- fNextTime
) / fInterval
;
272 fNextTime
+= skip
* fInterval
;
274 if (skip
+ fOverrunCount
> MAX_USER_TIMER_OVERRUN_COUNT
)
275 fOverrunCount
= MAX_USER_TIMER_OVERRUN_COUNT
;
277 fOverrunCount
+= skip
;
282 UserTimer::CancelTimer()
286 atomic_set(&fSkip
, 1);
287 cancel_timer(&fTimer
);
288 atomic_set(&fSkip
, 0);
292 // #pragma mark - SystemTimeUserTimer
296 SystemTimeUserTimer::Schedule(bigtime_t nextTime
, bigtime_t interval
,
297 uint32 flags
, bigtime_t
& _oldRemainingTime
, bigtime_t
& _oldInterval
)
299 InterruptsWriteSequentialLocker
locker(sUserTimerLock
);
301 // get the current time
302 bigtime_t now
= system_time();
304 // Cancel the old timer, if still scheduled, and get the previous values.
308 _oldRemainingTime
= fNextTime
- now
;
309 _oldInterval
= fInterval
;
313 _oldRemainingTime
= B_INFINITE_TIMEOUT
;
317 // schedule the new timer
318 fNextTime
= nextTime
;
319 fInterval
= interval
;
322 if (nextTime
== B_INFINITE_TIMEOUT
)
325 if ((flags
& B_RELATIVE_TIMEOUT
) != 0)
328 ScheduleKernelTimer(now
, fInterval
> 0);
333 SystemTimeUserTimer::GetInfo(bigtime_t
& _remainingTime
, bigtime_t
& _interval
,
334 uint32
& _overrunCount
)
338 count
= acquire_read_seqlock(&sUserTimerLock
);
341 _remainingTime
= fNextTime
- system_time();
342 _interval
= fInterval
;
344 _remainingTime
= B_INFINITE_TIMEOUT
;
348 _overrunCount
= fOverrunCount
;
349 } while (!release_read_seqlock(&sUserTimerLock
, count
));
354 SystemTimeUserTimer::HandleTimer()
356 UserTimer::HandleTimer();
358 // if periodic, reschedule the kernel timer
360 UpdatePeriodicStartTime();
361 ScheduleKernelTimer(system_time(), true);
366 /*! Schedules the kernel timer.
368 The caller must hold \c sUserTimerLock.
370 \param now The current system time to be used.
371 \param checkPeriodicOverrun If \c true, calls CheckPeriodicOverrun() first,
372 i.e. the start time will be adjusted to not lie too much in the past.
375 SystemTimeUserTimer::ScheduleKernelTimer(bigtime_t now
,
376 bool checkPeriodicOverrun
)
378 // If periodic, check whether the start time is too far in the past.
379 if (checkPeriodicOverrun
)
380 CheckPeriodicOverrun(now
);
382 uint32 timerFlags
= B_ONE_SHOT_ABSOLUTE_TIMER
383 | B_TIMER_USE_TIMER_STRUCT_TIMES
;
385 fTimer
.schedule_time
= std::max(fNextTime
, (bigtime_t
)0);
388 add_timer(&fTimer
, &HandleTimerHook
, fTimer
.schedule_time
, timerFlags
);
394 // #pragma mark - RealTimeUserTimer
398 RealTimeUserTimer::Schedule(bigtime_t nextTime
, bigtime_t interval
,
399 uint32 flags
, bigtime_t
& _oldRemainingTime
, bigtime_t
& _oldInterval
)
401 InterruptsWriteSequentialLocker
locker(sUserTimerLock
);
403 // get the current time
404 bigtime_t now
= system_time();
406 // Cancel the old timer, if still scheduled, and get the previous values.
410 _oldRemainingTime
= fNextTime
- now
;
411 _oldInterval
= fInterval
;
414 SpinLocker
globalListLocker(sAbsoluteRealTimeTimersLock
);
415 sAbsoluteRealTimeTimers
.Remove(this);
420 _oldRemainingTime
= B_INFINITE_TIMEOUT
;
424 // schedule the new timer
425 fNextTime
= nextTime
;
426 fInterval
= interval
;
429 if (nextTime
== B_INFINITE_TIMEOUT
)
432 fAbsolute
= (flags
& B_RELATIVE_TIMEOUT
) == 0;
435 fRealTimeOffset
= rtc_boot_time();
436 fNextTime
-= fRealTimeOffset
;
438 // If periodic, check whether the start time is too far in the past.
440 CheckPeriodicOverrun(now
);
442 // add the absolute timer to the global list
443 SpinLocker
globalListLocker(sAbsoluteRealTimeTimersLock
);
444 sAbsoluteRealTimeTimers
.Insert(this);
448 ScheduleKernelTimer(now
, false);
452 /*! Called when the real-time clock has been changed.
454 The caller must hold \c sUserTimerLock. Optionally the caller may also
455 hold \c sAbsoluteRealTimeTimersLock.
458 RealTimeUserTimer::TimeWarped()
460 ASSERT(fScheduled
&& fAbsolute
);
462 // get the new real-time offset
463 bigtime_t oldRealTimeOffset
= fRealTimeOffset
;
464 fRealTimeOffset
= rtc_boot_time();
465 if (fRealTimeOffset
== oldRealTimeOffset
)
468 // cancel the kernel timer and reschedule it
471 fNextTime
+= oldRealTimeOffset
- fRealTimeOffset
;
473 ScheduleKernelTimer(system_time(), fInterval
> 0);
478 RealTimeUserTimer::HandleTimer()
480 SystemTimeUserTimer::HandleTimer();
482 // remove from global list, if no longer scheduled
483 if (!fScheduled
&& fAbsolute
) {
484 SpinLocker
globalListLocker(sAbsoluteRealTimeTimersLock
);
485 sAbsoluteRealTimeTimers
.Remove(this);
490 // #pragma mark - TeamTimeUserTimer
493 TeamTimeUserTimer::TeamTimeUserTimer(team_id teamID
)
501 TeamTimeUserTimer::~TeamTimeUserTimer()
503 ASSERT(fTeam
== NULL
);
508 TeamTimeUserTimer::Schedule(bigtime_t nextTime
, bigtime_t interval
,
509 uint32 flags
, bigtime_t
& _oldRemainingTime
, bigtime_t
& _oldInterval
)
511 InterruptsWriteSequentialLocker
locker(sUserTimerLock
);
512 SpinLocker
timeLocker(fTeam
!= NULL
? &fTeam
->time_lock
: NULL
);
514 // get the current time, but only if needed
515 bool nowValid
= fTeam
!= NULL
;
516 bigtime_t now
= nowValid
? fTeam
->CPUTime(false) : 0;
518 // Cancel the old timer, if still scheduled, and get the previous values.
525 _oldRemainingTime
= fNextTime
- now
;
526 _oldInterval
= fInterval
;
528 fTeam
->UserTimerDeactivated(this);
529 fTeam
->ReleaseReference();
532 _oldRemainingTime
= B_INFINITE_TIMEOUT
;
536 // schedule the new timer
537 fNextTime
= nextTime
;
538 fInterval
= interval
;
541 if (fNextTime
== B_INFINITE_TIMEOUT
)
544 // Get the team. If it doesn't exist anymore, just don't schedule the
546 Team
* newTeam
= Team::Get(fTeamID
);
547 if (newTeam
== NULL
) {
550 } else if (fTeam
== NULL
)
551 timeLocker
.SetTo(newTeam
->time_lock
, false);
554 fAbsolute
= (flags
& B_RELATIVE_TIMEOUT
) == 0;
556 // convert relative to absolute timeouts
559 now
= fTeam
->CPUTime(false);
563 fTeam
->UserTimerActivated(this);
565 // schedule/udpate the kernel timer
571 TeamTimeUserTimer::GetInfo(bigtime_t
& _remainingTime
, bigtime_t
& _interval
,
572 uint32
& _overrunCount
)
576 count
= acquire_read_seqlock(&sUserTimerLock
);
579 InterruptsSpinLocker
timeLocker(fTeam
->time_lock
);
580 _remainingTime
= fNextTime
- fTeam
->CPUTime(false);
581 _interval
= fInterval
;
583 _remainingTime
= B_INFINITE_TIMEOUT
;
587 _overrunCount
= fOverrunCount
;
588 } while (!release_read_seqlock(&sUserTimerLock
, count
));
592 /*! Deactivates the timer, if it is activated.
594 The caller must hold \c time_lock and \c sUserTimerLock.
597 TeamTimeUserTimer::Deactivate()
602 // unschedule, if scheduled
609 fTeam
->UserTimerDeactivated(this);
610 fTeam
->ReleaseReference();
615 /*! Starts/stops the timer as necessary, if it is active.
617 Called whenever threads of the team whose CPU time is referred to by the
618 timer are scheduled or unscheduled (or leave the team), or when the timer
619 was just set. Schedules a kernel timer for the remaining time, respectively
622 The caller must hold \c time_lock and \c sUserTimerLock.
624 \param unscheduledThread If not \c NULL, this is the thread that is
625 currently running and which is in the process of being unscheduled.
628 TeamTimeUserTimer::Update(Thread
* unscheduledThread
, Thread
* lockedThread
)
633 // determine how many of the team's threads are currently running
635 int32 cpuCount
= smp_get_num_cpus();
636 for (int32 i
= 0; i
< cpuCount
; i
++) {
637 Thread
* thread
= gCPU
[i
].running_thread
;
638 if (thread
!= unscheduledThread
&& thread
->team
== fTeam
)
642 _Update(unscheduledThread
!= NULL
, lockedThread
);
646 /*! Called when the team's CPU time clock which this timer refers to has been
649 The caller must hold \c time_lock and \c sUserTimerLock.
651 \param changedBy The value by which the clock has changed.
654 TeamTimeUserTimer::TimeWarped(bigtime_t changedBy
)
656 if (fTeam
== NULL
|| changedBy
== 0)
659 // If this is a relative timer, adjust fNextTime by the value the clock has
662 fNextTime
+= changedBy
;
664 // reschedule the kernel timer
670 TeamTimeUserTimer::HandleTimer()
672 UserTimer::HandleTimer();
674 // If the timer is not periodic, it is no longer active. Otherwise
675 // reschedule the kernel timer.
677 if (fInterval
== 0) {
678 fTeam
->UserTimerDeactivated(this);
679 fTeam
->ReleaseReference();
682 UpdatePeriodicStartTime();
689 /*! Schedules/cancels the kernel timer as necessary.
691 \c fRunningThreads must be up-to-date.
692 The caller must hold \c time_lock and \c sUserTimerLock.
694 \param unscheduling \c true, when the current thread is in the process of
698 TeamTimeUserTimer::_Update(bool unscheduling
, Thread
* lockedThread
)
700 // unschedule the kernel timer, if scheduled
704 // if no more threads are running, we're done
705 if (fRunningThreads
== 0) {
710 // There are still threads running. Reschedule the kernel timer.
711 bigtime_t now
= fTeam
->CPUTime(unscheduling
, lockedThread
);
713 // If periodic, check whether the start time is too far in the past.
715 CheckPeriodicOverrun(now
);
717 if (fNextTime
> now
) {
718 fTimer
.schedule_time
= system_time()
719 + (fNextTime
- now
+ fRunningThreads
- 1) / fRunningThreads
;
720 // check for overflow
721 if (fTimer
.schedule_time
< 0)
722 fTimer
.schedule_time
= B_INFINITE_TIMEOUT
;
724 fTimer
.schedule_time
= 0;
726 // We reschedule periodic timers manually in HandleTimer() to avoid
729 add_timer(&fTimer
, &HandleTimerHook
, fTimer
.schedule_time
,
730 B_ONE_SHOT_ABSOLUTE_TIMER
| B_TIMER_USE_TIMER_STRUCT_TIMES
);
731 // We use B_TIMER_USE_TIMER_STRUCT_TIMES, so period remains 0, which
732 // our base class expects.
738 // #pragma mark - TeamUserTimeUserTimer
741 TeamUserTimeUserTimer::TeamUserTimeUserTimer(team_id teamID
)
749 TeamUserTimeUserTimer::~TeamUserTimeUserTimer()
751 ASSERT(fTeam
== NULL
);
756 TeamUserTimeUserTimer::Schedule(bigtime_t nextTime
, bigtime_t interval
,
757 uint32 flags
, bigtime_t
& _oldRemainingTime
, bigtime_t
& _oldInterval
)
759 InterruptsWriteSequentialLocker
locker(sUserTimerLock
);
760 SpinLocker
timeLocker(fTeam
!= NULL
? &fTeam
->time_lock
: NULL
);
762 // get the current time, but only if needed
763 bool nowValid
= fTeam
!= NULL
;
764 bigtime_t now
= nowValid
? fTeam
->UserCPUTime() : 0;
766 // Cancel the old timer, if still active, and get the previous values.
768 _oldRemainingTime
= fNextTime
- now
;
769 _oldInterval
= fInterval
;
771 fTeam
->UserTimerDeactivated(this);
772 fTeam
->ReleaseReference();
775 _oldRemainingTime
= B_INFINITE_TIMEOUT
;
779 // schedule the new timer
780 fNextTime
= nextTime
;
781 fInterval
= interval
;
784 if (fNextTime
== B_INFINITE_TIMEOUT
)
787 // Get the team. If it doesn't exist anymore, just don't schedule the
789 Team
* newTeam
= Team::Get(fTeamID
);
790 if (newTeam
== NULL
) {
793 } else if (fTeam
== NULL
)
794 timeLocker
.SetTo(newTeam
->time_lock
, false);
797 // convert relative to absolute timeouts
798 if ((flags
& B_RELATIVE_TIMEOUT
) != 0) {
800 now
= fTeam
->CPUTime(false);
804 fTeam
->UserTimerActivated(this);
806 // fire the event, if already timed out
812 TeamUserTimeUserTimer::GetInfo(bigtime_t
& _remainingTime
, bigtime_t
& _interval
,
813 uint32
& _overrunCount
)
817 count
= acquire_read_seqlock(&sUserTimerLock
);
820 InterruptsSpinLocker
timeLocker(fTeam
->time_lock
);
821 _remainingTime
= fNextTime
- fTeam
->UserCPUTime();
822 _interval
= fInterval
;
824 _remainingTime
= B_INFINITE_TIMEOUT
;
828 _overrunCount
= fOverrunCount
;
829 } while (!release_read_seqlock(&sUserTimerLock
, count
));
833 /*! Deactivates the timer, if it is activated.
835 The caller must hold \c time_lock and \c sUserTimerLock.
838 TeamUserTimeUserTimer::Deactivate()
844 fTeam
->UserTimerDeactivated(this);
845 fTeam
->ReleaseReference();
850 /*! Checks whether the timer is up, firing an event, if so.
852 The caller must hold \c time_lock and \c sUserTimerLock.
855 TeamUserTimeUserTimer::Check()
860 // check whether we need to fire the event yet
861 bigtime_t now
= fTeam
->UserCPUTime();
867 // If the timer is not periodic, it is no longer active. Otherwise compute
869 if (fInterval
== 0) {
870 fTeam
->UserTimerDeactivated(this);
871 fTeam
->ReleaseReference();
876 // First validate fNextTime, then increment it, so that fNextTime is > now
877 // (CheckPeriodicOverrun() only makes it > now - fInterval).
878 CheckPeriodicOverrun(now
);
879 fNextTime
+= fInterval
;
884 // #pragma mark - ThreadTimeUserTimer
887 ThreadTimeUserTimer::ThreadTimeUserTimer(thread_id threadID
)
895 ThreadTimeUserTimer::~ThreadTimeUserTimer()
897 ASSERT(fThread
== NULL
);
902 ThreadTimeUserTimer::Schedule(bigtime_t nextTime
, bigtime_t interval
,
903 uint32 flags
, bigtime_t
& _oldRemainingTime
, bigtime_t
& _oldInterval
)
905 InterruptsWriteSequentialLocker
locker(sUserTimerLock
);
906 SpinLocker
timeLocker(fThread
!= NULL
? &fThread
->time_lock
: NULL
);
908 // get the current time, but only if needed
909 bool nowValid
= fThread
!= NULL
;
910 bigtime_t now
= nowValid
? fThread
->CPUTime(false) : 0;
912 // Cancel the old timer, if still scheduled, and get the previous values.
913 if (fThread
!= NULL
) {
919 _oldRemainingTime
= fNextTime
- now
;
920 _oldInterval
= fInterval
;
922 fThread
->UserTimerDeactivated(this);
923 fThread
->ReleaseReference();
926 _oldRemainingTime
= B_INFINITE_TIMEOUT
;
930 // schedule the new timer
931 fNextTime
= nextTime
;
932 fInterval
= interval
;
935 if (fNextTime
== B_INFINITE_TIMEOUT
)
938 // Get the thread. If it doesn't exist anymore, just don't schedule the
940 Thread
* newThread
= Thread::Get(fThreadID
);
941 if (newThread
== NULL
) {
944 } else if (fThread
== NULL
)
945 timeLocker
.SetTo(newThread
->time_lock
, false);
948 fAbsolute
= (flags
& B_RELATIVE_TIMEOUT
) == 0;
950 // convert relative to absolute timeouts
953 now
= fThread
->CPUTime(false);
957 fThread
->UserTimerActivated(this);
959 // If the thread is currently running, also schedule a kernel timer.
960 if (fThread
->cpu
!= NULL
)
966 ThreadTimeUserTimer::GetInfo(bigtime_t
& _remainingTime
, bigtime_t
& _interval
,
967 uint32
& _overrunCount
)
971 count
= acquire_read_seqlock(&sUserTimerLock
);
973 if (fThread
!= NULL
) {
974 SpinLocker
timeLocker(fThread
->time_lock
);
975 _remainingTime
= fNextTime
- fThread
->CPUTime(false);
976 _interval
= fInterval
;
978 _remainingTime
= B_INFINITE_TIMEOUT
;
982 _overrunCount
= fOverrunCount
;
983 } while (!release_read_seqlock(&sUserTimerLock
, count
));
987 /*! Deactivates the timer, if it is activated.
989 The caller must hold \c time_lock and \c sUserTimerLock.
992 ThreadTimeUserTimer::Deactivate()
997 // unschedule, if scheduled
1004 fThread
->UserTimerDeactivated(this);
1005 fThread
->ReleaseReference();
1010 /*! Starts the timer, if it is active.
1012 Called when the thread whose CPU time is referred to by the timer is
1013 scheduled, or, when the timer was just set and the thread is already
1014 running. Schedules a kernel timer for the remaining time.
1016 The caller must hold \c time_lock and \c sUserTimerLock.
1019 ThreadTimeUserTimer::Start()
1021 if (fThread
== NULL
)
1024 ASSERT(!fScheduled
);
1026 // add the kernel timer
1027 bigtime_t now
= fThread
->CPUTime(false);
1029 // If periodic, check whether the start time is too far in the past.
1031 CheckPeriodicOverrun(now
);
1033 if (fNextTime
> now
) {
1034 fTimer
.schedule_time
= system_time() + fNextTime
- now
;
1035 // check for overflow
1036 if (fTimer
.schedule_time
< 0)
1037 fTimer
.schedule_time
= B_INFINITE_TIMEOUT
;
1039 fTimer
.schedule_time
= 0;
1042 uint32 flags
= B_ONE_SHOT_ABSOLUTE_TIMER
| B_TIMER_USE_TIMER_STRUCT_TIMES
;
1043 add_timer(&fTimer
, &HandleTimerHook
, fTimer
.schedule_time
, flags
);
1049 /*! Stops the timer, if it is active.
1051 Called when the thread whose CPU time is referred to by the timer is
1052 unscheduled, or, when the timer is canceled.
1054 The caller must hold \c sUserTimerLock.
1057 ThreadTimeUserTimer::Stop()
1059 if (fThread
== NULL
)
1064 // cancel the kernel timer
1068 // TODO: To avoid odd race conditions, we should check the current time of
1069 // the thread (ignoring the time since last_time) and manually fire the
1070 // user event, if necessary.
1074 /*! Called when the team's CPU time clock which this timer refers to has been
1077 The caller must hold \c time_lock and \c sUserTimerLock.
1079 \param changedBy The value by which the clock has changed.
1082 ThreadTimeUserTimer::TimeWarped(bigtime_t changedBy
)
1084 if (fThread
== NULL
|| changedBy
== 0)
1087 // If this is a relative timer, adjust fNextTime by the value the clock has
1090 fNextTime
+= changedBy
;
1092 // reschedule the kernel timer
1101 ThreadTimeUserTimer::HandleTimer()
1103 UserTimer::HandleTimer();
1105 if (fThread
!= NULL
) {
1106 // If the timer is periodic, reschedule the kernel timer. Otherwise it
1107 // is no longer active.
1108 if (fInterval
> 0) {
1109 UpdatePeriodicStartTime();
1112 fThread
->UserTimerDeactivated(this);
1113 fThread
->ReleaseReference();
1120 // #pragma mark - UserTimerList
1123 UserTimerList::UserTimerList()
1128 UserTimerList::~UserTimerList()
1130 ASSERT(fTimers
.IsEmpty());
1134 /*! Returns the user timer with the given ID.
1136 \param id The timer's ID
1137 \return The user timer with the given ID or \c NULL, if there is no such
1141 UserTimerList::TimerFor(int32 id
) const
1143 // TODO: Use a more efficient data structure. E.g. a sorted array.
1144 for (TimerList::ConstIterator it
= fTimers
.GetIterator();
1145 UserTimer
* timer
= it
.Next();) {
1146 if (timer
->ID() == id
)
1154 /*! Adds the given user timer and assigns it an ID.
1156 \param timer The timer to be added.
1159 UserTimerList::AddTimer(UserTimer
* timer
)
1161 int32 id
= timer
->ID();
1163 // user-defined timer -- find an usused ID
1164 id
= USER_TIMER_FIRST_USER_DEFINED_ID
;
1165 UserTimer
* insertAfter
= NULL
;
1166 for (TimerList::Iterator it
= fTimers
.GetIterator();
1167 UserTimer
* other
= it
.Next();) {
1168 if (other
->ID() > id
)
1170 if (other
->ID() == id
)
1172 insertAfter
= other
;
1177 fTimers
.InsertAfter(insertAfter
, timer
);
1179 // default timer -- find the insertion point
1180 UserTimer
* insertAfter
= NULL
;
1181 for (TimerList::Iterator it
= fTimers
.GetIterator();
1182 UserTimer
* other
= it
.Next();) {
1183 if (other
->ID() > id
)
1185 if (other
->ID() == id
) {
1186 panic("UserTimerList::AddTimer(): timer with ID %" B_PRId32
1187 " already exists!", id
);
1189 insertAfter
= other
;
1193 fTimers
.InsertAfter(insertAfter
, timer
);
1198 /*! Deletes all (or all user-defined) user timers.
1200 \param userDefinedOnly If \c true, only the user-defined timers are deleted,
1201 otherwise all timers are deleted.
1202 \return The number of user-defined timers that were removed and deleted.
1205 UserTimerList::DeleteTimers(bool userDefinedOnly
)
1207 int32 userDefinedCount
= 0;
1209 for (TimerList::Iterator it
= fTimers
.GetIterator();
1210 UserTimer
* timer
= it
.Next();) {
1211 if (timer
->ID() < USER_TIMER_FIRST_USER_DEFINED_ID
) {
1212 if (userDefinedOnly
)
1217 // remove, cancel, and delete the timer
1223 return userDefinedCount
;
1227 // #pragma mark - private
1231 create_timer(clockid_t clockID
, int32 timerID
, Team
* team
, Thread
* thread
,
1232 uint32 flags
, const struct sigevent
& event
,
1233 ThreadCreationAttributes
* threadAttributes
, bool isDefaultEvent
)
1235 // create the timer object
1238 case CLOCK_MONOTONIC
:
1239 timer
= new(std::nothrow
) SystemTimeUserTimer
;
1242 case CLOCK_REALTIME
:
1243 timer
= new(std::nothrow
) RealTimeUserTimer
;
1246 case CLOCK_THREAD_CPUTIME_ID
:
1247 timer
= new(std::nothrow
) ThreadTimeUserTimer(
1248 thread_get_current_thread()->id
);
1251 case CLOCK_PROCESS_CPUTIME_ID
:
1254 timer
= new(std::nothrow
) TeamTimeUserTimer(team
->id
);
1257 case CLOCK_PROCESS_USER_CPUTIME_ID
:
1260 timer
= new(std::nothrow
) TeamUserTimeUserTimer(team
->id
);
1265 // The clock ID is a ID of the team whose CPU time the clock refers
1266 // to. Check whether the team exists and we have permission to
1267 // access its clock.
1270 if (clockID
== team_get_kernel_team_id())
1271 return B_NOT_ALLOWED
;
1273 Team
* timedTeam
= Team::GetAndLock(clockID
);
1274 if (timedTeam
== NULL
)
1277 uid_t uid
= geteuid();
1278 uid_t teamUID
= timedTeam
->effective_uid
;
1280 timedTeam
->UnlockAndReleaseReference();
1282 if (uid
!= 0 && uid
!= teamUID
)
1283 return B_NOT_ALLOWED
;
1285 timer
= new(std::nothrow
) TeamTimeUserTimer(clockID
);
1292 ObjectDeleter
<UserTimer
> timerDeleter(timer
);
1295 timer
->SetID(timerID
);
1297 SignalEvent
* signalEvent
= NULL
;
1299 switch (event
.sigev_notify
) {
1301 // the timer's event remains NULL
1306 if (event
.sigev_signo
<= 0 || event
.sigev_signo
> MAX_SIGNAL_NUMBER
)
1309 if (thread
!= NULL
&& (flags
& USER_TIMER_SIGNAL_THREAD
) != 0) {
1310 // The signal shall be sent to the thread.
1311 signalEvent
= ThreadSignalEvent::Create(thread
,
1312 event
.sigev_signo
, SI_TIMER
, 0, team
->id
);
1314 // The signal shall be sent to the team.
1315 signalEvent
= TeamSignalEvent::Create(team
, event
.sigev_signo
,
1319 if (signalEvent
== NULL
)
1322 timer
->SetEvent(signalEvent
);
1328 if (threadAttributes
== NULL
)
1331 CreateThreadEvent
* event
1332 = CreateThreadEvent::Create(*threadAttributes
);
1336 timer
->SetEvent(event
);
1344 // add it to the team/thread
1345 TimerLocker timerLocker
;
1346 timerLocker
.Lock(team
, thread
);
1348 status_t error
= thread
!= NULL
1349 ? thread
->AddUserTimer(timer
) : team
->AddUserTimer(timer
);
1353 // set a signal event's user value
1354 if (signalEvent
!= NULL
) {
1355 // If no sigevent structure was given, use the timer ID.
1356 union sigval signalValue
= event
.sigev_value
;
1358 signalValue
.sival_int
= timer
->ID();
1360 signalEvent
->SetUserValue(signalValue
);
1363 return timerDeleter
.Detach()->ID();
1367 /*! Called when the CPU time clock of the given thread has been set.
1369 The caller must hold \c time_lock.
1371 \param thread The thread whose CPU time clock has been set.
1372 \param changedBy The value by which the CPU time clock has changed
1373 (new = old + changedBy).
1376 thread_clock_changed(Thread
* thread
, bigtime_t changedBy
)
1378 for (ThreadTimeUserTimerList::ConstIterator it
1379 = thread
->CPUTimeUserTimerIterator();
1380 ThreadTimeUserTimer
* timer
= it
.Next();) {
1381 timer
->TimeWarped(changedBy
);
1386 /*! Called when the CPU time clock of the given team has been set.
1388 The caller must hold \c time_lock.
1390 \param team The team whose CPU time clock has been set.
1391 \param changedBy The value by which the CPU time clock has changed
1392 (new = old + changedBy).
1395 team_clock_changed(Team
* team
, bigtime_t changedBy
)
1397 for (TeamTimeUserTimerList::ConstIterator it
1398 = team
->CPUTimeUserTimerIterator();
1399 TeamTimeUserTimer
* timer
= it
.Next();) {
1400 timer
->TimeWarped(changedBy
);
1405 // #pragma mark - kernel private
1408 /*! Creates the pre-defined user timers for the given thread.
1409 The thread may not have been added to its team yet, hence the team must be
1412 \param team The thread's (future) team.
1413 \param thread The thread whose pre-defined timers shall be created.
1414 \return \c B_OK, when everything when fine, another error code otherwise.
1417 user_timer_create_thread_timers(Team
* team
, Thread
* thread
)
1419 // create a real time user timer
1420 struct sigevent event
;
1421 event
.sigev_notify
= SIGEV_SIGNAL
;
1422 event
.sigev_signo
= SIGALRM
;
1424 int32 timerID
= create_timer(CLOCK_MONOTONIC
, USER_TIMER_REAL_TIME_ID
,
1425 team
, thread
, USER_TIMER_SIGNAL_THREAD
, event
, NULL
, true);
1433 /*! Creates the pre-defined user timers for the given team.
1435 \param team The team whose pre-defined timers shall be created.
1436 \return \c B_OK, when everything when fine, another error code otherwise.
1439 user_timer_create_team_timers(Team
* team
)
1441 // create a real time user timer
1442 struct sigevent event
;
1443 event
.sigev_notify
= SIGEV_SIGNAL
;
1444 event
.sigev_signo
= SIGALRM
;
1446 int32 timerID
= create_timer(CLOCK_MONOTONIC
, USER_TIMER_REAL_TIME_ID
,
1447 team
, NULL
, 0, event
, NULL
, true);
1451 // create a total CPU time user timer
1452 event
.sigev_notify
= SIGEV_SIGNAL
;
1453 event
.sigev_signo
= SIGPROF
;
1455 timerID
= create_timer(CLOCK_PROCESS_CPUTIME_ID
,
1456 USER_TIMER_TEAM_TOTAL_TIME_ID
, team
, NULL
, 0, event
, NULL
, true);
1460 // create a user CPU time user timer
1461 event
.sigev_notify
= SIGEV_SIGNAL
;
1462 event
.sigev_signo
= SIGVTALRM
;
1464 timerID
= create_timer(CLOCK_PROCESS_USER_CPUTIME_ID
,
1465 USER_TIMER_TEAM_USER_TIME_ID
, team
, NULL
, 0, event
, NULL
, true);
1474 user_timer_get_clock(clockid_t clockID
, bigtime_t
& _time
)
1477 case CLOCK_MONOTONIC
:
1478 _time
= system_time();
1481 case CLOCK_REALTIME
:
1482 _time
= real_time_clock_usecs();
1485 case CLOCK_THREAD_CPUTIME_ID
:
1487 Thread
* thread
= thread_get_current_thread();
1488 InterruptsSpinLocker
timeLocker(thread
->time_lock
);
1489 _time
= thread
->CPUTime(false);
1493 case CLOCK_PROCESS_USER_CPUTIME_ID
:
1495 Team
* team
= thread_get_current_thread()->team
;
1496 InterruptsSpinLocker
timeLocker(team
->time_lock
);
1497 _time
= team
->UserCPUTime();
1501 case CLOCK_PROCESS_CPUTIME_ID
:
1504 // get the ID of the target team (or the respective placeholder)
1506 if (clockID
== CLOCK_PROCESS_CPUTIME_ID
) {
1507 teamID
= B_CURRENT_TEAM
;
1511 if (clockID
== team_get_kernel_team_id())
1512 return B_NOT_ALLOWED
;
1518 Team
* team
= Team::Get(teamID
);
1521 BReference
<Team
> teamReference(team
, true);
1524 InterruptsSpinLocker
timeLocker(team
->time_lock
);
1525 _time
= team
->CPUTime(false);
1534 user_timer_real_time_clock_changed()
1536 // we need to update all absolute real-time timers
1537 InterruptsWriteSequentialLocker
locker(sUserTimerLock
);
1538 SpinLocker
globalListLocker(sAbsoluteRealTimeTimersLock
);
1540 for (RealTimeUserTimerList::Iterator it
1541 = sAbsoluteRealTimeTimers
.GetIterator();
1542 RealTimeUserTimer
* timer
= it
.Next();) {
1543 timer
->TimeWarped();
1549 user_timer_stop_cpu_timers(Thread
* thread
, Thread
* nextThread
)
1551 // stop thread timers
1552 for (ThreadTimeUserTimerList::ConstIterator it
1553 = thread
->CPUTimeUserTimerIterator();
1554 ThreadTimeUserTimer
* timer
= it
.Next();) {
1558 // update team timers
1559 if (nextThread
== NULL
|| nextThread
->team
!= thread
->team
) {
1560 for (TeamTimeUserTimerList::ConstIterator it
1561 = thread
->team
->CPUTimeUserTimerIterator();
1562 TeamTimeUserTimer
* timer
= it
.Next();) {
1563 timer
->Update(thread
, thread
);
1570 user_timer_continue_cpu_timers(Thread
* thread
, Thread
* previousThread
)
1572 // update team timers
1573 if (previousThread
== NULL
|| previousThread
->team
!= thread
->team
) {
1574 for (TeamTimeUserTimerList::ConstIterator it
1575 = thread
->team
->CPUTimeUserTimerIterator();
1576 TeamTimeUserTimer
* timer
= it
.Next();) {
1577 timer
->Update(NULL
, thread
);
1581 // start thread timers
1582 for (ThreadTimeUserTimerList::ConstIterator it
1583 = thread
->CPUTimeUserTimerIterator();
1584 ThreadTimeUserTimer
* timer
= it
.Next();) {
1591 user_timer_check_team_user_timers(Team
* team
)
1593 for (TeamUserTimeUserTimerList::ConstIterator it
1594 = team
->UserTimeUserTimerIterator();
1595 TeamUserTimeUserTimer
* timer
= it
.Next();) {
1601 // #pragma mark - syscalls
1605 _user_get_clock(clockid_t clockID
, bigtime_t
* userTime
)
1609 status_t error
= user_timer_get_clock(clockID
, time
);
1613 // copy the value back to userland
1614 if (userTime
== NULL
|| !IS_USER_ADDRESS(userTime
)
1615 || user_memcpy(userTime
, &time
, sizeof(time
)) != B_OK
) {
1616 return B_BAD_ADDRESS
;
1624 _user_set_clock(clockid_t clockID
, bigtime_t time
)
1627 case CLOCK_MONOTONIC
:
1630 case CLOCK_REALTIME
:
1631 // only root may set the time
1633 return B_NOT_ALLOWED
;
1635 set_real_time_clock_usecs(time
);
1638 case CLOCK_THREAD_CPUTIME_ID
:
1640 Thread
* thread
= thread_get_current_thread();
1641 InterruptsSpinLocker
timeLocker(thread
->time_lock
);
1642 bigtime_t diff
= time
- thread
->CPUTime(false);
1643 thread
->cpu_clock_offset
+= diff
;
1645 thread_clock_changed(thread
, diff
);
1649 case CLOCK_PROCESS_USER_CPUTIME_ID
:
1650 // not supported -- this clock is an Haiku-internal extension
1653 case CLOCK_PROCESS_CPUTIME_ID
:
1656 // get the ID of the target team (or the respective placeholder)
1658 if (clockID
== CLOCK_PROCESS_CPUTIME_ID
) {
1659 teamID
= B_CURRENT_TEAM
;
1663 if (clockID
== team_get_kernel_team_id())
1664 return B_NOT_ALLOWED
;
1670 Team
* team
= Team::Get(teamID
);
1673 BReference
<Team
> teamReference(team
, true);
1675 // set the time offset
1676 InterruptsSpinLocker
timeLocker(team
->time_lock
);
1677 bigtime_t diff
= time
- team
->CPUTime(false);
1678 team
->cpu_clock_offset
+= diff
;
1680 team_clock_changed(team
, diff
);
1690 _user_create_timer(clockid_t clockID
, thread_id threadID
, uint32 flags
,
1691 const struct sigevent
* userEvent
,
1692 const thread_creation_attributes
* userThreadAttributes
)
1694 // copy the sigevent structure from userland
1695 struct sigevent event
;
1696 if (userEvent
!= NULL
) {
1697 if (!IS_USER_ADDRESS(userEvent
)
1698 || user_memcpy(&event
, userEvent
, sizeof(event
)) != B_OK
) {
1699 return B_BAD_ADDRESS
;
1702 // none given -- use defaults
1703 event
.sigev_notify
= SIGEV_SIGNAL
;
1704 event
.sigev_signo
= SIGALRM
;
1707 // copy thread creation attributes from userland, if specified
1708 char nameBuffer
[B_OS_NAME_LENGTH
];
1709 ThreadCreationAttributes threadAttributes
;
1710 if (event
.sigev_notify
== SIGEV_THREAD
) {
1711 status_t error
= threadAttributes
.InitFromUserAttributes(
1712 userThreadAttributes
, nameBuffer
);
1717 // get team and thread
1718 Team
* team
= thread_get_current_thread()->team
;
1719 Thread
* thread
= NULL
;
1720 if (threadID
>= 0) {
1721 thread
= Thread::GetAndLock(threadID
);
1723 return B_BAD_THREAD_ID
;
1727 BReference
<Thread
> threadReference(thread
, true);
1730 return create_timer(clockID
, -1, team
, thread
, flags
, event
,
1731 userThreadAttributes
!= NULL
? &threadAttributes
: NULL
,
1737 _user_delete_timer(int32 timerID
, thread_id threadID
)
1739 // can only delete user-defined timers
1740 if (timerID
< USER_TIMER_FIRST_USER_DEFINED_ID
)
1744 TimerLocker timerLocker
;
1746 status_t error
= timerLocker
.LockAndGetTimer(threadID
, timerID
, timer
);
1750 // cancel, remove, and delete it
1754 timerLocker
.thread
->RemoveUserTimer(timer
);
1756 timerLocker
.team
->RemoveUserTimer(timer
);
1765 _user_get_timer(int32 timerID
, thread_id threadID
,
1766 struct user_timer_info
* userInfo
)
1769 TimerLocker timerLocker
;
1771 status_t error
= timerLocker
.LockAndGetTimer(threadID
, timerID
, timer
);
1776 user_timer_info info
;
1777 timer
->GetInfo(info
.remaining_time
, info
.interval
, info
.overrun_count
);
1779 // Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid
1781 if (info
.remaining_time
<= 0)
1782 info
.remaining_time
= 1;
1784 timerLocker
.Unlock();
1786 // copy it back to userland
1787 if (userInfo
!= NULL
1788 && (!IS_USER_ADDRESS(userInfo
)
1789 || user_memcpy(userInfo
, &info
, sizeof(info
)) != B_OK
)) {
1790 return B_BAD_ADDRESS
;
1798 _user_set_timer(int32 timerID
, thread_id threadID
, bigtime_t startTime
,
1799 bigtime_t interval
, uint32 flags
, struct user_timer_info
* userOldInfo
)
1802 if (startTime
< 0 || interval
< 0)
1806 TimerLocker timerLocker
;
1808 status_t error
= timerLocker
.LockAndGetTimer(threadID
, timerID
, timer
);
1812 // schedule the timer
1813 user_timer_info oldInfo
;
1814 timer
->Schedule(startTime
, interval
, flags
, oldInfo
.remaining_time
,
1817 // Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid
1819 if (oldInfo
.remaining_time
<= 0)
1820 oldInfo
.remaining_time
= 1;
1822 timerLocker
.Unlock();
1824 // copy back the old info
1825 if (userOldInfo
!= NULL
1826 && (!IS_USER_ADDRESS(userOldInfo
)
1827 || user_memcpy(userOldInfo
, &oldInfo
, sizeof(oldInfo
)) != B_OK
)) {
1828 return B_BAD_ADDRESS
;