libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / system / kernel / UserTimer.cpp
blob9cb843b15896b3f5680e7f59255e511f0efead2e
1 /*
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.
5 */
8 #include <UserTimer.h>
10 #include <algorithm>
12 #include <AutoDeleter.h>
14 #include <debug.h>
15 #include <kernel.h>
16 #include <real_time_clock.h>
17 #include <team.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
39 namespace {
41 struct TimerLocker {
42 Team* team;
43 Thread* thread;
45 TimerLocker()
47 team(NULL),
48 thread(NULL)
52 ~TimerLocker()
54 Unlock();
57 void Lock(Team* team, Thread* thread)
59 this->team = team;
60 team->Lock();
62 this->thread = thread;
64 if (thread != NULL) {
65 thread->AcquireReference();
66 thread->Lock();
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,
74 UserTimer*& _timer)
76 team = thread_get_current_thread()->team;
77 team->Lock();
79 if (threadID >= 0) {
80 thread = Thread::GetAndLock(threadID);
81 if (thread == NULL)
82 return B_BAD_THREAD_ID;
83 if (thread->team != team)
84 return B_NOT_ALLOWED;
87 UserTimer* timer = thread != NULL
88 ? thread->UserTimerFor(timerID) : team->UserTimerFor(timerID);
89 if (timer == NULL)
90 return B_BAD_VALUE;
92 _timer = timer;
93 return B_OK;
96 void Unlock()
98 if (thread != NULL) {
99 thread->UnlockAndReleaseReference();
100 thread = NULL;
102 if (team != NULL) {
103 team->Unlock();
104 team = NULL;
109 } // unnamed namespace
112 // #pragma mark - UserTimer
115 UserTimer::UserTimer()
117 fID(-1),
118 fEvent(NULL),
119 fNextTime(0),
120 fInterval(0),
121 fOverrunCount(0),
122 fScheduled(false),
123 fSkip(0)
125 // mark the timer unused
126 fTimer.user_data = this;
130 UserTimer::~UserTimer()
132 if (fEvent != NULL)
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
140 with new parameters.
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.
164 void
165 UserTimer::Cancel()
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.
190 /*static*/ int32
191 UserTimer::HandleTimerHook(struct timer* timer)
193 UserTimer* userTimer = reinterpret_cast<UserTimer*>(timer->user_data);
195 InterruptsLocker _;
197 bool locked = false;
198 while (!locked && atomic_get(&userTimer->fSkip) == 0) {
199 locked = try_acquire_write_seqlock(&sUserTimerLock);
200 if (!locked)
201 cpu_pause();
204 if (locked) {
205 userTimer->HandleTimer();
206 release_write_seqlock(&sUserTimerLock);
209 return B_HANDLED_INTERRUPT;
213 void
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)
221 fOverrunCount++;
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
227 // again.
228 fScheduled = false;
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.
237 void
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.
245 skip--;
247 if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT)
248 fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT;
249 else
250 fOverrunCount += skip;
251 } else
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.
263 void
264 UserTimer::CheckPeriodicOverrun(bigtime_t now)
266 if (fNextTime + fInterval > now)
267 return;
269 // The start time is a full interval or more in the past. Skip those
270 // intervals.
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;
276 else
277 fOverrunCount += skip;
281 void
282 UserTimer::CancelTimer()
284 ASSERT(fScheduled);
286 atomic_set(&fSkip, 1);
287 cancel_timer(&fTimer);
288 atomic_set(&fSkip, 0);
292 // #pragma mark - SystemTimeUserTimer
295 void
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.
305 if (fScheduled) {
306 CancelTimer();
308 _oldRemainingTime = fNextTime - now;
309 _oldInterval = fInterval;
311 fScheduled = false;
312 } else {
313 _oldRemainingTime = B_INFINITE_TIMEOUT;
314 _oldInterval = 0;
317 // schedule the new timer
318 fNextTime = nextTime;
319 fInterval = interval;
320 fOverrunCount = 0;
322 if (nextTime == B_INFINITE_TIMEOUT)
323 return;
325 if ((flags & B_RELATIVE_TIMEOUT) != 0)
326 fNextTime += now;
328 ScheduleKernelTimer(now, fInterval > 0);
332 void
333 SystemTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
334 uint32& _overrunCount)
336 uint32 count;
337 do {
338 count = acquire_read_seqlock(&sUserTimerLock);
340 if (fScheduled) {
341 _remainingTime = fNextTime - system_time();
342 _interval = fInterval;
343 } else {
344 _remainingTime = B_INFINITE_TIMEOUT;
345 _interval = 0;
348 _overrunCount = fOverrunCount;
349 } while (!release_read_seqlock(&sUserTimerLock, count));
353 void
354 SystemTimeUserTimer::HandleTimer()
356 UserTimer::HandleTimer();
358 // if periodic, reschedule the kernel timer
359 if (fInterval > 0) {
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.
374 void
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);
386 fTimer.period = 0;
388 add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, timerFlags);
390 fScheduled = true;
394 // #pragma mark - RealTimeUserTimer
397 void
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.
407 if (fScheduled) {
408 CancelTimer();
410 _oldRemainingTime = fNextTime - now;
411 _oldInterval = fInterval;
413 if (fAbsolute) {
414 SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
415 sAbsoluteRealTimeTimers.Remove(this);
418 fScheduled = false;
419 } else {
420 _oldRemainingTime = B_INFINITE_TIMEOUT;
421 _oldInterval = 0;
424 // schedule the new timer
425 fNextTime = nextTime;
426 fInterval = interval;
427 fOverrunCount = 0;
429 if (nextTime == B_INFINITE_TIMEOUT)
430 return;
432 fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
434 if (fAbsolute) {
435 fRealTimeOffset = rtc_boot_time();
436 fNextTime -= fRealTimeOffset;
438 // If periodic, check whether the start time is too far in the past.
439 if (fInterval > 0)
440 CheckPeriodicOverrun(now);
442 // add the absolute timer to the global list
443 SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
444 sAbsoluteRealTimeTimers.Insert(this);
445 } else
446 fNextTime += now;
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.
457 void
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)
466 return;
468 // cancel the kernel timer and reschedule it
469 CancelTimer();
471 fNextTime += oldRealTimeOffset - fRealTimeOffset;
473 ScheduleKernelTimer(system_time(), fInterval > 0);
477 void
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)
495 fTeamID(teamID),
496 fTeam(NULL)
501 TeamTimeUserTimer::~TeamTimeUserTimer()
503 ASSERT(fTeam == NULL);
507 void
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.
519 if (fTeam != NULL) {
520 if (fScheduled) {
521 CancelTimer();
522 fScheduled = false;
525 _oldRemainingTime = fNextTime - now;
526 _oldInterval = fInterval;
528 fTeam->UserTimerDeactivated(this);
529 fTeam->ReleaseReference();
530 fTeam = NULL;
531 } else {
532 _oldRemainingTime = B_INFINITE_TIMEOUT;
533 _oldInterval = 0;
536 // schedule the new timer
537 fNextTime = nextTime;
538 fInterval = interval;
539 fOverrunCount = 0;
541 if (fNextTime == B_INFINITE_TIMEOUT)
542 return;
544 // Get the team. If it doesn't exist anymore, just don't schedule the
545 // timer anymore.
546 Team* newTeam = Team::Get(fTeamID);
547 if (newTeam == NULL) {
548 fTeam = NULL;
549 return;
550 } else if (fTeam == NULL)
551 timeLocker.SetTo(newTeam->time_lock, false);
552 fTeam = newTeam;
554 fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
556 // convert relative to absolute timeouts
557 if (!fAbsolute) {
558 if (!nowValid)
559 now = fTeam->CPUTime(false);
560 fNextTime += now;
563 fTeam->UserTimerActivated(this);
565 // schedule/udpate the kernel timer
566 Update(NULL);
570 void
571 TeamTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
572 uint32& _overrunCount)
574 uint32 count;
575 do {
576 count = acquire_read_seqlock(&sUserTimerLock);
578 if (fTeam != NULL) {
579 InterruptsSpinLocker timeLocker(fTeam->time_lock);
580 _remainingTime = fNextTime - fTeam->CPUTime(false);
581 _interval = fInterval;
582 } else {
583 _remainingTime = B_INFINITE_TIMEOUT;
584 _interval = 0;
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.
596 void
597 TeamTimeUserTimer::Deactivate()
599 if (fTeam == NULL)
600 return;
602 // unschedule, if scheduled
603 if (fScheduled) {
604 CancelTimer();
605 fScheduled = false;
608 // deactivate
609 fTeam->UserTimerDeactivated(this);
610 fTeam->ReleaseReference();
611 fTeam = NULL;
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
620 cancels it.
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.
627 void
628 TeamTimeUserTimer::Update(Thread* unscheduledThread, Thread* lockedThread)
630 if (fTeam == NULL)
631 return;
633 // determine how many of the team's threads are currently running
634 fRunningThreads = 0;
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)
639 fRunningThreads++;
642 _Update(unscheduledThread != NULL, lockedThread);
646 /*! Called when the team's CPU time clock which this timer refers to has been
647 set.
649 The caller must hold \c time_lock and \c sUserTimerLock.
651 \param changedBy The value by which the clock has changed.
653 void
654 TeamTimeUserTimer::TimeWarped(bigtime_t changedBy)
656 if (fTeam == NULL || changedBy == 0)
657 return;
659 // If this is a relative timer, adjust fNextTime by the value the clock has
660 // changed.
661 if (!fAbsolute)
662 fNextTime += changedBy;
664 // reschedule the kernel timer
665 _Update(false);
669 void
670 TeamTimeUserTimer::HandleTimer()
672 UserTimer::HandleTimer();
674 // If the timer is not periodic, it is no longer active. Otherwise
675 // reschedule the kernel timer.
676 if (fTeam != NULL) {
677 if (fInterval == 0) {
678 fTeam->UserTimerDeactivated(this);
679 fTeam->ReleaseReference();
680 fTeam = NULL;
681 } else {
682 UpdatePeriodicStartTime();
683 _Update(false);
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
695 being unscheduled.
697 void
698 TeamTimeUserTimer::_Update(bool unscheduling, Thread* lockedThread)
700 // unschedule the kernel timer, if scheduled
701 if (fScheduled)
702 CancelTimer();
704 // if no more threads are running, we're done
705 if (fRunningThreads == 0) {
706 fScheduled = false;
707 return;
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.
714 if (fInterval > 0)
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;
723 } else
724 fTimer.schedule_time = 0;
725 fTimer.period = 0;
726 // We reschedule periodic timers manually in HandleTimer() to avoid
727 // rounding errors.
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.
734 fScheduled = true;
738 // #pragma mark - TeamUserTimeUserTimer
741 TeamUserTimeUserTimer::TeamUserTimeUserTimer(team_id teamID)
743 fTeamID(teamID),
744 fTeam(NULL)
749 TeamUserTimeUserTimer::~TeamUserTimeUserTimer()
751 ASSERT(fTeam == NULL);
755 void
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.
767 if (fTeam != NULL) {
768 _oldRemainingTime = fNextTime - now;
769 _oldInterval = fInterval;
771 fTeam->UserTimerDeactivated(this);
772 fTeam->ReleaseReference();
773 fTeam = NULL;
774 } else {
775 _oldRemainingTime = B_INFINITE_TIMEOUT;
776 _oldInterval = 0;
779 // schedule the new timer
780 fNextTime = nextTime;
781 fInterval = interval;
782 fOverrunCount = 0;
784 if (fNextTime == B_INFINITE_TIMEOUT)
785 return;
787 // Get the team. If it doesn't exist anymore, just don't schedule the
788 // timer anymore.
789 Team* newTeam = Team::Get(fTeamID);
790 if (newTeam == NULL) {
791 fTeam = NULL;
792 return;
793 } else if (fTeam == NULL)
794 timeLocker.SetTo(newTeam->time_lock, false);
795 fTeam = newTeam;
797 // convert relative to absolute timeouts
798 if ((flags & B_RELATIVE_TIMEOUT) != 0) {
799 if (!nowValid)
800 now = fTeam->CPUTime(false);
801 fNextTime += now;
804 fTeam->UserTimerActivated(this);
806 // fire the event, if already timed out
807 Check();
811 void
812 TeamUserTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
813 uint32& _overrunCount)
815 uint32 count;
816 do {
817 count = acquire_read_seqlock(&sUserTimerLock);
819 if (fTeam != NULL) {
820 InterruptsSpinLocker timeLocker(fTeam->time_lock);
821 _remainingTime = fNextTime - fTeam->UserCPUTime();
822 _interval = fInterval;
823 } else {
824 _remainingTime = B_INFINITE_TIMEOUT;
825 _interval = 0;
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.
837 void
838 TeamUserTimeUserTimer::Deactivate()
840 if (fTeam == NULL)
841 return;
843 // deactivate
844 fTeam->UserTimerDeactivated(this);
845 fTeam->ReleaseReference();
846 fTeam = NULL;
850 /*! Checks whether the timer is up, firing an event, if so.
852 The caller must hold \c time_lock and \c sUserTimerLock.
854 void
855 TeamUserTimeUserTimer::Check()
857 if (fTeam == NULL)
858 return;
860 // check whether we need to fire the event yet
861 bigtime_t now = fTeam->UserCPUTime();
862 if (now < fNextTime)
863 return;
865 HandleTimer();
867 // If the timer is not periodic, it is no longer active. Otherwise compute
868 // the event time.
869 if (fInterval == 0) {
870 fTeam->UserTimerDeactivated(this);
871 fTeam->ReleaseReference();
872 fTeam = NULL;
873 return;
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;
880 fScheduled = true;
884 // #pragma mark - ThreadTimeUserTimer
887 ThreadTimeUserTimer::ThreadTimeUserTimer(thread_id threadID)
889 fThreadID(threadID),
890 fThread(NULL)
895 ThreadTimeUserTimer::~ThreadTimeUserTimer()
897 ASSERT(fThread == NULL);
901 void
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) {
914 if (fScheduled) {
915 CancelTimer();
916 fScheduled = false;
919 _oldRemainingTime = fNextTime - now;
920 _oldInterval = fInterval;
922 fThread->UserTimerDeactivated(this);
923 fThread->ReleaseReference();
924 fThread = NULL;
925 } else {
926 _oldRemainingTime = B_INFINITE_TIMEOUT;
927 _oldInterval = 0;
930 // schedule the new timer
931 fNextTime = nextTime;
932 fInterval = interval;
933 fOverrunCount = 0;
935 if (fNextTime == B_INFINITE_TIMEOUT)
936 return;
938 // Get the thread. If it doesn't exist anymore, just don't schedule the
939 // timer anymore.
940 Thread* newThread = Thread::Get(fThreadID);
941 if (newThread == NULL) {
942 fThread = NULL;
943 return;
944 } else if (fThread == NULL)
945 timeLocker.SetTo(newThread->time_lock, false);
946 fThread = newThread;
948 fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
950 // convert relative to absolute timeouts
951 if (!fAbsolute) {
952 if (!nowValid)
953 now = fThread->CPUTime(false);
954 fNextTime += now;
957 fThread->UserTimerActivated(this);
959 // If the thread is currently running, also schedule a kernel timer.
960 if (fThread->cpu != NULL)
961 Start();
965 void
966 ThreadTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
967 uint32& _overrunCount)
969 uint32 count;
970 do {
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;
977 } else {
978 _remainingTime = B_INFINITE_TIMEOUT;
979 _interval = 0;
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.
991 void
992 ThreadTimeUserTimer::Deactivate()
994 if (fThread == NULL)
995 return;
997 // unschedule, if scheduled
998 if (fScheduled) {
999 CancelTimer();
1000 fScheduled = false;
1003 // deactivate
1004 fThread->UserTimerDeactivated(this);
1005 fThread->ReleaseReference();
1006 fThread = NULL;
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.
1018 void
1019 ThreadTimeUserTimer::Start()
1021 if (fThread == NULL)
1022 return;
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.
1030 if (fInterval > 0)
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;
1038 } else
1039 fTimer.schedule_time = 0;
1040 fTimer.period = 0;
1042 uint32 flags = B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES;
1043 add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, flags);
1045 fScheduled = true;
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.
1056 void
1057 ThreadTimeUserTimer::Stop()
1059 if (fThread == NULL)
1060 return;
1062 ASSERT(fScheduled);
1064 // cancel the kernel timer
1065 CancelTimer();
1066 fScheduled = false;
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
1075 set.
1077 The caller must hold \c time_lock and \c sUserTimerLock.
1079 \param changedBy The value by which the clock has changed.
1081 void
1082 ThreadTimeUserTimer::TimeWarped(bigtime_t changedBy)
1084 if (fThread == NULL || changedBy == 0)
1085 return;
1087 // If this is a relative timer, adjust fNextTime by the value the clock has
1088 // changed.
1089 if (!fAbsolute)
1090 fNextTime += changedBy;
1092 // reschedule the kernel timer
1093 if (fScheduled) {
1094 Stop();
1095 Start();
1100 void
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();
1110 Start();
1111 } else {
1112 fThread->UserTimerDeactivated(this);
1113 fThread->ReleaseReference();
1114 fThread = NULL;
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
1138 timer.
1140 UserTimer*
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)
1147 return timer;
1150 return NULL;
1154 /*! Adds the given user timer and assigns it an ID.
1156 \param timer The timer to be added.
1158 void
1159 UserTimerList::AddTimer(UserTimer* timer)
1161 int32 id = timer->ID();
1162 if (id < 0) {
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)
1169 break;
1170 if (other->ID() == id)
1171 id++;
1172 insertAfter = other;
1175 // insert the timer
1176 timer->SetID(id);
1177 fTimers.InsertAfter(insertAfter, timer);
1178 } else {
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)
1184 break;
1185 if (other->ID() == id) {
1186 panic("UserTimerList::AddTimer(): timer with ID %" B_PRId32
1187 " already exists!", id);
1189 insertAfter = other;
1192 // insert the timer
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.
1204 int32
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)
1213 continue;
1214 } else
1215 userDefinedCount++;
1217 // remove, cancel, and delete the timer
1218 it.Remove();
1219 timer->Cancel();
1220 delete timer;
1223 return userDefinedCount;
1227 // #pragma mark - private
1230 static int32
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
1236 UserTimer* timer;
1237 switch (clockID) {
1238 case CLOCK_MONOTONIC:
1239 timer = new(std::nothrow) SystemTimeUserTimer;
1240 break;
1242 case CLOCK_REALTIME:
1243 timer = new(std::nothrow) RealTimeUserTimer;
1244 break;
1246 case CLOCK_THREAD_CPUTIME_ID:
1247 timer = new(std::nothrow) ThreadTimeUserTimer(
1248 thread_get_current_thread()->id);
1249 break;
1251 case CLOCK_PROCESS_CPUTIME_ID:
1252 if (team == NULL)
1253 return B_BAD_VALUE;
1254 timer = new(std::nothrow) TeamTimeUserTimer(team->id);
1255 break;
1257 case CLOCK_PROCESS_USER_CPUTIME_ID:
1258 if (team == NULL)
1259 return B_BAD_VALUE;
1260 timer = new(std::nothrow) TeamUserTimeUserTimer(team->id);
1261 break;
1263 default:
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.
1268 if (clockID <= 0)
1269 return B_BAD_VALUE;
1270 if (clockID == team_get_kernel_team_id())
1271 return B_NOT_ALLOWED;
1273 Team* timedTeam = Team::GetAndLock(clockID);
1274 if (timedTeam == NULL)
1275 return B_BAD_VALUE;
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);
1286 break;
1290 if (timer == NULL)
1291 return B_NO_MEMORY;
1292 ObjectDeleter<UserTimer> timerDeleter(timer);
1294 if (timerID >= 0)
1295 timer->SetID(timerID);
1297 SignalEvent* signalEvent = NULL;
1299 switch (event.sigev_notify) {
1300 case SIGEV_NONE:
1301 // the timer's event remains NULL
1302 break;
1304 case SIGEV_SIGNAL:
1306 if (event.sigev_signo <= 0 || event.sigev_signo > MAX_SIGNAL_NUMBER)
1307 return B_BAD_VALUE;
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);
1313 } else {
1314 // The signal shall be sent to the team.
1315 signalEvent = TeamSignalEvent::Create(team, event.sigev_signo,
1316 SI_TIMER, 0);
1319 if (signalEvent == NULL)
1320 return B_NO_MEMORY;
1322 timer->SetEvent(signalEvent);
1323 break;
1326 case SIGEV_THREAD:
1328 if (threadAttributes == NULL)
1329 return B_BAD_VALUE;
1331 CreateThreadEvent* event
1332 = CreateThreadEvent::Create(*threadAttributes);
1333 if (event == NULL)
1334 return B_NO_MEMORY;
1336 timer->SetEvent(event);
1337 break;
1340 default:
1341 return B_BAD_VALUE;
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);
1350 if (error != B_OK)
1351 return error;
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;
1357 if (isDefaultEvent)
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).
1375 static void
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).
1394 static void
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
1410 passed
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.
1416 status_t
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);
1426 if (timerID < 0)
1427 return timerID;
1429 return B_OK;
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.
1438 status_t
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);
1448 if (timerID < 0)
1449 return timerID;
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);
1457 if (timerID < 0)
1458 return timerID;
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);
1466 if (timerID < 0)
1467 return timerID;
1469 return B_OK;
1473 status_t
1474 user_timer_get_clock(clockid_t clockID, bigtime_t& _time)
1476 switch (clockID) {
1477 case CLOCK_MONOTONIC:
1478 _time = system_time();
1479 return B_OK;
1481 case CLOCK_REALTIME:
1482 _time = real_time_clock_usecs();
1483 return B_OK;
1485 case CLOCK_THREAD_CPUTIME_ID:
1487 Thread* thread = thread_get_current_thread();
1488 InterruptsSpinLocker timeLocker(thread->time_lock);
1489 _time = thread->CPUTime(false);
1490 return B_OK;
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();
1498 return B_OK;
1501 case CLOCK_PROCESS_CPUTIME_ID:
1502 default:
1504 // get the ID of the target team (or the respective placeholder)
1505 team_id teamID;
1506 if (clockID == CLOCK_PROCESS_CPUTIME_ID) {
1507 teamID = B_CURRENT_TEAM;
1508 } else {
1509 if (clockID < 0)
1510 return B_BAD_VALUE;
1511 if (clockID == team_get_kernel_team_id())
1512 return B_NOT_ALLOWED;
1514 teamID = clockID;
1517 // get the team
1518 Team* team = Team::Get(teamID);
1519 if (team == NULL)
1520 return B_BAD_VALUE;
1521 BReference<Team> teamReference(team, true);
1523 // get the time
1524 InterruptsSpinLocker timeLocker(team->time_lock);
1525 _time = team->CPUTime(false);
1527 return B_OK;
1533 void
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();
1548 void
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();) {
1555 timer->Stop();
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);
1569 void
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();) {
1585 timer->Start();
1590 void
1591 user_timer_check_team_user_timers(Team* team)
1593 for (TeamUserTimeUserTimerList::ConstIterator it
1594 = team->UserTimeUserTimerIterator();
1595 TeamUserTimeUserTimer* timer = it.Next();) {
1596 timer->Check();
1601 // #pragma mark - syscalls
1604 status_t
1605 _user_get_clock(clockid_t clockID, bigtime_t* userTime)
1607 // get the time
1608 bigtime_t time;
1609 status_t error = user_timer_get_clock(clockID, time);
1610 if (error != B_OK)
1611 return error;
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;
1619 return B_OK;
1623 status_t
1624 _user_set_clock(clockid_t clockID, bigtime_t time)
1626 switch (clockID) {
1627 case CLOCK_MONOTONIC:
1628 return B_BAD_VALUE;
1630 case CLOCK_REALTIME:
1631 // only root may set the time
1632 if (geteuid() != 0)
1633 return B_NOT_ALLOWED;
1635 set_real_time_clock_usecs(time);
1636 return B_OK;
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);
1646 return B_OK;
1649 case CLOCK_PROCESS_USER_CPUTIME_ID:
1650 // not supported -- this clock is an Haiku-internal extension
1651 return B_BAD_VALUE;
1653 case CLOCK_PROCESS_CPUTIME_ID:
1654 default:
1656 // get the ID of the target team (or the respective placeholder)
1657 team_id teamID;
1658 if (clockID == CLOCK_PROCESS_CPUTIME_ID) {
1659 teamID = B_CURRENT_TEAM;
1660 } else {
1661 if (clockID < 0)
1662 return B_BAD_VALUE;
1663 if (clockID == team_get_kernel_team_id())
1664 return B_NOT_ALLOWED;
1666 teamID = clockID;
1669 // get the team
1670 Team* team = Team::Get(teamID);
1671 if (team == NULL)
1672 return B_BAD_VALUE;
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);
1681 return B_OK;
1685 return B_OK;
1689 int32
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;
1701 } else {
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);
1713 if (error != B_OK)
1714 return error;
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);
1722 if (thread == NULL)
1723 return B_BAD_THREAD_ID;
1725 thread->Unlock();
1727 BReference<Thread> threadReference(thread, true);
1729 // create the timer
1730 return create_timer(clockID, -1, team, thread, flags, event,
1731 userThreadAttributes != NULL ? &threadAttributes : NULL,
1732 userEvent == NULL);
1736 status_t
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)
1741 return B_BAD_VALUE;
1743 // get the timer
1744 TimerLocker timerLocker;
1745 UserTimer* timer;
1746 status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
1747 if (error != B_OK)
1748 return error;
1750 // cancel, remove, and delete it
1751 timer->Cancel();
1753 if (threadID >= 0)
1754 timerLocker.thread->RemoveUserTimer(timer);
1755 else
1756 timerLocker.team->RemoveUserTimer(timer);
1758 delete timer;
1760 return B_OK;
1764 status_t
1765 _user_get_timer(int32 timerID, thread_id threadID,
1766 struct user_timer_info* userInfo)
1768 // get the timer
1769 TimerLocker timerLocker;
1770 UserTimer* timer;
1771 status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
1772 if (error != B_OK)
1773 return error;
1775 // get the info
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
1780 // value.
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;
1793 return B_OK;
1797 status_t
1798 _user_set_timer(int32 timerID, thread_id threadID, bigtime_t startTime,
1799 bigtime_t interval, uint32 flags, struct user_timer_info* userOldInfo)
1801 // check the values
1802 if (startTime < 0 || interval < 0)
1803 return B_BAD_VALUE;
1805 // get the timer
1806 TimerLocker timerLocker;
1807 UserTimer* timer;
1808 status_t error = timerLocker.LockAndGetTimer(threadID, timerID, timer);
1809 if (error != B_OK)
1810 return error;
1812 // schedule the timer
1813 user_timer_info oldInfo;
1814 timer->Schedule(startTime, interval, flags, oldInfo.remaining_time,
1815 oldInfo.interval);
1817 // Sanitize remaining_time. If it's <= 0, we set it to 1, the least valid
1818 // value.
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;
1831 return B_OK;