mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Merge pull request #495 from bunnei/fix-waitsynch
Fix WaitSynchronization
This commit is contained in:
		
						commit
						24a63662ba
					
				
					 15 changed files with 474 additions and 371 deletions
				
			
		|  | @ -30,7 +30,8 @@ public: | ||||||
| 
 | 
 | ||||||
| /// Arbitrate an address
 | /// Arbitrate an address
 | ||||||
| ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value, u64 nanoseconds) { | ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value, u64 nanoseconds) { | ||||||
|     Object* object = Kernel::g_handle_table.GetGeneric(handle).get(); |     AddressArbiter* object = Kernel::g_handle_table.Get<AddressArbiter>(handle).get(); | ||||||
|  | 
 | ||||||
|     if (object == nullptr) |     if (object == nullptr) | ||||||
|         return InvalidHandle(ErrorModule::Kernel); |         return InvalidHandle(ErrorModule::Kernel); | ||||||
| 
 | 
 | ||||||
|  | @ -40,24 +41,24 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 | ||||||
|     case ArbitrationType::Signal: |     case ArbitrationType::Signal: | ||||||
|         // Negative value means resume all threads
 |         // Negative value means resume all threads
 | ||||||
|         if (value < 0) { |         if (value < 0) { | ||||||
|             ArbitrateAllThreads(object, address); |             ArbitrateAllThreads(address); | ||||||
|         } else { |         } else { | ||||||
|             // Resume first N threads
 |             // Resume first N threads
 | ||||||
|             for(int i = 0; i < value; i++) |             for(int i = 0; i < value; i++) | ||||||
|                 ArbitrateHighestPriorityThread(object, address); |                 ArbitrateHighestPriorityThread(address); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     // Wait current thread (acquire the arbiter)...
 |     // Wait current thread (acquire the arbiter)...
 | ||||||
|     case ArbitrationType::WaitIfLessThan: |     case ArbitrationType::WaitIfLessThan: | ||||||
|         if ((s32)Memory::Read32(address) <= value) { |         if ((s32)Memory::Read32(address) <= value) { | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); |             Kernel::WaitCurrentThread_ArbitrateAddress(address); | ||||||
|             HLE::Reschedule(__func__); |             HLE::Reschedule(__func__); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     case ArbitrationType::WaitIfLessThanWithTimeout: |     case ArbitrationType::WaitIfLessThanWithTimeout: | ||||||
|         if ((s32)Memory::Read32(address) <= value) { |         if ((s32)Memory::Read32(address) <= value) { | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); |             Kernel::WaitCurrentThread_ArbitrateAddress(address); | ||||||
|             Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); |             Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); | ||||||
|             HLE::Reschedule(__func__); |             HLE::Reschedule(__func__); | ||||||
|         } |         } | ||||||
|  | @ -67,7 +68,7 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 | ||||||
|         s32 memory_value = Memory::Read32(address) - 1; |         s32 memory_value = Memory::Read32(address) - 1; | ||||||
|         Memory::Write32(address, memory_value); |         Memory::Write32(address, memory_value); | ||||||
|         if (memory_value <= value) { |         if (memory_value <= value) { | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); |             Kernel::WaitCurrentThread_ArbitrateAddress(address); | ||||||
|             HLE::Reschedule(__func__); |             HLE::Reschedule(__func__); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|  | @ -77,7 +78,7 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 | ||||||
|         s32 memory_value = Memory::Read32(address) - 1; |         s32 memory_value = Memory::Read32(address) - 1; | ||||||
|         Memory::Write32(address, memory_value); |         Memory::Write32(address, memory_value); | ||||||
|         if (memory_value <= value) { |         if (memory_value <= value) { | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); |             Kernel::WaitCurrentThread_ArbitrateAddress(address); | ||||||
|             Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); |             Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); | ||||||
|             HLE::Reschedule(__func__); |             HLE::Reschedule(__func__); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
| class Event : public Object { | class Event : public WaitObject { | ||||||
| public: | public: | ||||||
|     std::string GetTypeName() const override { return "Event"; } |     std::string GetTypeName() const override { return "Event"; } | ||||||
|     std::string GetName() const override { return name; } |     std::string GetName() const override { return name; } | ||||||
|  | @ -25,99 +25,40 @@ public: | ||||||
|     ResetType intitial_reset_type;          ///< ResetType specified at Event initialization
 |     ResetType intitial_reset_type;          ///< ResetType specified at Event initialization
 | ||||||
|     ResetType reset_type;                   ///< Current ResetType
 |     ResetType reset_type;                   ///< Current ResetType
 | ||||||
| 
 | 
 | ||||||
|     bool locked;                            ///< Event signal wait
 |     bool signaled;                          ///< Whether the event has already been signaled
 | ||||||
|     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)
 |     std::string name;                       ///< Name of event (optional)
 | ||||||
| 
 | 
 | ||||||
|     ResultVal<bool> WaitSynchronization() override { |     bool ShouldWait() override { | ||||||
|         bool wait = locked; |         return !signaled; | ||||||
|         if (locked) { |     } | ||||||
|             Handle thread = GetCurrentThread()->GetHandle(); | 
 | ||||||
|             if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { |     void Acquire() override { | ||||||
|                 waiting_threads.push_back(thread); |         _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); | ||||||
|             } | 
 | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_EVENT, this); |         // Release the event if it's not sticky...
 | ||||||
|         } |         if (reset_type != RESETTYPE_STICKY) | ||||||
|         if (reset_type != RESETTYPE_STICKY && !permanent_locked) { |             signaled = false; | ||||||
|             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); |  | ||||||
| 
 |  | ||||||
|     if (!evt->permanent_locked) { |  | ||||||
|         evt->locked = locked; |  | ||||||
|     } |  | ||||||
|     return RESULT_SUCCESS; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * 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) { | ResultCode SignalEvent(const Handle handle) { | ||||||
|     Event* evt = g_handle_table.Get<Event>(handle).get(); |     Event* evt = g_handle_table.Get<Event>(handle).get(); | ||||||
|     if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |     if (evt == nullptr) | ||||||
|  |         return InvalidHandle(ErrorModule::Kernel); | ||||||
| 
 | 
 | ||||||
|     // Resume threads waiting for event to signal
 |     evt->signaled = true; | ||||||
|     bool event_caught = false; |     evt->WakeupAllWaitingThreads(); | ||||||
|     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; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * Clears an event |  | ||||||
|  * @param handle Handle to event to clear |  | ||||||
|  * @return Result of operation, 0 on success, otherwise error code |  | ||||||
|  */ |  | ||||||
| ResultCode ClearEvent(Handle handle) { | ResultCode ClearEvent(Handle handle) { | ||||||
|     Event* evt = g_handle_table.Get<Event>(handle).get(); |     Event* evt = g_handle_table.Get<Event>(handle).get(); | ||||||
|     if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); |     if (evt == nullptr) | ||||||
|  |         return InvalidHandle(ErrorModule::Kernel); | ||||||
|  | 
 | ||||||
|  |     evt->signaled = false; | ||||||
| 
 | 
 | ||||||
|     if (!evt->permanent_locked) { |  | ||||||
|         evt->locked = true; |  | ||||||
|     } |  | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -134,20 +75,13 @@ Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string | ||||||
|     // TOOD(yuriks): Fix error reporting
 |     // TOOD(yuriks): Fix error reporting
 | ||||||
|     handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE); |     handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE); | ||||||
| 
 | 
 | ||||||
|     evt->locked = true; |     evt->signaled = false; | ||||||
|     evt->permanent_locked = false; |  | ||||||
|     evt->reset_type = evt->intitial_reset_type = reset_type; |     evt->reset_type = evt->intitial_reset_type = reset_type; | ||||||
|     evt->name = name; |     evt->name = name; | ||||||
| 
 | 
 | ||||||
|     return evt; |     return evt; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * 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 CreateEvent(const ResetType reset_type, const std::string& name) { | ||||||
|     Handle handle; |     Handle handle; | ||||||
|     Event* evt = CreateEvent(handle, reset_type, name); |     Event* evt = CreateEvent(handle, reset_type, name); | ||||||
|  |  | ||||||
|  | @ -11,29 +11,17 @@ | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | 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 |  * Signals an event | ||||||
|  * @param handle Handle to event to signal |  * @param handle Handle to event to signal | ||||||
|  |  * @return Result of operation, 0 on success, otherwise error code | ||||||
|  */ |  */ | ||||||
| ResultCode SignalEvent(const Handle handle); | ResultCode SignalEvent(const Handle handle); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Clears an event |  * Clears an event | ||||||
|  * @param handle Handle to event to clear |  * @param handle Handle to event to clear | ||||||
|  |  * @return Result of operation, 0 on success, otherwise error code | ||||||
|  */ |  */ | ||||||
| ResultCode ClearEvent(Handle handle); | ResultCode ClearEvent(Handle handle); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -18,6 +18,41 @@ SharedPtr<Thread> g_main_thread = nullptr; | ||||||
| HandleTable g_handle_table; | HandleTable g_handle_table; | ||||||
| u64 g_program_id = 0; | u64 g_program_id = 0; | ||||||
| 
 | 
 | ||||||
|  | void WaitObject::AddWaitingThread(Thread* thread) { | ||||||
|  |     auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | ||||||
|  |     if (itr == waiting_threads.end()) | ||||||
|  |         waiting_threads.push_back(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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Thread* WaitObject::WakeupNextThread() { | ||||||
|  |     if (waiting_threads.empty()) | ||||||
|  |         return nullptr; | ||||||
|  | 
 | ||||||
|  |     auto next_thread = 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() { | HandleTable::HandleTable() { | ||||||
|     next_generation = 1; |     next_generation = 1; | ||||||
|     Clear(); |     Clear(); | ||||||
|  |  | ||||||
|  | @ -8,6 +8,8 @@ | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| #include <string> | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
| #include "common/common.h" | #include "common/common.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| 
 | 
 | ||||||
|  | @ -58,17 +60,35 @@ class Object : NonCopyable { | ||||||
| public: | public: | ||||||
|     virtual ~Object() {} |     virtual ~Object() {} | ||||||
|     Handle GetHandle() const { return handle; } |     Handle GetHandle() const { return handle; } | ||||||
|  | 
 | ||||||
|     virtual std::string GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; } |     virtual std::string GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; } | ||||||
|     virtual std::string GetName() const { return "[UNKNOWN KERNEL OBJECT]"; } |     virtual std::string GetName() const { return "[UNKNOWN KERNEL OBJECT]"; } | ||||||
|     virtual Kernel::HandleType GetHandleType() const = 0; |     virtual Kernel::HandleType GetHandleType() const = 0; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Wait for kernel object to synchronize. |      * Check if a thread can wait on the object | ||||||
|      * @return True if the current thread should wait as a result of the wait |      * @return True if a thread can wait on the object, otherwise false | ||||||
|      */ |      */ | ||||||
|     virtual ResultVal<bool> WaitSynchronization() { |     bool IsWaitable() const { | ||||||
|         LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); |         switch (GetHandleType()) { | ||||||
|         return UnimplementedFunction(ErrorModule::Kernel); |         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: | private: | ||||||
|  | @ -92,6 +112,44 @@ inline void intrusive_ptr_release(Object* object) { | ||||||
| template <typename T> | template <typename T> | ||||||
| using SharedPtr = boost::intrusive_ptr<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(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* thead); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Wake up the next thread waiting on this object | ||||||
|  |      * @return Pointer to the thread that was resumed, nullptr if no threads are waiting | ||||||
|  |      */ | ||||||
|  |     Thread* WakeupNextThread(); | ||||||
|  | 
 | ||||||
|  |     /// Wake up all threads waiting on this object
 | ||||||
|  |     void WakeupAllWaitingThreads(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::vector<Thread*> waiting_threads; ///< Threads waiting for this object to become available
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * This class allows the creation of Handles, which are references to objects that can be tested |  * 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 |  * for validity and looked up. Here they are used to pass references to kernel objects to/from the | ||||||
|  | @ -146,14 +204,14 @@ public: | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Looks up a handle. |      * 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; |     SharedPtr<Object> GetGeneric(Handle handle) const; | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Looks up a handle while verifying its type. |      * 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 |      * @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`. |      *         type differs from the handle type `T::HANDLE_TYPE`. | ||||||
|      */ |      */ | ||||||
|     template <class T> |     template <class T> | ||||||
|     SharedPtr<T> Get(Handle handle) const { |     SharedPtr<T> Get(Handle handle) const { | ||||||
|  | @ -164,6 +222,19 @@ public: | ||||||
|         return nullptr; |         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.
 |     /// Closes all handles held in this table.
 | ||||||
|     void Clear(); |     void Clear(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
| class Mutex : public Object { | class Mutex : public WaitObject { | ||||||
| public: | public: | ||||||
|     std::string GetTypeName() const override { return "Mutex"; } |     std::string GetTypeName() const override { return "Mutex"; } | ||||||
|     std::string GetName() const override { return name; } |     std::string GetName() const override { return name; } | ||||||
|  | @ -23,39 +23,26 @@ public: | ||||||
| 
 | 
 | ||||||
|     bool initial_locked;                        ///< Initial lock state when mutex was created
 |     bool initial_locked;                        ///< Initial lock state when mutex was created
 | ||||||
|     bool locked;                                ///< Current locked state
 |     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)
 |     std::string name;                           ///< Name of mutex (optional)
 | ||||||
|  |     SharedPtr<Thread> holding_thread;           ///< Thread that has acquired the mutex
 | ||||||
| 
 | 
 | ||||||
|     ResultVal<bool> WaitSynchronization() override; |     bool ShouldWait() override; | ||||||
|  |     void Acquire() override; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| 
 | 
 | ||||||
| typedef std::multimap<Handle, Handle> MutexMap; | typedef std::multimap<SharedPtr<Thread>, SharedPtr<Mutex>> MutexMap; | ||||||
| static MutexMap g_mutex_held_locks; | static MutexMap g_mutex_held_locks; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Acquires the specified mutex for the specified thread |  * Acquires the specified mutex for the specified thread | ||||||
|  * @param mutex Mutex that is to be acquired |  * @param mutex Mutex that is to be acquired | ||||||
|  * @param thread Thread that will acquired |  * @param thread Thread that will acquire the mutex | ||||||
|  */ |  */ | ||||||
| void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandle()) { | void MutexAcquireLock(Mutex* mutex, Thread* thread) { | ||||||
|     g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); |     g_mutex_held_locks.insert(std::make_pair(thread, mutex)); | ||||||
|     mutex->lock_thread = thread; |     mutex->holding_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; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -64,56 +51,41 @@ bool ReleaseMutexForThread(Mutex* mutex, Handle thread_handle) { | ||||||
|  */ |  */ | ||||||
| void ResumeWaitingThread(Mutex* mutex) { | void ResumeWaitingThread(Mutex* mutex) { | ||||||
|     // Find the next waiting thread for the mutex...
 |     // Find the next waiting thread for the mutex...
 | ||||||
|     if (mutex->waiting_threads.empty()) { |     auto next_thread = mutex->WakeupNextThread(); | ||||||
|  |     if (next_thread != nullptr) { | ||||||
|  |         MutexAcquireLock(mutex, next_thread); | ||||||
|  |     } else { | ||||||
|         // Reset mutex lock thread handle, nothing is waiting
 |         // Reset mutex lock thread handle, nothing is waiting
 | ||||||
|         mutex->locked = false; |         mutex->locked = false; | ||||||
|         mutex->lock_thread = -1; |         mutex->holding_thread = nullptr; | ||||||
|     } |  | ||||||
|     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); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MutexEraseLock(Mutex* mutex) { | void ReleaseThreadMutexes(Thread* thread) { | ||||||
|     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; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     mutex->lock_thread = -1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ReleaseThreadMutexes(Handle thread) { |  | ||||||
|     auto locked = g_mutex_held_locks.equal_range(thread); |     auto locked = g_mutex_held_locks.equal_range(thread); | ||||||
|      |      | ||||||
|     // Release every mutex that the thread holds, and resume execution on the waiting threads
 |     // Release every mutex that the thread holds, and resume execution on the waiting threads
 | ||||||
|     for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { |     for (auto iter = locked.first; iter != locked.second; ++iter) { | ||||||
|         Mutex* mutex = g_handle_table.Get<Mutex>(iter->second).get(); |         ResumeWaitingThread(iter->second.get()); | ||||||
|         ResumeWaitingThread(mutex); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Erase all the locks that this thread holds
 |     // Erase all the locks that this thread holds
 | ||||||
|     g_mutex_held_locks.erase(thread); |     g_mutex_held_locks.erase(thread); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool LockMutex(Mutex* mutex) { |  | ||||||
|     // Mutex alread locked?
 |  | ||||||
|     if (mutex->locked) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|     MutexAcquireLock(mutex); |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool ReleaseMutex(Mutex* mutex) { | bool ReleaseMutex(Mutex* mutex) { | ||||||
|     MutexEraseLock(mutex); |     if (mutex->locked) { | ||||||
|     ResumeWaitingThread(mutex); |         auto locked = g_mutex_held_locks.equal_range(mutex->holding_thread); | ||||||
|  | 
 | ||||||
|  |         for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { | ||||||
|  |             if (iter->second == mutex) { | ||||||
|  |                 g_mutex_held_locks.erase(iter); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ResumeWaitingThread(mutex); | ||||||
|  |     } | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -148,15 +120,12 @@ Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) | ||||||
| 
 | 
 | ||||||
|     mutex->locked = mutex->initial_locked = initial_locked; |     mutex->locked = mutex->initial_locked = initial_locked; | ||||||
|     mutex->name = name; |     mutex->name = name; | ||||||
|  |     mutex->holding_thread = nullptr; | ||||||
| 
 | 
 | ||||||
|     // Acquire mutex with current thread if initialized as locked...
 |     // Acquire mutex with current thread if initialized as locked...
 | ||||||
|     if (mutex->locked) { |     if (mutex->locked) | ||||||
|         MutexAcquireLock(mutex); |         MutexAcquireLock(mutex, GetCurrentThread()); | ||||||
| 
 | 
 | ||||||
|     // Otherwise, reset lock thread handle
 |  | ||||||
|     } else { |  | ||||||
|         mutex->lock_thread = -1; |  | ||||||
|     } |  | ||||||
|     return mutex; |     return mutex; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -172,17 +141,14 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { | ||||||
|     return handle; |     return handle; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultVal<bool> Mutex::WaitSynchronization() { | bool Mutex::ShouldWait() { | ||||||
|     bool wait = locked; |     return locked && holding_thread != GetCurrentThread(); | ||||||
|     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); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return MakeResult<bool>(wait); |  | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void Mutex::Acquire() { | ||||||
|  |     _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); | ||||||
|  |     locked = true; | ||||||
|  |     MutexAcquireLock(this, GetCurrentThread()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
|  |  | ||||||
|  | @ -28,6 +28,6 @@ Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); | ||||||
|  * Releases all the mutexes held by the specified thread |  * Releases all the mutexes held by the specified thread | ||||||
|  * @param thread Thread that is holding the mutexes |  * @param thread Thread that is holding the mutexes | ||||||
|  */ |  */ | ||||||
| void ReleaseThreadMutexes(Handle thread); | void ReleaseThreadMutexes(Thread* thread); | ||||||
| 
 | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
| class Semaphore : public Object { | class Semaphore : public WaitObject { | ||||||
| public: | public: | ||||||
|     std::string GetTypeName() const override { return "Semaphore"; } |     std::string GetTypeName() const override { return "Semaphore"; } | ||||||
|     std::string GetName() const override { return name; } |     std::string GetName() const override { return name; } | ||||||
|  | @ -22,28 +22,15 @@ public: | ||||||
| 
 | 
 | ||||||
|     s32 max_count;                              ///< Maximum number of simultaneous holders the semaphore can have
 |     s32 max_count;                              ///< Maximum number of simultaneous holders the semaphore can have
 | ||||||
|     s32 available_count;                        ///< Number of free slots left in the semaphore
 |     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)
 |     std::string name;                           ///< Name of semaphore (optional)
 | ||||||
| 
 | 
 | ||||||
|     /**
 |     bool ShouldWait() override { | ||||||
|      * Tests whether a semaphore still has free slots |         return available_count <= 0; | ||||||
|      * @return Whether the semaphore is available |  | ||||||
|      */ |  | ||||||
|     bool IsAvailable() const { |  | ||||||
|         return available_count > 0; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ResultVal<bool> WaitSynchronization() override { |     void Acquire() override { | ||||||
|         bool wait = !IsAvailable(); |         _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); | ||||||
| 
 |         --available_count; | ||||||
|         if (wait) { |  | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_SEMA, this); |  | ||||||
|             waiting_threads.push(GetCurrentThread()->GetHandle()); |  | ||||||
|         } else { |  | ||||||
|             --available_count; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return MakeResult<bool>(wait); |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -83,12 +70,8 @@ ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { | ||||||
| 
 | 
 | ||||||
|     // Notify some of the threads that the semaphore has been released
 |     // 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
 |     // stop once the semaphore is full again or there are no more waiting threads
 | ||||||
|     while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) { |     while (!semaphore->ShouldWait() && semaphore->WakeupNextThread() != nullptr) { | ||||||
|         Thread* thread = Kernel::g_handle_table.Get<Thread>(semaphore->waiting_threads.front()).get(); |         semaphore->Acquire(); | ||||||
|         if (thread != nullptr) |  | ||||||
|             thread->ResumeFromWait(); |  | ||||||
|         semaphore->waiting_threads.pop(); |  | ||||||
|         --semaphore->available_count; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
|  |  | ||||||
|  | @ -41,7 +41,7 @@ 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 |  * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as | ||||||
|  * opposed to HLE simulations. |  * opposed to HLE simulations. | ||||||
|  */ |  */ | ||||||
| class Session : public Object { | class Session : public WaitObject { | ||||||
| public: | public: | ||||||
|     std::string GetTypeName() const override { return "Session"; } |     std::string GetTypeName() const override { return "Session"; } | ||||||
| 
 | 
 | ||||||
|  | @ -53,6 +53,17 @@ public: | ||||||
|      * aren't supported yet. |      * aren't supported yet. | ||||||
|      */ |      */ | ||||||
|     virtual ResultVal<bool> SyncRequest() = 0; |     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!"); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -22,17 +22,12 @@ | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
| ResultVal<bool> Thread::WaitSynchronization() { | bool Thread::ShouldWait() { | ||||||
|     const bool wait = status != THREADSTATUS_DORMANT; |     return 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); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     return MakeResult<bool>(wait); | void Thread::Acquire() { | ||||||
|  |     _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Lists all thread ids that aren't deleted/etc.
 | // Lists all thread ids that aren't deleted/etc.
 | ||||||
|  | @ -67,8 +62,8 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | ||||||
|     if (t->current_priority < lowest_priority) { |     if (t->current_priority < lowest_priority) { | ||||||
|         t->current_priority = t->initial_priority; |         t->current_priority = t->initial_priority; | ||||||
|     } |     } | ||||||
|     t->wait_type = WAITTYPE_NONE; | 
 | ||||||
|     t->wait_object = nullptr; |     t->wait_objects.clear(); | ||||||
|     t->wait_address = 0; |     t->wait_address = 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -88,37 +83,32 @@ static void ChangeReadyState(Thread* t, bool ready) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Check if a thread is blocking on a specified wait type
 | /// Check if a thread is waiting on a the specified wait object
 | ||||||
| static bool CheckWaitType(const Thread* thread, WaitType type) { | static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { | ||||||
|     return (type == thread->wait_type) && (thread->IsWaiting()); |     auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); | ||||||
|  | 
 | ||||||
|  |     if (itr != thread->wait_objects.end()) | ||||||
|  |         return thread->IsWaiting(); | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Check if a thread is blocking on a specified wait type with a specified handle
 | /// Check if the specified thread is waiting on the specified address to be arbitrated
 | ||||||
| static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) { | static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { | ||||||
|     return CheckWaitType(thread, type) && wait_object == thread->wait_object; |     return thread->IsWaiting() && thread->wait_objects.empty() && wait_address == thread->wait_address; | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// 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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Stops the current thread
 | /// Stops the current thread
 | ||||||
| void Thread::Stop(const char* reason) { | void Thread::Stop(const char* reason) { | ||||||
|     // Release all the mutexes that this thread holds
 |     // Release all the mutexes that this thread holds
 | ||||||
|     ReleaseThreadMutexes(GetHandle()); |     ReleaseThreadMutexes(this); | ||||||
| 
 | 
 | ||||||
|     ChangeReadyState(this, false); |     ChangeReadyState(this, false); | ||||||
|     status = THREADSTATUS_DORMANT; |     status = THREADSTATUS_DORMANT; | ||||||
|     for (auto& waiting_thread : waiting_threads) { |     WakeupAllWaitingThreads(); | ||||||
|         if (CheckWaitType(waiting_thread.get(), WAITTYPE_THREADEND, this)) |  | ||||||
|             waiting_thread->ResumeFromWait(); |  | ||||||
|     } |  | ||||||
|     waiting_threads.clear(); |  | ||||||
| 
 | 
 | ||||||
|     // Stopped threads are never waiting.
 |     // Stopped threads are never waiting.
 | ||||||
|     wait_type = WAITTYPE_NONE; |     wait_objects.clear(); | ||||||
|     wait_object = nullptr; |  | ||||||
|     wait_address = 0; |     wait_address = 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -129,26 +119,20 @@ static void ChangeThreadState(Thread* t, ThreadStatus new_status) { | ||||||
|     } |     } | ||||||
|     ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); |     ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); | ||||||
|     t->status = new_status; |     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
 | /// Arbitrate the highest priority thread that is waiting
 | ||||||
| Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { | Thread* ArbitrateHighestPriorityThread(u32 address) { | ||||||
|     Thread* highest_priority_thread = nullptr; |     Thread* highest_priority_thread = nullptr; | ||||||
|     s32 priority = THREADPRIO_LOWEST; |     s32 priority = THREADPRIO_LOWEST; | ||||||
| 
 | 
 | ||||||
|     // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
 |     // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
 | ||||||
|     for (auto& thread : thread_list) { |     for (auto& thread : thread_list) { | ||||||
|         if (!CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) |         if (!CheckWait_AddressArbiter(thread.get(), address)) | ||||||
|             continue; |             continue; | ||||||
| 
 | 
 | ||||||
|         if (thread == nullptr) |         if (thread == nullptr) | ||||||
|             continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
 |             continue; | ||||||
| 
 | 
 | ||||||
|         if(thread->current_priority <= priority) { |         if(thread->current_priority <= priority) { | ||||||
|             highest_priority_thread = thread.get(); |             highest_priority_thread = thread.get(); | ||||||
|  | @ -165,11 +149,11 @@ Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Arbitrate all threads currently waiting
 | /// 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...
 |     // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
 | ||||||
|     for (auto& thread : thread_list) { |     for (auto& thread : thread_list) { | ||||||
|         if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) |         if (CheckWait_AddressArbiter(thread.get(), address)) | ||||||
|             thread->ResumeFromWait(); |             thread->ResumeFromWait(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -177,9 +161,6 @@ void ArbitrateAllThreads(Object* arbiter, u32 address) { | ||||||
| /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields)
 | /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields)
 | ||||||
| static void CallThread(Thread* t) { | static void CallThread(Thread* t) { | ||||||
|     // Stop waiting
 |     // Stop waiting
 | ||||||
|     if (t->wait_type != WAITTYPE_NONE) { |  | ||||||
|         t->wait_type = WAITTYPE_NONE; |  | ||||||
|     } |  | ||||||
|     ChangeThreadState(t, THREADSTATUS_READY); |     ChangeThreadState(t, THREADSTATUS_READY); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -200,7 +181,6 @@ static void SwitchContext(Thread* t) { | ||||||
|         current_thread = t; |         current_thread = t; | ||||||
|         ChangeReadyState(t, false); |         ChangeReadyState(t, false); | ||||||
|         t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; |         t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; | ||||||
|         t->wait_type = WAITTYPE_NONE; |  | ||||||
|         Core::g_app_core->LoadContext(t->context); |         Core::g_app_core->LoadContext(t->context); | ||||||
|     } else { |     } else { | ||||||
|         current_thread = nullptr; |         current_thread = nullptr; | ||||||
|  | @ -223,16 +203,27 @@ static Thread* NextThread() { | ||||||
|     return next; |     return next; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void WaitCurrentThread(WaitType wait_type, Object* wait_object) { | void WaitCurrentThread_Sleep() { | ||||||
|     Thread* thread = GetCurrentThread(); |     Thread* thread = GetCurrentThread(); | ||||||
|     thread->wait_type = wait_type; |  | ||||||
|     thread->wait_object = wait_object; |  | ||||||
|     ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); |     ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) { | void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) { | ||||||
|     WaitCurrentThread(wait_type, wait_object); |     Thread* thread = GetCurrentThread(); | ||||||
|     GetCurrentThread()->wait_address = wait_address; |     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
 | /// Event type for the thread wake up event
 | ||||||
|  | @ -247,6 +238,12 @@ static void ThreadWakeupCallback(u64 parameter, int cycles_late) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | ||||||
|  |         ErrorSummary::StatusChanged, ErrorLevel::Info)); | ||||||
|  | 
 | ||||||
|  |     if (thread->wait_set_output) | ||||||
|  |         thread->SetWaitSynchronizationOutput(-1); | ||||||
|  | 
 | ||||||
|     thread->ResumeFromWait(); |     thread->ResumeFromWait(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -261,14 +258,63 @@ void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { | ||||||
|     CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); |     CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Resumes a thread from waiting by marking it as "ready"
 | 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(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void Thread::ResumeFromWait() { | void Thread::ResumeFromWait() { | ||||||
|     // Cancel any outstanding wakeup events
 |     // Cancel any outstanding wakeup events
 | ||||||
|     CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); |     CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); | ||||||
| 
 | 
 | ||||||
|     status &= ~THREADSTATUS_WAIT; |     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))) { |     if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | ||||||
|         ChangeReadyState(this, true); |         ChangeReadyState(this, true); | ||||||
|     } |     } | ||||||
|  | @ -334,8 +380,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | ||||||
|     thread->stack_size = stack_size; |     thread->stack_size = stack_size; | ||||||
|     thread->initial_priority = thread->current_priority = priority; |     thread->initial_priority = thread->current_priority = priority; | ||||||
|     thread->processor_id = processor_id; |     thread->processor_id = processor_id; | ||||||
|     thread->wait_type = WAITTYPE_NONE; |     thread->wait_set_output = false; | ||||||
|     thread->wait_object = nullptr; |     thread->wait_all = false; | ||||||
|  |     thread->wait_objects.clear(); | ||||||
|     thread->wait_address = 0; |     thread->wait_address = 0; | ||||||
|     thread->name = std::move(name); |     thread->name = std::move(name); | ||||||
| 
 | 
 | ||||||
|  | @ -419,13 +466,20 @@ void Reschedule() { | ||||||
|         LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); |         LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); | ||||||
| 
 | 
 | ||||||
|         for (auto& thread : thread_list) { |         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", |             LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X", thread->GetHandle(),  | ||||||
|                 thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, |                       thread->current_priority, thread->status); | ||||||
|                 (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE)); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void Thread::SetWaitSynchronizationResult(ResultCode result) { | ||||||
|  |     context.cpu_registers[0] = result.raw; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Thread::SetWaitSynchronizationOutput(s32 output) { | ||||||
|  |     context.cpu_registers[1] = output; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| 
 | 
 | ||||||
| void ThreadingInit() { | void ThreadingInit() { | ||||||
|  |  | ||||||
|  | @ -38,21 +38,9 @@ enum ThreadStatus { | ||||||
|     THREADSTATUS_WAITSUSPEND    = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND |     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 { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
| class Thread : public Kernel::Object { | class Thread : public WaitObject { | ||||||
| public: | public: | ||||||
|     static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, |     static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, | ||||||
|         u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size); |         u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size); | ||||||
|  | @ -70,7 +58,8 @@ public: | ||||||
|     inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } |     inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } | ||||||
|     inline bool IsIdle() const { return idle; } |     inline bool IsIdle() const { return idle; } | ||||||
| 
 | 
 | ||||||
|     ResultVal<bool> WaitSynchronization() override; |     bool ShouldWait() override; | ||||||
|  |     void Acquire() override; | ||||||
| 
 | 
 | ||||||
|     s32 GetPriority() const { return current_priority; } |     s32 GetPriority() const { return current_priority; } | ||||||
|     void SetPriority(s32 priority); |     void SetPriority(s32 priority); | ||||||
|  | @ -78,9 +67,28 @@ public: | ||||||
|     u32 GetThreadId() const { return thread_id; } |     u32 GetThreadId() const { return thread_id; } | ||||||
| 
 | 
 | ||||||
|     void Stop(const char* reason); |     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(); |     void ResumeFromWait(); | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * 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; |     Core::ThreadContext context; | ||||||
| 
 | 
 | ||||||
|     u32 thread_id; |     u32 thread_id; | ||||||
|  | @ -95,11 +103,11 @@ public: | ||||||
| 
 | 
 | ||||||
|     s32 processor_id; |     s32 processor_id; | ||||||
| 
 | 
 | ||||||
|     WaitType wait_type; |     std::vector<SharedPtr<WaitObject>> wait_objects; ///< Objects that the thread is waiting on
 | ||||||
|     Object* wait_object; |  | ||||||
|     VAddr wait_address; |  | ||||||
| 
 | 
 | ||||||
|     std::vector<SharedPtr<Thread>> waiting_threads; |     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; |     std::string name; | ||||||
| 
 | 
 | ||||||
|  | @ -107,6 +115,7 @@ public: | ||||||
|     bool idle = false; |     bool idle = false; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  | 
 | ||||||
|     Thread() = default; |     Thread() = default; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -117,38 +126,38 @@ SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); | ||||||
| void Reschedule(); | void Reschedule(); | ||||||
| 
 | 
 | ||||||
| /// Arbitrate the highest priority thread that is waiting
 | /// Arbitrate the highest priority thread that is waiting
 | ||||||
| Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address); | Thread* ArbitrateHighestPriorityThread(u32 address); | ||||||
| 
 | 
 | ||||||
| /// Arbitrate all threads currently waiting...
 | /// Arbitrate all threads currently waiting...
 | ||||||
| void ArbitrateAllThreads(Object* arbiter, u32 address); | void ArbitrateAllThreads(u32 address); | ||||||
| 
 | 
 | ||||||
| /// Gets the current thread
 | /// Gets the current thread
 | ||||||
| Thread* GetCurrentThread(); | Thread* GetCurrentThread(); | ||||||
| 
 | 
 | ||||||
|  | /// Waits the current thread on a sleep
 | ||||||
|  | void WaitCurrentThread_Sleep(); | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Puts the current thread in the wait state for the given type |  * Waits the current thread from a WaitSynchronization call | ||||||
|  * @param wait_type Type of wait |  * @param wait_object Kernel object that we are waiting on | ||||||
|  * @param wait_object Kernel object that we are waiting on, defaults to current thread |  * @param wait_set_output If true, set the output parameter on thread wakeup (for WaitSynchronizationN only) | ||||||
|  |  * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only) | ||||||
|  */ |  */ | ||||||
| void WaitCurrentThread(WaitType wait_type, Object* wait_object = GetCurrentThread()); | void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Waits the current thread from an ArbitrateAddress call | ||||||
|  |  * @param wait_address Arbitration address used to resume from wait | ||||||
|  |  */ | ||||||
|  | void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Schedules an event to wake up the specified thread after the specified delay. |  * Schedules an event to wake up the specified thread after the specified delay. | ||||||
|  * @param thread The thread to wake after the delay. |  * @param handle The thread handle. | ||||||
|  * @param nanoseconds The time this thread will be allowed to sleep for. |  * @param nanoseconds The time this thread will be allowed to sleep for. | ||||||
|  */ |  */ | ||||||
| void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); | void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * 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 |  | ||||||
|  * @param wait_address Arbitration address used to resume from wait |  | ||||||
|  */ |  | ||||||
| void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * Sets up the idle thread, this is a thread that is intended to never execute instructions, |  * Sets up the idle thread, this is a thread that is intended to never execute instructions, | ||||||
|  * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue |  * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue | ||||||
|  | @ -156,6 +165,7 @@ void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_addre | ||||||
|  * @returns The handle of the idle thread |  * @returns The handle of the idle thread | ||||||
|  */ |  */ | ||||||
| Handle SetupIdleThread(); | Handle SetupIdleThread(); | ||||||
|  | 
 | ||||||
| /// Initialize threading
 | /// Initialize threading
 | ||||||
| void ThreadingInit(); | void ThreadingInit(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
| class Timer : public Object { | class Timer : public WaitObject { | ||||||
| public: | public: | ||||||
|     std::string GetTypeName() const override { return "Timer"; } |     std::string GetTypeName() const override { return "Timer"; } | ||||||
|     std::string GetName() const override { return name; } |     std::string GetName() const override { return name; } | ||||||
|  | @ -24,19 +24,17 @@ public: | ||||||
|     ResetType reset_type;                   ///< The ResetType of this timer
 |     ResetType reset_type;                   ///< The ResetType of this timer
 | ||||||
| 
 | 
 | ||||||
|     bool signaled;                          ///< Whether the timer has been signaled or not
 |     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)
 |     std::string name;                       ///< Name of timer (optional)
 | ||||||
| 
 | 
 | ||||||
|     u64 initial_delay;                      ///< The delay until the timer fires for the first time
 |     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
 |     u64 interval_delay;                     ///< The delay until the timer fires after the first time
 | ||||||
| 
 | 
 | ||||||
|     ResultVal<bool> WaitSynchronization() override { |     bool ShouldWait() override { | ||||||
|         bool wait = !signaled; |         return !signaled; | ||||||
|         if (wait) { |     } | ||||||
|             waiting_threads.insert(GetCurrentThread()->GetHandle()); | 
 | ||||||
|             Kernel::WaitCurrentThread(WAITTYPE_TIMER, this); |     void Acquire() override { | ||||||
|         } |         _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); | ||||||
|         return MakeResult<bool>(wait); |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -92,12 +90,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { | ||||||
|     timer->signaled = true; |     timer->signaled = true; | ||||||
| 
 | 
 | ||||||
|     // Resume all waiting threads
 |     // Resume all waiting threads
 | ||||||
|     for (Handle thread_handle : timer->waiting_threads) { |     timer->WakeupAllWaitingThreads(); | ||||||
|         if (SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(thread_handle)) |  | ||||||
|             thread->ResumeFromWait(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     timer->waiting_threads.clear(); |  | ||||||
| 
 | 
 | ||||||
|     if (timer->reset_type == RESETTYPE_ONESHOT) |     if (timer->reset_type == RESETTYPE_ONESHOT) | ||||||
|         timer->signaled = false; |         timer->signaled = false; | ||||||
|  |  | ||||||
|  | @ -50,8 +50,8 @@ void Initialize(Service::Interface* self) { | ||||||
|     cmd_buff[3] = notification_event_handle; |     cmd_buff[3] = notification_event_handle; | ||||||
|     cmd_buff[4] = pause_event_handle; |     cmd_buff[4] = pause_event_handle; | ||||||
| 
 | 
 | ||||||
|     Kernel::SetEventLocked(notification_event_handle, true); |     Kernel::ClearEvent(notification_event_handle); | ||||||
|     Kernel::SetEventLocked(pause_event_handle, false); // Fire start event
 |     Kernel::SignalEvent(pause_event_handle); // Fire start event
 | ||||||
| 
 | 
 | ||||||
|     _assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock"); |     _assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock"); | ||||||
|     Kernel::ReleaseMutex(lock_handle); |     Kernel::ReleaseMutex(lock_handle); | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ static void GetProcSemaphore(Service::Interface* self) { | ||||||
| 
 | 
 | ||||||
|     // TODO(bunnei): Change to a semaphore once these have been implemented
 |     // TODO(bunnei): Change to a semaphore once these have been implemented
 | ||||||
|     g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event"); |     g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event"); | ||||||
|     Kernel::SetEventLocked(g_event_handle, false); |     Kernel::ClearEvent(g_event_handle); | ||||||
| 
 | 
 | ||||||
|     cmd_buff[1] = 0; // No error
 |     cmd_buff[1] = 0; // No error
 | ||||||
|     cmd_buff[3] = g_event_handle; |     cmd_buff[3] = g_event_handle; | ||||||
|  |  | ||||||
|  | @ -29,6 +29,9 @@ using Kernel::SharedPtr; | ||||||
| 
 | 
 | ||||||
| namespace SVC { | namespace SVC { | ||||||
| 
 | 
 | ||||||
|  | /// An invalid result code that is meant to be overwritten when a thread resumes from waiting
 | ||||||
|  | const ResultCode RESULT_INVALID(0xDEADC0DE); | ||||||
|  | 
 | ||||||
| enum ControlMemoryOperation { | enum ControlMemoryOperation { | ||||||
|     MEMORY_OPERATION_HEAP       = 0x00000003, |     MEMORY_OPERATION_HEAP       = 0x00000003, | ||||||
|     MEMORY_OPERATION_GSP_HEAP   = 0x00010003, |     MEMORY_OPERATION_GSP_HEAP   = 0x00010003, | ||||||
|  | @ -103,12 +106,7 @@ static Result SendSyncRequest(Handle handle) { | ||||||
| 
 | 
 | ||||||
|     LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); |     LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); | ||||||
| 
 | 
 | ||||||
|     ResultVal<bool> wait = session->SyncRequest(); |     return session->SyncRequest().Code().raw; | ||||||
|     if (wait.Succeeded() && *wait) { |  | ||||||
|         Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return wait.Code().raw; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Close a handle
 | /// Close a handle
 | ||||||
|  | @ -120,64 +118,122 @@ static Result CloseHandle(Handle handle) { | ||||||
| 
 | 
 | ||||||
| /// Wait for a handle to synchronize, timeout after the specified nanoseconds
 | /// Wait for a handle to synchronize, timeout after the specified nanoseconds
 | ||||||
| static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { | static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { | ||||||
|     SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handle); |     auto object = Kernel::g_handle_table.GetWaitObject(handle); | ||||||
|     if (object == nullptr) |     if (object == nullptr) | ||||||
|         return InvalidHandle(ErrorModule::Kernel).raw; |         return InvalidHandle(ErrorModule::Kernel).raw; | ||||||
| 
 | 
 | ||||||
|     LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, |     LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, | ||||||
|             object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); |             object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); | ||||||
| 
 | 
 | ||||||
|     ResultVal<bool> wait = object->WaitSynchronization(); |  | ||||||
| 
 |  | ||||||
|     // Check for next thread to schedule
 |     // Check for next thread to schedule
 | ||||||
|     if (wait.Succeeded() && *wait) { |     if (object->ShouldWait()) { | ||||||
|  | 
 | ||||||
|  |         object->AddWaitingThread(Kernel::GetCurrentThread()); | ||||||
|  |         Kernel::WaitCurrentThread_WaitSynchronization(object, false, false); | ||||||
|  | 
 | ||||||
|         // Create an event to wake the thread up after the specified nanosecond delay has passed
 |         // Create an event to wake the thread up after the specified nanosecond delay has passed
 | ||||||
|         Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); |         Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); | ||||||
|  | 
 | ||||||
|         HLE::Reschedule(__func__); |         HLE::Reschedule(__func__); | ||||||
|  | 
 | ||||||
|  |         // NOTE: output of this SVC will be set later depending on how the thread resumes
 | ||||||
|  |         return RESULT_INVALID.raw; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return wait.Code().raw; |     object->Acquire(); | ||||||
|  | 
 | ||||||
|  |     return RESULT_SUCCESS.raw; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Wait for the given handles to synchronize, timeout after the specified nanoseconds
 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds
 | ||||||
| static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, | static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) { | ||||||
|     s64 nano_seconds) { |     bool wait_thread = !wait_all; | ||||||
|  |     int handle_index = 0; | ||||||
| 
 | 
 | ||||||
|     // TODO(bunnei): Do something with nano_seconds, currently ignoring this
 |     // Check if 'handles' is invalid
 | ||||||
|     bool unlock_all = true; |     if (handles == nullptr) | ||||||
|     bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
 |         return ResultCode(ErrorDescription::InvalidPointer, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent).raw; | ||||||
| 
 | 
 | ||||||
|     LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld", |     // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If
 | ||||||
|         handle_count, (wait_all ? "true" : "false"), nano_seconds); |     // this happens, the running application will crash.
 | ||||||
|  |     _assert_msg_(Kernel, out != nullptr, "invalid output pointer specified!"); | ||||||
| 
 | 
 | ||||||
|     // Iterate through each handle, synchronize kernel object
 |     // Check if 'handle_count' is invalid
 | ||||||
|     for (s32 i = 0; i < handle_count; i++) { |     if (handle_count < 0) | ||||||
|         SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[i]); |         return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw; | ||||||
|         if (object == nullptr) |  | ||||||
|             return InvalidHandle(ErrorModule::Kernel).raw; |  | ||||||
| 
 | 
 | ||||||
|         LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], |     // If 'handle_count' is non-zero, iterate through each handle and wait the current thread if
 | ||||||
|                 object->GetTypeName().c_str(), object->GetName().c_str()); |     // necessary
 | ||||||
|  |     if (handle_count != 0) { | ||||||
|  |         bool selected = false; // True once an object has been selected
 | ||||||
|  |         for (int i = 0; i < handle_count; ++i) { | ||||||
|  |             auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); | ||||||
|  |             if (object == nullptr) | ||||||
|  |                 return InvalidHandle(ErrorModule::Kernel).raw; | ||||||
| 
 | 
 | ||||||
|         // TODO(yuriks): Verify how the real function behaves when an error happens here
 |             // Check if the current thread should wait on this object...
 | ||||||
|         ResultVal<bool> wait_result = object->WaitSynchronization(); |             if (object->ShouldWait()) { | ||||||
|         bool wait = wait_result.Succeeded() && *wait_result; |  | ||||||
| 
 | 
 | ||||||
|         if (!wait && !wait_all) { |                 // Check we are waiting on all objects...
 | ||||||
|             *out = i; |                 if (wait_all) | ||||||
|             return RESULT_SUCCESS.raw; |                     // Wait the thread
 | ||||||
|         } else { |                     wait_thread = true; | ||||||
|             unlock_all = false; |             } else { | ||||||
|  |                 // Do not wait on this object, check if this object should be selected...
 | ||||||
|  |                 if (!wait_all && !selected) { | ||||||
|  |                     // Do not wait the thread
 | ||||||
|  |                     wait_thread = false; | ||||||
|  |                     handle_index = i; | ||||||
|  |                     selected = true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         // If no handles were passed in, put the thread to sleep only when 'wait_all' is false
 | ||||||
|  |         // NOTE: This should deadlock the current thread if no timeout was specified
 | ||||||
|  |         if (!wait_all) { | ||||||
|  |             wait_thread = true; | ||||||
|  |             Kernel::WaitCurrentThread_WaitSynchronization(nullptr, true, wait_all); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (wait_all && unlock_all) { |     // If thread should wait, then set its state to waiting and then reschedule...
 | ||||||
|         *out = handle_count; |     if (wait_thread) { | ||||||
|         return RESULT_SUCCESS.raw; | 
 | ||||||
|  |         // Actually wait the current thread on each object if we decided to wait...
 | ||||||
|  |         for (int i = 0; i < handle_count; ++i) { | ||||||
|  |             auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); | ||||||
|  |             object->AddWaitingThread(Kernel::GetCurrentThread()); | ||||||
|  |             Kernel::WaitCurrentThread_WaitSynchronization(object, true, wait_all); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Create an event to wake the thread up after the specified nanosecond delay has passed
 | ||||||
|  |         Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); | ||||||
|  | 
 | ||||||
|  |         HLE::Reschedule(__func__); | ||||||
|  | 
 | ||||||
|  |         // NOTE: output of this SVC will be set later depending on how the thread resumes
 | ||||||
|  |         return RESULT_INVALID.raw; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Check for next thread to schedule
 |     // Acquire objects if we did not wait...
 | ||||||
|     HLE::Reschedule(__func__); |     for (int i = 0; i < handle_count; ++i) { | ||||||
|  |         auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); | ||||||
|  | 
 | ||||||
|  |         // Acquire the object if it is not waiting...
 | ||||||
|  |         if (!object->ShouldWait()) { | ||||||
|  |             object->Acquire(); | ||||||
|  | 
 | ||||||
|  |             // If this was the first non-waiting object and 'wait_all' is false, don't acquire
 | ||||||
|  |             // any other objects
 | ||||||
|  |             if (!wait_all) | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does
 | ||||||
|  |     // not seem to set it to any meaningful value.
 | ||||||
|  |     *out = wait_all ? 0 : handle_index; | ||||||
| 
 | 
 | ||||||
|     return RESULT_SUCCESS.raw; |     return RESULT_SUCCESS.raw; | ||||||
| } | } | ||||||
|  | @ -351,6 +407,7 @@ static Result DuplicateHandle(Handle* out, Handle handle) { | ||||||
| /// Signals an event
 | /// Signals an event
 | ||||||
| static Result SignalEvent(Handle evt) { | static Result SignalEvent(Handle evt) { | ||||||
|     LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt); |     LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt); | ||||||
|  |     HLE::Reschedule(__func__); | ||||||
|     return Kernel::SignalEvent(evt).raw; |     return Kernel::SignalEvent(evt).raw; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -391,7 +448,7 @@ static void SleepThread(s64 nanoseconds) { | ||||||
|     LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); |     LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); | ||||||
| 
 | 
 | ||||||
|     // Sleep current thread and check for next thread to schedule
 |     // Sleep current thread and check for next thread to schedule
 | ||||||
|     Kernel::WaitCurrentThread(WAITTYPE_SLEEP); |     Kernel::WaitCurrentThread_Sleep(); | ||||||
| 
 | 
 | ||||||
|     // Create an event to wake the thread up after the specified nanosecond delay has passed
 |     // Create an event to wake the thread up after the specified nanosecond delay has passed
 | ||||||
|     Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nanoseconds); |     Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nanoseconds); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue