Przeglądaj źródła

Kernel/Arbiters: Mostly implement SignalToAddress

Michael Scire 8 lat temu
rodzic
commit
4f81bc4e1b

+ 104 - 6
src/core/hle/kernel/address_arbiter.cpp

@@ -27,24 +27,122 @@ namespace Kernel {
             current_thread->WakeAfterDelay(timeout);
 
             Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule();
-            return RESULT_SUCCESS;
+            return current_thread->arb_wait_result;
+        }
+
+        // Gets the threads waiting on an address.
+        void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>> &waiting_threads, VAddr address) {
+            auto RetrieveWaitingThreads =
+                [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) {
+                const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
+                auto& thread_list = scheduler->GetThreadList();
+
+                for (auto& thread : thread_list) {
+                    if (thread->arb_wait_address == arb_addr)
+                        waiting_threads.push_back(thread);
+                }
+            };
+
+            // Retrieve a list of all threads that are waiting for this address.
+            RetrieveWaitingThreads(0, waiting_threads, address);
+            RetrieveWaitingThreads(1, waiting_threads, address);
+            RetrieveWaitingThreads(2, waiting_threads, address);
+            RetrieveWaitingThreads(3, waiting_threads, address);
+            // Sort them by priority, such that the highest priority ones come first.
+            std::sort(waiting_threads.begin(), waiting_threads.end(),
+                [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
+                return lhs->current_priority < rhs->current_priority;
+            });
+        }
+
+        // Wake up num_to_wake (or all) threads in a vector.
+        void WakeThreads(std::vector<SharedPtr<Thread>> &waiting_threads, s32 num_to_wake) {
+            // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
+            // them all.
+            size_t last = waiting_threads.size();
+            if (num_to_wake > 0)
+                last = num_to_wake;
+
+            // Signal the waiting threads.
+            // TODO: Rescheduling should not occur while waking threads. How can it be prevented?
+            for (size_t i = 0; i < last; i++) {
+                ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB);
+                waiting_threads[i]->arb_wait_result = RESULT_SUCCESS;
+                waiting_threads[i]->ResumeFromWait();
+            }
+
         }
 
         // Signals an address being waited on.
-        ResultCode SignalToAddress(VAddr address, s32 value, s32 num_to_wake) {
-            // TODO
+        ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
+            // Get threads waiting on the address.
+            std::vector<SharedPtr<Thread>> waiting_threads;
+            GetThreadsWaitingOnAddress(waiting_threads, address);
+
+            WakeThreads(waiting_threads, num_to_wake);
             return RESULT_SUCCESS;
         }
 
         // Signals an address being waited on and increments its value if equal to the value argument.
         ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) {
-            // TODO
-            return RESULT_SUCCESS;
+            // Ensure that we can write to the address.
+            if (!Memory::IsValidVirtualAddress(address)) {
+                return ERR_INVALID_ADDRESS_STATE;
+            }
+
+            s32 cur_value;
+            // Get value, incrementing if equal.
+            {
+                // Increment if Equal must be an atomic operation.
+                std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+                cur_value = (s32)Memory::Read32(address);
+                if (cur_value == value) {
+                    Memory::Write32(address, (u32)(cur_value + 1));
+                }
+            }
+            if (cur_value != value) {
+                return ERR_INVALID_STATE;
+            }
+
+            return SignalToAddress(address, num_to_wake);
         }
 
         // Signals an address being waited on and modifies its value based on waiting thread count if equal to the value argument.
         ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) {
-            // TODO
+            // Ensure that we can write to the address.
+            if (!Memory::IsValidVirtualAddress(address)) {
+                return ERR_INVALID_ADDRESS_STATE;
+            }
+
+            // Get threads waiting on the address.
+            std::vector<SharedPtr<Thread>> waiting_threads;
+            GetThreadsWaitingOnAddress(waiting_threads, address);
+
+            // Determine the modified value depending on the waiting count.
+            s32 updated_value;
+            if (waiting_threads.size() == 0) {
+                updated_value = value - 1;
+            } else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) {
+                updated_value = value + 1;
+            } else {
+                updated_value = value;
+            }
+            s32 cur_value;
+            // Perform an atomic update if equal.
+            {
+                std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+                cur_value = (s32)Memory::Read32(address);
+                if (cur_value == value) {
+                    Memory::Write32(address, (u32)(updated_value));
+                }
+            }
+
+            // Only continue if equal.
+            if (cur_value != value) {
+                return ERR_INVALID_STATE;
+            }
+
+            WakeThreads(waiting_threads, num_to_wake);
             return RESULT_SUCCESS;
         }
 

+ 1 - 1
src/core/hle/kernel/address_arbiter.h

@@ -21,7 +21,7 @@ namespace Kernel {
             ModifyByWaitingCountAndSignalIfEqual = 2,
         };
 
-        ResultCode SignalToAddress(VAddr address, s32 value, s32 num_to_wake);
+        ResultCode SignalToAddress(VAddr address, s32 num_to_wake);
         ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
         ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
 

+ 1 - 1
src/core/hle/kernel/svc.cpp

@@ -726,7 +726,7 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
 
     switch ((AddressArbiter::SignalType)type) {
         case AddressArbiter::SignalType::Signal:
-            return AddressArbiter::SignalToAddress(address, value, num_to_wake);
+            return AddressArbiter::SignalToAddress(address, num_to_wake);
         case AddressArbiter::SignalType::IncrementAndSignalIfEqual:
             return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
         case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual:

+ 4 - 2
src/core/hle/kernel/thread.h

@@ -230,8 +230,10 @@ public:
     VAddr condvar_wait_address;
     VAddr mutex_wait_address;   ///< If waiting on a Mutex, this is the mutex address
     Handle wait_handle;         ///< The handle used to wait for the mutex.
-    VAddr arb_wait_address;     ///< If waiting for an AddressArbiter, this is the address
-    ResultCode arb_wait_result; ///< If waiting for an AddressArbiter, this is the result that will be returned.
+
+    // If waiting for an AddressArbiter, this is the address being waited on.
+    VAddr arb_wait_address;
+    ResultCode arb_wait_result{RESULT_SUCCESS}; ///< Result returned when done waiting on AddressArbiter.
 
     std::string name;
 

+ 1 - 1
src/yuzu/debugger/wait_tree.cpp

@@ -213,7 +213,7 @@ QString WaitTreeThread::GetText() const {
     case THREADSTATUS_WAIT_MUTEX:
         status = tr("waiting for mutex");
         break;
-    case THREADSTATUS_WAIT_MUTEX:
+    case THREADSTATUS_WAIT_ARB:
         status = tr("waiting for address arbiter");
         break;
     case THREADSTATUS_DORMANT: