Kernel: Refactor synchronization to better match RE
This commit is contained in:
parent
c5aefe42aa
commit
d23d504d77
|
@ -183,6 +183,8 @@ add_library(core STATIC
|
||||||
hle/kernel/svc_wrap.h
|
hle/kernel/svc_wrap.h
|
||||||
hle/kernel/synchronization_object.cpp
|
hle/kernel/synchronization_object.cpp
|
||||||
hle/kernel/synchronization_object.h
|
hle/kernel/synchronization_object.h
|
||||||
|
hle/kernel/synchronization.cpp
|
||||||
|
hle/kernel/synchronization.h
|
||||||
hle/kernel/thread.cpp
|
hle/kernel/thread.cpp
|
||||||
hle/kernel/thread.h
|
hle/kernel/thread.h
|
||||||
hle/kernel/transfer_memory.cpp
|
hle/kernel/transfer_memory.cpp
|
||||||
|
|
|
@ -31,6 +31,11 @@ void ClientSession::Acquire(Thread* thread) {
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ClientSession::IsSignaled() const {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kernel,
|
ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kernel,
|
||||||
std::shared_ptr<Session> parent,
|
std::shared_ptr<Session> parent,
|
||||||
std::string name) {
|
std::string name) {
|
||||||
|
|
|
@ -48,6 +48,8 @@ public:
|
||||||
|
|
||||||
void Acquire(Thread* thread) override;
|
void Acquire(Thread* thread) override;
|
||||||
|
|
||||||
|
bool IsSignaled() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel,
|
static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel,
|
||||||
std::shared_ptr<Session> parent,
|
std::shared_ptr<Session> parent,
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "core/hle/kernel/process.h"
|
#include "core/hle/kernel/process.h"
|
||||||
#include "core/hle/kernel/resource_limit.h"
|
#include "core/hle/kernel/resource_limit.h"
|
||||||
#include "core/hle/kernel/scheduler.h"
|
#include "core/hle/kernel/scheduler.h"
|
||||||
|
#include "core/hle/kernel/synchronization.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
#include "core/hle/lock.h"
|
#include "core/hle/lock.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
@ -96,7 +97,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KernelCore::Impl {
|
struct KernelCore::Impl {
|
||||||
explicit Impl(Core::System& system) : system{system}, global_scheduler{system} {}
|
explicit Impl(Core::System& system)
|
||||||
|
: system{system}, global_scheduler{system}, synchronization{system} {}
|
||||||
|
|
||||||
void Initialize(KernelCore& kernel) {
|
void Initialize(KernelCore& kernel) {
|
||||||
Shutdown();
|
Shutdown();
|
||||||
|
@ -191,6 +193,7 @@ struct KernelCore::Impl {
|
||||||
std::vector<std::shared_ptr<Process>> process_list;
|
std::vector<std::shared_ptr<Process>> process_list;
|
||||||
Process* current_process = nullptr;
|
Process* current_process = nullptr;
|
||||||
Kernel::GlobalScheduler global_scheduler;
|
Kernel::GlobalScheduler global_scheduler;
|
||||||
|
Kernel::Synchronization synchronization;
|
||||||
|
|
||||||
std::shared_ptr<ResourceLimit> system_resource_limit;
|
std::shared_ptr<ResourceLimit> system_resource_limit;
|
||||||
|
|
||||||
|
@ -270,6 +273,14 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
|
||||||
return impl->cores[id];
|
return impl->cores[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Kernel::Synchronization& KernelCore::Synchronization() {
|
||||||
|
return impl->synchronization;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Kernel::Synchronization& KernelCore::Synchronization() const {
|
||||||
|
return impl->synchronization;
|
||||||
|
}
|
||||||
|
|
||||||
Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
|
Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
|
||||||
return *impl->exclusive_monitor;
|
return *impl->exclusive_monitor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ class HandleTable;
|
||||||
class PhysicalCore;
|
class PhysicalCore;
|
||||||
class Process;
|
class Process;
|
||||||
class ResourceLimit;
|
class ResourceLimit;
|
||||||
|
class Synchronization;
|
||||||
class Thread;
|
class Thread;
|
||||||
|
|
||||||
/// Represents a single instance of the kernel.
|
/// Represents a single instance of the kernel.
|
||||||
|
@ -92,6 +93,12 @@ public:
|
||||||
/// Gets the an instance of the respective physical CPU core.
|
/// Gets the an instance of the respective physical CPU core.
|
||||||
const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
|
const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
|
||||||
|
|
||||||
|
/// Gets the an instance of the Synchronization Interface.
|
||||||
|
Kernel::Synchronization& Synchronization();
|
||||||
|
|
||||||
|
/// Gets the an instance of the Synchronization Interface.
|
||||||
|
const Kernel::Synchronization& Synchronization() const;
|
||||||
|
|
||||||
/// Stops execution of 'id' core, in order to reschedule a new thread.
|
/// Stops execution of 'id' core, in order to reschedule a new thread.
|
||||||
void PrepareReschedule(std::size_t id);
|
void PrepareReschedule(std::size_t id);
|
||||||
|
|
||||||
|
|
|
@ -357,7 +357,7 @@ void Process::ChangeStatus(ProcessStatus new_status) {
|
||||||
|
|
||||||
status = new_status;
|
status = new_status;
|
||||||
is_signaled = true;
|
is_signaled = true;
|
||||||
WakeupAllWaitingThreads();
|
Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::AllocateMainThreadStack(u64 stack_size) {
|
void Process::AllocateMainThreadStack(u64 stack_size) {
|
||||||
|
|
|
@ -359,10 +359,6 @@ private:
|
||||||
/// specified by metadata provided to the process during loading.
|
/// specified by metadata provided to the process during loading.
|
||||||
bool is_64bit_process = true;
|
bool is_64bit_process = true;
|
||||||
|
|
||||||
/// Whether or not this process is signaled. This occurs
|
|
||||||
/// upon the process changing to a different state.
|
|
||||||
bool is_signaled = false;
|
|
||||||
|
|
||||||
/// Total running time for the process in ticks.
|
/// Total running time for the process in ticks.
|
||||||
u64 total_process_running_time_ticks = 0;
|
u64 total_process_running_time_ticks = 0;
|
||||||
|
|
||||||
|
|
|
@ -15,26 +15,26 @@ ReadableEvent::ReadableEvent(KernelCore& kernel) : SynchronizationObject{kernel}
|
||||||
ReadableEvent::~ReadableEvent() = default;
|
ReadableEvent::~ReadableEvent() = default;
|
||||||
|
|
||||||
bool ReadableEvent::ShouldWait(const Thread* thread) const {
|
bool ReadableEvent::ShouldWait(const Thread* thread) const {
|
||||||
return !signaled;
|
return !is_signaled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadableEvent::Acquire(Thread* thread) {
|
void ReadableEvent::Acquire(Thread* thread) {
|
||||||
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
ASSERT_MSG(IsSignaled(), "object unavailable!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadableEvent::Signal() {
|
void ReadableEvent::Signal() {
|
||||||
if (!signaled) {
|
if (!is_signaled) {
|
||||||
signaled = true;
|
is_signaled = true;
|
||||||
WakeupAllWaitingThreads();
|
SynchronizationObject::Signal();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadableEvent::Clear() {
|
void ReadableEvent::Clear() {
|
||||||
signaled = false;
|
is_signaled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode ReadableEvent::Reset() {
|
ResultCode ReadableEvent::Reset() {
|
||||||
if (!signaled) {
|
if (!is_signaled) {
|
||||||
return ERR_INVALID_STATE;
|
return ERR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,13 +46,11 @@ public:
|
||||||
/// then ERR_INVALID_STATE will be returned.
|
/// then ERR_INVALID_STATE will be returned.
|
||||||
ResultCode Reset();
|
ResultCode Reset();
|
||||||
|
|
||||||
|
void Signal() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit ReadableEvent(KernelCore& kernel);
|
explicit ReadableEvent(KernelCore& kernel);
|
||||||
|
|
||||||
void Signal();
|
|
||||||
|
|
||||||
bool signaled{};
|
|
||||||
|
|
||||||
std::string name; ///< Name of event (optional)
|
std::string name; ///< Name of event (optional)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,10 @@ void ServerPort::Acquire(Thread* thread) {
|
||||||
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ServerPort::IsSignaled() const {
|
||||||
|
return !pending_sessions.empty();
|
||||||
|
}
|
||||||
|
|
||||||
ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions,
|
ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions,
|
||||||
std::string name) {
|
std::string name) {
|
||||||
std::shared_ptr<ServerPort> server_port = std::make_shared<ServerPort>(kernel);
|
std::shared_ptr<ServerPort> server_port = std::make_shared<ServerPort>(kernel);
|
||||||
|
|
|
@ -82,6 +82,8 @@ public:
|
||||||
bool ShouldWait(const Thread* thread) const override;
|
bool ShouldWait(const Thread* thread) const override;
|
||||||
void Acquire(Thread* thread) override;
|
void Acquire(Thread* thread) override;
|
||||||
|
|
||||||
|
bool IsSignaled() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// ServerSessions waiting to be accepted by the port
|
/// ServerSessions waiting to be accepted by the port
|
||||||
std::vector<std::shared_ptr<ServerSession>> pending_sessions;
|
std::vector<std::shared_ptr<ServerSession>> pending_sessions;
|
||||||
|
|
|
@ -50,6 +50,16 @@ bool ServerSession::ShouldWait(const Thread* thread) const {
|
||||||
return pending_requesting_threads.empty() || currently_handling != nullptr;
|
return pending_requesting_threads.empty() || currently_handling != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ServerSession::IsSignaled() const {
|
||||||
|
// Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
|
||||||
|
if (!parent->Client()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait if we have no pending requests, or if we're currently handling a request.
|
||||||
|
return !(pending_requesting_threads.empty() || currently_handling != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void ServerSession::Acquire(Thread* thread) {
|
void ServerSession::Acquire(Thread* thread) {
|
||||||
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
||||||
// We are now handling a request, pop it from the stack.
|
// We are now handling a request, pop it from the stack.
|
||||||
|
|
|
@ -73,6 +73,8 @@ public:
|
||||||
return parent.get();
|
return parent.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsSignaled() const override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the HLE handler for the session. This handler will be called to service IPC requests
|
* Sets the HLE handler for the session. This handler will be called to service IPC requests
|
||||||
* instead of the regular IPC machinery. (The regular IPC machinery is currently not
|
* instead of the regular IPC machinery. (The regular IPC machinery is currently not
|
||||||
|
|
|
@ -29,6 +29,11 @@ bool Session::ShouldWait(const Thread* thread) const {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Session::IsSignaled() const {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Session::Acquire(Thread* thread) {
|
void Session::Acquire(Thread* thread) {
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ public:
|
||||||
|
|
||||||
bool ShouldWait(const Thread* thread) const override;
|
bool ShouldWait(const Thread* thread) const override;
|
||||||
|
|
||||||
|
bool IsSignaled() const override;
|
||||||
|
|
||||||
void Acquire(Thread* thread) override;
|
void Acquire(Thread* thread) override;
|
||||||
|
|
||||||
std::shared_ptr<ClientSession> Client() {
|
std::shared_ptr<ClientSession> Client() {
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "core/hle/kernel/shared_memory.h"
|
#include "core/hle/kernel/shared_memory.h"
|
||||||
#include "core/hle/kernel/svc.h"
|
#include "core/hle/kernel/svc.h"
|
||||||
#include "core/hle/kernel/svc_wrap.h"
|
#include "core/hle/kernel/svc_wrap.h"
|
||||||
|
#include "core/hle/kernel/synchronization.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
#include "core/hle/kernel/transfer_memory.h"
|
#include "core/hle/kernel/transfer_memory.h"
|
||||||
#include "core/hle/kernel/writable_event.h"
|
#include "core/hle/kernel/writable_event.h"
|
||||||
|
@ -433,23 +434,6 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
|
||||||
return ERR_INVALID_HANDLE;
|
return ERR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default thread wakeup callback for WaitSynchronization
|
|
||||||
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
|
|
||||||
std::shared_ptr<SynchronizationObject> object,
|
|
||||||
std::size_t index) {
|
|
||||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
|
|
||||||
|
|
||||||
if (reason == ThreadWakeupReason::Timeout) {
|
|
||||||
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(reason == ThreadWakeupReason::Signal);
|
|
||||||
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
|
||||||
thread->SetWaitSynchronizationOutput(static_cast<u32>(index));
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
|
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
|
||||||
static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address,
|
static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address,
|
||||||
u64 handle_count, s64 nano_seconds) {
|
u64 handle_count, s64 nano_seconds) {
|
||||||
|
@ -473,10 +457,10 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* const thread = system.CurrentScheduler().GetCurrentThread();
|
auto* const thread = system.CurrentScheduler().GetCurrentThread();
|
||||||
|
auto& kernel = system.Kernel();
|
||||||
using ObjectPtr = Thread::ThreadSynchronizationObjects::value_type;
|
using ObjectPtr = Thread::ThreadSynchronizationObjects::value_type;
|
||||||
Thread::ThreadSynchronizationObjects objects(handle_count);
|
Thread::ThreadSynchronizationObjects objects(handle_count);
|
||||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
|
||||||
|
|
||||||
for (u64 i = 0; i < handle_count; ++i) {
|
for (u64 i = 0; i < handle_count; ++i) {
|
||||||
const Handle handle = memory.Read32(handles_address + i * sizeof(Handle));
|
const Handle handle = memory.Read32(handles_address + i * sizeof(Handle));
|
||||||
|
@ -489,47 +473,10 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
|
||||||
|
|
||||||
objects[i] = object;
|
objects[i] = object;
|
||||||
}
|
}
|
||||||
|
auto& synchronization = kernel.Synchronization();
|
||||||
// Find the first object that is acquirable in the provided list of objects
|
auto [result, handle_result] = synchronization.WaitFor(objects, nano_seconds);
|
||||||
auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) {
|
*index = handle_result;
|
||||||
return !object->ShouldWait(thread);
|
return result;
|
||||||
});
|
|
||||||
|
|
||||||
if (itr != objects.end()) {
|
|
||||||
// We found a ready object, acquire it and set the result value
|
|
||||||
SynchronizationObject* object = itr->get();
|
|
||||||
object->Acquire(thread);
|
|
||||||
*index = static_cast<s32>(std::distance(objects.begin(), itr));
|
|
||||||
return RESULT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No objects were ready to be acquired, prepare to suspend the thread.
|
|
||||||
|
|
||||||
// If a timeout value of 0 was provided, just return the Timeout error code instead of
|
|
||||||
// suspending the thread.
|
|
||||||
if (nano_seconds == 0) {
|
|
||||||
return RESULT_TIMEOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread->IsSyncCancelled()) {
|
|
||||||
thread->SetSyncCancelled(false);
|
|
||||||
return ERR_SYNCHRONIZATION_CANCELED;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& object : objects) {
|
|
||||||
object->AddWaitingThread(SharedFrom(thread));
|
|
||||||
}
|
|
||||||
|
|
||||||
thread->SetSynchronizationObjects(std::move(objects));
|
|
||||||
thread->SetStatus(ThreadStatus::WaitSynch);
|
|
||||||
|
|
||||||
// Create an event to wake the thread up after the specified nanosecond delay has passed
|
|
||||||
thread->WakeAfterDelay(nano_seconds);
|
|
||||||
thread->SetWakeupCallback(DefaultThreadWakeupCallback);
|
|
||||||
|
|
||||||
system.PrepareReschedule(thread->GetProcessorID());
|
|
||||||
|
|
||||||
return RESULT_TIMEOUT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resumes a thread waiting on WaitSynchronization
|
/// Resumes a thread waiting on WaitSynchronization
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/hle/kernel/errors.h"
|
||||||
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
#include "core/hle/kernel/scheduler.h"
|
||||||
|
#include "core/hle/kernel/synchronization.h"
|
||||||
|
#include "core/hle/kernel/synchronization_object.h"
|
||||||
|
#include "core/hle/kernel/thread.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
/// Default thread wakeup callback for WaitSynchronization
|
||||||
|
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
|
||||||
|
std::shared_ptr<SynchronizationObject> object,
|
||||||
|
std::size_t index) {
|
||||||
|
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
|
||||||
|
|
||||||
|
if (reason == ThreadWakeupReason::Timeout) {
|
||||||
|
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(reason == ThreadWakeupReason::Signal);
|
||||||
|
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
||||||
|
thread->SetWaitSynchronizationOutput(static_cast<u32>(index));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
Synchronization::Synchronization(Core::System& system) : system{system} {}
|
||||||
|
|
||||||
|
void Synchronization::SignalObject(SynchronizationObject& obj) const {
|
||||||
|
if (obj.IsSignaled()) {
|
||||||
|
obj.WakeupAllWaitingThreads();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<ResultCode, Handle> Synchronization::WaitFor(
|
||||||
|
std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) {
|
||||||
|
auto* const thread = system.CurrentScheduler().GetCurrentThread();
|
||||||
|
// Find the first object that is acquirable in the provided list of objects
|
||||||
|
auto itr = std::find_if(sync_objects.begin(), sync_objects.end(),
|
||||||
|
[thread](const std::shared_ptr<SynchronizationObject>& object) {
|
||||||
|
return object->IsSignaled();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (itr != sync_objects.end()) {
|
||||||
|
// We found a ready object, acquire it and set the result value
|
||||||
|
SynchronizationObject* object = itr->get();
|
||||||
|
object->Acquire(thread);
|
||||||
|
u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr));
|
||||||
|
return {RESULT_SUCCESS, index};
|
||||||
|
}
|
||||||
|
|
||||||
|
// No objects were ready to be acquired, prepare to suspend the thread.
|
||||||
|
|
||||||
|
// If a timeout value of 0 was provided, just return the Timeout error code instead of
|
||||||
|
// suspending the thread.
|
||||||
|
if (nano_seconds == 0) {
|
||||||
|
return {RESULT_TIMEOUT, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread->IsSyncCancelled()) {
|
||||||
|
thread->SetSyncCancelled(false);
|
||||||
|
return {ERR_SYNCHRONIZATION_CANCELED, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& object : sync_objects) {
|
||||||
|
object->AddWaitingThread(SharedFrom(thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
thread->SetSynchronizationObjects(std::move(sync_objects));
|
||||||
|
thread->SetStatus(ThreadStatus::WaitSynch);
|
||||||
|
|
||||||
|
// Create an event to wake the thread up after the specified nanosecond delay has passed
|
||||||
|
thread->WakeAfterDelay(nano_seconds);
|
||||||
|
thread->SetWakeupCallback(DefaultThreadWakeupCallback);
|
||||||
|
|
||||||
|
system.PrepareReschedule(thread->GetProcessorID());
|
||||||
|
|
||||||
|
return {RESULT_TIMEOUT, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Kernel
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "core/hle/kernel/object.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
} // namespace Core
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
|
||||||
|
class KernelCore;
|
||||||
|
class SynchronizationObject;
|
||||||
|
|
||||||
|
class Synchronization {
|
||||||
|
public:
|
||||||
|
Synchronization(Core::System& system);
|
||||||
|
|
||||||
|
void SignalObject(SynchronizationObject& obj) const;
|
||||||
|
|
||||||
|
std::pair<ResultCode, Handle> WaitFor(
|
||||||
|
std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Core::System& system;
|
||||||
|
};
|
||||||
|
} // namespace Kernel
|
|
@ -10,6 +10,7 @@
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/kernel/object.h"
|
#include "core/hle/kernel/object.h"
|
||||||
#include "core/hle/kernel/process.h"
|
#include "core/hle/kernel/process.h"
|
||||||
|
#include "core/hle/kernel/synchronization.h"
|
||||||
#include "core/hle/kernel/synchronization_object.h"
|
#include "core/hle/kernel/synchronization_object.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
|
|
||||||
|
@ -18,6 +19,10 @@ namespace Kernel {
|
||||||
SynchronizationObject::SynchronizationObject(KernelCore& kernel) : Object{kernel} {}
|
SynchronizationObject::SynchronizationObject(KernelCore& kernel) : Object{kernel} {}
|
||||||
SynchronizationObject::~SynchronizationObject() = default;
|
SynchronizationObject::~SynchronizationObject() = default;
|
||||||
|
|
||||||
|
void SynchronizationObject::Signal() {
|
||||||
|
kernel.Synchronization().SignalObject(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void SynchronizationObject::AddWaitingThread(std::shared_ptr<Thread> thread) {
|
void SynchronizationObject::AddWaitingThread(std::shared_ptr<Thread> thread) {
|
||||||
auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
|
auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
|
||||||
if (itr == waiting_threads.end())
|
if (itr == waiting_threads.end())
|
||||||
|
|
|
@ -30,6 +30,13 @@ public:
|
||||||
/// Acquire/lock the object for the specified thread if it is available
|
/// Acquire/lock the object for the specified thread if it is available
|
||||||
virtual void Acquire(Thread* thread) = 0;
|
virtual void Acquire(Thread* thread) = 0;
|
||||||
|
|
||||||
|
/// Signal this object
|
||||||
|
virtual void Signal();
|
||||||
|
|
||||||
|
virtual bool IsSignaled() const {
|
||||||
|
return is_signaled;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a thread to wait on this object
|
* Add a thread to wait on this object
|
||||||
* @param thread Pointer to thread to add
|
* @param thread Pointer to thread to add
|
||||||
|
@ -60,6 +67,9 @@ public:
|
||||||
/// Get a const reference to the waiting threads list for debug use
|
/// Get a const reference to the waiting threads list for debug use
|
||||||
const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const;
|
const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool is_signaled{}; // Tells if this sync object is signalled;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Threads waiting for this object to become available
|
/// Threads waiting for this object to become available
|
||||||
std::vector<std::shared_ptr<Thread>> waiting_threads;
|
std::vector<std::shared_ptr<Thread>> waiting_threads;
|
||||||
|
|
|
@ -31,6 +31,10 @@ bool Thread::ShouldWait(const Thread* thread) const {
|
||||||
return status != ThreadStatus::Dead;
|
return status != ThreadStatus::Dead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Thread::IsSignaled() const {
|
||||||
|
return status == ThreadStatus::Dead;
|
||||||
|
}
|
||||||
|
|
||||||
void Thread::Acquire(Thread* thread) {
|
void Thread::Acquire(Thread* thread) {
|
||||||
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
||||||
}
|
}
|
||||||
|
@ -45,7 +49,7 @@ void Thread::Stop() {
|
||||||
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
|
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
|
||||||
callback_handle = 0;
|
callback_handle = 0;
|
||||||
SetStatus(ThreadStatus::Dead);
|
SetStatus(ThreadStatus::Dead);
|
||||||
WakeupAllWaitingThreads();
|
Signal();
|
||||||
|
|
||||||
// Clean up any dangling references in objects that this thread was waiting for
|
// Clean up any dangling references in objects that this thread was waiting for
|
||||||
for (auto& wait_object : wait_objects) {
|
for (auto& wait_object : wait_objects) {
|
||||||
|
|
|
@ -146,6 +146,7 @@ public:
|
||||||
|
|
||||||
bool ShouldWait(const Thread* thread) const override;
|
bool ShouldWait(const Thread* thread) const override;
|
||||||
void Acquire(Thread* thread) override;
|
void Acquire(Thread* thread) override;
|
||||||
|
bool IsSignaled() const override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the thread's current priority
|
* Gets the thread's current priority
|
||||||
|
|
|
@ -22,7 +22,6 @@ EventPair WritableEvent::CreateEventPair(KernelCore& kernel, std::string name) {
|
||||||
writable_event->name = name + ":Writable";
|
writable_event->name = name + ":Writable";
|
||||||
writable_event->readable = readable_event;
|
writable_event->readable = readable_event;
|
||||||
readable_event->name = name + ":Readable";
|
readable_event->name = name + ":Readable";
|
||||||
readable_event->signaled = false;
|
|
||||||
|
|
||||||
return {std::move(readable_event), std::move(writable_event)};
|
return {std::move(readable_event), std::move(writable_event)};
|
||||||
}
|
}
|
||||||
|
@ -40,7 +39,7 @@ void WritableEvent::Clear() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WritableEvent::IsSignaled() const {
|
bool WritableEvent::IsSignaled() const {
|
||||||
return readable->signaled;
|
return readable->IsSignaled();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
Reference in New Issue