diff options
| author | 2014-06-14 12:13:16 -0400 | |
|---|---|---|
| committer | 2014-06-14 12:13:16 -0400 | |
| commit | 004df767953a949817da89bddcd5d1379240f769 (patch) | |
| tree | b2d54928dcbf3cb4dde0cd5d3277afe7999b7bd9 /src/core/hle/kernel | |
| parent | GPU debugger: Const correctness and build fix. (diff) | |
| parent | Kernel: Removed unnecessary "#pragma once". (diff) | |
| download | yuzu-004df767953a949817da89bddcd5d1379240f769.tar.gz yuzu-004df767953a949817da89bddcd5d1379240f769.tar.xz yuzu-004df767953a949817da89bddcd5d1379240f769.zip | |
Merge branch 'threading' of https://github.com/bunnei/citra
Conflicts:
src/core/hle/function_wrappers.h
src/core/hle/service/gsp.cpp
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/event.cpp | 159 | ||||
| -rw-r--r-- | src/core/hle/kernel/event.h | 52 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 11 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 33 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 50 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.h | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 223 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 15 |
8 files changed, 477 insertions, 72 deletions
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp new file mode 100644 index 000000000..127c0cfc6 --- /dev/null +++ b/src/core/hle/kernel/event.cpp | |||
| @@ -0,0 +1,159 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <map> | ||
| 6 | #include <algorithm> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "common/common.h" | ||
| 10 | |||
| 11 | #include "core/hle/kernel/kernel.h" | ||
| 12 | #include "core/hle/kernel/event.h" | ||
| 13 | #include "core/hle/kernel/thread.h" | ||
| 14 | |||
| 15 | namespace Kernel { | ||
| 16 | |||
| 17 | class Event : public Object { | ||
| 18 | public: | ||
| 19 | const char* GetTypeName() const { return "Event"; } | ||
| 20 | const char* GetName() const { return name.c_str(); } | ||
| 21 | |||
| 22 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; } | ||
| 23 | Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Event; } | ||
| 24 | |||
| 25 | ResetType intitial_reset_type; ///< ResetType specified at Event initialization | ||
| 26 | ResetType reset_type; ///< Current ResetType | ||
| 27 | |||
| 28 | bool locked; ///< Event signal wait | ||
| 29 | bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough) | ||
| 30 | std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event | ||
| 31 | std::string name; ///< Name of event (optional) | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Wait for kernel object to synchronize | ||
| 35 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 36 | * @return Result of operation, 0 on success, otherwise error code | ||
| 37 | */ | ||
| 38 | Result WaitSynchronization(bool* wait) { | ||
| 39 | *wait = locked; | ||
| 40 | if (locked) { | ||
| 41 | Handle thread = GetCurrentThreadHandle(); | ||
| 42 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { | ||
| 43 | waiting_threads.push_back(thread); | ||
| 44 | } | ||
| 45 | Kernel::WaitCurrentThread(WAITTYPE_EVENT); | ||
| 46 | } | ||
| 47 | if (reset_type != RESETTYPE_STICKY && !permanent_locked) { | ||
| 48 | locked = true; | ||
| 49 | } | ||
| 50 | return 0; | ||
| 51 | } | ||
| 52 | }; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Hackish function to set an events permanent lock state, used to pass through synch blocks | ||
| 56 | * @param handle Handle to event to change | ||
| 57 | * @param permanent_locked Boolean permanent locked value to set event | ||
| 58 | * @return Result of operation, 0 on success, otherwise error code | ||
| 59 | */ | ||
| 60 | Result SetPermanentLock(Handle handle, const bool permanent_locked) { | ||
| 61 | Event* evt = g_object_pool.GetFast<Event>(handle); | ||
| 62 | _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); | ||
| 63 | |||
| 64 | evt->permanent_locked = permanent_locked; | ||
| 65 | return 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Changes whether an event is locked or not | ||
| 70 | * @param handle Handle to event to change | ||
| 71 | * @param locked Boolean locked value to set event | ||
| 72 | * @return Result of operation, 0 on success, otherwise error code | ||
| 73 | */ | ||
| 74 | Result SetEventLocked(const Handle handle, const bool locked) { | ||
| 75 | Event* evt = g_object_pool.GetFast<Event>(handle); | ||
| 76 | _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); | ||
| 77 | |||
| 78 | if (!evt->permanent_locked) { | ||
| 79 | evt->locked = locked; | ||
| 80 | } | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Signals an event | ||
| 86 | * @param handle Handle to event to signal | ||
| 87 | * @return Result of operation, 0 on success, otherwise error code | ||
| 88 | */ | ||
| 89 | Result SignalEvent(const Handle handle) { | ||
| 90 | Event* evt = g_object_pool.GetFast<Event>(handle); | ||
| 91 | _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); | ||
| 92 | |||
| 93 | // Resume threads waiting for event to signal | ||
| 94 | bool event_caught = false; | ||
| 95 | for (size_t i = 0; i < evt->waiting_threads.size(); ++i) { | ||
| 96 | ResumeThreadFromWait( evt->waiting_threads[i]); | ||
| 97 | |||
| 98 | // If any thread is signalled awake by this event, assume the event was "caught" and reset | ||
| 99 | // the event. This will result in the next thread waiting on the event to block. Otherwise, | ||
| 100 | // the event will not be reset, and the next thread to call WaitSynchronization on it will | ||
| 101 | // not block. Not sure if this is correct behavior, but it seems to work. | ||
| 102 | event_caught = true; | ||
| 103 | } | ||
| 104 | evt->waiting_threads.clear(); | ||
| 105 | |||
| 106 | if (!evt->permanent_locked) { | ||
| 107 | evt->locked = event_caught; | ||
| 108 | } | ||
| 109 | return 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | /** | ||
| 113 | * Clears an event | ||
| 114 | * @param handle Handle to event to clear | ||
| 115 | * @return Result of operation, 0 on success, otherwise error code | ||
| 116 | */ | ||
| 117 | Result ClearEvent(Handle handle) { | ||
| 118 | Event* evt = g_object_pool.GetFast<Event>(handle); | ||
| 119 | _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); | ||
| 120 | |||
| 121 | if (!evt->permanent_locked) { | ||
| 122 | evt->locked = true; | ||
| 123 | } | ||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | /** | ||
| 128 | * Creates an event | ||
| 129 | * @param handle Reference to handle for the newly created mutex | ||
| 130 | * @param reset_type ResetType describing how to create event | ||
| 131 | * @param name Optional name of event | ||
| 132 | * @return Newly created Event object | ||
| 133 | */ | ||
| 134 | Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) { | ||
| 135 | Event* evt = new Event; | ||
| 136 | |||
| 137 | handle = Kernel::g_object_pool.Create(evt); | ||
| 138 | |||
| 139 | evt->locked = true; | ||
| 140 | evt->permanent_locked = false; | ||
| 141 | evt->reset_type = evt->intitial_reset_type = reset_type; | ||
| 142 | evt->name = name; | ||
| 143 | |||
| 144 | return evt; | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * Creates an event | ||
| 149 | * @param reset_type ResetType describing how to create event | ||
| 150 | * @param name Optional name of event | ||
| 151 | * @return Handle to newly created Event object | ||
| 152 | */ | ||
| 153 | Handle CreateEvent(const ResetType reset_type, const std::string& name) { | ||
| 154 | Handle handle; | ||
| 155 | Event* evt = CreateEvent(handle, reset_type, name); | ||
| 156 | return handle; | ||
| 157 | } | ||
| 158 | |||
| 159 | } // namespace | ||
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h new file mode 100644 index 000000000..c39b33180 --- /dev/null +++ b/src/core/hle/kernel/event.h | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 10 | #include "core/hle/svc.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | /** | ||
| 15 | * Changes whether an event is locked or not | ||
| 16 | * @param handle Handle to event to change | ||
| 17 | * @param locked Boolean locked value to set event | ||
| 18 | * @return Result of operation, 0 on success, otherwise error code | ||
| 19 | */ | ||
| 20 | Result SetEventLocked(const Handle handle, const bool locked); | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Hackish function to set an events permanent lock state, used to pass through synch blocks | ||
| 24 | * @param handle Handle to event to change | ||
| 25 | * @param permanent_locked Boolean permanent locked value to set event | ||
| 26 | * @return Result of operation, 0 on success, otherwise error code | ||
| 27 | */ | ||
| 28 | Result SetPermanentLock(Handle handle, const bool permanent_locked); | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Signals an event | ||
| 32 | * @param handle Handle to event to signal | ||
| 33 | * @return Result of operation, 0 on success, otherwise error code | ||
| 34 | */ | ||
| 35 | Result SignalEvent(const Handle handle); | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Clears an event | ||
| 39 | * @param handle Handle to event to clear | ||
| 40 | * @return Result of operation, 0 on success, otherwise error code | ||
| 41 | */ | ||
| 42 | Result ClearEvent(Handle handle); | ||
| 43 | |||
| 44 | /** | ||
| 45 | * Creates an event | ||
| 46 | * @param reset_type ResetType describing how to create event | ||
| 47 | * @param name Optional name of event | ||
| 48 | * @return Handle to newly created Event object | ||
| 49 | */ | ||
| 50 | Handle CreateEvent(const ResetType reset_type, const std::string& name="Unknown"); | ||
| 51 | |||
| 52 | } // namespace | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index de80de893..cda183add 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -2,8 +2,6 @@ | |||
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string.h> | 5 | #include <string.h> |
| 8 | 6 | ||
| 9 | #include "common/common.h" | 7 | #include "common/common.h" |
| @@ -14,6 +12,7 @@ | |||
| 14 | 12 | ||
| 15 | namespace Kernel { | 13 | namespace Kernel { |
| 16 | 14 | ||
| 15 | Handle g_main_thread = 0; | ||
| 17 | ObjectPool g_object_pool; | 16 | ObjectPool g_object_pool; |
| 18 | 17 | ||
| 19 | ObjectPool::ObjectPool() { | 18 | ObjectPool::ObjectPool() { |
| @@ -127,16 +126,20 @@ Object* ObjectPool::CreateByIDType(int type) { | |||
| 127 | 126 | ||
| 128 | default: | 127 | default: |
| 129 | ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type); | 128 | ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type); |
| 130 | return NULL; | 129 | return nullptr; |
| 131 | } | 130 | } |
| 132 | } | 131 | } |
| 133 | 132 | ||
| 133 | /// Initialize the kernel | ||
| 134 | void Init() { | 134 | void Init() { |
| 135 | Kernel::ThreadingInit(); | 135 | Kernel::ThreadingInit(); |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | /// Shutdown the kernel | ||
| 138 | void Shutdown() { | 139 | void Shutdown() { |
| 139 | Kernel::ThreadingShutdown(); | 140 | Kernel::ThreadingShutdown(); |
| 141 | |||
| 142 | g_object_pool.Clear(); // Free all kernel objects | ||
| 140 | } | 143 | } |
| 141 | 144 | ||
| 142 | /** | 145 | /** |
| @@ -150,7 +153,7 @@ bool LoadExec(u32 entry_point) { | |||
| 150 | Core::g_app_core->SetPC(entry_point); | 153 | Core::g_app_core->SetPC(entry_point); |
| 151 | 154 | ||
| 152 | // 0x30 is the typical main thread priority I've seen used so far | 155 | // 0x30 is the typical main thread priority I've seen used so far |
| 153 | Handle thread = Kernel::SetupMainThread(0x30); | 156 | g_main_thread = Kernel::SetupMainThread(0x30); |
| 154 | 157 | ||
| 155 | return true; | 158 | return true; |
| 156 | } | 159 | } |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 7cd79c2c4..3f15da0ac 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -11,6 +11,11 @@ typedef s32 Result; | |||
| 11 | 11 | ||
| 12 | namespace Kernel { | 12 | namespace Kernel { |
| 13 | 13 | ||
| 14 | enum KernelHandle { | ||
| 15 | CurrentThread = 0xFFFF8000, | ||
| 16 | CurrentProcess = 0xFFFF8001, | ||
| 17 | }; | ||
| 18 | |||
| 14 | enum class HandleType : u32 { | 19 | enum class HandleType : u32 { |
| 15 | Unknown = 0, | 20 | Unknown = 0, |
| 16 | Port = 1, | 21 | Port = 1, |
| @@ -39,9 +44,26 @@ class Object : NonCopyable { | |||
| 39 | public: | 44 | public: |
| 40 | virtual ~Object() {} | 45 | virtual ~Object() {} |
| 41 | Handle GetHandle() const { return handle; } | 46 | Handle GetHandle() const { return handle; } |
| 42 | virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; } | 47 | virtual const char* GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; } |
| 43 | virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; } | 48 | virtual const char* GetName() const { return "[UNKNOWN KERNEL OBJECT]"; } |
| 44 | virtual Kernel::HandleType GetHandleType() const = 0; | 49 | virtual Kernel::HandleType GetHandleType() const = 0; |
| 50 | |||
| 51 | /** | ||
| 52 | * Synchronize kernel object | ||
| 53 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 54 | * @return Result of operation, 0 on success, otherwise error code | ||
| 55 | */ | ||
| 56 | virtual Result SyncRequest(bool* wait) { | ||
| 57 | ERROR_LOG(KERNEL, "(UNIMPLEMENTED)"); | ||
| 58 | return -1; | ||
| 59 | } | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Wait for kernel object to synchronize | ||
| 63 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 64 | * @return Result of operation, 0 on success, otherwise error code | ||
| 65 | */ | ||
| 66 | virtual Result WaitSynchronization(bool* wait) = 0; | ||
| 45 | }; | 67 | }; |
| 46 | 68 | ||
| 47 | class ObjectPool : NonCopyable { | 69 | class ObjectPool : NonCopyable { |
| @@ -143,6 +165,13 @@ private: | |||
| 143 | }; | 165 | }; |
| 144 | 166 | ||
| 145 | extern ObjectPool g_object_pool; | 167 | extern ObjectPool g_object_pool; |
| 168 | extern Handle g_main_thread; | ||
| 169 | |||
| 170 | /// Initialize the kernel | ||
| 171 | void Init(); | ||
| 172 | |||
| 173 | /// Shutdown the kernel | ||
| 174 | void Shutdown(); | ||
| 146 | 175 | ||
| 147 | /** | 176 | /** |
| 148 | * Loads executable stored at specified address | 177 | * Loads executable stored at specified address |
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 019efbc78..1ccf1eb73 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -8,21 +8,51 @@ | |||
| 8 | #include "common/common.h" | 8 | #include "common/common.h" |
| 9 | 9 | ||
| 10 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 11 | #include "core/hle/kernel/mutex.h" | ||
| 11 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| 12 | 13 | ||
| 13 | namespace Kernel { | 14 | namespace Kernel { |
| 14 | 15 | ||
| 15 | class Mutex : public Object { | 16 | class Mutex : public Object { |
| 16 | public: | 17 | public: |
| 17 | const char* GetTypeName() { return "Mutex"; } | 18 | const char* GetTypeName() const { return "Mutex"; } |
| 19 | const char* GetName() const { return name.c_str(); } | ||
| 18 | 20 | ||
| 19 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; } | 21 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; } |
| 20 | Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; } | 22 | Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; } |
| 21 | 23 | ||
| 22 | bool initial_locked; ///< Initial lock state when mutex was created | 24 | bool initial_locked; ///< Initial lock state when mutex was created |
| 23 | bool locked; ///< Current locked state | 25 | bool locked; ///< Current locked state |
| 24 | Handle lock_thread; ///< Handle to thread that currently has mutex | 26 | Handle lock_thread; ///< Handle to thread that currently has mutex |
| 25 | std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex | 27 | std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex |
| 28 | std::string name; ///< Name of mutex (optional) | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Synchronize kernel object | ||
| 32 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 33 | * @return Result of operation, 0 on success, otherwise error code | ||
| 34 | */ | ||
| 35 | Result SyncRequest(bool* wait) { | ||
| 36 | // TODO(bunnei): ImplementMe | ||
| 37 | locked = true; | ||
| 38 | return 0; | ||
| 39 | } | ||
| 40 | |||
| 41 | /** | ||
| 42 | * Wait for kernel object to synchronize | ||
| 43 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 44 | * @return Result of operation, 0 on success, otherwise error code | ||
| 45 | */ | ||
| 46 | Result WaitSynchronization(bool* wait) { | ||
| 47 | // TODO(bunnei): ImplementMe | ||
| 48 | *wait = locked; | ||
| 49 | |||
| 50 | if (locked) { | ||
| 51 | Kernel::WaitCurrentThread(WAITTYPE_MUTEX); | ||
| 52 | } | ||
| 53 | |||
| 54 | return 0; | ||
| 55 | } | ||
| 26 | }; | 56 | }; |
| 27 | 57 | ||
| 28 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 58 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -70,10 +100,10 @@ bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { | |||
| 70 | bool ReleaseMutex(Mutex* mutex) { | 100 | bool ReleaseMutex(Mutex* mutex) { |
| 71 | MutexEraseLock(mutex); | 101 | MutexEraseLock(mutex); |
| 72 | bool woke_threads = false; | 102 | bool woke_threads = false; |
| 73 | auto iter = mutex->waiting_threads.begin(); | ||
| 74 | 103 | ||
| 75 | // Find the next waiting thread for the mutex... | 104 | // Find the next waiting thread for the mutex... |
| 76 | while (!woke_threads && !mutex->waiting_threads.empty()) { | 105 | while (!woke_threads && !mutex->waiting_threads.empty()) { |
| 106 | std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); | ||
| 77 | woke_threads |= ReleaseMutexForThread(mutex, *iter); | 107 | woke_threads |= ReleaseMutexForThread(mutex, *iter); |
| 78 | mutex->waiting_threads.erase(iter); | 108 | mutex->waiting_threads.erase(iter); |
| 79 | } | 109 | } |
| @@ -91,6 +121,9 @@ bool ReleaseMutex(Mutex* mutex) { | |||
| 91 | */ | 121 | */ |
| 92 | Result ReleaseMutex(Handle handle) { | 122 | Result ReleaseMutex(Handle handle) { |
| 93 | Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle); | 123 | Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle); |
| 124 | |||
| 125 | _assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!"); | ||
| 126 | |||
| 94 | if (!ReleaseMutex(mutex)) { | 127 | if (!ReleaseMutex(mutex)) { |
| 95 | return -1; | 128 | return -1; |
| 96 | } | 129 | } |
| @@ -101,12 +134,15 @@ Result ReleaseMutex(Handle handle) { | |||
| 101 | * Creates a mutex | 134 | * Creates a mutex |
| 102 | * @param handle Reference to handle for the newly created mutex | 135 | * @param handle Reference to handle for the newly created mutex |
| 103 | * @param initial_locked Specifies if the mutex should be locked initially | 136 | * @param initial_locked Specifies if the mutex should be locked initially |
| 137 | * @param name Optional name of mutex | ||
| 138 | * @return Pointer to new Mutex object | ||
| 104 | */ | 139 | */ |
| 105 | Mutex* CreateMutex(Handle& handle, bool initial_locked) { | 140 | Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) { |
| 106 | Mutex* mutex = new Mutex; | 141 | Mutex* mutex = new Mutex; |
| 107 | handle = Kernel::g_object_pool.Create(mutex); | 142 | handle = Kernel::g_object_pool.Create(mutex); |
| 108 | 143 | ||
| 109 | mutex->locked = mutex->initial_locked = initial_locked; | 144 | mutex->locked = mutex->initial_locked = initial_locked; |
| 145 | mutex->name = name; | ||
| 110 | 146 | ||
| 111 | // Acquire mutex with current thread if initialized as locked... | 147 | // Acquire mutex with current thread if initialized as locked... |
| 112 | if (mutex->locked) { | 148 | if (mutex->locked) { |
| @@ -122,10 +158,12 @@ Mutex* CreateMutex(Handle& handle, bool initial_locked) { | |||
| 122 | /** | 158 | /** |
| 123 | * Creates a mutex | 159 | * Creates a mutex |
| 124 | * @param initial_locked Specifies if the mutex should be locked initially | 160 | * @param initial_locked Specifies if the mutex should be locked initially |
| 161 | * @param name Optional name of mutex | ||
| 162 | * @return Handle to newly created object | ||
| 125 | */ | 163 | */ |
| 126 | Handle CreateMutex(bool initial_locked) { | 164 | Handle CreateMutex(bool initial_locked, const std::string& name) { |
| 127 | Handle handle; | 165 | Handle handle; |
| 128 | Mutex* mutex = CreateMutex(handle, initial_locked); | 166 | Mutex* mutex = CreateMutex(handle, initial_locked, name); |
| 129 | return handle; | 167 | return handle; |
| 130 | } | 168 | } |
| 131 | 169 | ||
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 871e2e562..7d7b5137e 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -13,14 +13,16 @@ namespace Kernel { | |||
| 13 | /** | 13 | /** |
| 14 | * Releases a mutex | 14 | * Releases a mutex |
| 15 | * @param handle Handle to mutex to release | 15 | * @param handle Handle to mutex to release |
| 16 | * @return Result of operation, 0 on success, otherwise error code | ||
| 16 | */ | 17 | */ |
| 17 | Result ReleaseMutex(Handle handle); | 18 | Result ReleaseMutex(Handle handle); |
| 18 | 19 | ||
| 19 | /** | 20 | /** |
| 20 | * Creates a mutex | 21 | * Creates a mutex |
| 21 | * @param handle Reference to handle for the newly created mutex | ||
| 22 | * @param initial_locked Specifies if the mutex should be locked initially | 22 | * @param initial_locked Specifies if the mutex should be locked initially |
| 23 | * @param name Optional name of mutex | ||
| 24 | * @return Handle to newly created object | ||
| 23 | */ | 25 | */ |
| 24 | Handle CreateMutex(bool initial_locked); | 26 | Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); |
| 25 | 27 | ||
| 26 | } // namespace | 28 | } // namespace |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index bf4c8353c..ab5a5559e 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <stdio.h> | 5 | #include <stdio.h> |
| 6 | 6 | ||
| 7 | #include <list> | 7 | #include <list> |
| 8 | #include <algorithm> | ||
| 8 | #include <vector> | 9 | #include <vector> |
| 9 | #include <map> | 10 | #include <map> |
| 10 | #include <string> | 11 | #include <string> |
| @@ -24,10 +25,10 @@ namespace Kernel { | |||
| 24 | class Thread : public Kernel::Object { | 25 | class Thread : public Kernel::Object { |
| 25 | public: | 26 | public: |
| 26 | 27 | ||
| 27 | const char* GetName() { return name; } | 28 | const char* GetName() const { return name; } |
| 28 | const char* GetTypeName() { return "Thread"; } | 29 | const char* GetTypeName() const { return "Thread"; } |
| 29 | 30 | ||
| 30 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } | 31 | static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } |
| 31 | Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; } | 32 | Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; } |
| 32 | 33 | ||
| 33 | inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } | 34 | inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } |
| @@ -36,6 +37,23 @@ public: | |||
| 36 | inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } | 37 | inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } |
| 37 | inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } | 38 | inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } |
| 38 | 39 | ||
| 40 | /** | ||
| 41 | * Wait for kernel object to synchronize | ||
| 42 | * @param wait Boolean wait set if current thread should wait as a result of sync operation | ||
| 43 | * @return Result of operation, 0 on success, otherwise error code | ||
| 44 | */ | ||
| 45 | Result WaitSynchronization(bool* wait) { | ||
| 46 | if (status != THREADSTATUS_DORMANT) { | ||
| 47 | Handle thread = GetCurrentThreadHandle(); | ||
| 48 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { | ||
| 49 | waiting_threads.push_back(thread); | ||
| 50 | } | ||
| 51 | WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle()); | ||
| 52 | *wait = true; | ||
| 53 | } | ||
| 54 | return 0; | ||
| 55 | } | ||
| 56 | |||
| 39 | ThreadContext context; | 57 | ThreadContext context; |
| 40 | 58 | ||
| 41 | u32 status; | 59 | u32 status; |
| @@ -49,6 +67,9 @@ public: | |||
| 49 | s32 processor_id; | 67 | s32 processor_id; |
| 50 | 68 | ||
| 51 | WaitType wait_type; | 69 | WaitType wait_type; |
| 70 | Handle wait_handle; | ||
| 71 | |||
| 72 | std::vector<Handle> waiting_threads; | ||
| 52 | 73 | ||
| 53 | char name[Kernel::MAX_NAME_LENGTH + 1]; | 74 | char name[Kernel::MAX_NAME_LENGTH + 1]; |
| 54 | }; | 75 | }; |
| @@ -62,7 +83,6 @@ Common::ThreadQueueList<Handle> g_thread_ready_queue; | |||
| 62 | Handle g_current_thread_handle; | 83 | Handle g_current_thread_handle; |
| 63 | Thread* g_current_thread; | 84 | Thread* g_current_thread; |
| 64 | 85 | ||
| 65 | |||
| 66 | /// Gets the current thread | 86 | /// Gets the current thread |
| 67 | inline Thread* GetCurrentThread() { | 87 | inline Thread* GetCurrentThread() { |
| 68 | return g_current_thread; | 88 | return g_current_thread; |
| @@ -94,15 +114,15 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | |||
| 94 | memset(&t->context, 0, sizeof(ThreadContext)); | 114 | memset(&t->context, 0, sizeof(ThreadContext)); |
| 95 | 115 | ||
| 96 | t->context.cpu_registers[0] = arg; | 116 | t->context.cpu_registers[0] = arg; |
| 97 | t->context.pc = t->entry_point; | 117 | t->context.pc = t->context.reg_15 = t->entry_point; |
| 98 | t->context.sp = t->stack_top; | 118 | t->context.sp = t->stack_top; |
| 99 | t->context.cpsr = 0x1F; // Usermode | 119 | t->context.cpsr = 0x1F; // Usermode |
| 100 | 120 | ||
| 101 | if (t->current_priority < lowest_priority) { | 121 | if (t->current_priority < lowest_priority) { |
| 102 | t->current_priority = t->initial_priority; | 122 | t->current_priority = t->initial_priority; |
| 103 | } | 123 | } |
| 104 | |||
| 105 | t->wait_type = WAITTYPE_NONE; | 124 | t->wait_type = WAITTYPE_NONE; |
| 125 | t->wait_handle = 0; | ||
| 106 | } | 126 | } |
| 107 | 127 | ||
| 108 | /// Change a thread to "ready" state | 128 | /// Change a thread to "ready" state |
| @@ -122,6 +142,37 @@ void ChangeReadyState(Thread* t, bool ready) { | |||
| 122 | } | 142 | } |
| 123 | } | 143 | } |
| 124 | 144 | ||
| 145 | /// Verify that a thread has not been released from waiting | ||
| 146 | inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) { | ||
| 147 | Thread* thread = g_object_pool.GetFast<Thread>(handle); | ||
| 148 | _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); | ||
| 149 | |||
| 150 | if (type != thread->wait_type || wait_handle != thread->wait_handle) | ||
| 151 | return false; | ||
| 152 | |||
| 153 | return true; | ||
| 154 | } | ||
| 155 | |||
| 156 | /// Stops the current thread | ||
| 157 | void StopThread(Handle handle, const char* reason) { | ||
| 158 | Thread* thread = g_object_pool.GetFast<Thread>(handle); | ||
| 159 | _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); | ||
| 160 | |||
| 161 | ChangeReadyState(thread, false); | ||
| 162 | thread->status = THREADSTATUS_DORMANT; | ||
| 163 | for (size_t i = 0; i < thread->waiting_threads.size(); ++i) { | ||
| 164 | const Handle waiting_thread = thread->waiting_threads[i]; | ||
| 165 | if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { | ||
| 166 | ResumeThreadFromWait(waiting_thread); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | thread->waiting_threads.clear(); | ||
| 170 | |||
| 171 | // Stopped threads are never waiting. | ||
| 172 | thread->wait_type = WAITTYPE_NONE; | ||
| 173 | thread->wait_handle = 0; | ||
| 174 | } | ||
| 175 | |||
| 125 | /// Changes a threads state | 176 | /// Changes a threads state |
| 126 | void ChangeThreadState(Thread* t, ThreadStatus new_status) { | 177 | void ChangeThreadState(Thread* t, ThreadStatus new_status) { |
| 127 | if (!t || t->status == new_status) { | 178 | if (!t || t->status == new_status) { |
| @@ -132,7 +183,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) { | |||
| 132 | 183 | ||
| 133 | if (new_status == THREADSTATUS_WAIT) { | 184 | if (new_status == THREADSTATUS_WAIT) { |
| 134 | if (t->wait_type == WAITTYPE_NONE) { | 185 | if (t->wait_type == WAITTYPE_NONE) { |
| 135 | printf("ERROR: Waittype none not allowed here\n"); | 186 | ERROR_LOG(KERNEL, "Waittype none not allowed"); |
| 136 | } | 187 | } |
| 137 | } | 188 | } |
| 138 | } | 189 | } |
| @@ -166,7 +217,7 @@ void SwitchContext(Thread* t) { | |||
| 166 | t->wait_type = WAITTYPE_NONE; | 217 | t->wait_type = WAITTYPE_NONE; |
| 167 | LoadContext(t->context); | 218 | LoadContext(t->context); |
| 168 | } else { | 219 | } else { |
| 169 | SetCurrentThread(NULL); | 220 | SetCurrentThread(nullptr); |
| 170 | } | 221 | } |
| 171 | } | 222 | } |
| 172 | 223 | ||
| @@ -181,26 +232,43 @@ Thread* NextThread() { | |||
| 181 | next = g_thread_ready_queue.pop_first(); | 232 | next = g_thread_ready_queue.pop_first(); |
| 182 | } | 233 | } |
| 183 | if (next == 0) { | 234 | if (next == 0) { |
| 184 | return NULL; | 235 | return nullptr; |
| 185 | } | 236 | } |
| 186 | return Kernel::g_object_pool.GetFast<Thread>(next); | 237 | return Kernel::g_object_pool.GetFast<Thread>(next); |
| 187 | } | 238 | } |
| 188 | 239 | ||
| 189 | /// Puts the current thread in the wait state for the given type | 240 | /// Puts the current thread in the wait state for the given type |
| 190 | void WaitCurrentThread(WaitType wait_type) { | 241 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { |
| 191 | Thread* t = GetCurrentThread(); | 242 | Thread* thread = GetCurrentThread(); |
| 192 | t->wait_type = wait_type; | 243 | thread->wait_type = wait_type; |
| 193 | ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); | 244 | thread->wait_handle = wait_handle; |
| 245 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | ||
| 194 | } | 246 | } |
| 195 | 247 | ||
| 196 | /// Resumes a thread from waiting by marking it as "ready" | 248 | /// Resumes a thread from waiting by marking it as "ready" |
| 197 | void ResumeThreadFromWait(Handle handle) { | 249 | void ResumeThreadFromWait(Handle handle) { |
| 198 | u32 error; | 250 | u32 error; |
| 199 | Thread* t = Kernel::g_object_pool.Get<Thread>(handle, error); | 251 | Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error); |
| 200 | if (t) { | 252 | if (thread) { |
| 201 | t->status &= ~THREADSTATUS_WAIT; | 253 | thread->status &= ~THREADSTATUS_WAIT; |
| 202 | if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | 254 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { |
| 203 | ChangeReadyState(t, true); | 255 | ChangeReadyState(thread, true); |
| 256 | } | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | /// Prints the thread queue for debugging purposes | ||
| 261 | void DebugThreadQueue() { | ||
| 262 | Thread* thread = GetCurrentThread(); | ||
| 263 | if (!thread) { | ||
| 264 | return; | ||
| 265 | } | ||
| 266 | INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); | ||
| 267 | for (u32 i = 0; i < g_thread_queue.size(); i++) { | ||
| 268 | Handle handle = g_thread_queue[i]; | ||
| 269 | s32 priority = g_thread_ready_queue.contains(handle); | ||
| 270 | if (priority != -1) { | ||
| 271 | INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle); | ||
| 204 | } | 272 | } |
| 205 | } | 273 | } |
| 206 | } | 274 | } |
| @@ -212,32 +280,34 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio | |||
| 212 | _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), | 280 | _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), |
| 213 | "CreateThread priority=%d, outside of allowable range!", priority) | 281 | "CreateThread priority=%d, outside of allowable range!", priority) |
| 214 | 282 | ||
| 215 | Thread* t = new Thread; | 283 | Thread* thread = new Thread; |
| 216 | 284 | ||
| 217 | handle = Kernel::g_object_pool.Create(t); | 285 | handle = Kernel::g_object_pool.Create(thread); |
| 218 | 286 | ||
| 219 | g_thread_queue.push_back(handle); | 287 | g_thread_queue.push_back(handle); |
| 220 | g_thread_ready_queue.prepare(priority); | 288 | g_thread_ready_queue.prepare(priority); |
| 221 | 289 | ||
| 222 | t->status = THREADSTATUS_DORMANT; | 290 | thread->status = THREADSTATUS_DORMANT; |
| 223 | t->entry_point = entry_point; | 291 | thread->entry_point = entry_point; |
| 224 | t->stack_top = stack_top; | 292 | thread->stack_top = stack_top; |
| 225 | t->stack_size = stack_size; | 293 | thread->stack_size = stack_size; |
| 226 | t->initial_priority = t->current_priority = priority; | 294 | thread->initial_priority = thread->current_priority = priority; |
| 227 | t->processor_id = processor_id; | 295 | thread->processor_id = processor_id; |
| 228 | t->wait_type = WAITTYPE_NONE; | 296 | thread->wait_type = WAITTYPE_NONE; |
| 229 | 297 | thread->wait_handle = 0; | |
| 230 | strncpy(t->name, name, Kernel::MAX_NAME_LENGTH); | 298 | |
| 231 | t->name[Kernel::MAX_NAME_LENGTH] = '\0'; | 299 | strncpy(thread->name, name, Kernel::MAX_NAME_LENGTH); |
| 232 | 300 | thread->name[Kernel::MAX_NAME_LENGTH] = '\0'; | |
| 233 | return t; | 301 | |
| 302 | return thread; | ||
| 234 | } | 303 | } |
| 235 | 304 | ||
| 236 | /// Creates a new thread - wrapper for external user | 305 | /// Creates a new thread - wrapper for external user |
| 237 | Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, | 306 | Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, |
| 238 | u32 stack_top, int stack_size) { | 307 | u32 stack_top, int stack_size) { |
| 239 | if (name == NULL) { | 308 | |
| 240 | ERROR_LOG(KERNEL, "CreateThread(): NULL name"); | 309 | if (name == nullptr) { |
| 310 | ERROR_LOG(KERNEL, "CreateThread(): nullptr name"); | ||
| 241 | return -1; | 311 | return -1; |
| 242 | } | 312 | } |
| 243 | if ((u32)stack_size < 0x200) { | 313 | if ((u32)stack_size < 0x200) { |
| @@ -258,20 +328,56 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 | |||
| 258 | return -1; | 328 | return -1; |
| 259 | } | 329 | } |
| 260 | Handle handle; | 330 | Handle handle; |
| 261 | Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, | 331 | Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, |
| 262 | stack_size); | 332 | stack_size); |
| 263 | 333 | ||
| 264 | ResetThread(t, arg, 0); | 334 | ResetThread(thread, arg, 0); |
| 335 | CallThread(thread); | ||
| 336 | |||
| 337 | return handle; | ||
| 338 | } | ||
| 265 | 339 | ||
| 266 | HLE::EatCycles(32000); | 340 | /// Get the priority of the thread specified by handle |
| 341 | u32 GetThreadPriority(const Handle handle) { | ||
| 342 | Thread* thread = g_object_pool.GetFast<Thread>(handle); | ||
| 343 | _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); | ||
| 344 | return thread->current_priority; | ||
| 345 | } | ||
| 267 | 346 | ||
| 268 | // This won't schedule to the new thread, but it may to one woken from eating cycles. | 347 | /// Set the priority of the thread specified by handle |
| 269 | // Technically, this should not eat all at once, and reschedule in the middle, but that's hard. | 348 | Result SetThreadPriority(Handle handle, s32 priority) { |
| 270 | HLE::ReSchedule("thread created"); | 349 | Thread* thread = nullptr; |
| 350 | if (!handle) { | ||
| 351 | thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? | ||
| 352 | } else { | ||
| 353 | thread = g_object_pool.GetFast<Thread>(handle); | ||
| 354 | } | ||
| 355 | _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); | ||
| 271 | 356 | ||
| 272 | CallThread(t); | 357 | // If priority is invalid, clamp to valid range |
| 273 | 358 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | |
| 274 | return handle; | 359 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); |
| 360 | WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority); | ||
| 361 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | ||
| 362 | // validity of this | ||
| 363 | priority = new_priority; | ||
| 364 | } | ||
| 365 | |||
| 366 | // Change thread priority | ||
| 367 | s32 old = thread->current_priority; | ||
| 368 | g_thread_ready_queue.remove(old, handle); | ||
| 369 | thread->current_priority = priority; | ||
| 370 | g_thread_ready_queue.prepare(thread->current_priority); | ||
| 371 | |||
| 372 | // Change thread status to "ready" and push to ready queue | ||
| 373 | if (thread->IsRunning()) { | ||
| 374 | thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; | ||
| 375 | } | ||
| 376 | if (thread->IsReady()) { | ||
| 377 | g_thread_ready_queue.push_back(thread->current_priority, handle); | ||
| 378 | } | ||
| 379 | |||
| 380 | return 0; | ||
| 275 | } | 381 | } |
| 276 | 382 | ||
| 277 | /// Sets up the primary application thread | 383 | /// Sets up the primary application thread |
| @@ -279,10 +385,10 @@ Handle SetupMainThread(s32 priority, int stack_size) { | |||
| 279 | Handle handle; | 385 | Handle handle; |
| 280 | 386 | ||
| 281 | // Initialize new "main" thread | 387 | // Initialize new "main" thread |
| 282 | Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, | 388 | Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, |
| 283 | THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); | 389 | THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); |
| 284 | 390 | ||
| 285 | ResetThread(t, 0, 0); | 391 | ResetThread(thread, 0, 0); |
| 286 | 392 | ||
| 287 | // If running another thread already, set it to "ready" state | 393 | // If running another thread already, set it to "ready" state |
| 288 | Thread* cur = GetCurrentThread(); | 394 | Thread* cur = GetCurrentThread(); |
| @@ -291,24 +397,31 @@ Handle SetupMainThread(s32 priority, int stack_size) { | |||
| 291 | } | 397 | } |
| 292 | 398 | ||
| 293 | // Run new "main" thread | 399 | // Run new "main" thread |
| 294 | SetCurrentThread(t); | 400 | SetCurrentThread(thread); |
| 295 | t->status = THREADSTATUS_RUNNING; | 401 | thread->status = THREADSTATUS_RUNNING; |
| 296 | LoadContext(t->context); | 402 | LoadContext(thread->context); |
| 297 | 403 | ||
| 298 | return handle; | 404 | return handle; |
| 299 | } | 405 | } |
| 300 | 406 | ||
| 407 | |||
| 301 | /// Reschedules to the next available thread (call after current thread is suspended) | 408 | /// Reschedules to the next available thread (call after current thread is suspended) |
| 302 | void Reschedule() { | 409 | void Reschedule() { |
| 303 | Thread* prev = GetCurrentThread(); | 410 | Thread* prev = GetCurrentThread(); |
| 304 | Thread* next = NextThread(); | 411 | Thread* next = NextThread(); |
| 412 | HLE::g_reschedule = false; | ||
| 305 | if (next > 0) { | 413 | if (next > 0) { |
| 414 | INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); | ||
| 415 | |||
| 306 | SwitchContext(next); | 416 | SwitchContext(next); |
| 307 | 417 | ||
| 308 | // Hack - automatically change previous thread (which would have been in "wait" state) to | 418 | // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep |
| 309 | // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to | 419 | // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. |
| 310 | // actually wait for whatever event it is supposed to be waiting on. | 420 | // This results in the current thread yielding on a VBLANK once, and then it will be |
| 311 | ChangeReadyState(prev, true); | 421 | // immediately placed back in the queue for execution. |
| 422 | if (prev->wait_type == WAITTYPE_VBLANK) { | ||
| 423 | ResumeThreadFromWait(prev->GetHandle()); | ||
| 424 | } | ||
| 312 | } | 425 | } |
| 313 | } | 426 | } |
| 314 | 427 | ||
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 9628f165d..04914ba90 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -34,7 +34,7 @@ enum WaitType { | |||
| 34 | WAITTYPE_NONE, | 34 | WAITTYPE_NONE, |
| 35 | WAITTYPE_SLEEP, | 35 | WAITTYPE_SLEEP, |
| 36 | WAITTYPE_SEMA, | 36 | WAITTYPE_SEMA, |
| 37 | WAITTYPE_EVENTFLAG, | 37 | WAITTYPE_EVENT, |
| 38 | WAITTYPE_THREADEND, | 38 | WAITTYPE_THREADEND, |
| 39 | WAITTYPE_VBLANK, | 39 | WAITTYPE_VBLANK, |
| 40 | WAITTYPE_MUTEX, | 40 | WAITTYPE_MUTEX, |
| @@ -53,8 +53,8 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); | |||
| 53 | /// Reschedules to the next available thread (call after current thread is suspended) | 53 | /// Reschedules to the next available thread (call after current thread is suspended) |
| 54 | void Reschedule(); | 54 | void Reschedule(); |
| 55 | 55 | ||
| 56 | /// Puts the current thread in the wait state for the given type | 56 | /// Stops the current thread |
| 57 | void WaitCurrentThread(WaitType wait_type); | 57 | void StopThread(Handle thread, const char* reason); |
| 58 | 58 | ||
| 59 | /// Resumes a thread from waiting by marking it as "ready" | 59 | /// Resumes a thread from waiting by marking it as "ready" |
| 60 | void ResumeThreadFromWait(Handle handle); | 60 | void ResumeThreadFromWait(Handle handle); |
| @@ -62,9 +62,18 @@ void ResumeThreadFromWait(Handle handle); | |||
| 62 | /// Gets the current thread handle | 62 | /// Gets the current thread handle |
| 63 | Handle GetCurrentThreadHandle(); | 63 | Handle GetCurrentThreadHandle(); |
| 64 | 64 | ||
| 65 | /// Puts the current thread in the wait state for the given type | ||
| 66 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); | ||
| 67 | |||
| 65 | /// Put current thread in a wait state - on WaitSynchronization | 68 | /// Put current thread in a wait state - on WaitSynchronization |
| 66 | void WaitThread_Synchronization(); | 69 | void WaitThread_Synchronization(); |
| 67 | 70 | ||
| 71 | /// Get the priority of the thread specified by handle | ||
| 72 | u32 GetThreadPriority(const Handle handle); | ||
| 73 | |||
| 74 | /// Set the priority of the thread specified by handle | ||
| 75 | Result SetThreadPriority(Handle handle, s32 priority); | ||
| 76 | |||
| 68 | /// Initialize threading | 77 | /// Initialize threading |
| 69 | void ThreadingInit(); | 78 | void ThreadingInit(); |
| 70 | 79 | ||