summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/thread.h
blob: 233bcbdbdab98e35c4172c7c7c8bd608218a496d (plain) (blame)
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#pragma once

#include <string>
#include <vector>

#include <boost/container/flat_set.hpp>

#include "common/common_types.h"

#include "core/core.h"
#include "core/mem_map.h"

#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"

enum ThreadPriority : s32{
    THREADPRIO_HIGHEST          = 0,  ///< Highest thread priority
    THREADPRIO_USERLAND_MAX     = 24, ///< Highest thread priority for userland apps
    THREADPRIO_DEFAULT          = 48, ///< Default thread priority for userland apps
    THREADPRIO_LOWEST           = 63, ///< Lowest thread priority
};

enum ThreadProcessorId : s32 {
    THREADPROCESSORID_DEFAULT   = -2, ///< Run thread on default core specified by exheader
    THREADPROCESSORID_ALL       = -1, ///< Run thread on either core
    THREADPROCESSORID_0         =  0, ///< Run thread on core 0 (AppCore)
    THREADPROCESSORID_1         =  1, ///< Run thread on core 1 (SysCore)
    THREADPROCESSORID_MAX       =  2, ///< Processor ID must be less than this
};

enum ThreadStatus {
    THREADSTATUS_RUNNING,       ///< Currently running
    THREADSTATUS_READY,         ///< Ready to run
    THREADSTATUS_WAIT_ARB,      ///< Waiting on an address arbiter
    THREADSTATUS_WAIT_SLEEP,    ///< Waiting due to a SleepThread SVC
    THREADSTATUS_WAIT_SYNCH,    ///< Waiting due to a WaitSynchronization SVC
    THREADSTATUS_DORMANT,       ///< Created but not yet made ready
    THREADSTATUS_DEAD           ///< Run to completion, or forcefully terminated
};

namespace Kernel {

class Mutex;

class Thread final : public WaitObject {
public:
    /**
     * Creates and returns a new thread. The new thread is immediately scheduled
     * @param name The friendly name desired for the thread
     * @param entry_point The address at which the thread should start execution
     * @param priority The thread's priority
     * @param arg User data to pass to the thread
     * @param processor_id The ID(s) of the processors on which the thread is desired to be run
     * @param stack_top The address of the thread's stack top
     * @param stack_size The size of the thread's stack
     * @return A shared pointer to the newly created thread
     */
    static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority,
        u32 arg, s32 processor_id, VAddr stack_top);

    std::string GetName() const override { return name; }
    std::string GetTypeName() const override { return "Thread"; }

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

    bool ShouldWait() override;
    void Acquire() override;

    /**
     * Checks if the thread is an idle (stub) thread
     * @return True if the thread is an idle (stub) thread, false otherwise
     */
    inline bool IsIdle() const { return idle; }

    /**
     * Gets the thread's current priority
     * @return The current thread's priority
     */
    s32 GetPriority() const { return current_priority; }

    /**
     * Sets the thread's current priority
     * @param priority The new priority
     */
    void SetPriority(s32 priority);

    /**
     * Temporarily boosts the thread's priority until the next time it is scheduled
     * @param priority The new priority
     */
    void BoostPriority(s32 priority);

    /**
     * Gets the thread's thread ID
     * @return The thread's ID
     */
    u32 GetThreadId() const { return thread_id; }
    
    /**
     * Release an acquired wait object
     * @param wait_object WaitObject to release
     */
    void ReleaseWaitObject(WaitObject* wait_object);

    /**
     * Resumes a thread from waiting
     */
    void ResumeFromWait();

    /**
    * Schedules an event to wake up the specified thread after the specified delay
    * @param nanoseconds The time this thread will be allowed to sleep for
    */
    void WakeAfterDelay(s64 nanoseconds);

    /**
     * Sets the result after the thread awakens (from either WaitSynchronization SVC)
     * @param result Value to set to the returned result
     */
    void SetWaitSynchronizationResult(ResultCode result);

    /**
     * Sets the output parameter value after the thread awakens (from WaitSynchronizationN SVC only)
     * @param output Value to set to the output parameter
     */
    void SetWaitSynchronizationOutput(s32 output);

    /**
     * Stops a thread, invalidating it from further use
     */
    void Stop();

    Core::ThreadContext context;

    u32 thread_id;

    u32 status;
    u32 entry_point;
    u32 stack_top;

    s32 nominal_priority;   ///< Nominal thread priority, as set by the emulated application
    s32 current_priority;   ///< Current thread priority, can be temporarily changed

    u64 last_running_ticks; ///< CPU tick when thread was last running

    s32 processor_id;

    /// Mutexes currently held by this thread, which will be released when it exits.
    boost::container::flat_set<SharedPtr<Mutex>> held_mutexes;

    std::vector<SharedPtr<WaitObject>> wait_objects; ///< Objects that the thread is waiting on
    VAddr wait_address;     ///< If waiting on an AddressArbiter, this is the arbitration address
    bool wait_all;          ///< True if the thread is waiting on all objects before resuming
    bool wait_set_output;   ///< True if the output parameter should be set on thread wakeup

    std::string name;

    /// Whether this thread is intended to never actually be executed, i.e. always idle
    bool idle = false;

private:
    Thread();
    ~Thread() override;

    /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
    Handle callback_handle;
};

extern SharedPtr<Thread> g_main_thread;

/**
 * Sets up the primary application thread
 * @param stack_size The size of the thread's stack
 * @param entry_point The address at which the thread should start execution
 * @param priority The priority to give the main thread
 * @return A shared pointer to the main thread
 */
SharedPtr<Thread> SetupMainThread(u32 stack_size, u32 entry_point, s32 priority);

/**
 * Reschedules to the next available thread (call after current thread is suspended)
 */
void Reschedule();

/**
 * Arbitrate the highest priority thread that is waiting
 * @param address The address for which waiting threads should be arbitrated
 */
Thread* ArbitrateHighestPriorityThread(u32 address);

/**
 * Arbitrate all threads currently waiting.
 * @param address The address for which waiting threads should be arbitrated
 */
void ArbitrateAllThreads(u32 address);

/**
 * Gets the current thread
 */
Thread* GetCurrentThread();

/**
 * Waits the current thread on a sleep
 */
void WaitCurrentThread_Sleep();

/**
 * Waits the current thread from a WaitSynchronization call
 * @param wait_objects Kernel objects that we are waiting on
 * @param wait_set_output If true, set the output parameter on thread wakeup (for WaitSynchronizationN only)
 * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only)
 */
void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, bool wait_set_output, bool wait_all);

/**
 * Waits the current thread from an ArbitrateAddress call
 * @param wait_address Arbitration address used to resume from wait
 */
void WaitCurrentThread_ArbitrateAddress(VAddr wait_address);

/**
 * Sets up the idle thread, this is a thread that is intended to never execute instructions,
 * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue
 * and will try to yield on every call.
 * @return The handle of the idle thread
 */
SharedPtr<Thread> SetupIdleThread();

/**
 * Initialize threading
 */
void ThreadingInit();

/**
 * Shutdown threading
 */
void ThreadingShutdown();

} // namespace