Merge pull request #2421 from Subv/timers

Timers: Immediately signal the timer if it was started with an initial value of 0
This commit is contained in:
Yuri Kunde Schlesner 2017-02-24 20:48:31 -08:00 committed by GitHub
commit fb5301cf6e
3 changed files with 36 additions and 16 deletions

View file

@ -52,10 +52,15 @@ void Timer::Set(s64 initial, s64 interval) {
initial_delay = initial; initial_delay = initial;
interval_delay = interval; interval_delay = interval;
if (initial == 0) {
// Immediately invoke the callback
Signal(0);
} else {
u64 initial_microseconds = initial / 1000; u64 initial_microseconds = initial / 1000;
CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), timer_callback_event_type, CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), timer_callback_event_type,
callback_handle); callback_handle);
} }
}
void Timer::Cancel() { void Timer::Cancel() {
CoreTiming::UnscheduleEvent(timer_callback_event_type, callback_handle); CoreTiming::UnscheduleEvent(timer_callback_event_type, callback_handle);
@ -72,6 +77,20 @@ void Timer::WakeupAllWaitingThreads() {
signaled = false; signaled = false;
} }
void Timer::Signal(int cycles_late) {
LOG_TRACE(Kernel, "Timer %08" PRIx64 " fired", timer_handle);
// Resume all waiting threads
WakeupAllWaitingThreads();
if (interval_delay != 0) {
// Reschedule the timer with the interval delay
u64 interval_microseconds = interval_delay / 1000;
CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late,
timer_callback_event_type, callback_handle);
}
}
/// The timer callback event, called when a timer is fired /// The timer callback event, called when a timer is fired
static void TimerCallback(u64 timer_handle, int cycles_late) { static void TimerCallback(u64 timer_handle, int cycles_late) {
SharedPtr<Timer> timer = SharedPtr<Timer> timer =
@ -82,19 +101,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) {
return; return;
} }
LOG_TRACE(Kernel, "Timer %08" PRIx64 " fired", timer_handle); timer->Signal(cycles_late);
timer->signaled = true;
// Resume all waiting threads
timer->WakeupAllWaitingThreads();
if (timer->interval_delay != 0) {
// Reschedule the timer with the interval delay
u64 interval_microseconds = timer->interval_delay / 1000;
CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late,
timer_callback_event_type, timer_handle);
}
} }
void TimersInit() { void TimersInit() {

View file

@ -54,6 +54,14 @@ public:
void Cancel(); void Cancel();
void Clear(); void Clear();
/**
* Signals the timer, waking up any waiting threads and rescheduling it
* for the next interval.
* This method should not be called from outside the timer callback handler,
* lest multiple callback events get scheduled.
*/
void Signal(int cycles_late);
private: private:
Timer(); Timer();
~Timer() override; ~Timer() override;

View file

@ -837,6 +837,11 @@ static ResultCode SetTimer(Kernel::Handle handle, s64 initial, s64 interval) {
LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
if (initial < 0 || interval < 0) {
return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
if (timer == nullptr) if (timer == nullptr)
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;