diff options
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r-- | src/core/hle/kernel/address_arbiter.cpp | 67 | ||||
-rw-r--r-- | src/core/hle/kernel/address_arbiter.h | 28 | ||||
-rw-r--r-- | src/core/hle/kernel/event.cpp | 146 | ||||
-rw-r--r-- | src/core/hle/kernel/event.h | 64 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.cpp | 45 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.h | 108 | ||||
-rw-r--r-- | src/core/hle/kernel/mutex.cpp | 192 | ||||
-rw-r--r-- | src/core/hle/kernel/mutex.h | 55 | ||||
-rw-r--r-- | src/core/hle/kernel/semaphore.cpp | 81 | ||||
-rw-r--r-- | src/core/hle/kernel/semaphore.h | 58 | ||||
-rw-r--r-- | src/core/hle/kernel/session.cpp | 13 | ||||
-rw-r--r-- | src/core/hle/kernel/session.h | 17 | ||||
-rw-r--r-- | src/core/hle/kernel/shared_memory.cpp | 70 | ||||
-rw-r--r-- | src/core/hle/kernel/shared_memory.h | 61 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.cpp | 226 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.h | 98 | ||||
-rw-r--r-- | src/core/hle/kernel/timer.cpp | 124 | ||||
-rw-r--r-- | src/core/hle/kernel/timer.h | 73 |
18 files changed, 751 insertions, 775 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 62e3460e1..42f8ce2d9 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -15,53 +15,64 @@ namespace Kernel { -class AddressArbiter : public Object { -public: - std::string GetTypeName() const override { return "Arbiter"; } - std::string GetName() const override { return name; } +AddressArbiter::AddressArbiter() {} +AddressArbiter::~AddressArbiter() {} - static const HandleType HANDLE_TYPE = HandleType::AddressArbiter; - HandleType GetHandleType() const override { return HANDLE_TYPE; } +SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) { + SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter); - std::string name; ///< Name of address arbiter object (optional) -}; + address_arbiter->name = std::move(name); -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// Arbitrate an address -ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) { - Object* object = Kernel::g_handle_table.GetGeneric(handle).get(); - if (object == nullptr) - return InvalidHandle(ErrorModule::Kernel); + return address_arbiter; +} +ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, + u64 nanoseconds) { switch (type) { // Signal thread(s) waiting for arbitrate address... case ArbitrationType::Signal: // Negative value means resume all threads if (value < 0) { - ArbitrateAllThreads(object, address); + ArbitrateAllThreads(address); } else { // Resume first N threads for(int i = 0; i < value; i++) - ArbitrateHighestPriorityThread(object, address); + ArbitrateHighestPriorityThread(address); } break; // Wait current thread (acquire the arbiter)... case ArbitrationType::WaitIfLessThan: if ((s32)Memory::Read32(address) <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); + Kernel::WaitCurrentThread_ArbitrateAddress(address); + HLE::Reschedule(__func__); + } + break; + case ArbitrationType::WaitIfLessThanWithTimeout: + if ((s32)Memory::Read32(address) <= value) { + Kernel::WaitCurrentThread_ArbitrateAddress(address); + GetCurrentThread()->WakeAfterDelay(nanoseconds); HLE::Reschedule(__func__); } break; - case ArbitrationType::DecrementAndWaitIfLessThan: { s32 memory_value = Memory::Read32(address) - 1; Memory::Write32(address, memory_value); if (memory_value <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); + Kernel::WaitCurrentThread_ArbitrateAddress(address); + HLE::Reschedule(__func__); + } + break; + } + case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: + { + s32 memory_value = Memory::Read32(address) - 1; + Memory::Write32(address, memory_value); + if (memory_value <= value) { + Kernel::WaitCurrentThread_ArbitrateAddress(address); + GetCurrentThread()->WakeAfterDelay(nanoseconds); HLE::Reschedule(__func__); } break; @@ -74,20 +85,4 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 return RESULT_SUCCESS; } -/// Create an address arbiter -AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) { - AddressArbiter* address_arbiter = new AddressArbiter; - // TOOD(yuriks): Fix error reporting - handle = Kernel::g_handle_table.Create(address_arbiter).ValueOr(INVALID_HANDLE); - address_arbiter->name = name; - return address_arbiter; -} - -/// Create an address arbiter -Handle CreateAddressArbiter(const std::string& name) { - Handle handle; - CreateAddressArbiter(handle, name); - return handle; -} - } // namespace Kernel diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 030e7ad7b..8f6a1a8df 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -18,7 +18,6 @@ namespace Kernel { -/// Address arbitration types enum class ArbitrationType : u32 { Signal, WaitIfLessThan, @@ -27,10 +26,29 @@ enum class ArbitrationType : u32 { DecrementAndWaitIfLessThanWithTimeout, }; -/// Arbitrate an address -ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value); +class AddressArbiter final : public Object { +public: + /** + * Creates an address arbiter. + * + * @param name Optional name used for debugging. + * @returns The created AddressArbiter. + */ + static SharedPtr<AddressArbiter> Create(std::string name = "Unknown"); -/// Create an address arbiter -Handle CreateAddressArbiter(const std::string& name = "Unknown"); + std::string GetTypeName() const override { return "Arbiter"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::AddressArbiter; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + std::string name; ///< Name of address arbiter object (optional) + + ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds); + +private: + AddressArbiter(); + ~AddressArbiter() override; +}; } // namespace FileSys diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 271190dbe..898e1c98f 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -14,144 +14,38 @@ namespace Kernel { -class Event : public Object { -public: - std::string GetTypeName() const override { return "Event"; } - std::string GetName() const override { return name; } +Event::Event() {} +Event::~Event() {} - static const HandleType HANDLE_TYPE = HandleType::Event; - HandleType GetHandleType() const override { return HANDLE_TYPE; } +SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) { + SharedPtr<Event> evt(new Event); - ResetType intitial_reset_type; ///< ResetType specified at Event initialization - ResetType reset_type; ///< Current ResetType - - bool locked; ///< Event signal wait - bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough) - std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event - std::string name; ///< Name of event (optional) - - ResultVal<bool> WaitSynchronization() override { - bool wait = locked; - if (locked) { - Handle thread = GetCurrentThread()->GetHandle(); - if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { - waiting_threads.push_back(thread); - } - Kernel::WaitCurrentThread(WAITTYPE_EVENT, this); - } - if (reset_type != RESETTYPE_STICKY && !permanent_locked) { - locked = true; - } - return MakeResult<bool>(wait); - } -}; - -/** - * Hackish function to set an events permanent lock state, used to pass through synch blocks - * @param handle Handle to event to change - * @param permanent_locked Boolean permanent locked value to set event - * @return Result of operation, 0 on success, otherwise error code - */ -ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { - Event* evt = g_handle_table.Get<Event>(handle).get(); - if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); - - evt->permanent_locked = permanent_locked; - return RESULT_SUCCESS; -} - -/** - * Changes whether an event is locked or not - * @param handle Handle to event to change - * @param locked Boolean locked value to set event - * @return Result of operation, 0 on success, otherwise error code - */ -ResultCode SetEventLocked(const Handle handle, const bool locked) { - Event* evt = g_handle_table.Get<Event>(handle).get(); - if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); + evt->signaled = false; + evt->reset_type = evt->intitial_reset_type = reset_type; + evt->name = std::move(name); - if (!evt->permanent_locked) { - evt->locked = locked; - } - return RESULT_SUCCESS; + return evt; } -/** - * Signals an event - * @param handle Handle to event to signal - * @return Result of operation, 0 on success, otherwise error code - */ -ResultCode SignalEvent(const Handle handle) { - Event* evt = g_handle_table.Get<Event>(handle).get(); - if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); - - // Resume threads waiting for event to signal - bool event_caught = false; - for (size_t i = 0; i < evt->waiting_threads.size(); ++i) { - Thread* thread = Kernel::g_handle_table.Get<Thread>(evt->waiting_threads[i]).get(); - if (thread != nullptr) - thread->ResumeFromWait(); - - // If any thread is signalled awake by this event, assume the event was "caught" and reset - // the event. This will result in the next thread waiting on the event to block. Otherwise, - // the event will not be reset, and the next thread to call WaitSynchronization on it will - // not block. Not sure if this is correct behavior, but it seems to work. - event_caught = true; - } - evt->waiting_threads.clear(); - - if (!evt->permanent_locked) { - evt->locked = event_caught; - } - return RESULT_SUCCESS; +bool Event::ShouldWait() { + return !signaled; } -/** - * Clears an event - * @param handle Handle to event to clear - * @return Result of operation, 0 on success, otherwise error code - */ -ResultCode ClearEvent(Handle handle) { - Event* evt = g_handle_table.Get<Event>(handle).get(); - if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); +void Event::Acquire() { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); - if (!evt->permanent_locked) { - evt->locked = true; - } - return RESULT_SUCCESS; + // Release the event if it's not sticky... + if (reset_type != RESETTYPE_STICKY) + signaled = false; } -/** - * Creates an event - * @param handle Reference to handle for the newly created mutex - * @param reset_type ResetType describing how to create event - * @param name Optional name of event - * @return Newly created Event object - */ -Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) { - Event* evt = new Event; - - // TOOD(yuriks): Fix error reporting - handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE); - - evt->locked = true; - evt->permanent_locked = false; - evt->reset_type = evt->intitial_reset_type = reset_type; - evt->name = name; - - return evt; +void Event::Signal() { + signaled = true; + WakeupAllWaitingThreads(); } -/** - * Creates an event - * @param reset_type ResetType describing how to create event - * @param name Optional name of event - * @return Handle to newly created Event object - */ -Handle CreateEvent(const ResetType reset_type, const std::string& name) { - Handle handle; - Event* evt = CreateEvent(handle, reset_type, name); - return handle; +void Event::Clear() { + signaled = false; } } // namespace diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index da793df1a..fba960d2a 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -11,38 +11,36 @@ namespace Kernel { -/** - * Changes whether an event is locked or not - * @param handle Handle to event to change - * @param locked Boolean locked value to set event - */ -ResultCode SetEventLocked(const Handle handle, const bool locked); - -/** - * Hackish function to set an events permanent lock state, used to pass through synch blocks - * @param handle Handle to event to change - * @param permanent_locked Boolean permanent locked value to set event - */ -ResultCode SetPermanentLock(Handle handle, const bool permanent_locked); - -/** - * Signals an event - * @param handle Handle to event to signal - */ -ResultCode SignalEvent(const Handle handle); - -/** - * Clears an event - * @param handle Handle to event to clear - */ -ResultCode ClearEvent(Handle handle); - -/** - * Creates an event - * @param reset_type ResetType describing how to create event - * @param name Optional name of event - * @return Handle to newly created Event object - */ -Handle CreateEvent(const ResetType reset_type, const std::string& name="Unknown"); +class Event final : public WaitObject { +public: + /** + * Creates an event + * @param reset_type ResetType describing how to create event + * @param name Optional name of event + */ + static SharedPtr<Event> Create(ResetType reset_type, std::string name = "Unknown"); + + std::string GetTypeName() const override { return "Event"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::Event; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + ResetType intitial_reset_type; ///< ResetType specified at Event initialization + ResetType reset_type; ///< Current ResetType + + bool signaled; ///< Whether the event has already been signaled + std::string name; ///< Name of event (optional) + + bool ShouldWait() override; + void Acquire() override; + + void Signal(); + void Clear(); + +private: + Event(); + ~Event() override; +}; } // namespace diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index d3684896f..52dca4dd8 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -14,10 +14,47 @@ namespace Kernel { +unsigned int Object::next_object_id = 0; + SharedPtr<Thread> g_main_thread = nullptr; HandleTable g_handle_table; u64 g_program_id = 0; +void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { + auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); + if (itr == waiting_threads.end()) + waiting_threads.push_back(std::move(thread)); +} + +void WaitObject::RemoveWaitingThread(Thread* thread) { + auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); + if (itr != waiting_threads.end()) + waiting_threads.erase(itr); +} + +SharedPtr<Thread> WaitObject::WakeupNextThread() { + if (waiting_threads.empty()) + return nullptr; + + auto next_thread = std::move(waiting_threads.front()); + waiting_threads.erase(waiting_threads.begin()); + + next_thread->ReleaseWaitObject(this); + + return next_thread; +} + +void WaitObject::WakeupAllWaitingThreads() { + auto waiting_threads_copy = waiting_threads; + + // We use a copy because ReleaseWaitObject will remove the thread from this object's + // waiting_threads list + for (auto thread : waiting_threads_copy) + thread->ReleaseWaitObject(this); + + _assert_msg_(Kernel, waiting_threads.empty(), "failed to awaken all waiting threads!"); +} + HandleTable::HandleTable() { next_generation = 1; Clear(); @@ -39,13 +76,10 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { // CTR-OS doesn't use generation 0, so skip straight to 1. if (next_generation >= (1 << 15)) next_generation = 1; - Handle handle = generation | (slot << 15); - if (obj->handle == INVALID_HANDLE) - obj->handle = handle; - generations[slot] = generation; objects[slot] = std::move(obj); + Handle handle = generation | (slot << 15); return MakeResult<Handle>(handle); } @@ -63,11 +97,10 @@ ResultCode HandleTable::Close(Handle handle) { return ERR_INVALID_HANDLE; size_t slot = GetSlot(handle); - u16 generation = GetGeneration(handle); objects[slot] = nullptr; - generations[generation] = next_free_slot; + generations[slot] = next_free_slot; next_free_slot = slot; return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 5e5217b78..4d8e388b6 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -8,12 +8,19 @@ #include <array> #include <string> +#include <vector> + #include "common/common.h" #include "core/hle/result.h" typedef u32 Handle; typedef s32 Result; +// TODO: It would be nice to eventually replace these with strong types that prevent accidental +// conversion between each other. +typedef u32 VAddr; ///< Represents a pointer in the userspace virtual address space. +typedef u32 PAddr; ///< Represents a pointer in the ARM11 physical address space. + const Handle INVALID_HANDLE = 0; namespace Kernel { @@ -24,7 +31,8 @@ class Thread; const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel, ErrorSummary::OutOfResource, ErrorLevel::Temporary); // TOOD: Verify code -const ResultCode ERR_INVALID_HANDLE = InvalidHandle(ErrorModule::Kernel); +const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); enum KernelHandle : Handle { CurrentThread = 0xFFFF8000, @@ -50,32 +58,51 @@ enum { DEFAULT_STACK_SIZE = 0x4000, }; -class HandleTable; - class Object : NonCopyable { - friend class HandleTable; - u32 handle = INVALID_HANDLE; public: virtual ~Object() {} - Handle GetHandle() const { return handle; } + + /// Returns a unique identifier for the object. For debugging purposes only. + unsigned int GetObjectId() const { return object_id; } + virtual std::string GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; } virtual std::string GetName() const { return "[UNKNOWN KERNEL OBJECT]"; } virtual Kernel::HandleType GetHandleType() const = 0; /** - * Wait for kernel object to synchronize. - * @return True if the current thread should wait as a result of the wait + * Check if a thread can wait on the object + * @return True if a thread can wait on the object, otherwise false */ - virtual ResultVal<bool> WaitSynchronization() { - LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); - return UnimplementedFunction(ErrorModule::Kernel); + bool IsWaitable() const { + switch (GetHandleType()) { + case HandleType::Session: + case HandleType::Event: + case HandleType::Mutex: + case HandleType::Thread: + case HandleType::Semaphore: + case HandleType::Timer: + return true; + + case HandleType::Unknown: + case HandleType::Port: + case HandleType::SharedMemory: + case HandleType::Redirection: + case HandleType::Process: + case HandleType::AddressArbiter: + return false; + } + + return false; } private: friend void intrusive_ptr_add_ref(Object*); friend void intrusive_ptr_release(Object*); + static unsigned int next_object_id; + unsigned int ref_count = 0; + unsigned int object_id = next_object_id++; }; // Special functions used by boost::instrusive_ptr to do automatic ref-counting @@ -92,6 +119,45 @@ inline void intrusive_ptr_release(Object* object) { template <typename T> using SharedPtr = boost::intrusive_ptr<T>; +/// Class that represents a Kernel object that a thread can be waiting on +class WaitObject : public Object { +public: + + /** + * Check if the current thread should wait until the object is available + * @return True if the current thread should wait due to this object being unavailable + */ + virtual bool ShouldWait() = 0; + + /// Acquire/lock the object if it is available + virtual void Acquire() = 0; + + /** + * Add a thread to wait on this object + * @param thread Pointer to thread to add + */ + void AddWaitingThread(SharedPtr<Thread> thread); + + /** + * Removes a thread from waiting on this object (e.g. if it was resumed already) + * @param thread Pointer to thread to remove + */ + void RemoveWaitingThread(Thread* thread); + + /** + * Wake up the next thread waiting on this object + * @return Pointer to the thread that was resumed, nullptr if no threads are waiting + */ + SharedPtr<Thread> WakeupNextThread(); + + /// Wake up all threads waiting on this object + void WakeupAllWaitingThreads(); + +private: + /// Threads waiting for this object to become available + std::vector<SharedPtr<Thread>> waiting_threads; +}; + /** * This class allows the creation of Handles, which are references to objects that can be tested * for validity and looked up. Here they are used to pass references to kernel objects to/from the @@ -146,14 +212,14 @@ public: /** * Looks up a handle. - * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid. + * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid. */ SharedPtr<Object> GetGeneric(Handle handle) const; /** * Looks up a handle while verifying its type. - * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid or its - * type differs from the handle type `T::HANDLE_TYPE`. + * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its + * type differs from the handle type `T::HANDLE_TYPE`. */ template <class T> SharedPtr<T> Get(Handle handle) const { @@ -164,6 +230,19 @@ public: return nullptr; } + /** + * Looks up a handle while verifying that it is an object that a thread can wait on + * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or it is + * not a waitable object. + */ + SharedPtr<WaitObject> GetWaitObject(Handle handle) const { + SharedPtr<Object> object = GetGeneric(handle); + if (object != nullptr && object->IsWaitable()) { + return boost::static_pointer_cast<WaitObject>(std::move(object)); + } + return nullptr; + } + /// Closes all handles held in this table. void Clear(); @@ -197,7 +276,6 @@ private: }; extern HandleTable g_handle_table; -extern SharedPtr<Thread> g_main_thread; /// The ID code of the currently running game /// TODO(Subv): This variable should not be here, diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 853a5dd74..9f7166ca4 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -5,6 +5,8 @@ #include <map> #include <vector> +#include <boost/range/algorithm_ext/erase.hpp> + #include "common/common.h" #include "core/hle/kernel/kernel.h" @@ -13,176 +15,72 @@ namespace Kernel { -class Mutex : public Object { -public: - std::string GetTypeName() const override { return "Mutex"; } - std::string GetName() const override { return name; } - - static const HandleType HANDLE_TYPE = HandleType::Mutex; - HandleType GetHandleType() const override { return HANDLE_TYPE; } - - bool initial_locked; ///< Initial lock state when mutex was created - bool locked; ///< Current locked state - Handle lock_thread; ///< Handle to thread that currently has mutex - std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex - std::string name; ///< Name of mutex (optional) - - ResultVal<bool> WaitSynchronization() override; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -typedef std::multimap<Handle, Handle> MutexMap; -static MutexMap g_mutex_held_locks; - -/** - * Acquires the specified mutex for the specified thread - * @param mutex Mutex that is to be acquired - * @param thread Thread that will acquired - */ -void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandle()) { - g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); - mutex->lock_thread = thread; -} - -bool ReleaseMutexForThread(Mutex* mutex, Handle thread_handle) { - MutexAcquireLock(mutex, thread_handle); - - Thread* thread = Kernel::g_handle_table.Get<Thread>(thread_handle).get(); - if (thread == nullptr) { - LOG_ERROR(Kernel, "Called with invalid handle: %08X", thread_handle); - return false; - } - - thread->ResumeFromWait(); - return true; -} - /** * Resumes a thread waiting for the specified mutex * @param mutex The mutex that some thread is waiting on */ -void ResumeWaitingThread(Mutex* mutex) { +static void ResumeWaitingThread(Mutex* mutex) { + // Reset mutex lock thread handle, nothing is waiting + mutex->locked = false; + mutex->holding_thread = nullptr; + // Find the next waiting thread for the mutex... - if (mutex->waiting_threads.empty()) { - // Reset mutex lock thread handle, nothing is waiting - mutex->locked = false; - mutex->lock_thread = -1; - } - else { - // Resume the next waiting thread and re-lock the mutex - std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); - ReleaseMutexForThread(mutex, *iter); - mutex->waiting_threads.erase(iter); + auto next_thread = mutex->WakeupNextThread(); + if (next_thread != nullptr) { + mutex->Acquire(next_thread); } } -void MutexEraseLock(Mutex* mutex) { - Handle handle = mutex->GetHandle(); - auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); - for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { - if (iter->second == handle) { - g_mutex_held_locks.erase(iter); - break; - } +void ReleaseThreadMutexes(Thread* thread) { + for (auto& mtx : thread->held_mutexes) { + ResumeWaitingThread(mtx.get()); } - mutex->lock_thread = -1; + thread->held_mutexes.clear(); } -void ReleaseThreadMutexes(Handle thread) { - auto locked = g_mutex_held_locks.equal_range(thread); - - // Release every mutex that the thread holds, and resume execution on the waiting threads - for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { - Mutex* mutex = g_handle_table.Get<Mutex>(iter->second).get(); - ResumeWaitingThread(mutex); - } +Mutex::Mutex() {} +Mutex::~Mutex() {} - // Erase all the locks that this thread holds - g_mutex_held_locks.erase(thread); -} +SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { + SharedPtr<Mutex> mutex(new Mutex); -bool LockMutex(Mutex* mutex) { - // Mutex alread locked? - if (mutex->locked) { - return false; - } - MutexAcquireLock(mutex); - return true; -} + mutex->initial_locked = initial_locked; + mutex->locked = false; + mutex->name = std::move(name); + mutex->holding_thread = nullptr; -bool ReleaseMutex(Mutex* mutex) { - MutexEraseLock(mutex); - ResumeWaitingThread(mutex); - return true; -} + // Acquire mutex with current thread if initialized as locked... + if (initial_locked) + mutex->Acquire(); -/** - * Releases a mutex - * @param handle Handle to mutex to release - */ -ResultCode ReleaseMutex(Handle handle) { - Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle).get(); - if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); - - if (!ReleaseMutex(mutex)) { - // TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure - // what error condition this is supposed to be signaling. - return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel, - ErrorSummary::NothingHappened, ErrorLevel::Temporary); - } - return RESULT_SUCCESS; + return mutex; } -/** - * Creates a mutex - * @param handle Reference to handle for the newly created mutex - * @param initial_locked Specifies if the mutex should be locked initially - * @param name Optional name of mutex - * @return Pointer to new Mutex object - */ -Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) { - Mutex* mutex = new Mutex; - // TODO(yuriks): Fix error reporting - handle = Kernel::g_handle_table.Create(mutex).ValueOr(INVALID_HANDLE); +bool Mutex::ShouldWait() { + return locked && holding_thread != GetCurrentThread(); +} - mutex->locked = mutex->initial_locked = initial_locked; - mutex->name = name; +void Mutex::Acquire() { + Acquire(GetCurrentThread()); +} - // Acquire mutex with current thread if initialized as locked... - if (mutex->locked) { - MutexAcquireLock(mutex); +void Mutex::Acquire(SharedPtr<Thread> thread) { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); + if (locked) + return; - // Otherwise, reset lock thread handle - } else { - mutex->lock_thread = -1; - } - return mutex; -} + locked = true; -/** - * Creates a mutex - * @param initial_locked Specifies if the mutex should be locked initially - * @param name Optional name of mutex - * @return Handle to newly created object - */ -Handle CreateMutex(bool initial_locked, const std::string& name) { - Handle handle; - Mutex* mutex = CreateMutex(handle, initial_locked, name); - return handle; + thread->held_mutexes.insert(this); + holding_thread = std::move(thread); } -ResultVal<bool> Mutex::WaitSynchronization() { - bool wait = locked; - if (locked) { - waiting_threads.push_back(GetCurrentThread()->GetHandle()); - Kernel::WaitCurrentThread(WAITTYPE_MUTEX, this); - } else { - // Lock the mutex when the first thread accesses it - locked = true; - MutexAcquireLock(this); - } +void Mutex::Release() { + if (!locked) + return; - return MakeResult<bool>(wait); + holding_thread->held_mutexes.erase(this); + ResumeWaitingThread(this); } + } // namespace diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index a8ca97014..548403614 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -4,30 +4,57 @@ #pragma once +#include <string> + #include "common/common_types.h" #include "core/hle/kernel/kernel.h" namespace Kernel { -/** - * Releases a mutex - * @param handle Handle to mutex to release - */ -ResultCode ReleaseMutex(Handle handle); - -/** - * Creates a mutex - * @param initial_locked Specifies if the mutex should be locked initially - * @param name Optional name of mutex - * @return Handle to newly created object - */ -Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); +class Thread; + +class Mutex final : public WaitObject { +public: + /** + * Creates a mutex. + * @param initial_locked Specifies if the mutex should be locked initially + * @param name Optional name of mutex + * @return Pointer to new Mutex object + */ + static SharedPtr<Mutex> Create(bool initial_locked, std::string name = "Unknown"); + + std::string GetTypeName() const override { return "Mutex"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::Mutex; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + bool initial_locked; ///< Initial lock state when mutex was created + bool locked; ///< Current locked state + std::string name; ///< Name of mutex (optional) + SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex + + bool ShouldWait() override; + void Acquire() override; + + /** + * Acquires the specified mutex for the specified thread + * @param mutex Mutex that is to be acquired + * @param thread Thread that will acquire the mutex + */ + void Acquire(SharedPtr<Thread> thread); + void Release(); + +private: + Mutex(); + ~Mutex() override; +}; /** * Releases all the mutexes held by the specified thread * @param thread Thread that is holding the mutexes */ -void ReleaseThreadMutexes(Handle thread); +void ReleaseThreadMutexes(Thread* thread); } // namespace diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 88ec9a104..c8cf8b9a2 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <queue> - #include "common/common.h" #include "core/hle/kernel/kernel.h" @@ -12,86 +10,51 @@ namespace Kernel { -class Semaphore : public Object { -public: - std::string GetTypeName() const override { return "Semaphore"; } - std::string GetName() const override { return name; } - - static const HandleType HANDLE_TYPE = HandleType::Semaphore; - HandleType GetHandleType() const override { return HANDLE_TYPE; } - - s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have - s32 available_count; ///< Number of free slots left in the semaphore - std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore - std::string name; ///< Name of semaphore (optional) - - /** - * Tests whether a semaphore still has free slots - * @return Whether the semaphore is available - */ - bool IsAvailable() const { - return available_count > 0; - } - - ResultVal<bool> WaitSynchronization() override { - bool wait = !IsAvailable(); - - if (wait) { - Kernel::WaitCurrentThread(WAITTYPE_SEMA, this); - waiting_threads.push(GetCurrentThread()->GetHandle()); - } else { - --available_count; - } - - return MakeResult<bool>(wait); - } -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// +Semaphore::Semaphore() {} +Semaphore::~Semaphore() {} -ResultCode CreateSemaphore(Handle* handle, s32 initial_count, - s32 max_count, const std::string& name) { +ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, + std::string name) { if (initial_count > max_count) return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Permanent); - Semaphore* semaphore = new Semaphore; - // TOOD(yuriks): Fix error reporting - *handle = g_handle_table.Create(semaphore).ValueOr(INVALID_HANDLE); + SharedPtr<Semaphore> semaphore(new Semaphore); // When the semaphore is created, some slots are reserved for other threads, // and the rest is reserved for the caller thread semaphore->max_count = max_count; semaphore->available_count = initial_count; - semaphore->name = name; + semaphore->name = std::move(name); - return RESULT_SUCCESS; + return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore)); } -ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { - Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle).get(); - if (semaphore == nullptr) - return InvalidHandle(ErrorModule::Kernel); +bool Semaphore::ShouldWait() { + return available_count <= 0; +} + +void Semaphore::Acquire() { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); + --available_count; +} - if (semaphore->max_count - semaphore->available_count < release_count) +ResultVal<s32> Semaphore::Release(s32 release_count) { + if (max_count - available_count < release_count) return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); - *count = semaphore->available_count; - semaphore->available_count += release_count; + s32 previous_count = available_count; + available_count += release_count; // Notify some of the threads that the semaphore has been released // stop once the semaphore is full again or there are no more waiting threads - while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) { - Thread* thread = Kernel::g_handle_table.Get<Thread>(semaphore->waiting_threads.front()).get(); - if (thread != nullptr) - thread->ResumeFromWait(); - semaphore->waiting_threads.pop(); - --semaphore->available_count; + while (!ShouldWait() && WakeupNextThread() != nullptr) { + Acquire(); } - return RESULT_SUCCESS; + return MakeResult<s32>(previous_count); } } // namespace diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index 8644ecf0c..d8dc1fd78 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h @@ -4,29 +4,51 @@ #pragma once +#include <queue> +#include <string> + #include "common/common_types.h" #include "core/hle/kernel/kernel.h" namespace Kernel { -/** - * Creates a semaphore. - * @param handle Pointer to the handle of the newly created object - * @param initial_count Number of slots reserved for other threads - * @param max_count Maximum number of slots the semaphore can have - * @param name Optional name of semaphore - * @return ResultCode of the error - */ -ResultCode CreateSemaphore(Handle* handle, s32 initial_count, s32 max_count, const std::string& name = "Unknown"); - -/** - * Releases a certain number of slots from a semaphore. - * @param count The number of free slots the semaphore had before this call - * @param handle The handle of the semaphore to release - * @param release_count The number of slots to release - * @return ResultCode of the error - */ -ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count); +class Semaphore final : public WaitObject { +public: + /** + * Creates a semaphore. + * @param handle Pointer to the handle of the newly created object + * @param initial_count Number of slots reserved for other threads + * @param max_count Maximum number of slots the semaphore can have + * @param name Optional name of semaphore + * @return The created semaphore + */ + static ResultVal<SharedPtr<Semaphore>> Create(s32 initial_count, s32 max_count, + std::string name = "Unknown"); + + std::string GetTypeName() const override { return "Semaphore"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::Semaphore; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have + s32 available_count; ///< Number of free slots left in the semaphore + std::string name; ///< Name of semaphore (optional) + + bool ShouldWait() override; + void Acquire() override; + + /** + * Releases a certain number of slots from a semaphore. + * @param release_count The number of slots to release + * @return The number of free slots the semaphore had before this call + */ + ResultVal<s32> Release(s32 release_count); + +private: + Semaphore(); + ~Semaphore() override; +}; } // namespace diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp new file mode 100644 index 000000000..0594967f8 --- /dev/null +++ b/src/core/hle/kernel/session.cpp @@ -0,0 +1,13 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/kernel/session.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +Session::Session() {} +Session::~Session() {} + +} diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 91f3ffc2c..7cc9332c9 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -5,6 +5,7 @@ #pragma once #include "core/hle/kernel/kernel.h" +#include "core/mem_map.h" namespace Kernel { @@ -41,8 +42,11 @@ inline static u32* GetCommandBuffer(const int offset=0) { * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as * opposed to HLE simulations. */ -class Session : public Object { +class Session : public WaitObject { public: + Session(); + ~Session() override; + std::string GetTypeName() const override { return "Session"; } static const HandleType HANDLE_TYPE = HandleType::Session; @@ -53,6 +57,17 @@ public: * aren't supported yet. */ virtual ResultVal<bool> SyncRequest() = 0; + + // TODO(bunnei): These functions exist to satisfy a hardware test with a Session object + // passed into WaitSynchronization. Figure out the meaning of them. + + bool ShouldWait() override { + return true; + } + + void Acquire() override { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); + } }; } diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 5368e4728..4211fcf04 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -9,76 +9,40 @@ namespace Kernel { -class SharedMemory : public Object { -public: - std::string GetTypeName() const override { return "SharedMemory"; } +SharedMemory::SharedMemory() {} +SharedMemory::~SharedMemory() {} - static const HandleType HANDLE_TYPE = HandleType::SharedMemory; - HandleType GetHandleType() const override { return HANDLE_TYPE; } +SharedPtr<SharedMemory> SharedMemory::Create(std::string name) { + SharedPtr<SharedMemory> shared_memory(new SharedMemory); - u32 base_address; ///< Address of shared memory block in RAM - MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) - MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field) - std::string name; ///< Name of shared memory object (optional) -}; + shared_memory->name = std::move(name); -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Creates a shared memory object - * @param handle Handle of newly created shared memory object - * @param name Name of shared memory object - * @return Pointer to newly created shared memory object - */ -SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) { - SharedMemory* shared_memory = new SharedMemory; - // TOOD(yuriks): Fix error reporting - handle = Kernel::g_handle_table.Create(shared_memory).ValueOr(INVALID_HANDLE); - shared_memory->name = name; return shared_memory; } -Handle CreateSharedMemory(const std::string& name) { - Handle handle; - CreateSharedMemory(handle, name); - return handle; -} - -/** - * Maps a shared memory block to an address in system memory - * @param handle Shared memory block handle - * @param address Address in system memory to map shared memory block to - * @param permissions Memory block map permissions (specified by SVC field) - * @param other_permissions Memory block map other permissions (specified by SVC field) - * @return Result of operation, 0 on success, otherwise error code - */ -ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions, - MemoryPermission other_permissions) { +ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions, + MemoryPermission other_permissions) { if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { - LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", - handle, address); + LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X outside of shared mem bounds!", + GetObjectId(), address); + // TODO: Verify error code with hardware return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); } - SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get(); - if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); - shared_memory->base_address = address; - shared_memory->permissions = permissions; - shared_memory->other_permissions = other_permissions; + this->base_address = address; + this->permissions = permissions; + this->other_permissions = other_permissions; return RESULT_SUCCESS; } -ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { - SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get(); - if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); - - if (0 != shared_memory->base_address) - return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset)); +ResultVal<u8*> SharedMemory::GetPointer(u32 offset) { + if (base_address != 0) + return MakeResult<u8*>(Memory::GetPointer(base_address + offset)); - LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle); + LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId()); // TODO(yuriks): Verify error code. return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, ErrorSummary::InvalidState, ErrorLevel::Permanent); diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index bb65c7ccd..5833b411c 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -23,29 +23,42 @@ enum class MemoryPermission : u32 { DontCare = (1u << 28) }; -/** - * Creates a shared memory object - * @param name Optional name of shared memory object - * @return Handle of newly created shared memory object - */ -Handle CreateSharedMemory(const std::string& name="Unknown"); - -/** - * Maps a shared memory block to an address in system memory - * @param handle Shared memory block handle - * @param address Address in system memory to map shared memory block to - * @param permissions Memory block map permissions (specified by SVC field) - * @param other_permissions Memory block map other permissions (specified by SVC field) - */ -ResultCode MapSharedMemory(Handle handle, u32 address, MemoryPermission permissions, - MemoryPermission other_permissions); - -/** - * Gets a pointer to the shared memory block - * @param handle Shared memory block handle - * @param offset Offset from the start of the shared memory block to get pointer - * @return Pointer to the shared memory block from the specified offset - */ -ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset); +class SharedMemory final : public Object { +public: + /** + * Creates a shared memory object + * @param name Optional object name, used only for debugging purposes. + */ + static SharedPtr<SharedMemory> Create(std::string name = "Unknown"); + + std::string GetTypeName() const override { return "SharedMemory"; } + + static const HandleType HANDLE_TYPE = HandleType::SharedMemory; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + /** + * Maps a shared memory block to an address in system memory + * @param address Address in system memory to map shared memory block to + * @param permissions Memory block map permissions (specified by SVC field) + * @param other_permissions Memory block map other permissions (specified by SVC field) + */ + ResultCode Map(VAddr address, MemoryPermission permissions, MemoryPermission other_permissions); + + /** + * Gets a pointer to the shared memory block + * @param offset Offset from the start of the shared memory block to get pointer + * @return Pointer to the shared memory block from the specified offset + */ + ResultVal<u8*> GetPointer(u32 offset = 0); + + VAddr base_address; ///< Address of shared memory block in RAM + MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) + MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field) + std::string name; ///< Name of shared memory object (optional) + +private: + SharedMemory(); + ~SharedMemory() override; +}; } // namespace diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index dd20ca30e..3987f9608 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -4,7 +4,6 @@ #include <algorithm> #include <list> -#include <map> #include <vector> #include "common/common.h" @@ -22,17 +21,12 @@ namespace Kernel { -ResultVal<bool> Thread::WaitSynchronization() { - const bool wait = status != THREADSTATUS_DORMANT; - if (wait) { - Thread* thread = GetCurrentThread(); - if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { - waiting_threads.push_back(thread); - } - WaitCurrentThread(WAITTYPE_THREADEND, this); - } +bool Thread::ShouldWait() { + return status != THREADSTATUS_DORMANT; +} - return MakeResult<bool>(wait); +void Thread::Acquire() { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); } // Lists all thread ids that aren't deleted/etc. @@ -46,6 +40,9 @@ static Thread* current_thread; static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup static u32 next_thread_id; ///< The next available thread id +Thread::Thread() {} +Thread::~Thread() {} + Thread* GetCurrentThread() { return current_thread; } @@ -55,7 +52,7 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { memset(&t->context, 0, sizeof(Core::ThreadContext)); t->context.cpu_registers[0] = arg; - t->context.pc = t->context.reg_15 = t->entry_point; + t->context.pc = t->entry_point; t->context.sp = t->stack_top; t->context.cpsr = 0x1F; // Usermode @@ -67,8 +64,8 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { if (t->current_priority < lowest_priority) { t->current_priority = t->initial_priority; } - t->wait_type = WAITTYPE_NONE; - t->wait_object = nullptr; + + t->wait_objects.clear(); t->wait_address = 0; } @@ -88,37 +85,35 @@ static void ChangeReadyState(Thread* t, bool ready) { } } -/// Check if a thread is blocking on a specified wait type -static bool CheckWaitType(const Thread* thread, WaitType type) { - return (type == thread->wait_type) && (thread->IsWaiting()); -} +/// Check if a thread is waiting on a the specified wait object +static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { + auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); + + if (itr != thread->wait_objects.end()) + return thread->IsWaiting(); -/// Check if a thread is blocking on a specified wait type with a specified handle -static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) { - return CheckWaitType(thread, type) && wait_object == thread->wait_object; + return false; } -/// Check if a thread is blocking on a specified wait type with a specified handle and address -static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) { - return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address); +/// Check if the specified thread is waiting on the specified address to be arbitrated +static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { + return thread->IsWaiting() && thread->wait_objects.empty() && wait_address == thread->wait_address; } /// Stops the current thread void Thread::Stop(const char* reason) { // Release all the mutexes that this thread holds - ReleaseThreadMutexes(GetHandle()); + ReleaseThreadMutexes(this); ChangeReadyState(this, false); status = THREADSTATUS_DORMANT; - for (auto& waiting_thread : waiting_threads) { - if (CheckWaitType(waiting_thread.get(), WAITTYPE_THREADEND, this)) - waiting_thread->ResumeFromWait(); - } - waiting_threads.clear(); + WakeupAllWaitingThreads(); // Stopped threads are never waiting. - wait_type = WAITTYPE_NONE; - wait_object = nullptr; + for (auto& wait_object : wait_objects) { + wait_object->RemoveWaitingThread(this); + } + wait_objects.clear(); wait_address = 0; } @@ -129,26 +124,20 @@ static void ChangeThreadState(Thread* t, ThreadStatus new_status) { } ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); t->status = new_status; - - if (new_status == THREADSTATUS_WAIT) { - if (t->wait_type == WAITTYPE_NONE) { - LOG_ERROR(Kernel, "Waittype none not allowed"); - } - } } /// Arbitrate the highest priority thread that is waiting -Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { +Thread* ArbitrateHighestPriorityThread(u32 address) { Thread* highest_priority_thread = nullptr; s32 priority = THREADPRIO_LOWEST; // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (auto& thread : thread_list) { - if (!CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) + if (!CheckWait_AddressArbiter(thread.get(), address)) continue; if (thread == nullptr) - continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. + continue; if(thread->current_priority <= priority) { highest_priority_thread = thread.get(); @@ -165,11 +154,11 @@ Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { } /// Arbitrate all threads currently waiting -void ArbitrateAllThreads(Object* arbiter, u32 address) { +void ArbitrateAllThreads(u32 address) { // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (auto& thread : thread_list) { - if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) + if (CheckWait_AddressArbiter(thread.get(), address)) thread->ResumeFromWait(); } } @@ -177,9 +166,6 @@ void ArbitrateAllThreads(Object* arbiter, u32 address) { /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) static void CallThread(Thread* t) { // Stop waiting - if (t->wait_type != WAITTYPE_NONE) { - t->wait_type = WAITTYPE_NONE; - } ChangeThreadState(t, THREADSTATUS_READY); } @@ -200,7 +186,6 @@ static void SwitchContext(Thread* t) { current_thread = t; ChangeReadyState(t, false); t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; - t->wait_type = WAITTYPE_NONE; Core::g_app_core->LoadContext(t->context); } else { current_thread = nullptr; @@ -223,49 +208,119 @@ static Thread* NextThread() { return next; } -void WaitCurrentThread(WaitType wait_type, Object* wait_object) { +void WaitCurrentThread_Sleep() { Thread* thread = GetCurrentThread(); - thread->wait_type = wait_type; - thread->wait_object = wait_object; ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } -void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) { - WaitCurrentThread(wait_type, wait_object); - GetCurrentThread()->wait_address = wait_address; +void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) { + Thread* thread = GetCurrentThread(); + thread->wait_set_output = wait_set_output; + thread->wait_all = wait_all; + + // It's possible to call WaitSynchronizationN without any objects passed in... + if (wait_object != nullptr) + thread->wait_objects.push_back(wait_object); + + ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); +} + +void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { + Thread* thread = GetCurrentThread(); + thread->wait_address = wait_address; + ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } /// Event type for the thread wake up event static int ThreadWakeupEventType = -1; +// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing +// us to simply use a pool index or similar. +static Kernel::HandleTable wakeup_callback_handle_table; /// Callback that will wake up the thread it was scheduled for -static void ThreadWakeupCallback(u64 parameter, int cycles_late) { - Handle handle = static_cast<Handle>(parameter); - SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(handle); +static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { + SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>((Handle)thread_handle); if (thread == nullptr) { - LOG_ERROR(Kernel, "Thread doesn't exist %u", handle); + LOG_CRITICAL(Kernel, "Callback fired for invalid thread %08X", thread_handle); return; } + thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, + ErrorSummary::StatusChanged, ErrorLevel::Info)); + + if (thread->wait_set_output) + thread->SetWaitSynchronizationOutput(-1); + thread->ResumeFromWait(); } -void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { +void Thread::WakeAfterDelay(s64 nanoseconds) { // Don't schedule a wakeup if the thread wants to wait forever if (nanoseconds == -1) return; - _dbg_assert_(Kernel, thread != nullptr); u64 microseconds = nanoseconds / 1000; - CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); + CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); +} + +void Thread::ReleaseWaitObject(WaitObject* wait_object) { + if (wait_objects.empty()) { + LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); + return; + } + + // Remove this thread from the waiting object's thread list + wait_object->RemoveWaitingThread(this); + + unsigned index = 0; + bool wait_all_failed = false; // Will be set to true if any object is unavailable + + // Iterate through all waiting objects to check availability... + for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) { + if ((*itr)->ShouldWait()) + wait_all_failed = true; + + // The output should be the last index of wait_object + if (*itr == wait_object) + index = itr - wait_objects.begin(); + } + + // If we are waiting on all objects... + if (wait_all) { + // Resume the thread only if all are available... + if (!wait_all_failed) { + SetWaitSynchronizationResult(RESULT_SUCCESS); + SetWaitSynchronizationOutput(-1); + + ResumeFromWait(); + } + } else { + // Otherwise, resume + SetWaitSynchronizationResult(RESULT_SUCCESS); + + if (wait_set_output) + SetWaitSynchronizationOutput(index); + + ResumeFromWait(); + } } -/// Resumes a thread from waiting by marking it as "ready" void Thread::ResumeFromWait() { + // Cancel any outstanding wakeup events + CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); + status &= ~THREADSTATUS_WAIT; - wait_object = nullptr; - wait_type = WAITTYPE_NONE; + + // Remove this thread from all other WaitObjects + for (auto wait_object : wait_objects) + wait_object->RemoveWaitingThread(this); + + wait_objects.clear(); + wait_set_output = false; + wait_all = false; + wait_address = 0; + if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { ChangeReadyState(this, true); } @@ -277,11 +332,11 @@ static void DebugThreadQueue() { if (!thread) { return; } - LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThread()->GetHandle()); + LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, GetCurrentThread()->GetObjectId()); for (auto& t : thread_list) { s32 priority = thread_ready_queue.contains(t.get()); if (priority != -1) { - LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, t->GetHandle()); + LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); } } } @@ -313,14 +368,6 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, SharedPtr<Thread> thread(new Thread); - // TODO(yuriks): Thread requires a handle to be inserted into the various scheduling queues for - // the time being. Create a handle here, it will be copied to the handle field in - // the object and use by the rest of the code. This should be removed when other - // code doesn't rely on the handle anymore. - ResultVal<Handle> handle = Kernel::g_handle_table.Create(thread); - if (handle.Failed()) - return handle.Code(); - thread_list.push_back(thread); thread_ready_queue.prepare(priority); @@ -331,10 +378,12 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, thread->stack_size = stack_size; thread->initial_priority = thread->current_priority = priority; thread->processor_id = processor_id; - thread->wait_type = WAITTYPE_NONE; - thread->wait_object = nullptr; + thread->wait_set_output = false; + thread->wait_all = false; + thread->wait_objects.clear(); thread->wait_address = 0; thread->name = std::move(name); + thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); ResetThread(thread.get(), arg, 0); CallThread(thread.get()); @@ -368,16 +417,14 @@ void Thread::SetPriority(s32 priority) { } } -Handle SetupIdleThread() { +SharedPtr<Thread> SetupIdleThread() { // We need to pass a few valid values to get around parameter checking in Thread::Create. - auto thread_res = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, - THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE); - _dbg_assert_(Kernel, thread_res.Succeeded()); - SharedPtr<Thread> thread = std::move(*thread_res); + auto thread = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, + THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE).MoveFrom(); thread->idle = true; CallThread(thread.get()); - return thread->GetHandle(); + return thread; } SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) { @@ -410,19 +457,26 @@ void Reschedule() { HLE::g_reschedule = false; if (next != nullptr) { - LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); + LOG_TRACE(Kernel, "context switch %u -> %u", prev->GetObjectId(), next->GetObjectId()); SwitchContext(next); } else { - LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); + LOG_TRACE(Kernel, "cannot context switch from %u, no higher priority thread!", prev->GetObjectId()); for (auto& thread : thread_list) { - LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", - thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, - (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE)); + LOG_TRACE(Kernel, "\tid=%u prio=0x%02X, status=0x%08X", thread->GetObjectId(), + thread->current_priority, thread->status); } } } +void Thread::SetWaitSynchronizationResult(ResultCode result) { + context.cpu_registers[0] = result.raw; +} + +void Thread::SetWaitSynchronizationOutput(s32 output) { + context.cpu_registers[1] = output; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// void ThreadingInit() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 284dec400..633bb7c98 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -7,6 +7,8 @@ #include <string> #include <vector> +#include <boost/container/flat_set.hpp> + #include "common/common_types.h" #include "core/core.h" @@ -38,21 +40,11 @@ enum ThreadStatus { THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND }; -enum WaitType { - WAITTYPE_NONE, - WAITTYPE_SLEEP, - WAITTYPE_SEMA, - WAITTYPE_EVENT, - WAITTYPE_THREADEND, - WAITTYPE_MUTEX, - WAITTYPE_SYNCH, - WAITTYPE_ARB, - WAITTYPE_TIMER, -}; - namespace Kernel { -class Thread : public Kernel::Object { +class Mutex; + +class Thread final : public WaitObject { public: static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size); @@ -70,7 +62,8 @@ public: inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } inline bool IsIdle() const { return idle; } - ResultVal<bool> WaitSynchronization() override; + bool ShouldWait() override; + void Acquire() override; s32 GetPriority() const { return current_priority; } void SetPriority(s32 priority); @@ -78,9 +71,34 @@ public: u32 GetThreadId() const { return thread_id; } void Stop(const char* reason); - /// Resumes a thread from waiting by marking it as "ready". + + /** + * Release an acquired wait object + * @param wait_object WaitObject to release + */ + void ReleaseWaitObject(WaitObject* wait_object); + + /// Resumes a thread from waiting by marking it as "ready" 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); + Core::ThreadContext context; u32 thread_id; @@ -95,11 +113,13 @@ public: s32 processor_id; - WaitType wait_type; - Object* wait_object; - VAddr wait_address; + /// Mutexes currently held by this thread, which will be released when it exits. + boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; - std::vector<SharedPtr<Thread>> waiting_threads; + 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; @@ -107,9 +127,15 @@ public: bool idle = false; private: - Thread() = default; + 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 SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); @@ -117,37 +143,30 @@ SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); void Reschedule(); /// Arbitrate the highest priority thread that is waiting -Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address); +Thread* ArbitrateHighestPriorityThread(u32 address); /// Arbitrate all threads currently waiting... -void ArbitrateAllThreads(Object* arbiter, u32 address); +void ArbitrateAllThreads(u32 address); /// Gets the current thread Thread* GetCurrentThread(); -/** - * Puts the current thread in the wait state for the given type - * @param wait_type Type of wait - * @param wait_object Kernel object that we are waiting on, defaults to current thread - */ -void WaitCurrentThread(WaitType wait_type, Object* wait_object = GetCurrentThread()); +/// Waits the current thread on a sleep +void WaitCurrentThread_Sleep(); /** - * Schedules an event to wake up the specified thread after the specified delay. - * @param handle The thread handle. - * @param nanoseconds The time this thread will be allowed to sleep for. + * Waits the current thread from a WaitSynchronization call + * @param wait_object Kernel object 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 WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); +void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all); /** - * Puts the current thread in the wait state for the given type - * @param wait_type Type of wait - * @param wait_object Kernel object that we are waiting on + * Waits the current thread from an ArbitrateAddress call * @param wait_address Arbitration address used to resume from wait */ -void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address); - - +void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); /** * Sets up the idle thread, this is a thread that is intended to never execute instructions, @@ -155,7 +174,8 @@ void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_addre * and will try to yield on every call. * @returns The handle of the idle thread */ -Handle SetupIdleThread(); +SharedPtr<Thread> SetupIdleThread(); + /// Initialize threading void ThreadingInit(); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 3b0452d4d..4352fc99c 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -2,8 +2,6 @@ // 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" @@ -13,77 +11,62 @@ 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); - } -}; +/// The event type of the generic timer callback event +static int timer_callback_event_type = -1; +// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future, allowing +// us to simply use a pool index or similar. +static Kernel::HandleTable timer_callback_handle_table; -/** - * 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; +Timer::Timer() {} +Timer::~Timer() {} - handle = Kernel::g_handle_table.Create(timer).ValueOr(INVALID_HANDLE); +SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) { + SharedPtr<Timer> timer(new Timer); timer->reset_type = reset_type; timer->signaled = false; - timer->name = name; + timer->name = std::move(name); timer->initial_delay = 0; timer->interval_delay = 0; + timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom(); + return timer; } -ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name) { - CreateTimer(*handle, reset_type, name); - return RESULT_SUCCESS; +bool Timer::ShouldWait() { + return !signaled; +} + +void Timer::Acquire() { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); } -ResultCode ClearTimer(Handle handle) { - SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); - - if (timer == nullptr) - return InvalidHandle(ErrorModule::Kernel); +void Timer::Set(s64 initial, s64 interval) { + // Ensure we get rid of any previous scheduled event + Cancel(); - timer->signaled = false; - return RESULT_SUCCESS; + initial_delay = initial; + interval_delay = interval; + + u64 initial_microseconds = initial / 1000; + CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), + timer_callback_event_type, callback_handle); } -/// The event type of the generic timer callback event -static int TimerCallbackEventType = -1; +void Timer::Cancel() { + CoreTiming::UnscheduleEvent(timer_callback_event_type, callback_handle); +} + +void Timer::Clear() { + signaled = false; +} /// 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); + SharedPtr<Timer> timer = timer_callback_handle_table.Get<Timer>(timer_handle); if (timer == nullptr) { - LOG_CRITICAL(Kernel, "Callback fired for invalid timer %u", timer_handle); + LOG_CRITICAL(Kernel, "Callback fired for invalid timer %08X", timer_handle); return; } @@ -92,12 +75,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { 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(); + timer->WakeupAllWaitingThreads(); if (timer->reset_type == RESETTYPE_ONESHOT) timer->signaled = false; @@ -106,36 +84,12 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { // Reschedule the timer with the interval delay u64 interval_microseconds = timer->interval_delay / 1000; CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late, - TimerCallbackEventType, timer_handle); + timer_callback_event_type, 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); + timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback); } void TimersShutdown() { diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index f8aa66b60..540e4e187 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -11,37 +11,54 @@ namespace Kernel { -/** - * Cancels a timer - * @param handle Handle of the timer to cancel - */ -ResultCode CancelTimer(Handle handle); - -/** - * Starts a timer with the specified initial delay and interval - * @param handle Handle of the timer to start - * @param initial Delay until the timer is first fired - * @param interval Delay until the timer is fired after the first time - */ -ResultCode SetTimer(Handle handle, s64 initial, s64 interval); - -/** - * Clears a timer - * @param handle Handle of the timer to clear - */ -ResultCode ClearTimer(Handle handle); - -/** - * Creates a timer - * @param Handle to newly created Timer object - * @param reset_type ResetType describing how to create the timer - * @param name Optional name of timer - * @return ResultCode of the error - */ -ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name="Unknown"); +class Timer final : public WaitObject { +public: + /** + * Creates a timer + * @param reset_type ResetType describing how to create the timer + * @param name Optional name of timer + * @return The created Timer + */ + static SharedPtr<Timer> Create(ResetType reset_type, std::string name = "Unknown"); + + 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::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 + + bool ShouldWait() override; + void Acquire() override; + + /** + * Starts the timer, with the specified initial delay and interval. + * @param initial Delay until the timer is first fired + * @param interval Delay until the timer is fired after the first time + */ + void Set(s64 initial, s64 interval); + + void Cancel(); + void Clear(); + +private: + Timer(); + ~Timer() override; + + /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. + Handle callback_handle; +}; /// Initializes the required variables for timers void TimersInit(); /// Tears down the timer variables void TimersShutdown(); + } // namespace |