|
|
|
@ -10,78 +10,88 @@ |
|
|
|
#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 { |
|
|
|
|
|
|
|
/// 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 { |
|
|
|
SchedulerLock lock(system.Kernel()); |
|
|
|
if (obj.IsSignaled()) { |
|
|
|
obj.WakeupAllWaitingThreads(); |
|
|
|
for (auto thread : obj.GetWaitingThreads()) { |
|
|
|
if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { |
|
|
|
thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); |
|
|
|
thread->ResumeFromWait(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
std::pair<ResultCode, Handle> Synchronization::WaitFor( |
|
|
|
std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) { |
|
|
|
auto& kernel = system.Kernel(); |
|
|
|
auto* const thread = system.CurrentScheduler().GetCurrentThread(); |
|
|
|
// Find the first object that is acquirable in the provided list of objects
|
|
|
|
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)); |
|
|
|
return {RESULT_SUCCESS, index}; |
|
|
|
Handle event_handle = InvalidHandle; |
|
|
|
{ |
|
|
|
SchedulerLockAndSleep 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}; |
|
|
|
} |
|
|
|
|
|
|
|
/// TODO(Blinkhawk): Check for termination pending
|
|
|
|
|
|
|
|
if (thread->IsSyncCancelled()) { |
|
|
|
thread->SetSyncCancelled(false); |
|
|
|
lock.CancelSleep(); |
|
|
|
return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; |
|
|
|
} |
|
|
|
|
|
|
|
for (auto& object : sync_objects) { |
|
|
|
object->AddWaitingThread(SharedFrom(thread)); |
|
|
|
} |
|
|
|
thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); |
|
|
|
thread->SetStatus(ThreadStatus::WaitSynch); |
|
|
|
} |
|
|
|
|
|
|
|
// 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, InvalidHandle}; |
|
|
|
if (event_handle != InvalidHandle) { |
|
|
|
auto& time_manager = kernel.TimeManager(); |
|
|
|
time_manager.UnscheduleTimeEvent(event_handle); |
|
|
|
} |
|
|
|
|
|
|
|
if (thread->IsSyncCancelled()) { |
|
|
|
thread->SetSyncCancelled(false); |
|
|
|
return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; |
|
|
|
{ |
|
|
|
SchedulerLock lock(kernel); |
|
|
|
ResultCode signaling_result = thread->GetSignalingResult(); |
|
|
|
SynchronizationObject* signaling_object = thread->GetSignalingObject(); |
|
|
|
if (signaling_result == RESULT_SUCCESS) { |
|
|
|
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 {RESULT_SUCCESS, index}; |
|
|
|
} |
|
|
|
return {signaling_result, -1}; |
|
|
|
} |
|
|
|
|
|
|
|
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, InvalidHandle}; |
|
|
|
} |
|
|
|
|
|
|
|
} // namespace Kernel
|