33 changed files with 397 additions and 621 deletions
-
6src/core/CMakeLists.txt
-
10src/core/hle/kernel/address_arbiter.cpp
-
3src/core/hle/kernel/client_port.cpp
-
11src/core/hle/kernel/client_session.cpp
-
8src/core/hle/kernel/client_session.h
-
3src/core/hle/kernel/errors.h
-
5src/core/hle/kernel/k_scheduler.cpp
-
171src/core/hle/kernel/k_synchronization_object.cpp
-
58src/core/hle/kernel/k_synchronization_object.h
-
15src/core/hle/kernel/kernel.cpp
-
6src/core/hle/kernel/kernel.h
-
4src/core/hle/kernel/mutex.cpp
-
21src/core/hle/kernel/process.cpp
-
14src/core/hle/kernel/process.h
-
18src/core/hle/kernel/readable_event.cpp
-
12src/core/hle/kernel/readable_event.h
-
14src/core/hle/kernel/server_port.cpp
-
7src/core/hle/kernel/server_port.h
-
23src/core/hle/kernel/server_session.cpp
-
12src/core/hle/kernel/server_session.h
-
11src/core/hle/kernel/session.cpp
-
8src/core/hle/kernel/session.h
-
47src/core/hle/kernel/svc.cpp
-
9src/core/hle/kernel/svc_wrap.h
-
116src/core/hle/kernel/synchronization.cpp
-
44src/core/hle/kernel/synchronization.h
-
49src/core/hle/kernel/synchronization_object.cpp
-
77src/core/hle/kernel/synchronization_object.h
-
66src/core/hle/kernel/thread.cpp
-
131src/core/hle/kernel/thread.h
-
3src/core/hle/service/sm/sm.cpp
-
19src/yuzu/debugger/wait_tree.cpp
-
17src/yuzu/debugger/wait_tree.h
@ -0,0 +1,171 @@ |
|||
// Copyright 2021 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "common/common_types.h"
|
|||
#include "core/hle/kernel/k_scheduler.h"
|
|||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
|||
#include "core/hle/kernel/k_synchronization_object.h"
|
|||
#include "core/hle/kernel/kernel.h"
|
|||
#include "core/hle/kernel/svc_results.h"
|
|||
#include "core/hle/kernel/thread.h"
|
|||
|
|||
namespace Kernel { |
|||
|
|||
ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, |
|||
KSynchronizationObject** objects, const s32 num_objects, |
|||
s64 timeout) { |
|||
// Allocate space on stack for thread nodes.
|
|||
std::vector<ThreadListNode> thread_nodes(num_objects); |
|||
|
|||
// Prepare for wait.
|
|||
Thread* thread = kernel.CurrentScheduler()->GetCurrentThread(); |
|||
Handle timer = InvalidHandle; |
|||
|
|||
{ |
|||
// Setup the scheduling lock and sleep.
|
|||
KScopedSchedulerLockAndSleep slp(kernel, timer, thread, timeout); |
|||
|
|||
// Check if any of the objects are already signaled.
|
|||
for (auto i = 0; i < num_objects; ++i) { |
|||
ASSERT(objects[i] != nullptr); |
|||
|
|||
if (objects[i]->IsSignaled()) { |
|||
*out_index = i; |
|||
slp.CancelSleep(); |
|||
return RESULT_SUCCESS; |
|||
} |
|||
} |
|||
|
|||
// Check if the timeout is zero.
|
|||
if (timeout == 0) { |
|||
slp.CancelSleep(); |
|||
return Svc::ResultTimedOut; |
|||
} |
|||
|
|||
// Check if the thread should terminate.
|
|||
if (thread->IsTerminationRequested()) { |
|||
slp.CancelSleep(); |
|||
return Svc::ResultTerminationRequested; |
|||
} |
|||
|
|||
// Check if waiting was canceled.
|
|||
if (thread->IsWaitCancelled()) { |
|||
slp.CancelSleep(); |
|||
thread->ClearWaitCancelled(); |
|||
return Svc::ResultCancelled; |
|||
} |
|||
|
|||
// Add the waiters.
|
|||
for (auto i = 0; i < num_objects; ++i) { |
|||
thread_nodes[i].thread = thread; |
|||
thread_nodes[i].next = nullptr; |
|||
|
|||
if (objects[i]->thread_list_tail == nullptr) { |
|||
objects[i]->thread_list_head = std::addressof(thread_nodes[i]); |
|||
} else { |
|||
objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]); |
|||
} |
|||
|
|||
objects[i]->thread_list_tail = std::addressof(thread_nodes[i]); |
|||
} |
|||
|
|||
// For debugging only
|
|||
thread->SetWaitObjectsForDebugging(objects, num_objects); |
|||
|
|||
// Mark the thread as waiting.
|
|||
thread->SetCancellable(); |
|||
thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); |
|||
thread->SetState(ThreadState::WaitSynch); |
|||
} |
|||
|
|||
// The lock/sleep is done, so we should be able to get our result.
|
|||
|
|||
// Thread is no longer cancellable.
|
|||
thread->ClearCancellable(); |
|||
|
|||
// For debugging only
|
|||
thread->SetWaitObjectsForDebugging(nullptr, 0); |
|||
|
|||
// Cancel the timer as needed.
|
|||
if (timer != InvalidHandle) { |
|||
auto& time_manager = kernel.TimeManager(); |
|||
time_manager.UnscheduleTimeEvent(timer); |
|||
} |
|||
|
|||
// Get the wait result.
|
|||
ResultCode wait_result{RESULT_SUCCESS}; |
|||
s32 sync_index = -1; |
|||
{ |
|||
KScopedSchedulerLock lock(kernel); |
|||
KSynchronizationObject* synced_obj; |
|||
wait_result = thread->GetWaitResult(std::addressof(synced_obj)); |
|||
|
|||
for (auto i = 0; i < num_objects; ++i) { |
|||
// Unlink the object from the list.
|
|||
ThreadListNode* prev_ptr = |
|||
reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head)); |
|||
ThreadListNode* prev_val = nullptr; |
|||
ThreadListNode *prev, *tail_prev; |
|||
|
|||
do { |
|||
prev = prev_ptr; |
|||
prev_ptr = prev_ptr->next; |
|||
tail_prev = prev_val; |
|||
prev_val = prev_ptr; |
|||
} while (prev_ptr != std::addressof(thread_nodes[i])); |
|||
|
|||
if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) { |
|||
objects[i]->thread_list_tail = tail_prev; |
|||
} |
|||
|
|||
prev->next = thread_nodes[i].next; |
|||
|
|||
if (objects[i] == synced_obj) { |
|||
sync_index = i; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Set output.
|
|||
*out_index = sync_index; |
|||
return wait_result; |
|||
} |
|||
|
|||
KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {} |
|||
|
|||
KSynchronizationObject ::~KSynchronizationObject() = default; |
|||
|
|||
void KSynchronizationObject::NotifyAvailable(ResultCode result) { |
|||
KScopedSchedulerLock lock(kernel); |
|||
|
|||
// If we're not signaled, we've nothing to notify.
|
|||
if (!this->IsSignaled()) { |
|||
return; |
|||
} |
|||
|
|||
// Iterate over each thread.
|
|||
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { |
|||
Thread* thread = cur_node->thread; |
|||
if (thread->GetState() == ThreadSchedStatus::Paused) { |
|||
thread->SetSyncedObject(this, result); |
|||
thread->SetState(ThreadStatus::Ready); |
|||
} |
|||
} |
|||
} |
|||
|
|||
std::vector<Thread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const { |
|||
std::vector<Thread*> threads; |
|||
|
|||
// If debugging, dump the list of waiters.
|
|||
{ |
|||
KScopedSchedulerLock lock(kernel); |
|||
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { |
|||
threads.emplace_back(cur_node->thread); |
|||
} |
|||
} |
|||
|
|||
return threads; |
|||
} |
|||
} // namespace Kernel
|
|||
@ -0,0 +1,58 @@ |
|||
// Copyright 2021 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
|
|||
#include "core/hle/kernel/object.h" |
|||
#include "core/hle/result.h" |
|||
|
|||
namespace Kernel { |
|||
|
|||
class KernelCore; |
|||
class Synchronization; |
|||
class Thread; |
|||
|
|||
/// Class that represents a Kernel object that a thread can be waiting on |
|||
class KSynchronizationObject : public Object { |
|||
public: |
|||
struct ThreadListNode { |
|||
ThreadListNode* next{}; |
|||
Thread* thread{}; |
|||
}; |
|||
|
|||
[[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index, |
|||
KSynchronizationObject** objects, const s32 num_objects, |
|||
s64 timeout); |
|||
|
|||
[[nodiscard]] virtual bool IsSignaled() const = 0; |
|||
|
|||
[[nodiscard]] std::vector<Thread*> GetWaitingThreadsForDebugging() const; |
|||
|
|||
protected: |
|||
explicit KSynchronizationObject(KernelCore& kernel); |
|||
virtual ~KSynchronizationObject(); |
|||
|
|||
void NotifyAvailable(ResultCode result); |
|||
void NotifyAvailable() { |
|||
return this->NotifyAvailable(RESULT_SUCCESS); |
|||
} |
|||
|
|||
private: |
|||
ThreadListNode* thread_list_head{}; |
|||
ThreadListNode* thread_list_tail{}; |
|||
}; |
|||
|
|||
// Specialization of DynamicObjectCast for KSynchronizationObjects |
|||
template <> |
|||
inline std::shared_ptr<KSynchronizationObject> DynamicObjectCast<KSynchronizationObject>( |
|||
std::shared_ptr<Object> object) { |
|||
if (object != nullptr && object->IsWaitable()) { |
|||
return std::static_pointer_cast<KSynchronizationObject>(object); |
|||
} |
|||
return nullptr; |
|||
} |
|||
|
|||
} // namespace Kernel |
|||
@ -1,116 +0,0 @@ |
|||
// 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/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/synchronization.h"
|
|||
#include "core/hle/kernel/synchronization_object.h"
|
|||
#include "core/hle/kernel/thread.h"
|
|||
#include "core/hle/kernel/time_manager.h"
|
|||
|
|||
namespace Kernel { |
|||
|
|||
Synchronization::Synchronization(Core::System& system) : system{system} {} |
|||
|
|||
void Synchronization::SignalObject(SynchronizationObject& obj) const { |
|||
auto& kernel = system.Kernel(); |
|||
KScopedSchedulerLock lock(kernel); |
|||
if (obj.IsSignaled()) { |
|||
for (auto thread : obj.GetWaitingThreads()) { |
|||
if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { |
|||
if (thread->GetStatus() != ThreadStatus::WaitHLEEvent) { |
|||
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); |
|||
ASSERT(thread->IsWaitingSync()); |
|||
} |
|||
thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); |
|||
thread->ResumeFromWait(); |
|||
} |
|||
} |
|||
obj.ClearWaitingThreads(); |
|||
} |
|||
} |
|||
|
|||
std::pair<ResultCode, Handle> Synchronization::WaitFor( |
|||
std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) { |
|||
auto& kernel = system.Kernel(); |
|||
auto* const thread = kernel.CurrentScheduler()->GetCurrentThread(); |
|||
Handle event_handle = InvalidHandle; |
|||
{ |
|||
KScopedSchedulerLockAndSleep lock(kernel, event_handle, thread, nano_seconds); |
|||
const 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); |
|||
const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); |
|||
lock.CancelSleep(); |
|||
return {RESULT_SUCCESS, index}; |
|||
} |
|||
|
|||
if (nano_seconds == 0) { |
|||
lock.CancelSleep(); |
|||
return {RESULT_TIMEOUT, InvalidHandle}; |
|||
} |
|||
|
|||
if (thread->IsPendingTermination()) { |
|||
lock.CancelSleep(); |
|||
return {ERR_THREAD_TERMINATING, InvalidHandle}; |
|||
} |
|||
|
|||
if (thread->IsSyncCancelled()) { |
|||
thread->SetSyncCancelled(false); |
|||
lock.CancelSleep(); |
|||
return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; |
|||
} |
|||
|
|||
for (auto& object : sync_objects) { |
|||
object->AddWaitingThread(SharedFrom(thread)); |
|||
} |
|||
|
|||
thread->SetSynchronizationObjects(&sync_objects); |
|||
thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); |
|||
thread->SetStatus(ThreadStatus::WaitSynch); |
|||
thread->SetWaitingSync(true); |
|||
} |
|||
thread->SetWaitingSync(false); |
|||
|
|||
if (event_handle != InvalidHandle) { |
|||
auto& time_manager = kernel.TimeManager(); |
|||
time_manager.UnscheduleTimeEvent(event_handle); |
|||
} |
|||
|
|||
{ |
|||
KScopedSchedulerLock lock(kernel); |
|||
ResultCode signaling_result = thread->GetSignalingResult(); |
|||
SynchronizationObject* signaling_object = thread->GetSignalingObject(); |
|||
thread->SetSynchronizationObjects(nullptr); |
|||
auto shared_thread = SharedFrom(thread); |
|||
for (auto& obj : sync_objects) { |
|||
obj->RemoveWaitingThread(shared_thread); |
|||
} |
|||
if (signaling_object != nullptr) { |
|||
const auto itr = std::find_if( |
|||
sync_objects.begin(), sync_objects.end(), |
|||
[signaling_object](const std::shared_ptr<SynchronizationObject>& object) { |
|||
return object.get() == signaling_object; |
|||
}); |
|||
ASSERT(itr != sync_objects.end()); |
|||
signaling_object->Acquire(thread); |
|||
const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); |
|||
return {signaling_result, index}; |
|||
} |
|||
return {signaling_result, -1}; |
|||
} |
|||
} |
|||
|
|||
} // namespace Kernel
|
|||
@ -1,44 +0,0 @@ |
|||
// 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 <vector> |
|||
|
|||
#include "core/hle/kernel/object.h" |
|||
#include "core/hle/result.h" |
|||
|
|||
namespace Core { |
|||
class System; |
|||
} // namespace Core |
|||
|
|||
namespace Kernel { |
|||
|
|||
class SynchronizationObject; |
|||
|
|||
/** |
|||
* The 'Synchronization' class is an interface for handling synchronization methods |
|||
* used by Synchronization objects and synchronization SVCs. This centralizes processing of |
|||
* such |
|||
*/ |
|||
class Synchronization { |
|||
public: |
|||
explicit Synchronization(Core::System& system); |
|||
|
|||
/// Signals a synchronization object, waking up all its waiting threads |
|||
void SignalObject(SynchronizationObject& obj) const; |
|||
|
|||
/// Tries to see if waiting for any of the sync_objects is necessary, if not |
|||
/// it returns Success and the handle index of the signaled sync object. In |
|||
/// case not, the current thread will be locked and wait for nano_seconds or |
|||
/// for a synchronization object to signal. |
|||
std::pair<ResultCode, Handle> WaitFor( |
|||
std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds); |
|||
|
|||
private: |
|||
Core::System& system; |
|||
}; |
|||
} // namespace Kernel |
|||
@ -1,49 +0,0 @@ |
|||
// Copyright 2014 Citra Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <algorithm>
|
|||
#include "common/assert.h"
|
|||
#include "common/common_types.h"
|
|||
#include "common/logging/log.h"
|
|||
#include "core/core.h"
|
|||
#include "core/hle/kernel/kernel.h"
|
|||
#include "core/hle/kernel/object.h"
|
|||
#include "core/hle/kernel/process.h"
|
|||
#include "core/hle/kernel/synchronization.h"
|
|||
#include "core/hle/kernel/synchronization_object.h"
|
|||
#include "core/hle/kernel/thread.h"
|
|||
|
|||
namespace Kernel { |
|||
|
|||
SynchronizationObject::SynchronizationObject(KernelCore& kernel) : Object{kernel} {} |
|||
SynchronizationObject::~SynchronizationObject() = default; |
|||
|
|||
void SynchronizationObject::Signal() { |
|||
kernel.Synchronization().SignalObject(*this); |
|||
} |
|||
|
|||
void SynchronizationObject::AddWaitingThread(std::shared_ptr<Thread> thread) { |
|||
auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); |
|||
if (itr == waiting_threads.end()) |
|||
waiting_threads.push_back(std::move(thread)); |
|||
} |
|||
|
|||
void SynchronizationObject::RemoveWaitingThread(std::shared_ptr<Thread> thread) { |
|||
auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); |
|||
// If a thread passed multiple handles to the same object,
|
|||
// the kernel might attempt to remove the thread from the object's
|
|||
// waiting threads list multiple times.
|
|||
if (itr != waiting_threads.end()) |
|||
waiting_threads.erase(itr); |
|||
} |
|||
|
|||
void SynchronizationObject::ClearWaitingThreads() { |
|||
waiting_threads.clear(); |
|||
} |
|||
|
|||
const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const { |
|||
return waiting_threads; |
|||
} |
|||
|
|||
} // namespace Kernel
|
|||
@ -1,77 +0,0 @@ |
|||
// Copyright 2014 Citra Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <atomic> |
|||
#include <memory> |
|||
#include <vector> |
|||
|
|||
#include "core/hle/kernel/object.h" |
|||
|
|||
namespace Kernel { |
|||
|
|||
class KernelCore; |
|||
class Synchronization; |
|||
class Thread; |
|||
|
|||
/// Class that represents a Kernel object that a thread can be waiting on |
|||
class SynchronizationObject : public Object { |
|||
public: |
|||
explicit SynchronizationObject(KernelCore& kernel); |
|||
~SynchronizationObject() override; |
|||
|
|||
/** |
|||
* Check if the specified thread should wait until the object is available |
|||
* @param thread The thread about which we're deciding. |
|||
* @return True if the current thread should wait due to this object being unavailable |
|||
*/ |
|||
virtual bool ShouldWait(const Thread* thread) const = 0; |
|||
|
|||
/// Acquire/lock the object for the specified thread if it is available |
|||
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 |
|||
* @param thread Pointer to thread to add |
|||
*/ |
|||
void AddWaitingThread(std::shared_ptr<Thread> thread); |
|||
|
|||
/** |
|||
* Removes a thread from waiting on this object (e.g. if it was resumed already) |
|||
* @param thread Pointer to thread to remove |
|||
*/ |
|||
void RemoveWaitingThread(std::shared_ptr<Thread> thread); |
|||
|
|||
/// Get a const reference to the waiting threads list for debug use |
|||
const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; |
|||
|
|||
void ClearWaitingThreads(); |
|||
|
|||
protected: |
|||
std::atomic_bool is_signaled{}; // Tells if this sync object is signaled |
|||
|
|||
private: |
|||
/// Threads waiting for this object to become available |
|||
std::vector<std::shared_ptr<Thread>> waiting_threads; |
|||
}; |
|||
|
|||
// Specialization of DynamicObjectCast for SynchronizationObjects |
|||
template <> |
|||
inline std::shared_ptr<SynchronizationObject> DynamicObjectCast<SynchronizationObject>( |
|||
std::shared_ptr<Object> object) { |
|||
if (object != nullptr && object->IsWaitable()) { |
|||
return std::static_pointer_cast<SynchronizationObject>(object); |
|||
} |
|||
return nullptr; |
|||
} |
|||
|
|||
} // namespace Kernel |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue