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 */
6
7
8 #include <UserTimer.h>
9
10 #include <algorithm>
11
12 #include <AutoDeleter.h>
13
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>
21
22
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;
29
30 static RealTimeUserTimerList sAbsoluteRealTimeTimers;
31 static spinlock sAbsoluteRealTimeTimersLock = B_SPINLOCK_INITIALIZER;
32
33 static seqlock sUserTimerLock = B_SEQLOCK_INITIALIZER;
34
35
36 // #pragma mark - TimerLocker
37
38
39 namespace {
40
41 struct TimerLocker {
42 Team* team;
43 Thread* thread;
44
TimerLocker__anon628463ac0111::TimerLocker45 TimerLocker()
46 :
47 team(NULL),
48 thread(NULL)
49 {
50 }
51
~TimerLocker__anon628463ac0111::TimerLocker52 ~TimerLocker()
53 {
54 Unlock();
55 }
56
Lock__anon628463ac0111::TimerLocker57 void Lock(Team* team, Thread* thread)
58 {
59 this->team = team;
60 team->Lock();
61
62 this->thread = thread;
63
64 if (thread != NULL) {
65 thread->AcquireReference();
66 thread->Lock();
67 }
68
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.
71 }
72
LockAndGetTimer__anon628463ac0111::TimerLocker73 status_t LockAndGetTimer(thread_id threadID, int32 timerID,
74 UserTimer*& _timer)
75 {
76 team = thread_get_current_thread()->team;
77 team->Lock();
78
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;
85 }
86
87 UserTimer* timer = thread != NULL
88 ? thread->UserTimerFor(timerID) : team->UserTimerFor(timerID);
89 if (timer == NULL)
90 return B_BAD_VALUE;
91
92 _timer = timer;
93 return B_OK;
94 }
95
Unlock__anon628463ac0111::TimerLocker96 void Unlock()
97 {
98 if (thread != NULL) {
99 thread->UnlockAndReleaseReference();
100 thread = NULL;
101 }
102 if (team != NULL) {
103 team->Unlock();
104 team = NULL;
105 }
106 }
107 };
108
109 } // unnamed namespace
110
111
112 // #pragma mark - UserTimer
113
114
UserTimer()115 UserTimer::UserTimer()
116 :
117 fID(-1),
118 fEvent(NULL),
119 fNextTime(0),
120 fInterval(0),
121 fOverrunCount(0),
122 fScheduled(false),
123 fSkip(0)
124 {
125 // mark the timer unused
126 fTimer.user_data = this;
127 }
128
129
~UserTimer()130 UserTimer::~UserTimer()
131 {
132 if (fEvent != NULL)
133 fEvent->ReleaseReference();
134 }
135
136
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.
141
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.
159 */
160
161
162 /*! Cancels the timer, if it is scheduled.
163 */
164 void
Cancel()165 UserTimer::Cancel()
166 {
167 bigtime_t oldNextTime;
168 bigtime_t oldInterval;
169 return Schedule(B_INFINITE_TIMEOUT, 0, 0, oldNextTime, oldInterval);
170 }
171
172
173 /*! \fn UserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
174 uint32& _overrunCount)
175 Return information on the current timer.
176
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.
187 */
188
189
190 /*static*/ int32
HandleTimerHook(struct timer * timer)191 UserTimer::HandleTimerHook(struct timer* timer)
192 {
193 UserTimer* userTimer = reinterpret_cast<UserTimer*>(timer->user_data);
194
195 InterruptsLocker _;
196
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();
202 }
203
204 if (locked) {
205 userTimer->HandleTimer();
206 release_write_seqlock(&sUserTimerLock);
207 }
208
209 return B_HANDLED_INTERRUPT;
210 }
211
212
213 void
HandleTimer()214 UserTimer::HandleTimer()
215 {
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++;
222 }
223 }
224
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;
229 }
230
231
232 /*! Updates the start time for a periodic timer after it expired, enforcing
233 sanity limits and updating \c fOverrunCount, if necessary.
234
235 The caller must not hold \c sUserTimerLock.
236 */
237 void
UpdatePeriodicStartTime()238 UserTimer::UpdatePeriodicStartTime()
239 {
240 if (fInterval < kMinPeriodicTimerInterval) {
241 bigtime_t skip = (kMinPeriodicTimerInterval + fInterval - 1) / fInterval;
242 fNextTime += skip * fInterval;
243
244 // One interval is the normal advance, so don't consider it skipped.
245 skip--;
246
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;
253 }
254
255
256 /*! Checks whether the timer start time lies too much in the past and, if so,
257 adjusts it and updates \c fOverrunCount.
258
259 The caller must not hold \c sUserTimerLock.
260
261 \param now The current time.
262 */
263 void
CheckPeriodicOverrun(bigtime_t now)264 UserTimer::CheckPeriodicOverrun(bigtime_t now)
265 {
266 if (fNextTime + fInterval > now)
267 return;
268
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;
273
274 if (skip + fOverrunCount > MAX_USER_TIMER_OVERRUN_COUNT)
275 fOverrunCount = MAX_USER_TIMER_OVERRUN_COUNT;
276 else
277 fOverrunCount += skip;
278 }
279
280
281 void
CancelTimer()282 UserTimer::CancelTimer()
283 {
284 ASSERT(fScheduled);
285
286 atomic_set(&fSkip, 1);
287 cancel_timer(&fTimer);
288 atomic_set(&fSkip, 0);
289 }
290
291
292 // #pragma mark - SystemTimeUserTimer
293
294
295 void
Schedule(bigtime_t nextTime,bigtime_t interval,uint32 flags,bigtime_t & _oldRemainingTime,bigtime_t & _oldInterval)296 SystemTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
297 uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
298 {
299 InterruptsWriteSequentialLocker locker(sUserTimerLock);
300
301 // get the current time
302 bigtime_t now = system_time();
303
304 // Cancel the old timer, if still scheduled, and get the previous values.
305 if (fScheduled) {
306 CancelTimer();
307
308 _oldRemainingTime = fNextTime - now;
309 _oldInterval = fInterval;
310
311 fScheduled = false;
312 } else {
313 _oldRemainingTime = B_INFINITE_TIMEOUT;
314 _oldInterval = 0;
315 }
316
317 // schedule the new timer
318 fNextTime = nextTime;
319 fInterval = interval;
320 fOverrunCount = 0;
321
322 if (nextTime == B_INFINITE_TIMEOUT)
323 return;
324
325 if ((flags & B_RELATIVE_TIMEOUT) != 0)
326 fNextTime += now;
327
328 ScheduleKernelTimer(now, fInterval > 0);
329 }
330
331
332 void
GetInfo(bigtime_t & _remainingTime,bigtime_t & _interval,uint32 & _overrunCount)333 SystemTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
334 uint32& _overrunCount)
335 {
336 uint32 count;
337 do {
338 count = acquire_read_seqlock(&sUserTimerLock);
339
340 if (fScheduled) {
341 _remainingTime = fNextTime - system_time();
342 _interval = fInterval;
343 } else {
344 _remainingTime = B_INFINITE_TIMEOUT;
345 _interval = 0;
346 }
347
348 _overrunCount = fOverrunCount;
349 } while (!release_read_seqlock(&sUserTimerLock, count));
350 }
351
352
353 void
HandleTimer()354 SystemTimeUserTimer::HandleTimer()
355 {
356 UserTimer::HandleTimer();
357
358 // if periodic, reschedule the kernel timer
359 if (fInterval > 0) {
360 UpdatePeriodicStartTime();
361 ScheduleKernelTimer(system_time(), true);
362 }
363 }
364
365
366 /*! Schedules the kernel timer.
367
368 The caller must hold \c sUserTimerLock.
369
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.
373 */
374 void
ScheduleKernelTimer(bigtime_t now,bool checkPeriodicOverrun)375 SystemTimeUserTimer::ScheduleKernelTimer(bigtime_t now,
376 bool checkPeriodicOverrun)
377 {
378 // If periodic, check whether the start time is too far in the past.
379 if (checkPeriodicOverrun)
380 CheckPeriodicOverrun(now);
381
382 uint32 timerFlags = B_ONE_SHOT_ABSOLUTE_TIMER
383 | B_TIMER_USE_TIMER_STRUCT_TIMES;
384
385 fTimer.schedule_time = std::max(fNextTime, (bigtime_t)0);
386 fTimer.period = 0;
387
388 add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, timerFlags);
389
390 fScheduled = true;
391 }
392
393
394 // #pragma mark - RealTimeUserTimer
395
396
397 void
Schedule(bigtime_t nextTime,bigtime_t interval,uint32 flags,bigtime_t & _oldRemainingTime,bigtime_t & _oldInterval)398 RealTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
399 uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
400 {
401 InterruptsWriteSequentialLocker locker(sUserTimerLock);
402
403 // get the current time
404 bigtime_t now = system_time();
405
406 // Cancel the old timer, if still scheduled, and get the previous values.
407 if (fScheduled) {
408 CancelTimer();
409
410 _oldRemainingTime = fNextTime - now;
411 _oldInterval = fInterval;
412
413 if (fAbsolute) {
414 SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
415 sAbsoluteRealTimeTimers.Remove(this);
416 }
417
418 fScheduled = false;
419 } else {
420 _oldRemainingTime = B_INFINITE_TIMEOUT;
421 _oldInterval = 0;
422 }
423
424 // schedule the new timer
425 fNextTime = nextTime;
426 fInterval = interval;
427 fOverrunCount = 0;
428
429 if (nextTime == B_INFINITE_TIMEOUT)
430 return;
431
432 fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
433
434 if (fAbsolute) {
435 fRealTimeOffset = rtc_boot_time();
436 fNextTime -= fRealTimeOffset;
437
438 // If periodic, check whether the start time is too far in the past.
439 if (fInterval > 0)
440 CheckPeriodicOverrun(now);
441
442 // add the absolute timer to the global list
443 SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
444 sAbsoluteRealTimeTimers.Insert(this);
445 } else
446 fNextTime += now;
447
448 ScheduleKernelTimer(now, false);
449 }
450
451
452 /*! Called when the real-time clock has been changed.
453
454 The caller must hold \c sUserTimerLock. Optionally the caller may also
455 hold \c sAbsoluteRealTimeTimersLock.
456 */
457 void
TimeWarped()458 RealTimeUserTimer::TimeWarped()
459 {
460 ASSERT(fScheduled && fAbsolute);
461
462 // get the new real-time offset
463 bigtime_t oldRealTimeOffset = fRealTimeOffset;
464 fRealTimeOffset = rtc_boot_time();
465 if (fRealTimeOffset == oldRealTimeOffset)
466 return;
467
468 // cancel the kernel timer and reschedule it
469 CancelTimer();
470
471 fNextTime += oldRealTimeOffset - fRealTimeOffset;
472
473 ScheduleKernelTimer(system_time(), fInterval > 0);
474 }
475
476
477 void
HandleTimer()478 RealTimeUserTimer::HandleTimer()
479 {
480 SystemTimeUserTimer::HandleTimer();
481
482 // remove from global list, if no longer scheduled
483 if (!fScheduled && fAbsolute) {
484 SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
485 sAbsoluteRealTimeTimers.Remove(this);
486 }
487 }
488
489
490 // #pragma mark - TeamTimeUserTimer
491
492
TeamTimeUserTimer(team_id teamID)493 TeamTimeUserTimer::TeamTimeUserTimer(team_id teamID)
494 :
495 fTeamID(teamID),
496 fTeam(NULL)
497 {
498 }
499
500
~TeamTimeUserTimer()501 TeamTimeUserTimer::~TeamTimeUserTimer()
502 {
503 ASSERT(fTeam == NULL);
504 }
505
506
507 void
Schedule(bigtime_t nextTime,bigtime_t interval,uint32 flags,bigtime_t & _oldRemainingTime,bigtime_t & _oldInterval)508 TeamTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
509 uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
510 {
511 InterruptsWriteSequentialLocker locker(sUserTimerLock);
512 SpinLocker timeLocker(fTeam != NULL ? &fTeam->time_lock : NULL);
513
514 // get the current time, but only if needed
515 bool nowValid = fTeam != NULL;
516 bigtime_t now = nowValid ? fTeam->CPUTime(false) : 0;
517
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;
523 }
524
525 _oldRemainingTime = fNextTime - now;
526 _oldInterval = fInterval;
527
528 fTeam->UserTimerDeactivated(this);
529 fTeam->ReleaseReference();
530 fTeam = NULL;
531 } else {
532 _oldRemainingTime = B_INFINITE_TIMEOUT;
533 _oldInterval = 0;
534 }
535
536 // schedule the new timer
537 fNextTime = nextTime;
538 fInterval = interval;
539 fOverrunCount = 0;
540
541 if (fNextTime == B_INFINITE_TIMEOUT)
542 return;
543
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;
553
554 fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
555
556 // convert relative to absolute timeouts
557 if (!fAbsolute) {
558 if (!nowValid)
559 now = fTeam->CPUTime(false);
560 fNextTime += now;
561 }
562
563 fTeam->UserTimerActivated(this);
564
565 // schedule/udpate the kernel timer
566 Update(NULL);
567 }
568
569
570 void
GetInfo(bigtime_t & _remainingTime,bigtime_t & _interval,uint32 & _overrunCount)571 TeamTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
572 uint32& _overrunCount)
573 {
574 uint32 count;
575 do {
576 count = acquire_read_seqlock(&sUserTimerLock);
577
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;
585 }
586
587 _overrunCount = fOverrunCount;
588 } while (!release_read_seqlock(&sUserTimerLock, count));
589 }
590
591
592 /*! Deactivates the timer, if it is activated.
593
594 The caller must hold \c time_lock and \c sUserTimerLock.
595 */
596 void
Deactivate()597 TeamTimeUserTimer::Deactivate()
598 {
599 if (fTeam == NULL)
600 return;
601
602 // unschedule, if scheduled
603 if (fScheduled) {
604 CancelTimer();
605 fScheduled = false;
606 }
607
608 // deactivate
609 fTeam->UserTimerDeactivated(this);
610 fTeam->ReleaseReference();
611 fTeam = NULL;
612 }
613
614
615 /*! Starts/stops the timer as necessary, if it is active.
616
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.
621
622 The caller must hold \c time_lock and \c sUserTimerLock.
623
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.
626 */
627 void
Update(Thread * unscheduledThread,Thread * lockedThread)628 TeamTimeUserTimer::Update(Thread* unscheduledThread, Thread* lockedThread)
629 {
630 if (fTeam == NULL)
631 return;
632
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++;
640 }
641
642 _Update(unscheduledThread != NULL, lockedThread);
643 }
644
645
646 /*! Called when the team's CPU time clock which this timer refers to has been
647 set.
648
649 The caller must hold \c time_lock and \c sUserTimerLock.
650
651 \param changedBy The value by which the clock has changed.
652 */
653 void
TimeWarped(bigtime_t changedBy)654 TeamTimeUserTimer::TimeWarped(bigtime_t changedBy)
655 {
656 if (fTeam == NULL || changedBy == 0)
657 return;
658
659 // If this is a relative timer, adjust fNextTime by the value the clock has
660 // changed.
661 if (!fAbsolute)
662 fNextTime += changedBy;
663
664 // reschedule the kernel timer
665 _Update(false);
666 }
667
668
669 void
HandleTimer()670 TeamTimeUserTimer::HandleTimer()
671 {
672 UserTimer::HandleTimer();
673
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);
684 }
685 }
686 }
687
688
689 /*! Schedules/cancels the kernel timer as necessary.
690
691 \c fRunningThreads must be up-to-date.
692 The caller must hold \c time_lock and \c sUserTimerLock.
693
694 \param unscheduling \c true, when the current thread is in the process of
695 being unscheduled.
696 */
697 void
_Update(bool unscheduling,Thread * lockedThread)698 TeamTimeUserTimer::_Update(bool unscheduling, Thread* lockedThread)
699 {
700 // unschedule the kernel timer, if scheduled
701 if (fScheduled)
702 CancelTimer();
703
704 // if no more threads are running, we're done
705 if (fRunningThreads == 0) {
706 fScheduled = false;
707 return;
708 }
709
710 // There are still threads running. Reschedule the kernel timer.
711 bigtime_t now = fTeam->CPUTime(unscheduling, lockedThread);
712
713 // If periodic, check whether the start time is too far in the past.
714 if (fInterval > 0)
715 CheckPeriodicOverrun(now);
716
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.
728
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.
733
734 fScheduled = true;
735 }
736
737
738 // #pragma mark - TeamUserTimeUserTimer
739
740
TeamUserTimeUserTimer(team_id teamID)741 TeamUserTimeUserTimer::TeamUserTimeUserTimer(team_id teamID)
742 :
743 fTeamID(teamID),
744 fTeam(NULL)
745 {
746 }
747
748
~TeamUserTimeUserTimer()749 TeamUserTimeUserTimer::~TeamUserTimeUserTimer()
750 {
751 ASSERT(fTeam == NULL);
752 }
753
754
755 void
Schedule(bigtime_t nextTime,bigtime_t interval,uint32 flags,bigtime_t & _oldRemainingTime,bigtime_t & _oldInterval)756 TeamUserTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
757 uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
758 {
759 InterruptsWriteSequentialLocker locker(sUserTimerLock);
760 SpinLocker timeLocker(fTeam != NULL ? &fTeam->time_lock : NULL);
761
762 // get the current time, but only if needed
763 bool nowValid = fTeam != NULL;
764 bigtime_t now = nowValid ? fTeam->UserCPUTime() : 0;
765
766 // Cancel the old timer, if still active, and get the previous values.
767 if (fTeam != NULL) {
768 _oldRemainingTime = fNextTime - now;
769 _oldInterval = fInterval;
770
771 fTeam->UserTimerDeactivated(this);
772 fTeam->ReleaseReference();
773 fTeam = NULL;
774 } else {
775 _oldRemainingTime = B_INFINITE_TIMEOUT;
776 _oldInterval = 0;
777 }
778
779 // schedule the new timer
780 fNextTime = nextTime;
781 fInterval = interval;
782 fOverrunCount = 0;
783
784 if (fNextTime == B_INFINITE_TIMEOUT)
785 return;
786
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;
796
797 // convert relative to absolute timeouts
798 if ((flags & B_RELATIVE_TIMEOUT) != 0) {
799 if (!nowValid)
800 now = fTeam->CPUTime(false);
801 fNextTime += now;
802 }
803
804 fTeam->UserTimerActivated(this);
805
806 // fire the event, if already timed out
807 Check();
808 }
809
810
811 void
GetInfo(bigtime_t & _remainingTime,bigtime_t & _interval,uint32 & _overrunCount)812 TeamUserTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
813 uint32& _overrunCount)
814 {
815 uint32 count;
816 do {
817 count = acquire_read_seqlock(&sUserTimerLock);
818
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;
826 }
827
828 _overrunCount = fOverrunCount;
829 } while (!release_read_seqlock(&sUserTimerLock, count));
830 }
831
832
833 /*! Deactivates the timer, if it is activated.
834
835 The caller must hold \c time_lock and \c sUserTimerLock.
836 */
837 void
Deactivate()838 TeamUserTimeUserTimer::Deactivate()
839 {
840 if (fTeam == NULL)
841 return;
842
843 // deactivate
844 fTeam->UserTimerDeactivated(this);
845 fTeam->ReleaseReference();
846 fTeam = NULL;
847 }
848
849
850 /*! Checks whether the timer is up, firing an event, if so.
851
852 The caller must hold \c time_lock and \c sUserTimerLock.
853 */
854 void
Check()855 TeamUserTimeUserTimer::Check()
856 {
857 if (fTeam == NULL)
858 return;
859
860 // check whether we need to fire the event yet
861 bigtime_t now = fTeam->UserCPUTime();
862 if (now < fNextTime)
863 return;
864
865 HandleTimer();
866
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;
874 }
875
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;
881 }
882
883
884 // #pragma mark - ThreadTimeUserTimer
885
886
ThreadTimeUserTimer(thread_id threadID)887 ThreadTimeUserTimer::ThreadTimeUserTimer(thread_id threadID)
888 :
889 fThreadID(threadID),
890 fThread(NULL)
891 {
892 }
893
894
~ThreadTimeUserTimer()895 ThreadTimeUserTimer::~ThreadTimeUserTimer()
896 {
897 ASSERT(fThread == NULL);
898 }
899
900
901 void
Schedule(bigtime_t nextTime,bigtime_t interval,uint32 flags,bigtime_t & _oldRemainingTime,bigtime_t & _oldInterval)902 ThreadTimeUserTimer::Schedule(bigtime_t nextTime, bigtime_t interval,
903 uint32 flags, bigtime_t& _oldRemainingTime, bigtime_t& _oldInterval)
904 {
905 InterruptsWriteSequentialLocker locker(sUserTimerLock);
906 SpinLocker timeLocker(fThread != NULL ? &fThread->time_lock : NULL);
907
908 // get the current time, but only if needed
909 bool nowValid = fThread != NULL;
910 bigtime_t now = nowValid ? fThread->CPUTime(false) : 0;
911
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;
917 }
918
919 _oldRemainingTime = fNextTime - now;
920 _oldInterval = fInterval;
921
922 fThread->UserTimerDeactivated(this);
923 fThread->ReleaseReference();
924 fThread = NULL;
925 } else {
926 _oldRemainingTime = B_INFINITE_TIMEOUT;
927 _oldInterval = 0;
928 }
929
930 // schedule the new timer
931 fNextTime = nextTime;
932 fInterval = interval;
933 fOverrunCount = 0;
934
935 if (fNextTime == B_INFINITE_TIMEOUT)
936 return;
937
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;
947
948 fAbsolute = (flags & B_RELATIVE_TIMEOUT) == 0;
949
950 // convert relative to absolute timeouts
951 if (!fAbsolute) {
952 if (!nowValid)
953 now = fThread->CPUTime(false);
954 fNextTime += now;
955 }
956
957 fThread->UserTimerActivated(this);
958
959 // If the thread is currently running, also schedule a kernel timer.
960 if (fThread->cpu != NULL)
961 Start();
962 }
963
964
965 void
GetInfo(bigtime_t & _remainingTime,bigtime_t & _interval,uint32 & _overrunCount)966 ThreadTimeUserTimer::GetInfo(bigtime_t& _remainingTime, bigtime_t& _interval,
967 uint32& _overrunCount)
968 {
969 uint32 count;
970 do {
971 count = acquire_read_seqlock(&sUserTimerLock);
972
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;
980 }
981
982 _overrunCount = fOverrunCount;
983 } while (!release_read_seqlock(&sUserTimerLock, count));
984 }
985
986
987 /*! Deactivates the timer, if it is activated.
988
989 The caller must hold \c time_lock and \c sUserTimerLock.
990 */
991 void
Deactivate()992 ThreadTimeUserTimer::Deactivate()
993 {
994 if (fThread == NULL)
995 return;
996
997 // unschedule, if scheduled
998 if (fScheduled) {
999 CancelTimer();
1000 fScheduled = false;
1001 }
1002
1003 // deactivate
1004 fThread->UserTimerDeactivated(this);
1005 fThread->ReleaseReference();
1006 fThread = NULL;
1007 }
1008
1009
1010 /*! Starts the timer, if it is active.
1011
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.
1015
1016 The caller must hold \c time_lock and \c sUserTimerLock.
1017 */
1018 void
Start()1019 ThreadTimeUserTimer::Start()
1020 {
1021 if (fThread == NULL)
1022 return;
1023
1024 ASSERT(!fScheduled);
1025
1026 // add the kernel timer
1027 bigtime_t now = fThread->CPUTime(false);
1028
1029 // If periodic, check whether the start time is too far in the past.
1030 if (fInterval > 0)
1031 CheckPeriodicOverrun(now);
1032
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;
1041
1042 uint32 flags = B_ONE_SHOT_ABSOLUTE_TIMER | B_TIMER_USE_TIMER_STRUCT_TIMES;
1043 add_timer(&fTimer, &HandleTimerHook, fTimer.schedule_time, flags);
1044
1045 fScheduled = true;
1046 }
1047
1048
1049 /*! Stops the timer, if it is active.
1050
1051 Called when the thread whose CPU time is referred to by the timer is
1052 unscheduled, or, when the timer is canceled.
1053
1054 The caller must hold \c sUserTimerLock.
1055 */
1056 void
Stop()1057 ThreadTimeUserTimer::Stop()
1058 {
1059 if (fThread == NULL)
1060 return;
1061
1062 ASSERT(fScheduled);
1063
1064 // cancel the kernel timer
1065 CancelTimer();
1066 fScheduled = false;
1067
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.
1071 }
1072
1073
1074 /*! Called when the team's CPU time clock which this timer refers to has been
1075 set.
1076
1077 The caller must hold \c time_lock and \c sUserTimerLock.
1078
1079 \param changedBy The value by which the clock has changed.
1080 */
1081 void
TimeWarped(bigtime_t changedBy)1082 ThreadTimeUserTimer::TimeWarped(bigtime_t changedBy)
1083 {
1084 if (fThread == NULL || changedBy == 0)
1085 return;
1086
1087 // If this is a relative timer, adjust fNextTime by the value the clock has
1088 // changed.
1089 if (!fAbsolute)
1090 fNextTime += changedBy;
1091
1092 // reschedule the kernel timer
1093 if (fScheduled) {
1094 Stop();
1095 Start();
1096 }
1097 }
1098
1099
1100 void
HandleTimer()1101 ThreadTimeUserTimer::HandleTimer()
1102 {
1103 UserTimer::HandleTimer();
1104
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;
1115 }
1116 }
1117 }
1118
1119
1120 // #pragma mark - UserTimerList
1121
1122
UserTimerList()1123 UserTimerList::UserTimerList()
1124 {
1125 }
1126
1127
~UserTimerList()1128 UserTimerList::~UserTimerList()
1129 {
1130 ASSERT(fTimers.IsEmpty());
1131 }
1132
1133
1134 /*! Returns the user timer with the given ID.
1135
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.
1139 */
1140 UserTimer*
TimerFor(int32 id) const1141 UserTimerList::TimerFor(int32 id) const
1142 {
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;
1148 }
1149
1150 return NULL;
1151 }
1152
1153
1154 /*! Adds the given user timer and assigns it an ID.
1155
1156 \param timer The timer to be added.
1157 */
1158 void
AddTimer(UserTimer * timer)1159 UserTimerList::AddTimer(UserTimer* timer)
1160 {
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;
1173 }
1174
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);
1188 }
1189 insertAfter = other;
1190 }
1191
1192 // insert the timer
1193 fTimers.InsertAfter(insertAfter, timer);
1194 }
1195 }
1196
1197
1198 /*! Deletes all (or all user-defined) user timers.
1199
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.
1203 */
1204 int32
DeleteTimers(bool userDefinedOnly)1205 UserTimerList::DeleteTimers(bool userDefinedOnly)
1206 {
1207 int32 userDefinedCount = 0;
1208
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++;
1216
1217 // remove, cancel, and delete the timer
1218 it.Remove();
1219 timer->Cancel();
1220 delete timer;
1221 }
1222
1223 return userDefinedCount;
1224 }
1225
1226
1227 // #pragma mark - private
1228
1229
1230 static int32
create_timer(clockid_t clockID,int32 timerID,Team * team,Thread * thread,uint32 flags,const struct sigevent & event,ThreadCreationAttributes * threadAttributes,bool isDefaultEvent)1231 create_timer(clockid_t clockID, int32 timerID, Team* team, Thread* thread,
1232 uint32 flags, const struct sigevent& event,
1233 ThreadCreationAttributes* threadAttributes, bool isDefaultEvent)
1234 {
1235 // create the timer object
1236 UserTimer* timer;
1237 switch (clockID) {
1238 case CLOCK_MONOTONIC:
1239 timer = new(std::nothrow) SystemTimeUserTimer;
1240 break;
1241
1242 case CLOCK_REALTIME:
1243 timer = new(std::nothrow) RealTimeUserTimer;
1244 break;
1245
1246 case CLOCK_THREAD_CPUTIME_ID:
1247 timer = new(std::nothrow) ThreadTimeUserTimer(
1248 thread_get_current_thread()->id);
1249 break;
1250
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;
1256
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;
1262
1263 default:
1264 {
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;
1272
1273 Team* timedTeam = Team::GetAndLock(clockID);
1274 if (timedTeam == NULL)
1275 return B_BAD_VALUE;
1276
1277 uid_t uid = geteuid();
1278 uid_t teamUID = timedTeam->effective_uid;
1279
1280 timedTeam->UnlockAndReleaseReference();
1281
1282 if (uid != 0 && uid != teamUID)
1283 return B_NOT_ALLOWED;
1284
1285 timer = new(std::nothrow) TeamTimeUserTimer(clockID);
1286 break;
1287 }
1288 }
1289
1290 if (timer == NULL)
1291 return B_NO_MEMORY;
1292 ObjectDeleter<UserTimer> timerDeleter(timer);
1293
1294 if (timerID >= 0)
1295 timer->SetID(timerID);
1296
1297 SignalEvent* signalEvent = NULL;
1298
1299 switch (event.sigev_notify) {
1300 case SIGEV_NONE:
1301 // the timer's event remains NULL
1302 break;
1303
1304 case SIGEV_SIGNAL:
1305 {
1306 if (event.sigev_signo <= 0 || event.sigev_signo > MAX_SIGNAL_NUMBER)
1307 return B_BAD_VALUE;
1308
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);
1317 }
1318
1319 if (signalEvent == NULL)
1320 return B_NO_MEMORY;
1321
1322 timer->SetEvent(signalEvent);
1323 break;
1324 }
1325
1326 case SIGEV_THREAD:
1327 {
1328 if (threadAttributes == NULL)
1329 return B_BAD_VALUE;
1330
1331 CreateThreadEvent* event
1332 = CreateThreadEvent::Create(*threadAttributes);
1333 if (event == NULL)
1334 return B_NO_MEMORY;
1335
1336 timer->SetEvent(event);
1337 break;
1338 }
1339
1340 default:
1341 return B_BAD_VALUE;
1342 }
1343
1344 // add it to the team/thread
1345 TimerLocker timerLocker;
1346 timerLocker.Lock(team, thread);
1347
1348 status_t error = thread != NULL
1349 ? thread->AddUserTimer(timer) : team->AddUserTimer(timer);
1350 if (error != B_OK)
1351 return error;
1352
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();
1359
1360 signalEvent->SetUserValue(signalValue);
1361 }
1362
1363 return timerDeleter.Detach()->ID();
1364 }
1365
1366
1367 /*! Called when the CPU time clock of the given thread has been set.
1368
1369 The caller must hold \c time_lock.
1370
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).
1374 */
1375 static void
thread_clock_changed(Thread * thread,bigtime_t changedBy)1376 thread_clock_changed(Thread* thread, bigtime_t changedBy)
1377 {
1378 for (ThreadTimeUserTimerList::ConstIterator it
1379 = thread->CPUTimeUserTimerIterator();
1380 ThreadTimeUserTimer* timer = it.Next();) {
1381 timer->TimeWarped(changedBy);
1382 }
1383 }
1384
1385
1386 /*! Called when the CPU time clock of the given team has been set.
1387
1388 The caller must hold \c time_lock.
1389
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).
1393 */
1394 static void
team_clock_changed(Team * team,bigtime_t changedBy)1395 team_clock_changed(Team* team, bigtime_t changedBy)
1396 {
1397 for (TeamTimeUserTimerList::ConstIterator it
1398 = team->CPUTimeUserTimerIterator();
1399 TeamTimeUserTimer* timer = it.Next();) {
1400 timer->TimeWarped(changedBy);
1401 }
1402 }
1403
1404
1405 // #pragma mark - kernel private
1406
1407
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
1411
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.
1415 */
1416 status_t
user_timer_create_thread_timers(Team * team,Thread * thread)1417 user_timer_create_thread_timers(Team* team, Thread* thread)
1418 {
1419 // create a real time user timer
1420 struct sigevent event = {0};
1421 event.sigev_notify = SIGEV_SIGNAL;
1422 event.sigev_signo = SIGALRM;
1423
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;
1428
1429 return B_OK;
1430 }
1431
1432
1433 /*! Creates the pre-defined user timers for the given team.
1434
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.
1437 */
1438 status_t
user_timer_create_team_timers(Team * team)1439 user_timer_create_team_timers(Team* team)
1440 {
1441 // create a real time user timer
1442 struct sigevent event = {0};
1443 event.sigev_notify = SIGEV_SIGNAL;
1444 event.sigev_signo = SIGALRM;
1445
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;
1450
1451 // create a total CPU time user timer
1452 event.sigev_notify = SIGEV_SIGNAL;
1453 event.sigev_signo = SIGPROF;
1454
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;
1459
1460 // create a user CPU time user timer
1461 event.sigev_notify = SIGEV_SIGNAL;
1462 event.sigev_signo = SIGVTALRM;
1463
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;
1468
1469 return B_OK;
1470 }
1471
1472
1473 status_t
user_timer_get_clock(clockid_t clockID,bigtime_t & _time)1474 user_timer_get_clock(clockid_t clockID, bigtime_t& _time)
1475 {
1476 switch (clockID) {
1477 case CLOCK_MONOTONIC:
1478 _time = system_time();
1479 return B_OK;
1480
1481 case CLOCK_REALTIME:
1482 _time = real_time_clock_usecs();
1483 return B_OK;
1484
1485 case CLOCK_THREAD_CPUTIME_ID:
1486 {
1487 Thread* thread = thread_get_current_thread();
1488 InterruptsSpinLocker timeLocker(thread->time_lock);
1489 _time = thread->CPUTime(false);
1490 return B_OK;
1491 }
1492
1493 case CLOCK_PROCESS_USER_CPUTIME_ID:
1494 {
1495 Team* team = thread_get_current_thread()->team;
1496 InterruptsSpinLocker timeLocker(team->time_lock);
1497 _time = team->UserCPUTime();
1498 return B_OK;
1499 }
1500
1501 case CLOCK_PROCESS_CPUTIME_ID:
1502 default:
1503 {
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;
1513
1514 teamID = clockID;
1515 }
1516
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);
1522
1523 // get the time
1524 InterruptsSpinLocker timeLocker(team->time_lock);
1525 _time = team->CPUTime(false);
1526
1527 return B_OK;
1528 }
1529 }
1530 }
1531
1532
1533 void
user_timer_real_time_clock_changed()1534 user_timer_real_time_clock_changed()
1535 {
1536 // we need to update all absolute real-time timers
1537 InterruptsWriteSequentialLocker locker(sUserTimerLock);
1538 SpinLocker globalListLocker(sAbsoluteRealTimeTimersLock);
1539
1540 for (RealTimeUserTimerList::Iterator it
1541 = sAbsoluteRealTimeTimers.GetIterator();
1542 RealTimeUserTimer* timer = it.Next();) {
1543 timer->TimeWarped();
1544 }
1545 }
1546
1547
1548 void
user_timer_stop_cpu_timers(Thread * thread,Thread * nextThread)1549 user_timer_stop_cpu_timers(Thread* thread, Thread* nextThread)
1550 {
1551 // stop thread timers
1552 for (ThreadTimeUserTimerList::ConstIterator it
1553 = thread->CPUTimeUserTimerIterator();
1554 ThreadTimeUserTimer* timer = it.Next();) {
1555 timer->Stop();
1556 }
1557
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);
1564 }
1565 }
1566 }
1567
1568
1569 void
user_timer_continue_cpu_timers(Thread * thread,Thread * previousThread)1570 user_timer_continue_cpu_timers(Thread* thread, Thread* previousThread)
1571 {
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);
1578 }
1579 }
1580
1581 // start thread timers
1582 for (ThreadTimeUserTimerList::ConstIterator it
1583 = thread->CPUTimeUserTimerIterator();
1584 ThreadTimeUserTimer* timer = it.Next();) {
1585 timer->Start();
1586 }
1587 }
1588
1589
1590 void
user_timer_check_team_user_timers(Team * team)1591 user_timer_check_team_user_timers(Team* team)
1592 {
1593 for (TeamUserTimeUserTimerList::ConstIterator it
1594 = team->UserTimeUserTimerIterator();
1595 TeamUserTimeUserTimer* timer = it.Next();) {
1596 timer->Check();
1597 }
1598 }
1599
1600
1601 // #pragma mark - syscalls
1602
1603
1604 status_t
_user_get_clock(clockid_t clockID,bigtime_t * userTime)1605 _user_get_clock(clockid_t clockID, bigtime_t* userTime)
1606 {
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;
1612
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;
1617 }
1618
1619 return B_OK;
1620 }
1621
1622
1623 status_t
_user_set_clock(clockid_t clockID,bigtime_t time)1624 _user_set_clock(clockid_t clockID, bigtime_t time)
1625 {
1626 switch (clockID) {
1627 case CLOCK_MONOTONIC:
1628 return B_BAD_VALUE;
1629
1630 case CLOCK_REALTIME:
1631 // only root may set the time
1632 if (geteuid() != 0)
1633 return B_NOT_ALLOWED;
1634
1635 set_real_time_clock_usecs(time);
1636 return B_OK;
1637
1638 case CLOCK_THREAD_CPUTIME_ID:
1639 {
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;
1644
1645 thread_clock_changed(thread, diff);
1646 return B_OK;
1647 }
1648
1649 case CLOCK_PROCESS_USER_CPUTIME_ID:
1650 // not supported -- this clock is an Haiku-internal extension
1651 return B_BAD_VALUE;
1652
1653 case CLOCK_PROCESS_CPUTIME_ID:
1654 default:
1655 {
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;
1665
1666 teamID = clockID;
1667 }
1668
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);
1674
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;
1679
1680 team_clock_changed(team, diff);
1681 return B_OK;
1682 }
1683 }
1684
1685 return B_OK;
1686 }
1687
1688
1689 int32
_user_create_timer(clockid_t clockID,thread_id threadID,uint32 flags,const struct sigevent * userEvent,const thread_creation_attributes * userThreadAttributes)1690 _user_create_timer(clockid_t clockID, thread_id threadID, uint32 flags,
1691 const struct sigevent* userEvent,
1692 const thread_creation_attributes* userThreadAttributes)
1693 {
1694 // copy the sigevent structure from userland
1695 struct sigevent event = {0};
1696 if (userEvent != NULL) {
1697 if (!IS_USER_ADDRESS(userEvent)
1698 || user_memcpy(&event, userEvent, sizeof(event)) != B_OK) {
1699 return B_BAD_ADDRESS;
1700 }
1701 } else {
1702 // none given -- use defaults
1703 event.sigev_notify = SIGEV_SIGNAL;
1704 event.sigev_signo = SIGALRM;
1705 }
1706
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;
1715 }
1716
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;
1724
1725 thread->Unlock();
1726 }
1727 BReference<Thread> threadReference(thread, true);
1728
1729 // create the timer
1730 return create_timer(clockID, -1, team, thread, flags, event,
1731 userThreadAttributes != NULL ? &threadAttributes : NULL,
1732 userEvent == NULL);
1733 }
1734
1735
1736 status_t
_user_delete_timer(int32 timerID,thread_id threadID)1737 _user_delete_timer(int32 timerID, thread_id threadID)
1738 {
1739 // can only delete user-defined timers
1740 if (timerID < USER_TIMER_FIRST_USER_DEFINED_ID)
1741 return B_BAD_VALUE;
1742
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;
1749
1750 // cancel, remove, and delete it
1751 timer->Cancel();
1752
1753 if (threadID >= 0)
1754 timerLocker.thread->RemoveUserTimer(timer);
1755 else
1756 timerLocker.team->RemoveUserTimer(timer);
1757
1758 delete timer;
1759
1760 return B_OK;
1761 }
1762
1763
1764 status_t
_user_get_timer(int32 timerID,thread_id threadID,struct user_timer_info * userInfo)1765 _user_get_timer(int32 timerID, thread_id threadID,
1766 struct user_timer_info* userInfo)
1767 {
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;
1774
1775 // get the info
1776 user_timer_info info;
1777 timer->GetInfo(info.remaining_time, info.interval, info.overrun_count);
1778
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;
1783
1784 timerLocker.Unlock();
1785
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;
1791 }
1792
1793 return B_OK;
1794 }
1795
1796
1797 status_t
_user_set_timer(int32 timerID,thread_id threadID,bigtime_t startTime,bigtime_t interval,uint32 flags,struct user_timer_info * userOldInfo)1798 _user_set_timer(int32 timerID, thread_id threadID, bigtime_t startTime,
1799 bigtime_t interval, uint32 flags, struct user_timer_info* userOldInfo)
1800 {
1801 // check the values
1802 if (startTime < 0 || interval < 0)
1803 return B_BAD_VALUE;
1804
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;
1811
1812 // schedule the timer
1813 user_timer_info oldInfo;
1814 timer->Schedule(startTime, interval, flags, oldInfo.remaining_time,
1815 oldInfo.interval);
1816
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;
1821
1822 timerLocker.Unlock();
1823
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;
1829 }
1830
1831 return B_OK;
1832 }
1833