Browse Source
core: hle: Integrate new KConditionVariable and KAddressArbiter implementations.
nce_cpp
core: hle: Integrate new KConditionVariable and KAddressArbiter implementations.
nce_cpp
15 changed files with 508 additions and 1182 deletions
-
4src/core/CMakeLists.txt
-
317src/core/hle/kernel/address_arbiter.cpp
-
91src/core/hle/kernel/address_arbiter.h
-
4src/core/hle/kernel/k_synchronization_object.cpp
-
170src/core/hle/kernel/mutex.cpp
-
42src/core/hle/kernel/mutex.h
-
48src/core/hle/kernel/process.cpp
-
50src/core/hle/kernel/process.h
-
364src/core/hle/kernel/svc.cpp
-
1src/core/hle/kernel/svc_common.h
-
38src/core/hle/kernel/svc_wrap.h
-
227src/core/hle/kernel/thread.cpp
-
318src/core/hle/kernel/thread.h
-
6src/core/hle/kernel/time_manager.cpp
-
10src/yuzu/debugger/wait_tree.cpp
@ -1,317 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <algorithm>
|
|||
#include <vector>
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "common/common_types.h"
|
|||
#include "core/arm/exclusive_monitor.h"
|
|||
#include "core/core.h"
|
|||
#include "core/hle/kernel/address_arbiter.h"
|
|||
#include "core/hle/kernel/errors.h"
|
|||
#include "core/hle/kernel/handle_table.h"
|
|||
#include "core/hle/kernel/k_scheduler.h"
|
|||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
|||
#include "core/hle/kernel/kernel.h"
|
|||
#include "core/hle/kernel/thread.h"
|
|||
#include "core/hle/kernel/time_manager.h"
|
|||
#include "core/hle/result.h"
|
|||
#include "core/memory.h"
|
|||
|
|||
namespace Kernel { |
|||
|
|||
// Wake up num_to_wake (or all) threads in a vector.
|
|||
void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, |
|||
s32 num_to_wake) { |
|||
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
|
|||
// them all.
|
|||
std::size_t last = waiting_threads.size(); |
|||
if (num_to_wake > 0) { |
|||
last = std::min(last, static_cast<std::size_t>(num_to_wake)); |
|||
} |
|||
|
|||
// Signal the waiting threads.
|
|||
for (std::size_t i = 0; i < last; i++) { |
|||
waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS); |
|||
RemoveThread(waiting_threads[i]); |
|||
waiting_threads[i]->WaitForArbitration(false); |
|||
waiting_threads[i]->Wakeup(); |
|||
} |
|||
} |
|||
|
|||
AddressArbiter::AddressArbiter(Core::System& system) : system{system} {} |
|||
AddressArbiter::~AddressArbiter() = default; |
|||
|
|||
ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value, |
|||
s32 num_to_wake) { |
|||
switch (type) { |
|||
case SignalType::Signal: |
|||
return SignalToAddressOnly(address, num_to_wake); |
|||
case SignalType::IncrementAndSignalIfEqual: |
|||
return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake); |
|||
case SignalType::ModifyByWaitingCountAndSignalIfEqual: |
|||
return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake); |
|||
default: |
|||
return ERR_INVALID_ENUM_VALUE; |
|||
} |
|||
} |
|||
|
|||
ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { |
|||
KScopedSchedulerLock lock(system.Kernel()); |
|||
const std::vector<std::shared_ptr<Thread>> waiting_threads = |
|||
GetThreadsWaitingOnAddress(address); |
|||
WakeThreads(waiting_threads, num_to_wake); |
|||
return RESULT_SUCCESS; |
|||
} |
|||
|
|||
ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, |
|||
s32 num_to_wake) { |
|||
KScopedSchedulerLock lock(system.Kernel()); |
|||
auto& memory = system.Memory(); |
|||
|
|||
// Ensure that we can write to the address.
|
|||
if (!memory.IsValidVirtualAddress(address)) { |
|||
return ERR_INVALID_ADDRESS_STATE; |
|||
} |
|||
|
|||
const std::size_t current_core = system.CurrentCoreIndex(); |
|||
auto& monitor = system.Monitor(); |
|||
u32 current_value; |
|||
do { |
|||
current_value = monitor.ExclusiveRead32(current_core, address); |
|||
|
|||
if (current_value != static_cast<u32>(value)) { |
|||
return ERR_INVALID_STATE; |
|||
} |
|||
current_value++; |
|||
} while (!monitor.ExclusiveWrite32(current_core, address, current_value)); |
|||
|
|||
return SignalToAddressOnly(address, num_to_wake); |
|||
} |
|||
|
|||
ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, |
|||
s32 num_to_wake) { |
|||
KScopedSchedulerLock lock(system.Kernel()); |
|||
auto& memory = system.Memory(); |
|||
|
|||
// Ensure that we can write to the address.
|
|||
if (!memory.IsValidVirtualAddress(address)) { |
|||
return ERR_INVALID_ADDRESS_STATE; |
|||
} |
|||
|
|||
// Get threads waiting on the address.
|
|||
const std::vector<std::shared_ptr<Thread>> waiting_threads = |
|||
GetThreadsWaitingOnAddress(address); |
|||
|
|||
const std::size_t current_core = system.CurrentCoreIndex(); |
|||
auto& monitor = system.Monitor(); |
|||
s32 updated_value; |
|||
do { |
|||
updated_value = monitor.ExclusiveRead32(current_core, address); |
|||
|
|||
if (updated_value != value) { |
|||
return ERR_INVALID_STATE; |
|||
} |
|||
// Determine the modified value depending on the waiting count.
|
|||
if (num_to_wake <= 0) { |
|||
if (waiting_threads.empty()) { |
|||
updated_value = value + 1; |
|||
} else { |
|||
updated_value = value - 1; |
|||
} |
|||
} else { |
|||
if (waiting_threads.empty()) { |
|||
updated_value = value + 1; |
|||
} else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) { |
|||
updated_value = value - 1; |
|||
} else { |
|||
updated_value = value; |
|||
} |
|||
} |
|||
} while (!monitor.ExclusiveWrite32(current_core, address, updated_value)); |
|||
|
|||
WakeThreads(waiting_threads, num_to_wake); |
|||
return RESULT_SUCCESS; |
|||
} |
|||
|
|||
ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value, |
|||
s64 timeout_ns) { |
|||
switch (type) { |
|||
case ArbitrationType::WaitIfLessThan: |
|||
return WaitForAddressIfLessThan(address, value, timeout_ns, false); |
|||
case ArbitrationType::DecrementAndWaitIfLessThan: |
|||
return WaitForAddressIfLessThan(address, value, timeout_ns, true); |
|||
case ArbitrationType::WaitIfEqual: |
|||
return WaitForAddressIfEqual(address, value, timeout_ns); |
|||
default: |
|||
return ERR_INVALID_ENUM_VALUE; |
|||
} |
|||
} |
|||
|
|||
ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, |
|||
bool should_decrement) { |
|||
auto& memory = system.Memory(); |
|||
auto& kernel = system.Kernel(); |
|||
Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread(); |
|||
|
|||
Handle event_handle = InvalidHandle; |
|||
{ |
|||
KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); |
|||
|
|||
if (current_thread->IsTerminationRequested()) { |
|||
lock.CancelSleep(); |
|||
return ERR_THREAD_TERMINATING; |
|||
} |
|||
|
|||
// Ensure that we can read the address.
|
|||
if (!memory.IsValidVirtualAddress(address)) { |
|||
lock.CancelSleep(); |
|||
return ERR_INVALID_ADDRESS_STATE; |
|||
} |
|||
|
|||
s32 current_value = static_cast<s32>(memory.Read32(address)); |
|||
if (current_value >= value) { |
|||
lock.CancelSleep(); |
|||
return ERR_INVALID_STATE; |
|||
} |
|||
|
|||
current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); |
|||
|
|||
s32 decrement_value; |
|||
|
|||
const std::size_t current_core = system.CurrentCoreIndex(); |
|||
auto& monitor = system.Monitor(); |
|||
do { |
|||
current_value = static_cast<s32>(monitor.ExclusiveRead32(current_core, address)); |
|||
if (should_decrement) { |
|||
decrement_value = current_value - 1; |
|||
} else { |
|||
decrement_value = current_value; |
|||
} |
|||
} while ( |
|||
!monitor.ExclusiveWrite32(current_core, address, static_cast<u32>(decrement_value))); |
|||
|
|||
// Short-circuit without rescheduling, if timeout is zero.
|
|||
if (timeout == 0) { |
|||
lock.CancelSleep(); |
|||
return RESULT_TIMEOUT; |
|||
} |
|||
|
|||
current_thread->SetArbiterWaitAddress(address); |
|||
InsertThread(SharedFrom(current_thread)); |
|||
current_thread->SetState(ThreadState::Waiting); |
|||
current_thread->WaitForArbitration(true); |
|||
} |
|||
|
|||
if (event_handle != InvalidHandle) { |
|||
auto& time_manager = kernel.TimeManager(); |
|||
time_manager.UnscheduleTimeEvent(event_handle); |
|||
} |
|||
|
|||
{ |
|||
KScopedSchedulerLock lock(kernel); |
|||
if (current_thread->IsWaitingForArbitration()) { |
|||
RemoveThread(SharedFrom(current_thread)); |
|||
current_thread->WaitForArbitration(false); |
|||
} |
|||
} |
|||
|
|||
return current_thread->GetSignalingResult(); |
|||
} |
|||
|
|||
ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { |
|||
auto& memory = system.Memory(); |
|||
auto& kernel = system.Kernel(); |
|||
Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread(); |
|||
|
|||
Handle event_handle = InvalidHandle; |
|||
{ |
|||
KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); |
|||
|
|||
if (current_thread->IsTerminationRequested()) { |
|||
lock.CancelSleep(); |
|||
return ERR_THREAD_TERMINATING; |
|||
} |
|||
|
|||
// Ensure that we can read the address.
|
|||
if (!memory.IsValidVirtualAddress(address)) { |
|||
lock.CancelSleep(); |
|||
return ERR_INVALID_ADDRESS_STATE; |
|||
} |
|||
|
|||
s32 current_value = static_cast<s32>(memory.Read32(address)); |
|||
if (current_value != value) { |
|||
lock.CancelSleep(); |
|||
return ERR_INVALID_STATE; |
|||
} |
|||
|
|||
// Short-circuit without rescheduling, if timeout is zero.
|
|||
if (timeout == 0) { |
|||
lock.CancelSleep(); |
|||
return RESULT_TIMEOUT; |
|||
} |
|||
|
|||
current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); |
|||
current_thread->SetArbiterWaitAddress(address); |
|||
InsertThread(SharedFrom(current_thread)); |
|||
current_thread->SetState(ThreadState::Waiting); |
|||
current_thread->WaitForArbitration(true); |
|||
} |
|||
|
|||
if (event_handle != InvalidHandle) { |
|||
auto& time_manager = kernel.TimeManager(); |
|||
time_manager.UnscheduleTimeEvent(event_handle); |
|||
} |
|||
|
|||
{ |
|||
KScopedSchedulerLock lock(kernel); |
|||
if (current_thread->IsWaitingForArbitration()) { |
|||
RemoveThread(SharedFrom(current_thread)); |
|||
current_thread->WaitForArbitration(false); |
|||
} |
|||
} |
|||
|
|||
return current_thread->GetSignalingResult(); |
|||
} |
|||
|
|||
void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) { |
|||
const VAddr arb_addr = thread->GetArbiterWaitAddress(); |
|||
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr]; |
|||
|
|||
const auto iter = |
|||
std::find_if(thread_list.cbegin(), thread_list.cend(), [&thread](const auto& entry) { |
|||
return entry->GetPriority() >= thread->GetPriority(); |
|||
}); |
|||
|
|||
if (iter == thread_list.cend()) { |
|||
thread_list.push_back(std::move(thread)); |
|||
} else { |
|||
thread_list.insert(iter, std::move(thread)); |
|||
} |
|||
} |
|||
|
|||
void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) { |
|||
const VAddr arb_addr = thread->GetArbiterWaitAddress(); |
|||
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr]; |
|||
|
|||
const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(), |
|||
[&thread](const auto& entry) { return thread == entry; }); |
|||
|
|||
if (iter != thread_list.cend()) { |
|||
thread_list.erase(iter); |
|||
} |
|||
} |
|||
|
|||
std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress( |
|||
VAddr address) const { |
|||
const auto iter = arb_threads.find(address); |
|||
if (iter == arb_threads.cend()) { |
|||
return {}; |
|||
} |
|||
|
|||
const std::list<std::shared_ptr<Thread>>& thread_list = iter->second; |
|||
return {thread_list.cbegin(), thread_list.cend()}; |
|||
} |
|||
} // namespace Kernel
|
|||
@ -1,91 +0,0 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <list> |
|||
#include <memory> |
|||
#include <unordered_map> |
|||
#include <vector> |
|||
|
|||
#include "common/common_types.h" |
|||
|
|||
union ResultCode; |
|||
|
|||
namespace Core { |
|||
class System; |
|||
} |
|||
|
|||
namespace Kernel { |
|||
|
|||
class Thread; |
|||
|
|||
class AddressArbiter { |
|||
public: |
|||
enum class ArbitrationType { |
|||
WaitIfLessThan = 0, |
|||
DecrementAndWaitIfLessThan = 1, |
|||
WaitIfEqual = 2, |
|||
}; |
|||
|
|||
enum class SignalType { |
|||
Signal = 0, |
|||
IncrementAndSignalIfEqual = 1, |
|||
ModifyByWaitingCountAndSignalIfEqual = 2, |
|||
}; |
|||
|
|||
explicit AddressArbiter(Core::System& system); |
|||
~AddressArbiter(); |
|||
|
|||
AddressArbiter(const AddressArbiter&) = delete; |
|||
AddressArbiter& operator=(const AddressArbiter&) = delete; |
|||
|
|||
AddressArbiter(AddressArbiter&&) = default; |
|||
AddressArbiter& operator=(AddressArbiter&&) = delete; |
|||
|
|||
/// Signals an address being waited on with a particular signaling type. |
|||
ResultCode SignalToAddress(VAddr address, SignalType type, s32 value, s32 num_to_wake); |
|||
|
|||
/// Waits on an address with a particular arbitration type. |
|||
ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns); |
|||
|
|||
private: |
|||
/// Signals an address being waited on. |
|||
ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake); |
|||
|
|||
/// 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); |
|||
|
|||
/// 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); |
|||
|
|||
/// Waits on an address if the value passed is less than the argument value, |
|||
/// optionally decrementing. |
|||
ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, |
|||
bool should_decrement); |
|||
|
|||
/// Waits on an address if the value passed is equal to the argument value. |
|||
ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); |
|||
|
|||
/// Wake up num_to_wake (or all) threads in a vector. |
|||
void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake); |
|||
|
|||
/// Insert a thread into the address arbiter container |
|||
void InsertThread(std::shared_ptr<Thread> thread); |
|||
|
|||
/// Removes a thread from the address arbiter container |
|||
void RemoveThread(std::shared_ptr<Thread> thread); |
|||
|
|||
// Gets the threads waiting on an address. |
|||
std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const; |
|||
|
|||
/// List of threads waiting for a address arbiter |
|||
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads; |
|||
|
|||
Core::System& system; |
|||
}; |
|||
|
|||
} // namespace Kernel |
|||
@ -1,170 +0,0 @@ |
|||
// Copyright 2014 Citra Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <memory>
|
|||
#include <utility>
|
|||
#include <vector>
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "common/logging/log.h"
|
|||
#include "core/core.h"
|
|||
#include "core/hle/kernel/errors.h"
|
|||
#include "core/hle/kernel/handle_table.h"
|
|||
#include "core/hle/kernel/k_scheduler.h"
|
|||
#include "core/hle/kernel/kernel.h"
|
|||
#include "core/hle/kernel/mutex.h"
|
|||
#include "core/hle/kernel/object.h"
|
|||
#include "core/hle/kernel/process.h"
|
|||
#include "core/hle/kernel/thread.h"
|
|||
#include "core/hle/result.h"
|
|||
#include "core/memory.h"
|
|||
|
|||
namespace Kernel { |
|||
|
|||
/// Returns the number of threads that are waiting for a mutex, and the highest priority one among
|
|||
/// those.
|
|||
static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThread( |
|||
const std::shared_ptr<Thread>& current_thread, VAddr mutex_addr) { |
|||
|
|||
std::shared_ptr<Thread> highest_priority_thread; |
|||
u32 num_waiters = 0; |
|||
|
|||
for (const auto& thread : current_thread->GetMutexWaitingThreads()) { |
|||
if (thread->GetMutexWaitAddress() != mutex_addr) |
|||
continue; |
|||
|
|||
++num_waiters; |
|||
if (highest_priority_thread == nullptr || |
|||
thread->GetPriority() < highest_priority_thread->GetPriority()) { |
|||
highest_priority_thread = thread; |
|||
} |
|||
} |
|||
|
|||
return {highest_priority_thread, num_waiters}; |
|||
} |
|||
|
|||
/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
|
|||
static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread, |
|||
std::shared_ptr<Thread> new_owner) { |
|||
current_thread->RemoveMutexWaiter(new_owner); |
|||
const auto threads = current_thread->GetMutexWaitingThreads(); |
|||
for (const auto& thread : threads) { |
|||
if (thread->GetMutexWaitAddress() != mutex_addr) |
|||
continue; |
|||
|
|||
ASSERT(thread->GetLockOwner() == current_thread.get()); |
|||
current_thread->RemoveMutexWaiter(thread); |
|||
if (new_owner != thread) |
|||
new_owner->AddMutexWaiter(thread); |
|||
} |
|||
} |
|||
|
|||
Mutex::Mutex(Core::System& system) : system{system} {} |
|||
Mutex::~Mutex() = default; |
|||
|
|||
ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, |
|||
Handle requesting_thread_handle) { |
|||
// The mutex address must be 4-byte aligned
|
|||
if ((address % sizeof(u32)) != 0) { |
|||
LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); |
|||
return ERR_INVALID_ADDRESS; |
|||
} |
|||
|
|||
auto& kernel = system.Kernel(); |
|||
std::shared_ptr<Thread> current_thread = |
|||
SharedFrom(kernel.CurrentScheduler()->GetCurrentThread()); |
|||
{ |
|||
KScopedSchedulerLock lock(kernel); |
|||
// The mutex address must be 4-byte aligned
|
|||
if ((address % sizeof(u32)) != 0) { |
|||
return ERR_INVALID_ADDRESS; |
|||
} |
|||
|
|||
const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); |
|||
std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); |
|||
std::shared_ptr<Thread> requesting_thread = |
|||
handle_table.Get<Thread>(requesting_thread_handle); |
|||
|
|||
// TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of
|
|||
// another thread.
|
|||
ASSERT(requesting_thread == current_thread); |
|||
|
|||
current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); |
|||
|
|||
const u32 addr_value = system.Memory().Read32(address); |
|||
|
|||
// If the mutex isn't being held, just return success.
|
|||
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { |
|||
return RESULT_SUCCESS; |
|||
} |
|||
|
|||
if (holding_thread == nullptr) { |
|||
return ERR_INVALID_HANDLE; |
|||
} |
|||
|
|||
// Wait until the mutex is released
|
|||
current_thread->SetMutexWaitAddress(address); |
|||
current_thread->SetWaitHandle(requesting_thread_handle); |
|||
|
|||
current_thread->SetState(ThreadState::Waiting); |
|||
|
|||
// Update the lock holder thread's priority to prevent priority inversion.
|
|||
holding_thread->AddMutexWaiter(current_thread); |
|||
} |
|||
|
|||
{ |
|||
KScopedSchedulerLock lock(kernel); |
|||
auto* owner = current_thread->GetLockOwner(); |
|||
if (owner != nullptr) { |
|||
owner->RemoveMutexWaiter(current_thread); |
|||
} |
|||
} |
|||
return current_thread->GetSignalingResult(); |
|||
} |
|||
|
|||
std::pair<ResultCode, std::shared_ptr<Thread>> Mutex::Unlock(std::shared_ptr<Thread> owner, |
|||
VAddr address) { |
|||
// The mutex address must be 4-byte aligned
|
|||
if ((address % sizeof(u32)) != 0) { |
|||
LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address); |
|||
return {ERR_INVALID_ADDRESS, nullptr}; |
|||
} |
|||
|
|||
auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address); |
|||
if (new_owner == nullptr) { |
|||
system.Memory().Write32(address, 0); |
|||
return {RESULT_SUCCESS, nullptr}; |
|||
} |
|||
// Transfer the ownership of the mutex from the previous owner to the new one.
|
|||
TransferMutexOwnership(address, owner, new_owner); |
|||
u32 mutex_value = new_owner->GetWaitHandle(); |
|||
if (num_waiters >= 2) { |
|||
// Notify the guest that there are still some threads waiting for the mutex
|
|||
mutex_value |= Mutex::MutexHasWaitersFlag; |
|||
} |
|||
new_owner->SetSynchronizationResults(nullptr, RESULT_SUCCESS); |
|||
new_owner->SetLockOwner(nullptr); |
|||
new_owner->Wakeup(); |
|||
|
|||
system.Memory().Write32(address, mutex_value); |
|||
return {RESULT_SUCCESS, new_owner}; |
|||
} |
|||
|
|||
ResultCode Mutex::Release(VAddr address) { |
|||
auto& kernel = system.Kernel(); |
|||
KScopedSchedulerLock lock(kernel); |
|||
|
|||
std::shared_ptr<Thread> current_thread = |
|||
SharedFrom(kernel.CurrentScheduler()->GetCurrentThread()); |
|||
|
|||
auto [result, new_owner] = Unlock(current_thread, address); |
|||
|
|||
if (result != RESULT_SUCCESS && new_owner != nullptr) { |
|||
new_owner->SetSynchronizationResults(nullptr, result); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
} // namespace Kernel
|
|||
@ -1,42 +0,0 @@ |
|||
// Copyright 2014 Citra Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_types.h" |
|||
|
|||
union ResultCode; |
|||
|
|||
namespace Core { |
|||
class System; |
|||
} |
|||
|
|||
namespace Kernel { |
|||
|
|||
class Mutex final { |
|||
public: |
|||
explicit Mutex(Core::System& system); |
|||
~Mutex(); |
|||
|
|||
/// Flag that indicates that a mutex still has threads waiting for it. |
|||
static constexpr u32 MutexHasWaitersFlag = 0x40000000; |
|||
/// Mask of the bits in a mutex address value that contain the mutex owner. |
|||
static constexpr u32 MutexOwnerMask = 0xBFFFFFFF; |
|||
|
|||
/// Attempts to acquire a mutex at the specified address. |
|||
ResultCode TryAcquire(VAddr address, Handle holding_thread_handle, |
|||
Handle requesting_thread_handle); |
|||
|
|||
/// Unlocks a mutex for owner at address |
|||
std::pair<ResultCode, std::shared_ptr<Thread>> Unlock(std::shared_ptr<Thread> owner, |
|||
VAddr address); |
|||
|
|||
/// Releases the mutex at the specified address. |
|||
ResultCode Release(VAddr address); |
|||
|
|||
private: |
|||
Core::System& system; |
|||
}; |
|||
|
|||
} // namespace Kernel |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue