Browse Source
core: hle: Integrate new KConditionVariable and KAddressArbiter implementations.
pull/15/merge
core: hle: Integrate new KConditionVariable and KAddressArbiter implementations.
pull/15/merge
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