Merge pull request #774 from Subv/atomic_signal

Kernel/SVC: Perform atomic accesses in SignalProcessWideKey as per the real kernel.
This commit is contained in:
bunnei 2018-07-22 12:26:03 -07:00 committed by GitHub
commit a4b2af7382
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -650,12 +650,27 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
ASSERT(thread->condvar_wait_address == condition_variable_addr); ASSERT(thread->condvar_wait_address == condition_variable_addr);
// If the mutex is not yet acquired, acquire it. size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
u32 mutex_val = Memory::Read32(thread->mutex_wait_address);
auto& monitor = Core::System::GetInstance().Monitor();
// Atomically read the value of the mutex.
u32 mutex_val = 0;
do {
monitor.SetExclusive(current_core, thread->mutex_wait_address);
// If the mutex is not yet acquired, acquire it.
mutex_val = Memory::Read32(thread->mutex_wait_address);
if (mutex_val != 0) {
monitor.ClearExclusive();
break;
}
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
thread->wait_handle));
if (mutex_val == 0) { if (mutex_val == 0) {
// We were able to acquire the mutex, resume this thread. // We were able to acquire the mutex, resume this thread.
Memory::Write32(thread->mutex_wait_address, thread->wait_handle);
ASSERT(thread->status == ThreadStatus::WaitMutex); ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->ResumeFromWait(); thread->ResumeFromWait();
@ -668,7 +683,19 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
thread->condvar_wait_address = 0; thread->condvar_wait_address = 0;
thread->wait_handle = 0; thread->wait_handle = 0;
} else { } else {
// Couldn't acquire the mutex, block the thread. // Atomically signal that the mutex now has a waiting thread.
do {
monitor.SetExclusive(current_core, thread->mutex_wait_address);
// Ensure that the mutex value is still what we expect.
u32 value = Memory::Read32(thread->mutex_wait_address);
// TODO(Subv): When this happens, the kernel just clears the exclusive state and
// retries the initial read for this thread.
ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case");
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
mutex_val | Mutex::MutexHasWaitersFlag));
// The mutex is already owned by some other thread, make this thread wait on it.
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
auto owner = g_handle_table.Get<Thread>(owner_handle); auto owner = g_handle_table.Get<Thread>(owner_handle);
ASSERT(owner); ASSERT(owner);
@ -676,9 +703,6 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
thread->status = ThreadStatus::WaitMutex; thread->status = ThreadStatus::WaitMutex;
thread->wakeup_callback = nullptr; thread->wakeup_callback = nullptr;
// Signal that the mutex now has a waiting thread.
Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
owner->AddMutexWaiter(thread); owner->AddMutexWaiter(thread);
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();