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