aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/hle/kernel/timer.cpp
blob: 3b0452d4d92f83afc4b90940c27b6bb2f7005312 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#include <set>

#include "common/common.h"

#include "core/core_timing.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/timer.h"
#include "core/hle/kernel/thread.h"

namespace Kernel {

class Timer : public Object {
public:
    std::string GetTypeName() const override { return "Timer"; }
    std::string GetName() const override { return name; }

    static const HandleType HANDLE_TYPE = HandleType::Timer;
    HandleType GetHandleType() const override { return HANDLE_TYPE; }

    ResetType reset_type;                   ///< The ResetType of this timer

    bool signaled;                          ///< Whether the timer has been signaled or not
    std::set<Handle> waiting_threads;       ///< Threads that are waiting for the timer
    std::string name;                       ///< Name of timer (optional)

    u64 initial_delay;                      ///< The delay until the timer fires for the first time
    u64 interval_delay;                     ///< The delay until the timer fires after the first time

    ResultVal<bool> WaitSynchronization() override {
        bool wait = !signaled;
        if (wait) {
            waiting_threads.insert(GetCurrentThread()->GetHandle());
            Kernel::WaitCurrentThread(WAITTYPE_TIMER, this);
        }
        return MakeResult<bool>(wait);
    }
};

/**
 * Creates a timer.
 * @param handle Reference to handle for the newly created timer
 * @param reset_type ResetType describing how to create timer
 * @param name Optional name of timer
 * @return Newly created Timer object
 */
Timer* CreateTimer(Handle& handle, const ResetType reset_type, const std::string& name) {
    Timer* timer = new Timer;

    handle = Kernel::g_handle_table.Create(timer).ValueOr(INVALID_HANDLE);

    timer->reset_type = reset_type;
    timer->signaled = false;
    timer->name = name;
    timer->initial_delay = 0;
    timer->interval_delay = 0;
    return timer;
}

ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name) {
    CreateTimer(*handle, reset_type, name);
    return RESULT_SUCCESS;
}

ResultCode ClearTimer(Handle handle) {
    SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
    
    if (timer == nullptr)
        return InvalidHandle(ErrorModule::Kernel);

    timer->signaled = false;
    return RESULT_SUCCESS;
}

/// The event type of the generic timer callback event
static int TimerCallbackEventType = -1;

/// The timer callback event, called when a timer is fired
static void TimerCallback(u64 timer_handle, int cycles_late) {
    SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(timer_handle);

    if (timer == nullptr) {
        LOG_CRITICAL(Kernel, "Callback fired for invalid timer %u", timer_handle);
        return;
    }

    LOG_TRACE(Kernel, "Timer %u fired", timer_handle);

    timer->signaled = true;

    // Resume all waiting threads
    for (Handle thread_handle : timer->waiting_threads) {
        if (SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(thread_handle))
            thread->ResumeFromWait();
    }

    timer->waiting_threads.clear();

    if (timer->reset_type == RESETTYPE_ONESHOT)
        timer->signaled = false;

    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, 
                TimerCallbackEventType, timer_handle);
    }
}

ResultCode SetTimer(Handle handle, s64 initial, s64 interval) {
    SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);

    if (timer == nullptr)
        return InvalidHandle(ErrorModule::Kernel);

    timer->initial_delay = initial;
    timer->interval_delay = interval;

    u64 initial_microseconds = initial / 1000;
    CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), TimerCallbackEventType, handle);
    return RESULT_SUCCESS;
}

ResultCode CancelTimer(Handle handle) {
    SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);

    if (timer == nullptr)
        return InvalidHandle(ErrorModule::Kernel);

    CoreTiming::UnscheduleEvent(TimerCallbackEventType, handle);
    return RESULT_SUCCESS;
}

void TimersInit() {
    TimerCallbackEventType = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
}

void TimersShutdown() {
}

} // namespace