core: hle: Integrate new KConditionVariable and KAddressArbiter implementations.
parent
952d1ac487
commit
912dd50146
@ -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
|
|
Loading…
Reference in New Issue