kern: implement SvcWaitSynchronization
This commit is contained in:
@@ -20,7 +20,87 @@ namespace ams::kern {
|
||||
Result KSynchronization::Wait(s32 *out_index, KSynchronizationObject **objects, const s32 num_objects, s64 timeout) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
MESOSPHERE_UNIMPLEMENTED();
|
||||
/* Allocate space on stack for thread iterators. */
|
||||
KSynchronizationObject::iterator *thread_iters = static_cast<KSynchronizationObject::iterator *>(__builtin_alloca(sizeof(KSynchronizationObject::iterator) * num_objects));
|
||||
|
||||
/* Prepare for wait. */
|
||||
KThread *thread = GetCurrentThreadPointer();
|
||||
s32 sync_index = -1;
|
||||
KHardwareTimer *timer;
|
||||
|
||||
{
|
||||
/* Setup the scheduling lock and sleep. */
|
||||
KScopedSchedulerLockAndSleep slp(std::addressof(timer), thread, timeout);
|
||||
|
||||
/* Check if any of the objects are already signaled. */
|
||||
for (auto i = 0; i < num_objects; ++i) {
|
||||
AMS_ASSERT(objects[i] != nullptr);
|
||||
|
||||
if (objects[i]->IsSignaled()) {
|
||||
*out_index = i;
|
||||
slp.CancelSleep();
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/* 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_iters[i] = objects[i]->RegisterWaitingThread(thread);
|
||||
}
|
||||
|
||||
/* Mark the thread as waiting. */
|
||||
thread->SetCancellable();
|
||||
thread->SetSyncedObject(nullptr, svc::ResultTimedOut());
|
||||
thread->SetState(KThread::ThreadState_Waiting);
|
||||
}
|
||||
|
||||
/* The lock/sleep is done, so we should be able to get our result. */
|
||||
|
||||
/* Thread is no longer cancellable. */
|
||||
thread->ClearCancellable();
|
||||
|
||||
/* Cancel the timer as needed. */
|
||||
if (timer != nullptr) {
|
||||
timer->CancelTask(thread);
|
||||
}
|
||||
|
||||
/* Get the wait result. */
|
||||
Result wait_result;
|
||||
{
|
||||
KScopedSchedulerLock lk;
|
||||
KSynchronizationObject *synced_obj;
|
||||
wait_result = thread->GetWaitResult(std::addressof(synced_obj));
|
||||
|
||||
for (auto i = 0; i < num_objects; ++i) {
|
||||
objects[i]->UnregisterWaitingThread(thread_iters[i]);
|
||||
if (objects[i] == synced_obj) {
|
||||
sync_index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set output. */
|
||||
*out_index = sync_index;
|
||||
return wait_result;
|
||||
}
|
||||
|
||||
void KSynchronization::OnAvailable(KSynchronizationObject *object) {
|
||||
|
||||
@@ -42,13 +42,13 @@ namespace ams::kern {
|
||||
MESOSPHERE_TODO("Do useful debug operation here.");
|
||||
}
|
||||
|
||||
KSynchronizationObject::iterator KSynchronizationObject::AddWaiterThread(KThread *thread) {
|
||||
KSynchronizationObject::iterator KSynchronizationObject::RegisterWaitingThread(KThread *thread) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
return this->thread_list.insert(this->thread_list.end(), *thread);
|
||||
}
|
||||
|
||||
KSynchronizationObject::iterator KSynchronizationObject::RemoveWaiterThread(KSynchronizationObject::iterator it) {
|
||||
KSynchronizationObject::iterator KSynchronizationObject::UnregisterWaitingThread(KSynchronizationObject::iterator it) {
|
||||
MESOSPHERE_ASSERT_THIS();
|
||||
|
||||
return this->thread_list.erase(it);
|
||||
|
||||
@@ -27,6 +27,58 @@ namespace ams::kern::svc {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result WaitSynchronizationImpl(int32_t *out_index, KSynchronizationObject **objs, int32_t num_handles, int64_t timeout_ns) {
|
||||
/* Convert the timeout from nanoseconds to ticks. */
|
||||
s64 timeout;
|
||||
if (timeout_ns > 0) {
|
||||
u64 ticks = KHardwareTimer::GetTick();
|
||||
ticks += ams::svc::Tick(TimeSpan::FromNanoSeconds(timeout_ns));
|
||||
ticks += 2;
|
||||
|
||||
timeout = ticks;
|
||||
} else {
|
||||
timeout = timeout_ns;
|
||||
}
|
||||
|
||||
return Kernel::GetSynchronization().Wait(out_index, objs, num_handles, timeout);
|
||||
}
|
||||
|
||||
Result WaitSynchronization(int32_t *out_index, KUserPointer<const ams::svc::Handle *> user_handles, int32_t num_handles, int64_t timeout_ns) {
|
||||
/* Ensure number of handles is valid. */
|
||||
R_UNLESS(0 <= num_handles && num_handles <= ams::svc::ArgumentHandleCountMax, svc::ResultOutOfRange());
|
||||
|
||||
/* Get the synchronization context. */
|
||||
auto &handle_table = GetCurrentProcess().GetHandleTable();
|
||||
KSynchronizationObject **objs = GetCurrentThread().GetSynchronizationObjectBuffer();
|
||||
ams::svc::Handle *handles = GetCurrentThread().GetHandleBuffer();
|
||||
|
||||
/* Copy user handles. */
|
||||
if (num_handles > 0) {
|
||||
/* Ensure that we can try to get the handles. */
|
||||
R_UNLESS(GetCurrentProcess().GetPageTable().Contains(KProcessAddress(user_handles.GetUnsafePointer()), num_handles * sizeof(ams::svc::Handle)), svc::ResultInvalidPointer());
|
||||
|
||||
/* Get the handles. */
|
||||
R_TRY(user_handles.CopyArrayTo(handles, num_handles));
|
||||
|
||||
/* Convert the handles to objects. */
|
||||
R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs, handles, num_handles), svc::ResultInvalidHandle());
|
||||
}
|
||||
|
||||
/* Ensure handles are closed when we're done. */
|
||||
ON_SCOPE_EXIT {
|
||||
for (auto i = 0; i < num_handles; ++i) {
|
||||
objs[i]->Close();
|
||||
}
|
||||
};
|
||||
|
||||
/* Wait on the objects. */
|
||||
R_TRY_CATCH(WaitSynchronizationImpl(out_index, objs, num_handles, timeout_ns)) {
|
||||
R_CONVERT(svc::ResultSessionClosed, ResultSuccess())
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================= 64 ABI ============================= */
|
||||
@@ -39,8 +91,10 @@ namespace ams::kern::svc {
|
||||
MESOSPHERE_PANIC("Stubbed SvcResetSignal64 was called.");
|
||||
}
|
||||
|
||||
Result WaitSynchronization64(int32_t *out_index, KUserPointer<const ams::svc::Handle *> handles, int32_t numHandles, int64_t timeout_ns) {
|
||||
MESOSPHERE_PANIC("Stubbed SvcWaitSynchronization64 was called.");
|
||||
Result WaitSynchronization64(int32_t *out_index, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, int64_t timeout_ns) {
|
||||
Result result = WaitSynchronization(out_index, handles, num_handles, timeout_ns);
|
||||
MESOSPHERE_LOG("WaitSynchronization returned %08x\n", result.GetValue());
|
||||
return result;
|
||||
}
|
||||
|
||||
Result CancelSynchronization64(ams::svc::Handle handle) {
|
||||
@@ -61,8 +115,8 @@ namespace ams::kern::svc {
|
||||
MESOSPHERE_PANIC("Stubbed SvcResetSignal64From32 was called.");
|
||||
}
|
||||
|
||||
Result WaitSynchronization64From32(int32_t *out_index, KUserPointer<const ams::svc::Handle *> handles, int32_t numHandles, int64_t timeout_ns) {
|
||||
MESOSPHERE_PANIC("Stubbed SvcWaitSynchronization64From32 was called.");
|
||||
Result WaitSynchronization64From32(int32_t *out_index, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, int64_t timeout_ns) {
|
||||
return WaitSynchronization(out_index, handles, num_handles, timeout_ns);
|
||||
}
|
||||
|
||||
Result CancelSynchronization64From32(ams::svc::Handle handle) {
|
||||
|
||||
Reference in New Issue
Block a user