os: implement 11.x SdkReplyAndReceive

This commit is contained in:
Michael Scire 2021-01-12 18:18:39 -08:00
parent b26ebc12e1
commit 8ac8abf295
12 changed files with 296 additions and 37 deletions

View file

@ -20,23 +20,32 @@
namespace ams::os::impl {
WaitableHolderBase *WaitableManagerImpl::WaitAnyImpl(bool infinite, TimeSpan timeout) {
Result WaitableManagerImpl::WaitAnyImpl(WaitableHolderBase **out, bool infinite, TimeSpan timeout, bool reply, Handle reply_target) {
/* Prepare for processing. */
this->signaled_holder = nullptr;
this->target_impl.SetCurrentThreadHandleForCancelWait();
WaitableHolderBase *result = this->LinkHoldersToObjectList();
WaitableHolderBase *holder = this->LinkHoldersToObjectList();
/* Check if we've been signaled. */
{
std::scoped_lock lk(this->cs_wait);
if (this->signaled_holder != nullptr) {
result = this->signaled_holder;
holder = this->signaled_holder;
}
}
/* Process object array. */
if (result == nullptr) {
result = this->WaitAnyHandleImpl(infinite, timeout);
Result wait_result = ResultSuccess();
if (holder != nullptr) {
if (reply && reply_target != svc::InvalidHandle) {
s32 index;
wait_result = this->target_impl.TimedReplyAndReceive(std::addressof(index), nullptr, 0, 0, reply_target, TimeSpan::FromNanoSeconds(0));
if (R_FAILED(wait_result)) {
holder = nullptr;
}
}
} else {
wait_result = this->WaitAnyHandleImpl(std::addressof(holder), infinite, timeout, reply, reply_target);
}
/* Unlink holders from the current object list. */
@ -44,10 +53,13 @@ namespace ams::os::impl {
this->target_impl.ClearCurrentThreadHandleForCancelWait();
return result;
/* Set output holder. */
*out = holder;
return wait_result;
}
WaitableHolderBase *WaitableManagerImpl::WaitAnyHandleImpl(bool infinite, TimeSpan timeout) {
Result WaitableManagerImpl::WaitAnyHandleImpl(WaitableHolderBase **out, bool infinite, TimeSpan timeout, bool reply, Handle reply_target) {
Handle object_handles[MaximumHandleCount];
WaitableHolderBase *objects[MaximumHandleCount];
@ -60,18 +72,30 @@ namespace ams::os::impl {
TimeSpan min_timeout = 0;
WaitableHolderBase *min_timeout_object = this->RecalculateNextTimeout(&min_timeout, end_time);
s32 index;
if (infinite && min_timeout_object == nullptr) {
index = this->target_impl.WaitAny(object_handles, MaximumHandleCount, count);
s32 index = WaitInvalid;
Result wait_result = ResultSuccess();
if (reply) {
if (infinite && min_timeout_object == nullptr) {
} else {
}
} else if (infinite && min_timeout_object == nullptr) {
wait_result = this->target_impl.WaitAny(std::addressof(index), object_handles, MaximumHandleCount, count);
} else {
if (count == 0 && min_timeout == 0) {
index = WaitTimedOut;
} else {
index = this->target_impl.TimedWaitAny(object_handles, MaximumHandleCount, count, min_timeout);
wait_result = this->target_impl.TimedWaitAny(std::addressof(index), object_handles, MaximumHandleCount, count, min_timeout);
AMS_ABORT_UNLESS(index != WaitInvalid);
}
}
if (index == WaitInvalid) {
*out = nullptr;
return wait_result;
}
switch (index) {
case WaitTimedOut:
if (min_timeout_object) {
@ -79,23 +103,35 @@ namespace ams::os::impl {
if (min_timeout_object->IsSignaled() == TriBool::True) {
std::scoped_lock lk(this->cs_wait);
this->signaled_holder = min_timeout_object;
return this->signaled_holder;
*out = min_timeout_object;
return wait_result;
}
continue;
} else {
*out = nullptr;
return wait_result;
}
return nullptr;
break;
case WaitCancelled:
if (this->signaled_holder) {
return this->signaled_holder;
}
continue;
default: /* 0 - 0x3F, valid. */
{
std::scoped_lock lk(this->cs_wait);
if (this->signaled_holder) {
*out = this->signaled_holder;
return wait_result;
}
}
break;
default: /* 0 - 0x3F, valid. */
{
AMS_ASSERT(0 <= index && index < static_cast<s32>(MaximumHandleCount));
std::scoped_lock lk(this->cs_wait);
this->signaled_holder = objects[index];
return this->signaled_holder;
*out = objects[index];
return wait_result;
}
}
reply_target = svc::InvalidHandle;
}
}

View file

@ -38,14 +38,21 @@ namespace ams::os::impl {
InternalCriticalSection cs_wait;
WaitableManagerTargetImpl target_impl;
private:
WaitableHolderBase *WaitAnyImpl(bool infinite, TimeSpan timeout);
WaitableHolderBase *WaitAnyHandleImpl(bool infinite, TimeSpan timeout);
Result WaitAnyImpl(WaitableHolderBase **out, bool infinite, TimeSpan timeout, bool reply, Handle reply_target);
Result WaitAnyHandleImpl(WaitableHolderBase **out, bool infinite, TimeSpan timeout, bool reply, Handle reply_target);
s32 BuildHandleArray(Handle out_handles[], WaitableHolderBase *out_objects[], s32 num);
WaitableHolderBase *LinkHoldersToObjectList();
void UnlinkHoldersFromObjectList();
WaitableHolderBase *RecalculateNextTimeout(TimeSpan *out_min_timeout, TimeSpan end_time);
WaitableHolderBase *WaitAnyImpl(bool infinite, TimeSpan timeout) {
WaitableHolderBase *holder = nullptr;
const Result wait_result = this->WaitAnyImpl(std::addressof(holder), infinite, timeout, false, svc::InvalidHandle);
AMS_ASSERT(R_SUCCEEDED(wait_result));
return holder;
}
public:
/* Wait. */
WaitableHolderBase *WaitAny() {
@ -60,6 +67,10 @@ namespace ams::os::impl {
return this->WaitAnyImpl(false, ts);
}
Result ReplyAndReceive(WaitableHolderBase **out, Handle reply_target) {
return this->WaitAnyImpl(out, true, TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max()), true, reply_target);
}
/* List management. */
bool IsEmpty() const {
return this->waitable_list.empty();

View file

@ -19,13 +19,13 @@
namespace ams::os::impl {
s32 WaitableManagerHorizonImpl::WaitSynchronizationN(s32 num, Handle arr[], s32 array_size, s64 ns) {
Result WaitableManagerHorizonImpl::WaitSynchronizationN(s32 *out_index, s32 num, Handle arr[], s32 array_size, s64 ns) {
AMS_ASSERT(!(num == 0 && ns == 0));
s32 index = WaitableManagerImpl::WaitInvalid;
R_TRY_CATCH(svc::WaitSynchronization(std::addressof(index), static_cast<const svc::Handle *>(arr), num, ns)) {
R_CATCH(svc::ResultTimedOut) { return WaitableManagerImpl::WaitTimedOut; }
R_CATCH(svc::ResultCancelled) { return WaitableManagerImpl::WaitCancelled; }
R_CATCH(svc::ResultTimedOut) { index = WaitableManagerImpl::WaitTimedOut; }
R_CATCH(svc::ResultCancelled) { index = WaitableManagerImpl::WaitCancelled; }
/* All other results are critical errors. */
/* svc::ResultThreadTerminating */
/* svc::ResultInvalidHandle. */
@ -33,7 +33,34 @@ namespace ams::os::impl {
/* svc::ResultOutOfRange */
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return index;
*out_index = index;
return ResultSuccess();
}
Result ReplyAndReceiveN(s32 *out_index, s32 num, Handle arr[], s32 array_size, s64 ns, Handle reply_target) {
/* NOTE: Nintendo does not initialize this value, which seems like it can cause incorrect behavior. */
s32 index = WaitableManagerImpl::WaitInvalid;
static_assert(WaitableManagerImpl::WaitInvalid != -1);
R_TRY_CATCH(svc::ReplyAndReceive(std::addressof(index), arr, num, ns, reply_target)) {
R_CATCH(svc::ResultTimedOut) { *out_index = WaitableManagerImpl::WaitTimedOut; return R_CURRENT_RESULT; }
R_CATCH(svc::ResultCancelled) { *out_index = WaitableManagerImpl::WaitCancelled; return R_CURRENT_RESULT; }
R_CATCH(svc::ResultSessionClosed) {
if (index == -1) {
*out_index = WaitableManagerImpl::WaitInvalid;
return os::ResultSessionClosedForReply();
} else {
*out_index = index;
return os::ResultSessionClosedForReceive();
}
}
R_CATCH(svc::ResultReceiveListBroken) {
*out_index = index;
return os::ResultReceiveListBroken();
}
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return ResultSuccess();
}
void WaitableManagerHorizonImpl::CancelWait() {

View file

@ -25,24 +25,33 @@ namespace ams::os::impl {
private:
Handle handle;
private:
s32 WaitSynchronizationN(s32 num, Handle arr[], s32 array_size, s64 ns);
Result WaitSynchronizationN(s32 *out_index, s32 num, Handle arr[], s32 array_size, s64 ns);
Result ReplyAndReceiveN(s32 *out_index, s32 num, Handle arr[], s32 array_size, s64 ns, Handle reply_target);
public:
void CancelWait();
s32 WaitAny(Handle arr[], s32 array_size, s32 num) {
return this->WaitSynchronizationN(num, arr, array_size, svc::WaitInfinite);
Result WaitAny(s32 *out_index, Handle arr[], s32 array_size, s32 num) {
return this->WaitSynchronizationN(out_index, num, arr, array_size, svc::WaitInfinite);
}
s32 TryWaitAny(Handle arr[], s32 array_size, s32 num) {
return this->WaitSynchronizationN(num, arr, array_size, 0);
Result TryWaitAny(s32 *out_index, Handle arr[], s32 array_size, s32 num) {
return this->WaitSynchronizationN(out_index, num, arr, array_size, 0);
}
s32 TimedWaitAny(Handle arr[], s32 array_size, s32 num, TimeSpan ts) {
Result TimedWaitAny(s32 *out_index, Handle arr[], s32 array_size, s32 num, TimeSpan ts) {
s64 timeout = ts.GetNanoSeconds();
if (timeout < 0) {
timeout = 0;
}
return this->WaitSynchronizationN(num, arr, array_size, timeout);
return this->WaitSynchronizationN(out_index, num, arr, array_size, timeout);
}
Result ReplyAndReceive(s32 *out_index, Handle arr[], s32 array_size, s32 num, Handle reply_target) {
return this->ReplyAndReceiveN(out_index, num, arr, array_size, std::numeric_limits<s64>::max(), reply_target);
}
Result TimedReplyAndReceive(s32 *out_index, Handle arr[], s32 array_size, s32 num, Handle reply_target, TimeSpan ts) {
return this->ReplyAndReceiveN(out_index, num, arr, array_size, ts.GetNanoSeconds(), reply_target);
}
void SetCurrentThreadHandleForCancelWait() {