os: refactor/rewrite entire namespace.

This commit is contained in:
Michael Scire 2020-04-08 02:21:35 -07:00
parent 6193283f03
commit 065485b971
181 changed files with 5353 additions and 1929 deletions

View file

@ -13,167 +13,139 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_inter_process_event.hpp"
#include "os_inter_process_event_impl.hpp"
#include "os_waitable_object_list.hpp"
namespace ams::os::impl {
namespace {
Result CreateEventHandles(Handle *out_readable, Handle *out_writable) {
/* Create the event handles. */
R_TRY_CATCH(svcCreateEvent(out_writable, out_readable)) {
R_CONVERT(svc::ResultOutOfResource, ResultOutOfResource());
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
inline void SetupInterProcessEventType(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode) {
/* Set handles. */
event->readable_handle = read_handle;
event->is_readable_handle_managed = read_handle_managed;
event->writable_handle = write_handle;
event->is_writable_handle_managed = write_handle_managed;
return ResultSuccess();
/* Set auto clear. */
event->auto_clear = (clear_mode == EventClearMode_AutoClear);
/* Create the waitlist node. */
new (GetPointer(event->waitable_object_list_storage)) impl::WaitableObjectList;
/* Set state. */
event->state = InterProcessEventType::State_Initialized;
}
}
InterProcessEvent::InterProcessEvent(bool autoclear) : is_initialized(false) {
R_ABORT_UNLESS(this->Initialize(autoclear));
}
InterProcessEvent::~InterProcessEvent() {
this->Finalize();
}
Result InterProcessEvent::Initialize(bool autoclear) {
AMS_ABORT_UNLESS(!this->is_initialized);
Result CreateInterProcessEvent(InterProcessEventType *event, EventClearMode clear_mode) {
Handle rh, wh;
R_TRY(CreateEventHandles(&rh, &wh));
this->Initialize(rh, true, wh, true, autoclear);
R_TRY(impl::InterProcessEventImpl::Create(std::addressof(wh), std::addressof(rh)));
SetupInterProcessEventType(event, rh, true, wh, true, clear_mode);
return ResultSuccess();
}
void InterProcessEvent::Initialize(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) {
AMS_ABORT_UNLESS(!this->is_initialized);
AMS_ABORT_UNLESS(read_handle != INVALID_HANDLE || write_handle != INVALID_HANDLE);
this->read_handle = read_handle;
this->manage_read_handle = manage_read_handle;
this->write_handle = write_handle;
this->manage_write_handle = manage_write_handle;
this->auto_clear = autoclear;
this->is_initialized = true;
void DestroyInterProcessEvent(InterProcessEventType *event) {
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
/* Clear the state. */
event->state = InterProcessEventType::State_NotInitialized;
/* Close handles if required. */
if (event->is_readable_handle_managed) {
if (event->readable_handle != svc::InvalidHandle) {
impl::InterProcessEventImpl::Close(event->readable_handle);
}
event->is_readable_handle_managed = false;
}
if (event->is_writable_handle_managed) {
if (event->writable_handle != svc::InvalidHandle) {
impl::InterProcessEventImpl::Close(event->writable_handle);
}
event->is_writable_handle_managed = false;
}
/* Destroy the waitlist. */
GetReference(event->waitable_object_list_storage).~WaitableObjectList();
}
Handle InterProcessEvent::DetachReadableHandle() {
AMS_ABORT_UNLESS(this->is_initialized);
const Handle handle = this->read_handle;
AMS_ABORT_UNLESS(handle != INVALID_HANDLE);
this->read_handle = INVALID_HANDLE;
this->manage_read_handle = false;
void AttachInterProcessEvent(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode) {
AMS_ASSERT(read_handle != svc::InvalidHandle || write_handle != svc::InvalidHandle);
return SetupInterProcessEventType(event, read_handle, read_handle_managed, write_handle, write_handle_managed, clear_mode);
}
Handle DetachReadableHandleOfInterProcessEvent(InterProcessEventType *event) {
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
const Handle handle = event->readable_handle;
event->readable_handle = svc::InvalidHandle;
event->is_readable_handle_managed = false;
return handle;
}
Handle DetachWritableHandleOfInterProcessEvent(InterProcessEventType *event) {
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
const Handle handle = event->writable_handle;
event->writable_handle = svc::InvalidHandle;
event->is_writable_handle_managed = false;
return handle;
}
Handle InterProcessEvent::DetachWritableHandle() {
AMS_ABORT_UNLESS(this->is_initialized);
const Handle handle = this->write_handle;
AMS_ABORT_UNLESS(handle != INVALID_HANDLE);
this->write_handle = INVALID_HANDLE;
this->manage_write_handle = false;
return handle;
void WaitInterProcessEvent(InterProcessEventType *event) {
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
return impl::InterProcessEventImpl::Wait(event->readable_handle, event->auto_clear);
}
Handle InterProcessEvent::GetReadableHandle() const {
AMS_ABORT_UNLESS(this->is_initialized);
return this->read_handle;
bool TryWaitInterProcessEvent(InterProcessEventType *event) {
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
return impl::InterProcessEventImpl::TryWait(event->readable_handle, event->auto_clear);
}
Handle InterProcessEvent::GetWritableHandle() const {
AMS_ABORT_UNLESS(this->is_initialized);
return this->write_handle;
bool TimedWaitInterProcessEvent(InterProcessEventType *event, TimeSpan timeout) {
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
return impl::InterProcessEventImpl::TimedWait(event->readable_handle, event->auto_clear, timeout);
}
void InterProcessEvent::Finalize() {
if (this->is_initialized) {
if (this->manage_read_handle && this->read_handle != INVALID_HANDLE) {
R_ABORT_UNLESS(svcCloseHandle(this->read_handle));
}
if (this->manage_write_handle && this->write_handle != INVALID_HANDLE) {
R_ABORT_UNLESS(svcCloseHandle(this->write_handle));
}
void SignalInterProcessEvent(InterProcessEventType *event) {
AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
return impl::InterProcessEventImpl::Signal(event->writable_handle);
}
void ClearInterProcessEvent(InterProcessEventType *event) {
AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
auto handle = event->readable_handle;
if (handle == svc::InvalidHandle) {
handle = event->writable_handle;
}
this->read_handle = INVALID_HANDLE;
this->manage_read_handle = false;
this->write_handle = INVALID_HANDLE;
this->manage_write_handle = false;
this->is_initialized = false;
return impl::InterProcessEventImpl::Clear(handle);
}
void InterProcessEvent::Signal() {
R_ABORT_UNLESS(svcSignalEvent(this->GetWritableHandle()));
Handle GetReadableHandleOfInterProcessEvent(const InterProcessEventType *event) {
AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
return event->readable_handle;
}
void InterProcessEvent::Reset() {
Handle handle = this->GetReadableHandle();
if (handle == INVALID_HANDLE) {
handle = this->GetWritableHandle();
}
R_ABORT_UNLESS(svcClearEvent(handle));
Handle GetWritableHandleOfInterProcessEvent(const InterProcessEventType *event) {
AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
return event->writable_handle;
}
void InterProcessEvent::Wait() {
const Handle handle = this->GetReadableHandle();
while (true) {
/* Continuously wait, until success. */
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, std::numeric_limits<u64>::max())) {
R_CATCH(svc::ResultCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* Clear, if we must. */
if (this->auto_clear) {
R_TRY_CATCH(svcResetSignal(handle)) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return;
}
}
bool InterProcessEvent::TryWait() {
const Handle handle = this->GetReadableHandle();
if (this->auto_clear) {
/* Auto-clear. Just try to reset. */
return R_SUCCEEDED(svcResetSignal(handle));
} else {
/* Not auto-clear. */
while (true) {
/* Continuously wait, until success or timeout. */
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, 0)) {
R_CATCH(svc::ResultTimedOut) { return false; }
R_CATCH(svc::ResultCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* We succeeded, so we're signaled. */
return true;
}
}
}
bool InterProcessEvent::TimedWait(u64 ns) {
const Handle handle = this->GetReadableHandle();
TimeoutHelper timeout_helper(ns);
while (true) {
/* Continuously wait, until success or timeout. */
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, timeout_helper.NsUntilTimeout())) {
R_CATCH(svc::ResultTimedOut) { return false; }
R_CATCH(svc::ResultCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* Clear, if we must. */
if (this->auto_clear) {
R_TRY_CATCH(svcResetSignal(handle)) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return true;
}
}
}

View file

@ -18,37 +18,24 @@
namespace ams::os::impl {
class WaitableHolderOfInterProcessEvent;
Result CreateInterProcessEvent(InterProcessEventType *event, EventClearMode clear_mode);
void DestroyInterProcessEvent(InterProcessEventType *event);
class InterProcessEvent {
friend class WaitableHolderOfInterProcessEvent;
NON_COPYABLE(InterProcessEvent);
NON_MOVEABLE(InterProcessEvent);
private:
Handle read_handle;
Handle write_handle;
bool manage_read_handle;
bool manage_write_handle;
bool auto_clear;
bool is_initialized;
public:
InterProcessEvent() : is_initialized(false) { /* ... */ }
InterProcessEvent(bool autoclear);
~InterProcessEvent();
void AttachInterProcessEvent(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode);
Result Initialize(bool autoclear = true);
void Initialize(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true);
Handle DetachReadableHandle();
Handle DetachWritableHandle();
Handle GetReadableHandle() const;
Handle GetWritableHandle() const;
void Finalize();
Handle DetachReadableHandleOfInterProcessEvent(InterProcessEventType *event);
Handle DetachWritableHandleOfInterProcessEvent(InterProcessEventType *event);
void Signal();
void Reset();
void Wait();
bool TryWait();
bool TimedWait(u64 ns);
};
void WaitInterProcessEvent(InterProcessEventType *event);
bool TryWaitInterProcessEvent(InterProcessEventType *event);
bool TimedWaitInterProcessEvent(InterProcessEventType *event, TimeSpan timeout);
void SignalInterProcessEvent(InterProcessEventType *event);
void ClearInterProcessEvent(InterProcessEventType *event);
Handle GetReadableHandleOfInterProcessEvent(const InterProcessEventType *event);
Handle GetWritableHandleOfInterProcessEvent(const InterProcessEventType *event);
void InitializeWaitableHolder(WaitableHolderType *waitable_holder, InterProcessEventType *event);
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_inter_process_event_impl.os.horizon.hpp"
#else
#error "Unknown OS for ams::os::InterProcessEventImpl"
#endif

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_inter_process_event.hpp"
#include "os_inter_process_event_impl.os.horizon.hpp"
#include "os_timeout_helper.hpp"
namespace ams::os::impl {
Result InterProcessEventImpl::Create(Handle *out_write, Handle *out_read) {
/* Create the event handles. */
svc::Handle wh, rh;
R_TRY_CATCH(svc::CreateEvent(std::addressof(wh), std::addressof(rh))) {
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
*out_write = wh;
*out_read = rh;
return ResultSuccess();
}
void InterProcessEventImpl::Close(Handle handle) {
if (handle != svc::InvalidHandle) {
R_ABORT_UNLESS(svc::CloseHandle(svc::Handle(handle)));
}
}
void InterProcessEventImpl::Signal(Handle handle) {
R_ABORT_UNLESS(svc::SignalEvent(svc::Handle(handle)));
}
void InterProcessEventImpl::Clear(Handle handle) {
R_ABORT_UNLESS(svc::ClearEvent(svc::Handle(handle)));
}
void InterProcessEventImpl::Wait(Handle handle, bool auto_clear) {
while (true) {
/* Continuously wait, until success. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), reinterpret_cast<svc::Handle *>(std::addressof(handle)), 1, svc::WaitInfinite);
if (R_SUCCEEDED(res)) {
/* Clear, if we must. */
if (auto_clear) {
R_TRY_CATCH(svc::ResetSignal(svc::Handle(handle))) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return;
}
AMS_ASSERT(svc::ResultCancelled::Includes(res));
}
}
bool InterProcessEventImpl::TryWait(Handle handle, bool auto_clear) {
/* If we're auto clear, just try to reset. */
if (auto_clear) {
return R_SUCCEEDED(svc::ResetSignal(svc::Handle(handle)));
}
/* Not auto-clear. */
while (true) {
/* Continuously wait, until success or timeout. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), reinterpret_cast<svc::Handle *>(std::addressof(handle)), 1, 0);
/* If we succeeded, we're signaled. */
if (R_SUCCEEDED(res)) {
return true;
}
/* If we timed out, we're not signaled. */
if (svc::ResultTimedOut::Includes(res)) {
return false;
}
AMS_ASSERT(svc::ResultCancelled::Includes(res));
}
}
bool InterProcessEventImpl::TimedWait(Handle handle, bool auto_clear, TimeSpan timeout) {
TimeoutHelper timeout_helper(timeout);
while (true) {
/* Continuously wait, until success. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), reinterpret_cast<svc::Handle *>(std::addressof(handle)), 1, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds());
if (R_SUCCEEDED(res)) {
/* Clear, if we must. */
if (auto_clear) {
R_TRY_CATCH(svc::ResetSignal(svc::Handle(handle))) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return true;
}
if (svc::ResultTimedOut::Includes(res)) {
return false;
}
AMS_ASSERT(svc::ResultCancelled::Includes(res));
}
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
class InterProcessEventImpl {
public:
static Result Create(Handle *out_write, Handle *out_read);
static void Close(Handle handle);
static void Signal(Handle handle);
static void Clear(Handle handle);
static void Wait(Handle handle, bool auto_clear);
static bool TryWait(Handle handle, bool auto_clear);
static bool TimedWait(Handle handle, bool auto_clear, TimeSpan timeout);
};
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_timeout_helper.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
void InternalConditionVariableImpl::Signal() {
ams::svc::SignalProcessWideKey(reinterpret_cast<uintptr_t>(std::addressof(this->value)), 1);
}
void InternalConditionVariableImpl::Broadcast() {
ams::svc::SignalProcessWideKey(reinterpret_cast<uintptr_t>(std::addressof(this->value)), -1);
}
void InternalConditionVariableImpl::Wait(InternalCriticalSection *cs) {
const auto cur_handle = GetCurrentThreadHandle();
AMS_ASSERT((cs->Get()->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle);
R_ABORT_UNLESS(ams::svc::WaitProcessWideKeyAtomic(reinterpret_cast<uintptr_t>(std::addressof(cs->Get()->thread_handle)), reinterpret_cast<uintptr_t>(std::addressof(this->value)), cur_handle, -1));
}
ConditionVariableStatus InternalConditionVariableImpl::TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper) {
const auto cur_handle = GetCurrentThreadHandle();
AMS_ASSERT((cs->Get()->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle);
const TimeSpan left = timeout_helper.GetTimeLeftOnTarget();
if (left > 0) {
R_TRY_CATCH(ams::svc::WaitProcessWideKeyAtomic(reinterpret_cast<uintptr_t>(std::addressof(cs->Get()->thread_handle)), reinterpret_cast<uintptr_t>(std::addressof(this->value)), cur_handle, left.GetNanoSeconds())) {
R_CATCH(svc::ResultTimedOut) {
cs->Enter();
return ConditionVariableStatus::TimedOut;
}
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return ConditionVariableStatus::Success;
} else {
return ConditionVariableStatus::TimedOut;
}
}
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_timeout_helper.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
namespace {
ALWAYS_INLINE void DataMemoryBarrierForCriticalSection() {
#if defined(ATMOSPHERE_ARCH_ARM64)
/* ... */
#else
#error "Unknown architecture for os::impl::InternalCriticalSectionImpl DataMemoryBarrier"
#endif
}
ALWAYS_INLINE u32 LoadExclusive(u32 *ptr) {
u32 value;
#if defined(ATMOSPHERE_ARCH_ARM64)
__asm__ __volatile__("ldaxr %w[value], [%[ptr]]" : [value]"=&r"(value) : [ptr]"r"(ptr) : "memory");
#else
#error "Unknown architecture for os::impl::InternalCriticalSectionImpl LoadExclusive"
#endif
return value;
}
ALWAYS_INLINE int StoreExclusive(u32 *ptr, u32 value) {
int result;
#if defined(ATMOSPHERE_ARCH_ARM64)
__asm__ __volatile__("stlxr %w[result], %w[value], [%[ptr]]" : [result]"=&r"(result) : [value]"r"(value), [ptr]"r"(ptr) : "memory");
#else
#error "Unknown architecture for os::impl::InternalCriticalSectionImpl StoreExclusive"
#endif
return result;
}
ALWAYS_INLINE void ClearExclusive() {
#if defined(ATMOSPHERE_ARCH_ARM64)
__asm__ __volatile__("clrex" ::: "memory");
#else
#error "Unknown architecture for os::impl::InternalCriticalSectionImpl ClearExclusive"
#endif
}
}
void InternalCriticalSectionImpl::Enter() {
AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0);
const auto cur_handle = GetCurrentThreadHandle();
AMS_ASSERT((this->thread_handle & ~ams::svc::HandleWaitMask) != cur_handle);
u32 value = LoadExclusive(std::addressof(this->thread_handle));
while (true) {
if (AMS_LIKELY(value == svc::InvalidHandle)) {
if (AMS_UNLIKELY(StoreExclusive(std::addressof(this->thread_handle), cur_handle) != 0)) {
value = LoadExclusive(std::addressof(this->thread_handle));
continue;
}
break;
}
if (AMS_LIKELY((value & ams::svc::HandleWaitMask) == 0)) {
if (AMS_UNLIKELY(StoreExclusive(std::addressof(this->thread_handle), value | ams::svc::HandleWaitMask) != 0)) {
value = LoadExclusive(std::addressof(this->thread_handle));
continue;
}
}
R_ABORT_UNLESS(ams::svc::ArbitrateLock(value & ~ams::svc::HandleWaitMask, reinterpret_cast<uintptr_t>(std::addressof(this->thread_handle)), cur_handle));
value = LoadExclusive(std::addressof(this->thread_handle));
if (AMS_LIKELY((value & ~ams::svc::HandleWaitMask) == cur_handle)) {
ClearExclusive();
break;
}
}
DataMemoryBarrierForCriticalSection();
}
bool InternalCriticalSectionImpl::TryEnter() {
AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0);
const auto cur_handle = GetCurrentThreadHandle();
while (true) {
u32 value = LoadExclusive(std::addressof(this->thread_handle));
if (AMS_UNLIKELY(value != svc::InvalidHandle)) {
break;
}
DataMemoryBarrierForCriticalSection();
if (AMS_LIKELY(StoreExclusive(std::addressof(this->thread_handle), cur_handle) == 0)) {
return true;
}
}
ClearExclusive();
DataMemoryBarrierForCriticalSection();
return false;
}
void InternalCriticalSectionImpl::Leave() {
AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0);
const auto cur_handle = GetCurrentThreadHandle();
u32 value = LoadExclusive(std::addressof(this->thread_handle));
while (true) {
if (AMS_UNLIKELY(value != cur_handle)) {
ClearExclusive();
break;
}
DataMemoryBarrierForCriticalSection();
if (AMS_LIKELY(StoreExclusive(std::addressof(this->thread_handle), 0) == 0)) {
break;
}
value = LoadExclusive(std::addressof(this->thread_handle));
}
DataMemoryBarrierForCriticalSection();
AMS_ASSERT((value | ams::svc::HandleWaitMask) == (cur_handle | ams::svc::HandleWaitMask));
if (value & ams::svc::HandleWaitMask) {
R_ABORT_UNLESS(ams::svc::ArbitrateUnlock(reinterpret_cast<uintptr_t>(std::addressof(this->thread_handle))));
}
}
bool InternalCriticalSectionImpl::IsLockedByCurrentThread() const {
const auto cur_handle = GetCurrentThreadHandle();
return (this->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle;
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_interrupt_event_target_impl.os.horizon.hpp"
#else
#error "Unknown OS for ams::os::InterruptEventImpl"
#endif
namespace ams::os::impl {
class InterruptEventImpl {
private:
InterruptEventTargetImpl impl;
public:
explicit InterruptEventImpl(InterruptName name, EventClearMode clear_mode) : impl(name, clear_mode) { /* ... */ }
void Clear() {
return this->impl.Clear();
}
void Wait() {
return this->impl.Wait();
}
bool TryWait() {
return this->impl.TryWait();
}
bool TimedWait(TimeSpan timeout) {
return this->impl.TimedWait(timeout);
}
TriBool IsSignaled() {
return this->impl.IsSignaled();
}
Handle GetHandle() const {
return this->impl.GetHandle();
}
};
}

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_interrupt_event_target_impl.os.horizon.hpp"
#include "os_timeout_helper.hpp"
namespace ams::os::impl {
InterruptEventHorizonImpl::InterruptEventHorizonImpl(InterruptName name, EventClearMode clear_mode) {
this->manual_clear = (clear_mode == EventClearMode_ManualClear);
auto interrupt_type = this->manual_clear ? svc::InterruptType_Level : svc::InterruptType_Edge;
svc::Handle handle;
R_ABORT_UNLESS(svc::CreateInterruptEvent(std::addressof(handle), static_cast<s32>(name), interrupt_type));
this->handle = handle;
}
InterruptEventHorizonImpl::~InterruptEventHorizonImpl() {
R_ABORT_UNLESS(svc::CloseHandle(this->handle));
}
void InterruptEventHorizonImpl::Clear() {
R_ABORT_UNLESS(svc::ClearEvent(this->handle));
}
void InterruptEventHorizonImpl::Wait() {
while (true) {
/* Continuously wait, until success. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(this->handle), 1, svc::WaitInfinite);
if (R_SUCCEEDED(res)) {
/* Clear, if we must. */
if (!this->manual_clear) {
R_TRY_CATCH(svc::ResetSignal(this->handle)) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return;
}
AMS_ASSERT(svc::ResultCancelled::Includes(res));
}
}
bool InterruptEventHorizonImpl::TryWait() {
/* If we're auto clear, just try to reset. */
if (!this->manual_clear) {
return R_SUCCEEDED(svc::ResetSignal(this->handle));
}
/* Not auto-clear. */
while (true) {
/* Continuously wait, until success or timeout. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(this->handle), 1, 0);
/* If we succeeded, we're signaled. */
if (R_SUCCEEDED(res)) {
return true;
}
/* If we timed out, we're not signaled. */
if (svc::ResultTimedOut::Includes(res)) {
return false;
}
AMS_ASSERT(svc::ResultCancelled::Includes(res));
}
}
bool InterruptEventHorizonImpl::TimedWait(TimeSpan timeout) {
TimeoutHelper timeout_helper(timeout);
while (true) {
/* Continuously wait, until success. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(this->handle), 1, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds());
if (R_SUCCEEDED(res)) {
/* Clear, if we must. */
if (!this->manual_clear) {
R_TRY_CATCH(svc::ResetSignal(this->handle)) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return true;
}
if (svc::ResultTimedOut::Includes(res)) {
return false;
}
AMS_ASSERT(svc::ResultCancelled::Includes(res));
}
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
class InterruptEventHorizonImpl {
private:
svc::Handle handle;
bool manual_clear;
public:
explicit InterruptEventHorizonImpl(InterruptName name, EventClearMode mode);
~InterruptEventHorizonImpl();
void Clear();
void Wait();
bool TryWait();
bool TimedWait(TimeSpan timeout);
TriBool IsSignaled() {
return TriBool::Undefined;
}
Handle GetHandle() const {
return this->handle;
}
};
using InterruptEventTargetImpl = InterruptEventHorizonImpl;
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
void PushAndCheckLockLevel(MutexType *mutex);
void PopAndCheckLockLevel(MutexType *mutex);
}

View file

@ -16,6 +16,7 @@
#pragma once
#include <stratosphere.hpp>
#include "os_rng_manager_impl.hpp"
#include "os_thread_manager_types.hpp"
#include "os_tick_manager_impl.hpp"
namespace ams::os::impl {
@ -24,12 +25,15 @@ namespace ams::os::impl {
private:
RngManager rng_manager{};
/* TODO */
ThreadManager thread_manager{};
/* TODO */
TickManager tick_manager{};
/* TODO */
public:
constexpr OsResourceManager() = default;
OsResourceManager() = default;
constexpr ALWAYS_INLINE RngManager &GetRngManager() { return this->rng_manager; }
constexpr ALWAYS_INLINE ThreadManager &GetThreadManager() { return this->thread_manager; }
constexpr ALWAYS_INLINE TickManager &GetTickManager() { return this->tick_manager; }
};

View file

@ -15,6 +15,7 @@
*/
#pragma once
#include <stratosphere.hpp>
#include "os_resource_manager.hpp"
namespace ams::os::impl {

View file

@ -26,7 +26,7 @@ namespace ams::os::impl {
private:
void Initialize();
public:
constexpr RngManager() : mt(), lock(), initialized() { /* ... */ }
constexpr RngManager() : mt(), lock(false), initialized() { /* ... */ }
public:
u64 GenerateRandomU64();
};

View file

@ -0,0 +1,226 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_thread_manager.hpp"
#include "os_waitable_manager_impl.hpp"
#include "os_waitable_holder_base.hpp"
#include "os_waitable_holder_impl.hpp"
#include "os_waitable_object_list.hpp"
namespace ams::os::impl {
void SetupThreadObjectUnsafe(ThreadType *thread, ThreadImpl *thread_impl, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority) {
/* Setup objects. */
new (GetPointer(thread->cs_thread)) impl::InternalCriticalSection;
new (GetPointer(thread->cv_thread)) impl::InternalConditionVariable;
new (GetPointer(thread->all_threads_node)) util::IntrusiveListNode;
new (GetPointer(thread->waitlist)) WaitableObjectList;
/* Set member variables. */
thread->thread_impl = (thread_impl != nullptr) ? thread_impl : std::addressof(thread->thread_impl_storage);
thread->function = function;
thread->argument = arg;
thread->stack = stack;
thread->stack_size = stack_size;
thread->base_priority = priority;
thread->suspend_count = 0;
thread->name_buffer[0] = '\x00';
thread->name_pointer = thread->name_buffer;
/* Mark initialized. */
thread->state = ThreadType::State_Initialized;
}
void ThreadManager::InvokeThread(ThreadType *thread) {
auto &manager = GetThreadManager();
manager.SetCurrentThread(thread);
manager.NotifyThreadNameChanged(thread);
{
std::unique_lock lk(GetReference(thread->cs_thread));
while (thread->state == ThreadType::State_Initialized) {
GetReference(thread->cv_thread).Wait(GetPointer(thread->cs_thread));
}
if (thread->state == ThreadType::State_Started) {
lk.unlock();
thread->function(thread->argument);
}
}
manager.CleanupThread();
}
ThreadManager::ThreadManager() : impl(std::addressof(main_thread)), total_thread_stack_size(0), num_created_threads(0) {
this->main_thread.state = ThreadType::State_Started;
this->SetCurrentThread(std::addressof(this->main_thread));
this->PlaceThreadObjectUnderThreadManagerSafe(std::addressof(this->main_thread));
}
void ThreadManager::CleanupThread() {
ThreadType *thread = this->GetCurrentThread();
{
std::scoped_lock lk(GetReference(thread->cs_thread));
thread->state = ThreadType::State_Terminated;
GetReference(thread->cv_thread).Broadcast();
GetReference(thread->waitlist).SignalAllThreads();
}
}
Result ThreadManager::CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority, s32 ideal_core) {
SetupThreadObjectUnsafe(thread, nullptr, function, argument, stack, stack_size, priority);
auto guard = SCOPE_GUARD { thread->state = ThreadType::State_NotInitialized; };
R_TRY(this->impl.CreateThread(thread, ideal_core));
guard.Cancel();
this->PlaceThreadObjectUnderThreadManagerSafe(thread);
return ResultSuccess();
}
Result ThreadManager::CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority) {
return this->CreateThread(thread, function, argument, stack, stack_size, priority, this->impl.GetDefaultCoreNumber());
}
void ThreadManager::DestroyThread(ThreadType *thread) {
{
std::scoped_lock lk(GetReference(thread->cs_thread));
if (thread->state == ThreadType::State_Initialized) {
thread->state = ThreadType::State_DestroyedBeforeStarted;
this->impl.StartThread(thread);
GetReference(thread->cv_thread).Signal();
}
}
this->impl.WaitForThreadExit(thread);
AMS_ASSERT(thread->state == ThreadType::State_Initialized);
{
std::scoped_lock lk(GetReference(thread->cs_thread));
/* NOTE: Here Nintendo would cleanup the alias stack. */
this->impl.DestroyThreadUnsafe(thread);
thread->state = ThreadType::State_NotInitialized;
GetReference(thread->waitlist).~WaitableObjectList();
thread->name_buffer[0] = '\x00';
{
std::scoped_lock tlk(this->cs);
this->EraseFromAllThreadsListUnsafe(thread);
}
}
}
void ThreadManager::StartThread(ThreadType *thread) {
std::scoped_lock lk(GetReference(thread->cs_thread));
AMS_ASSERT(thread->state == ThreadType::State_Initialized);
this->impl.StartThread(thread);
thread->state = ThreadType::State_Started;
GetReference(thread->cv_thread).Signal();
}
void ThreadManager::WaitThread(ThreadType *thread) {
this->impl.WaitForThreadExit(thread);
{
std::scoped_lock lk(GetReference(thread->cs_thread));
/* Note: Here Nintendo would cleanup the alias stack. */
}
}
bool ThreadManager::TryWaitThread(ThreadType *thread) {
const bool result = this->impl.TryWaitForThreadExit(thread);
if (result) {
std::scoped_lock lk(GetReference(thread->cs_thread));
/* Note: Here Nintendo would cleanup the alias stack. */
}
return result;
}
s32 ThreadManager::SuspendThread(ThreadType *thread) {
std::scoped_lock lk(GetReference(thread->cs_thread));
auto prev_suspend_count = thread->suspend_count;
AMS_ASSERT(prev_suspend_count < ThreadSuspendCountMax);
thread->suspend_count = prev_suspend_count + 1;
if (prev_suspend_count == 0) {
this->impl.SuspendThreadUnsafe(thread);
}
return prev_suspend_count;
}
s32 ThreadManager::ResumeThread(ThreadType *thread) {
std::scoped_lock lk(GetReference(thread->cs_thread));
auto prev_suspend_count = thread->suspend_count;
if (prev_suspend_count > 0) {
thread->suspend_count = prev_suspend_count - 1;
if (prev_suspend_count == 1) {
this->impl.ResumeThreadUnsafe(thread);
}
}
return prev_suspend_count;
}
void ThreadManager::CancelThreadSynchronization(ThreadType *thread) {
std::scoped_lock lk(GetReference(thread->cs_thread));
this->impl.CancelThreadSynchronizationUnsafe(thread);
}
/* TODO void ThreadManager::GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */
void ThreadManager::SetInitialThreadNameUnsafe(ThreadType *thread) {
if (thread == std::addressof(this->main_thread)) {
constexpr const char MainThreadName[] = "MainThread";
static_assert(sizeof(thread->name_buffer) >= sizeof(MainThreadName));
static_assert(MainThreadName[sizeof(MainThreadName) - 1] == '\x00');
std::memcpy(thread->name_buffer, MainThreadName, sizeof(MainThreadName));
} else {
constexpr const char ThreadNamePrefix[] = "Thread_0x";
constexpr size_t ThreadNamePrefixSize = sizeof(ThreadNamePrefix) - 1;
const u64 func = reinterpret_cast<u64>(thread->function);
static_assert(ThreadNamePrefixSize + sizeof(func) * 2 + 1 <= sizeof(thread->name_buffer));
std::snprintf(thread->name_buffer, sizeof(thread->name_buffer), "%s%016lX", ThreadNamePrefix, func);
}
thread->name_pointer = thread->name_buffer;
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "os_thread_manager_types.hpp"
#include "os_resource_manager.hpp"
namespace ams::os::impl {
constexpr inline s32 CoreAffinityMaskBitWidth = BITSIZEOF(u64);
ALWAYS_INLINE ThreadManager &GetThreadManager() {
return GetResourceManager().GetThreadManager();
}
ALWAYS_INLINE ThreadType *GetCurrentThread() {
return GetThreadManager().GetCurrentThread();
}
ALWAYS_INLINE Handle GetCurrentThreadHandle() {
/* return GetCurrentThread()->thread_impl->handle; */
return ::threadGetCurHandle();
}
void SetupThreadObjectUnsafe(ThreadType *thread, ThreadImpl *thread_impl, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority);
}

View file

@ -0,0 +1,214 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_thread_manager_impl.os.horizon.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
thread_local ThreadType *g_current_thread_pointer;
namespace {
s32 ConvertToHorizonPriority(s32 user_priority) {
const s32 horizon_priority = user_priority + UserThreadPriorityOffset;
AMS_ASSERT(HighestTargetThreadPriority <= horizon_priority && horizon_priority <= LowestTargetThreadPriority);
return horizon_priority;
}
s32 ConvertToUserPriority(s32 horizon_priority) {
AMS_ASSERT(HighestTargetThreadPriority <= horizon_priority && horizon_priority <= LowestTargetThreadPriority);
return horizon_priority - UserThreadPriorityOffset;
}
void InvokeThread(uintptr_t _thread) {
ThreadType *thread = reinterpret_cast<ThreadType *>(_thread);
/* Set the thread's id. */
u64 thread_id;
R_ABORT_UNLESS(svc::GetThreadId(std::addressof(thread_id), svc::Handle(thread->thread_impl->handle)));
thread->thread_id = thread_id;
/* Invoke the thread. */
ThreadManager::InvokeThread(thread);
}
}
ThreadManagerHorizonImpl::ThreadManagerHorizonImpl(ThreadType *main_thread) {
/* Get the thread impl object from libnx. */
ThreadImpl *thread_impl = ::threadGetSelf();
/* Get the thread priority. */
s32 horizon_priority;
R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(horizon_priority), thread_impl->handle));
SetupThreadObjectUnsafe(main_thread, thread_impl, nullptr, nullptr, thread_impl->stack_mirror, thread_impl->stack_sz, ConvertToUserPriority(horizon_priority));
/* Set the thread id. */
u64 thread_id;
R_ABORT_UNLESS(svc::GetThreadId(std::addressof(thread_id), svc::Handle(thread_impl->handle)));
main_thread->thread_id = thread_id;
/* NOTE: Here Nintendo would set the thread pointer in TLS. */
}
Result ThreadManagerHorizonImpl::CreateThread(ThreadType *thread, s32 ideal_core) {
/* Note: Here Nintendo would set the stack args to point to ThreadManager::InvokeThread. */
s32 count = 0;
while (true) {
R_TRY_CATCH(::threadCreate(thread->thread_impl, reinterpret_cast<::ThreadFunc>(&InvokeThread), thread, thread->stack, thread->stack_size, ConvertToHorizonPriority(thread->base_priority), ideal_core)) {
R_CATCH(svc::ResultOutOfResource) {
if ((++count) < 10) {
os::SleepThread(TimeSpan::FromMilliSeconds(10));
continue;
}
return os::ResultOutOfResource();
}
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return ResultSuccess();
}
}
void ThreadManagerHorizonImpl::DestroyThreadUnsafe(ThreadType *thread) {
R_ABORT_UNLESS(::threadClose(thread->thread_impl));
}
void ThreadManagerHorizonImpl::StartThread(const ThreadType *thread) {
R_ABORT_UNLESS(::threadStart(thread->thread_impl));
}
void ThreadManagerHorizonImpl::WaitForThreadExit(ThreadType *thread) {
const svc::Handle handle(thread->thread_impl->handle);
while (true) {
s32 index;
R_TRY_CATCH(svc::WaitSynchronization(std::addressof(index), std::addressof(handle), 1, svc::WaitInfinite)) {
R_CATCH(svc::ResultCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return;
}
}
bool ThreadManagerHorizonImpl::TryWaitForThreadExit(ThreadType *thread) {
const svc::Handle handle(thread->thread_impl->handle);
while (true) {
/* Continuously wait, until success or timeout. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(handle), 1, 0);
/* If we succeeded, we're signaled. */
if (R_SUCCEEDED(res)) {
return true;
}
/* If we timed out, we're not signaled. */
if (svc::ResultTimedOut::Includes(res)) {
return false;
}
AMS_ABORT_UNLESS(svc::ResultCancelled::Includes(res));
}
}
void ThreadManagerHorizonImpl::YieldThread() {
if (hos::GetVersion() >= hos::Version_400) {
svc::SleepThread(svc::YieldType_WithCoreMigration);
} else {
svc::SleepThread(svc::YieldType_WithoutCoreMigration);
}
}
bool ThreadManagerHorizonImpl::ChangePriority(ThreadType *thread, s32 priority) {
const svc::Handle handle(thread->thread_impl->handle);
auto res = svc::SetThreadPriority(handle, ConvertToHorizonPriority(priority));
if (svc::ResultInvalidPriority::Includes(res)) {
AMS_ABORT("Invalid thread priority");
}
return R_SUCCEEDED(res);
}
s32 ThreadManagerHorizonImpl::GetCurrentPriority(const ThreadType *thread) const {
const svc::Handle handle(thread->thread_impl->handle);
s32 priority;
R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(priority), handle));
return ConvertToUserPriority(priority);
}
ThreadId ThreadManagerHorizonImpl::GetThreadId(const ThreadType *thread) const {
return thread->thread_id;
}
void ThreadManagerHorizonImpl::SuspendThreadUnsafe(ThreadType *thread) {
const svc::Handle handle(thread->thread_impl->handle);
R_ABORT_UNLESS(svc::SetThreadActivity(handle, svc::ThreadActivity_Paused));
}
void ThreadManagerHorizonImpl::ResumeThreadUnsafe(ThreadType *thread) {
const svc::Handle handle(thread->thread_impl->handle);
R_ABORT_UNLESS(svc::SetThreadActivity(handle, svc::ThreadActivity_Runnable));
}
void ThreadManagerHorizonImpl::CancelThreadSynchronizationUnsafe(ThreadType *thread) {
const svc::Handle handle(thread->thread_impl->handle);
R_ABORT_UNLESS(svc::CancelSynchronization(handle));
}
/* TODO: void GetThreadContextUnsafe(ThreadContextInfo *out_context, const ThreadType *thread); */
s32 ThreadManagerHorizonImpl::GetCurrentCoreNumber() const {
return svc::GetCurrentProcessorNumber();
}
void ThreadManagerHorizonImpl::SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const {
const svc::Handle handle(thread->thread_impl->handle);
R_ABORT_UNLESS(svc::SetThreadCoreMask(handle, ideal_core, affinity_mask));
}
void ThreadManagerHorizonImpl::GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const {
s32 ideal_core;
u64 affinity_mask;
const svc::Handle handle(thread->thread_impl->handle);
R_ABORT_UNLESS(svc::GetThreadCoreMask(std::addressof(ideal_core), std::addressof(affinity_mask), handle));
if (out_ideal_core) {
*out_ideal_core = ideal_core;
}
if (out_affinity_mask) {
*out_affinity_mask = affinity_mask;
}
}
u64 ThreadManagerHorizonImpl::GetThreadAvailableCoreMask() const {
u64 core_mask;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(core_mask), svc::InfoType_CoreMask, svc::PseudoHandle::CurrentProcess, 0));
return core_mask;
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
constexpr inline s32 TargetThreadPriorityRangeSize = svc::LowestThreadPriority - svc::HighestThreadPriority + 1;
constexpr inline s32 ReservedThreadPriorityRangeSize = svc::SystemThreadPriorityHighest;
constexpr inline s32 SystemThreadPriorityRangeSize = TargetThreadPriorityRangeSize - ReservedThreadPriorityRangeSize;
constexpr inline s32 UserThreadPriorityOffset = 28;
constexpr inline s32 HighestTargetThreadPriority = 0;
constexpr inline s32 LowestTargetThreadPriority = TargetThreadPriorityRangeSize - 1;
extern thread_local ThreadType *g_current_thread_pointer;
class ThreadManagerHorizonImpl {
NON_COPYABLE(ThreadManagerHorizonImpl);
NON_MOVEABLE(ThreadManagerHorizonImpl);
public:
explicit ThreadManagerHorizonImpl(ThreadType *main_thread);
Result CreateThread(ThreadType *thread, s32 ideal_core);
void DestroyThreadUnsafe(ThreadType *thread);
void StartThread(const ThreadType *thread);
void WaitForThreadExit(ThreadType *thread);
bool TryWaitForThreadExit(ThreadType *thread);
void YieldThread();
bool ChangePriority(ThreadType *thread, s32 priority);
s32 GetCurrentPriority(const ThreadType *thread) const;
ThreadId GetThreadId(const ThreadType *thread) const;
void SuspendThreadUnsafe(ThreadType *thread);
void ResumeThreadUnsafe(ThreadType *thread);
void CancelThreadSynchronizationUnsafe(ThreadType *thread);
/* TODO: void GetThreadContextUnsafe(ThreadContextInfo *out_context, const ThreadType *thread); */
void NotifyThreadNameChangedImpl(const ThreadType *thread) const { /* ... */ }
void SetCurrentThread(ThreadType *thread) const {
g_current_thread_pointer = thread;
}
ThreadType *GetCurrentThread() const {
return g_current_thread_pointer;
}
s32 GetCurrentCoreNumber() const;
s32 GetDefaultCoreNumber() const { return svc::IdealCoreUseProcessValue; }
void SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const;
void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const;
u64 GetThreadAvailableCoreMask() const;
NORETURN void ExitProcessImpl() {
svc::ExitProcess();
AMS_ABORT("Process was exited");
}
};
using ThreadManagerImpl = ThreadManagerHorizonImpl;
}

View file

@ -0,0 +1,151 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#ifdef ATMOSPHERE_OS_HORIZON
#include "os_thread_manager_impl.os.horizon.hpp"
#else
#error "Unknown OS for ThreadManagerImpl"
#endif
namespace ams::os::impl {
class ThreadManager {
NON_COPYABLE(ThreadManager);
NON_MOVEABLE(ThreadManager);
private:
class ThreadListTraits {
public:
using ListType = util::IntrusiveList<ThreadType, ThreadListTraits>;
private:
friend class util::IntrusiveList<ThreadType, ThreadListTraits>;
static constexpr util::IntrusiveListNode &GetNode(ThreadType &parent) {
return GetReference(parent.all_threads_node);
}
static constexpr util::IntrusiveListNode const &GetNode(ThreadType const &parent) {
return GetReference(parent.all_threads_node);
}
static ThreadType &GetParent(util::IntrusiveListNode &node) {
return *reinterpret_cast<ThreadType *>(reinterpret_cast<char *>(std::addressof(node)) - OFFSETOF(ThreadType, all_threads_node));
}
static ThreadType const &GetParent(util::IntrusiveListNode const &node) {
return *reinterpret_cast<const ThreadType *>(reinterpret_cast<const char *>(std::addressof(node)) - OFFSETOF(ThreadType, all_threads_node));
}
};
using AllThreadsList = ThreadListTraits::ListType;
private:
ThreadManagerImpl impl;
ThreadType main_thread;
InternalCriticalSection cs;
AllThreadsList all_threads_list;
size_t total_thread_stack_size;
s32 num_created_threads;
public:
ThreadManager();
void CleanupThread();
s32 GetThreadCountForDebug() const { return this->num_created_threads; }
Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority, s32 ideal_core);
Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority);
void DestroyThread(ThreadType *thread);
void StartThread(ThreadType *thread);
void WaitThread(ThreadType *thread);
bool TryWaitThread(ThreadType *thread);
void YieldThread() { return this->impl.YieldThread(); }
bool ChangePriority(ThreadType *thread, s32 priority) { return this->impl.ChangePriority(thread, priority); }
s32 GetCurrentPriority(const ThreadType *thread) const { return this->impl.GetCurrentPriority(thread); }
ThreadType *GetCurrentThread() const { return this->impl.GetCurrentThread(); }
s32 SuspendThread(ThreadType *thread);
s32 ResumeThread(ThreadType *thread);
void CancelThreadSynchronization(ThreadType *thread);
/* TODO void GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */
void SetInitialThreadNameUnsafe(ThreadType *thread);
void NotifyThreadNameChanged(const ThreadType *thread) const { return this->impl.NotifyThreadNameChangedImpl(thread); }
void SetCurrentThread(ThreadType *thread) const { return this->impl.SetCurrentThread(thread); }
s32 GetCurrentCoreNumber() const { return this->impl.GetCurrentCoreNumber(); }
void SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const { return this->impl.SetThreadCoreMask(thread, ideal_core, affinity_mask); }
void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const { return this->impl.GetThreadCoreMask(out_ideal_core, out_affinity_mask, thread); }
u64 GetThreadAvailableCoreMask() const { return this->impl.GetThreadAvailableCoreMask(); }
void PushBackToAllThreadsListUnsafe(ThreadType *thread) {
this->all_threads_list.push_back(*thread);
++this->num_created_threads;
this->total_thread_stack_size += thread->stack_size;
}
void EraseFromAllThreadsListUnsafe(ThreadType *thread) {
this->all_threads_list.erase(this->all_threads_list.iterator_to(*thread));
--this->num_created_threads;
this->total_thread_stack_size -= thread->stack_size;
}
void PushBackToAllThreadsListSafe(ThreadType *thread) {
std::scoped_lock lk(this->cs);
this->PushBackToAllThreadsListUnsafe(thread);
}
void EraseFromAllThreadsListSafe(ThreadType *thread) {
std::scoped_lock lk(this->cs);
this->EraseFromAllThreadsListUnsafe(thread);
}
void PlaceThreadObjectUnderThreadManagerSafe(ThreadType *thread) {
SetInitialThreadNameUnsafe(thread);
{
std::scoped_lock lk(this->cs);
this->PushBackToAllThreadsListUnsafe(thread);
}
}
ThreadType *AllocateThreadType() const {
return reinterpret_cast<ThreadType *>(std::malloc(sizeof(ThreadType)));
}
void FreeThreadType(ThreadType *thread) const {
std::free(thread);
}
const ThreadType *GetMainThread() const {
return std::addressof(this->main_thread);
}
size_t GetTotalThreadStackSize() const {
return this->total_thread_stack_size;
}
ThreadId GetThreadId(const ThreadType *thread) {
return this->impl.GetThreadId(thread);
}
public:
static void InvokeThread(ThreadType *thread);
};
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_timeout_helper.hpp"
namespace ams::os::impl {
TargetTimeSpan TimeoutHelper::GetTimeLeftOnTarget() const {
/* If the absolute tick is zero, we're expired. */
if (this->absolute_end_tick.GetInt64Value() == 0) {
return 0;
}
/* Check if we've expired. */
const Tick cur_tick = impl::GetTickManager().GetTick();
if (cur_tick >= this->absolute_end_tick) {
return 0;
}
/* Return the converted difference as a timespan. */
return TimeoutHelperImpl::ConvertToImplTime(this->absolute_end_tick - cur_tick);
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "os_tick_manager.hpp"
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_timeout_helper_impl.os.horizon.hpp"
#else
#error "Unknown OS for ams::os::TimeoutHelper"
#endif
namespace ams::os::impl {
class TimeoutHelper {
private:
Tick absolute_end_tick;
public:
explicit TimeoutHelper(TimeSpan timeout) {
if (timeout == 0) {
/* If timeout is zero, don't do relative tick calculations. */
this->absolute_end_tick = Tick(0);
} else {
const auto &tick_manager = impl::GetTickManager();
const u64 cur_tick = tick_manager.GetTick().GetInt64Value();
const u64 timeout_tick = tick_manager.ConvertToTick(timeout).GetInt64Value();
const u64 end_tick = cur_tick + timeout_tick + 1;
this->absolute_end_tick = Tick(std::min<u64>(std::numeric_limits<s64>::max(), end_tick));
}
}
static void Sleep(TimeSpan tm) {
TimeoutHelperImpl::Sleep(tm);
}
bool TimedOut() const {
if (this->absolute_end_tick.GetInt64Value() == 0) {
return true;
}
const Tick cur_tick = impl::GetTickManager().GetTick();
return cur_tick >= this->absolute_end_tick;
}
TargetTimeSpan GetTimeLeftOnTarget() const;
};
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_timeout_helper_impl.os.horizon.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
void TimeoutHelperImpl::Sleep(TimeSpan tm) {
if (tm == TimeSpan(0)) {
GetThreadManager().YieldThread();
} else {
ams::svc::SleepThread(tm.GetNanoSeconds());
}
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "os_tick_manager.hpp"
namespace ams::os::impl {
using TargetTimeSpan = ::ams::TimeSpan;
class TimeoutHelperImpl {
public:
static TargetTimeSpan ConvertToImplTime(Tick tick) {
return impl::GetTickManager().ConvertToTimeSpan(tick);
}
static void Sleep(TimeSpan tm);
};
}

View file

@ -37,8 +37,8 @@ namespace ams::os::impl {
/* Gets handle to output, returns INVALID_HANDLE on failure. */
virtual Handle GetHandle() const = 0;
/* Gets the amount of time remaining until this wakes up. */
virtual u64 GetWakeupTime() const {
return std::numeric_limits<u64>::max();
virtual TimeSpan GetAbsoluteWakeupTime() const {
return TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max());
}
/* Interface with manager. */
@ -58,18 +58,18 @@ namespace ams::os::impl {
class WaitableHolderOfUserObject : public WaitableHolderBase {
public:
/* All user objects have no handle to wait on. */
virtual Handle GetHandle() const override {
return INVALID_HANDLE;
virtual Handle GetHandle() const override final {
return svc::InvalidHandle;
}
};
class WaitableHolderOfKernelObject : public WaitableHolderBase {
public:
/* All kernel objects have native handles, and thus don't have object list semantics. */
virtual TriBool LinkToObjectList() override {
virtual TriBool LinkToObjectList() override final {
return TriBool::Undefined;
}
virtual void UnlinkFromObjectList() override {
virtual void UnlinkFromObjectList() override final {
/* ... */
}
};

View file

@ -51,6 +51,6 @@ namespace ams::os::impl {
#undef CHECK_HOLDER
static_assert(std::is_trivial<WaitableHolderImpl>::value && std::is_trivially_destructible<WaitableHolderImpl>::value, "WaitableHolderImpl");
static_assert(sizeof(WaitableHolderImpl) == WaitableHolder::ImplStorageSize, "WaitableHolderImpl size");
static_assert(std::is_trivial<WaitableHolderImpl>::value && std::is_trivially_destructible<WaitableHolderImpl>::value);
static_assert(sizeof(WaitableHolderImpl) == sizeof(os::WaitableHolderType::impl_storage));
}

View file

@ -21,29 +21,29 @@ namespace ams::os::impl {
class WaitableHolderOfEvent : public WaitableHolderOfUserObject {
private:
Event *event;
EventType *event;
private:
TriBool IsSignaledImpl() const {
return this->event->signaled ? TriBool::True : TriBool::False;
}
public:
explicit WaitableHolderOfEvent(Event *e) : event(e) { /* ... */ }
explicit WaitableHolderOfEvent(EventType *e) : event(e) { /* ... */ }
/* IsSignaled, Link, Unlink implemented. */
virtual TriBool IsSignaled() const override {
std::scoped_lock lk(this->event->lock);
std::scoped_lock lk(GetReference(this->event->cs_event));
return this->IsSignaledImpl();
}
virtual TriBool LinkToObjectList() override {
std::scoped_lock lk(this->event->lock);
std::scoped_lock lk(GetReference(this->event->cs_event));
GetReference(this->event->waitable_object_list_storage).LinkWaitableHolder(*this);
return this->IsSignaledImpl();
}
virtual void UnlinkFromObjectList() override {
std::scoped_lock lk(this->event->lock);
std::scoped_lock lk(GetReference(this->event->cs_event));
GetReference(this->event->waitable_object_list_storage).UnlinkWaitableHolder(*this);
}

View file

@ -21,9 +21,9 @@ namespace ams::os::impl {
class WaitableHolderOfInterProcessEvent : public WaitableHolderOfKernelObject {
private:
InterProcessEvent *event;
InterProcessEventType *event;
public:
explicit WaitableHolderOfInterProcessEvent(InterProcessEvent *e) : event(e) { /* ... */ }
explicit WaitableHolderOfInterProcessEvent(InterProcessEventType *e) : event(e) { /* ... */ }
/* IsSignaled, GetHandle both implemented. */
virtual TriBool IsSignaled() const override {
@ -31,8 +31,7 @@ namespace ams::os::impl {
}
virtual Handle GetHandle() const override {
AMS_ABORT_UNLESS(this->event->is_initialized);
return this->event->GetReadableHandle();
return this->event->readable_handle;
}
};

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_waitable_holder_of_interrupt_event.hpp"
#include "os_interrupt_event_impl.hpp"
namespace ams::os::impl {
Handle WaitableHolderOfInterruptEvent::GetHandle() const {
return GetReference(event->impl).GetHandle();
}
}

View file

@ -20,19 +20,16 @@ namespace ams::os::impl {
class WaitableHolderOfInterruptEvent : public WaitableHolderOfKernelObject {
private:
InterruptEvent *event;
InterruptEventType *event;
public:
explicit WaitableHolderOfInterruptEvent(InterruptEvent *e) : event(e) { /* ... */ }
explicit WaitableHolderOfInterruptEvent(InterruptEventType *e) : event(e) { /* ... */ }
/* IsSignaled, GetHandle both implemented. */
virtual TriBool IsSignaled() const override {
return TriBool::Undefined;
}
virtual Handle GetHandle() const override {
AMS_ABORT_UNLESS(this->event->is_initialized);
return this->event->handle.Get();
}
virtual Handle GetHandle() const override;
};
}

View file

@ -19,57 +19,57 @@
namespace ams::os::impl {
template<MessageQueueWaitKind WaitKind>
template<MessageQueueWaitType WaitType>
class WaitableHolderOfMessageQueue : public WaitableHolderOfUserObject {
static_assert(WaitKind == MessageQueueWaitKind::ForNotEmpty || WaitKind == MessageQueueWaitKind::ForNotFull, "MessageQueueHolder WaitKind!");
static_assert(WaitType == MessageQueueWaitType::ForNotEmpty || WaitType == MessageQueueWaitType::ForNotFull);
private:
MessageQueue *message_queue;
MessageQueueType *mq;
private:
constexpr inline TriBool IsSignaledImpl() const {
if constexpr (WaitKind == MessageQueueWaitKind::ForNotEmpty) {
if constexpr (WaitType == MessageQueueWaitType::ForNotEmpty) {
/* ForNotEmpty. */
return this->message_queue->IsEmpty() ? TriBool::False : TriBool::True;
} else if constexpr (WaitKind == MessageQueueWaitKind::ForNotFull) {
return this->mq->count > 0 ? TriBool::True : TriBool::False;
} else if constexpr (WaitType == MessageQueueWaitType::ForNotFull) {
/* ForNotFull */
return this->message_queue->IsFull() ? TriBool::False : TriBool::True;
return this->mq->count < this->mq->capacity ? TriBool::True : TriBool::False;
} else {
static_assert(WaitKind != WaitKind);
static_assert(WaitType != WaitType);
}
}
constexpr inline WaitableObjectList &GetObjectList() const {
if constexpr (WaitKind == MessageQueueWaitKind::ForNotEmpty) {
return GetReference(this->message_queue->waitlist_not_empty);
} else if constexpr (WaitKind == MessageQueueWaitKind::ForNotFull) {
return GetReference(this->message_queue->waitlist_not_full);
if constexpr (WaitType == MessageQueueWaitType::ForNotEmpty) {
return GetReference(this->mq->waitlist_not_empty);
} else if constexpr (WaitType == MessageQueueWaitType::ForNotFull) {
return GetReference(this->mq->waitlist_not_full);
} else {
static_assert(WaitKind != WaitKind);
static_assert(WaitType != WaitType);
}
}
public:
explicit WaitableHolderOfMessageQueue(MessageQueue *mq) : message_queue(mq) { /* ... */ }
explicit WaitableHolderOfMessageQueue(MessageQueueType *mq) : mq(mq) { /* ... */ }
/* IsSignaled, Link, Unlink implemented. */
virtual TriBool IsSignaled() const override {
std::scoped_lock lk(this->message_queue->queue_lock);
std::scoped_lock lk(GetReference(this->mq->cs_queue));
return this->IsSignaledImpl();
}
virtual TriBool LinkToObjectList() override {
std::scoped_lock lk(this->message_queue->queue_lock);
std::scoped_lock lk(GetReference(this->mq->cs_queue));
this->GetObjectList().LinkWaitableHolder(*this);
return this->IsSignaledImpl();
}
virtual void UnlinkFromObjectList() override {
std::scoped_lock lk(this->message_queue->queue_lock);
std::scoped_lock lk(GetReference(this->mq->cs_queue));
this->GetObjectList().UnlinkWaitableHolder(*this);
}
};
using WaitableHolderOfMessageQueueForNotEmpty = WaitableHolderOfMessageQueue<MessageQueueWaitKind::ForNotEmpty>;
using WaitableHolderOfMessageQueueForNotFull = WaitableHolderOfMessageQueue<MessageQueueWaitKind::ForNotFull>;
using WaitableHolderOfMessageQueueForNotEmpty = WaitableHolderOfMessageQueue<MessageQueueWaitType::ForNotEmpty>;
using WaitableHolderOfMessageQueueForNotFull = WaitableHolderOfMessageQueue<MessageQueueWaitType::ForNotFull>;
}

View file

@ -21,29 +21,29 @@ namespace ams::os::impl {
class WaitableHolderOfSemaphore : public WaitableHolderOfUserObject {
private:
Semaphore *semaphore;
SemaphoreType *semaphore;
private:
TriBool IsSignaledImpl() const {
return this->semaphore->count > 0 ? TriBool::True : TriBool::False;
}
public:
explicit WaitableHolderOfSemaphore(Semaphore *s) : semaphore(s) { /* ... */ }
explicit WaitableHolderOfSemaphore(SemaphoreType *s) : semaphore(s) { /* ... */ }
/* IsSignaled, Link, Unlink implemented. */
virtual TriBool IsSignaled() const override {
std::scoped_lock lk(this->semaphore->mutex);
std::scoped_lock lk(GetReference(this->semaphore->cs_sema));
return this->IsSignaledImpl();
}
virtual TriBool LinkToObjectList() override {
std::scoped_lock lk(this->semaphore->mutex);
std::scoped_lock lk(GetReference(this->semaphore->cs_sema));
GetReference(this->semaphore->waitlist).LinkWaitableHolder(*this);
return this->IsSignaledImpl();
}
virtual void UnlinkFromObjectList() override {
std::scoped_lock lk(this->semaphore->mutex);
std::scoped_lock lk(GetReference(this->semaphore->cs_sema));
GetReference(this->semaphore->waitlist).UnlinkWaitableHolder(*this);
}

View file

@ -18,21 +18,33 @@
namespace ams::os::impl {
/* Nintendo implements this as a user wait object, operating on Thread state. */
/* Libnx doesn't have an equivalent, so we'll use the thread's handle for kernel semantics. */
class WaitableHolderOfThread : public WaitableHolderOfKernelObject {
class WaitableHolderOfThread : public WaitableHolderOfUserObject {
private:
Thread *thread;
ThreadType *thread;
private:
TriBool IsSignaledImpl() const {
return this->thread->state == ThreadType::State_Terminated ? TriBool::True : TriBool::False;
}
public:
explicit WaitableHolderOfThread(Thread *t) : thread(t) { /* ... */ }
explicit WaitableHolderOfThread(ThreadType *t) : thread(t) { /* ... */ }
/* IsSignaled, GetHandle both implemented. */
/* IsSignaled, Link, Unlink implemented. */
virtual TriBool IsSignaled() const override {
return TriBool::Undefined;
std::scoped_lock lk(GetReference(this->thread->cs_thread));
return this->IsSignaledImpl();
}
virtual Handle GetHandle() const override {
return this->thread->GetHandle();
virtual TriBool LinkToObjectList() override {
std::scoped_lock lk(GetReference(this->thread->cs_thread));
GetReference(this->thread->waitlist).LinkWaitableHolder(*this);
return this->IsSignaledImpl();
}
virtual void UnlinkFromObjectList() override {
std::scoped_lock lk(GetReference(this->thread->cs_thread));
GetReference(this->thread->waitlist).UnlinkWaitableHolder(*this);
}
};

View file

@ -15,21 +15,19 @@
*/
#include "os_waitable_manager_impl.hpp"
#include "os_waitable_object_list.hpp"
#include "os_tick_manager.hpp"
namespace ams::os::impl{
WaitableHolderBase *WaitableManagerImpl::WaitAnyImpl(bool infinite, u64 timeout) {
/* Set processing thread handle while in scope. */
this->waiting_thread_handle = threadGetCurHandle();
ON_SCOPE_EXIT { this->waiting_thread_handle = INVALID_HANDLE; };
namespace ams::os::impl {
WaitableHolderBase *WaitableManagerImpl::WaitAnyImpl(bool infinite, TimeSpan timeout) {
/* Prepare for processing. */
this->signaled_holder = nullptr;
this->target_impl.SetCurrentThreadHandleForCancelWait();
WaitableHolderBase *result = this->LinkHoldersToObjectList();
/* Check if we've been signaled. */
{
std::scoped_lock lk(this->lock);
std::scoped_lock lk(this->cs_wait);
if (this->signaled_holder != nullptr) {
result = this->signaled_holder;
}
@ -43,36 +41,42 @@ namespace ams::os::impl{
/* Unlink holders from the current object list. */
this->UnlinkHoldersFromObjectList();
this->target_impl.ClearCurrentThreadHandleForCancelWait();
return result;
}
WaitableHolderBase *WaitableManagerImpl::WaitAnyHandleImpl(bool infinite, u64 timeout) {
WaitableHolderBase *WaitableManagerImpl::WaitAnyHandleImpl(bool infinite, TimeSpan timeout) {
Handle object_handles[MaximumHandleCount];
WaitableHolderBase *objects[MaximumHandleCount];
const size_t count = this->BuildHandleArray(object_handles, objects);
const u64 end_time = infinite ? std::numeric_limits<u64>::max() : armTicksToNs(armGetSystemTick());
const s32 count = this->BuildHandleArray(object_handles, objects, MaximumHandleCount);
const TimeSpan end_time = infinite ? TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max()) : GetCurrentTick().ToTimeSpan() + timeout;
while (true) {
this->current_time = armTicksToNs(armGetSystemTick());
this->current_time = GetCurrentTick().ToTimeSpan();
u64 min_timeout = 0;
TimeSpan min_timeout = 0;
WaitableHolderBase *min_timeout_object = this->RecalculateNextTimeout(&min_timeout, end_time);
s32 index;
if (count == 0 && min_timeout == 0) {
index = WaitTimedOut;
if (infinite && min_timeout_object == nullptr) {
index = this->target_impl.WaitAny(object_handles, MaximumHandleCount, count);
} else {
index = this->WaitSynchronization(object_handles, count, min_timeout);
AMS_ABORT_UNLESS(index != WaitInvalid);
if (count == 0 && min_timeout == 0) {
index = WaitTimedOut;
} else {
index = this->target_impl.TimedWaitAny(object_handles, MaximumHandleCount, count, min_timeout);
AMS_ABORT_UNLESS(index != WaitInvalid);
}
}
switch (index) {
case WaitTimedOut:
if (min_timeout_object) {
this->current_time = armTicksToNs(armGetSystemTick());
this->current_time = GetCurrentTick().ToTimeSpan();
if (min_timeout_object->IsSignaled() == TriBool::True) {
std::scoped_lock lk(this->lock);
std::scoped_lock lk(this->cs_wait);
this->signaled_holder = min_timeout_object;
return this->signaled_holder;
}
@ -86,7 +90,7 @@ namespace ams::os::impl{
continue;
default: /* 0 - 0x3F, valid. */
{
std::scoped_lock lk(this->lock);
std::scoped_lock lk(this->cs_wait);
this->signaled_holder = objects[index];
return this->signaled_holder;
}
@ -94,28 +98,12 @@ namespace ams::os::impl{
}
}
s32 WaitableManagerImpl::WaitSynchronization(Handle *handles, size_t count, u64 timeout) {
s32 index = WaitInvalid;
R_TRY_CATCH(svcWaitSynchronization(&index, handles, count, timeout)) {
R_CATCH(svc::ResultTimedOut) { return WaitTimedOut; }
R_CATCH(svc::ResultCancelled) { return WaitCancelled; }
/* All other results are critical errors. */
/* svc::ResultThreadTerminating */
/* svc::ResultInvalidHandle. */
/* svc::ResultInvalidPointer */
/* svc::ResultOutOfRange */
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return index;
}
size_t WaitableManagerImpl::BuildHandleArray(Handle *out_handles, WaitableHolderBase **out_objects) {
size_t count = 0;
s32 WaitableManagerImpl::BuildHandleArray(Handle out_handles[], WaitableHolderBase *out_objects[], s32 num) {
s32 count = 0;
for (WaitableHolderBase &holder_base : this->waitable_list) {
if (Handle handle = holder_base.GetHandle(); handle != INVALID_HANDLE) {
AMS_ABORT_UNLESS(count < MaximumHandleCount);
if (Handle handle = holder_base.GetHandle(); handle != svc::InvalidHandle) {
AMS_ASSERT(count < num);
out_handles[count] = handle;
out_objects[count] = &holder_base;
@ -146,12 +134,12 @@ namespace ams::os::impl{
}
}
WaitableHolderBase *WaitableManagerImpl::RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time) {
WaitableHolderBase *WaitableManagerImpl::RecalculateNextTimeout(TimeSpan *out_min_timeout, TimeSpan end_time) {
WaitableHolderBase *min_timeout_holder = nullptr;
u64 min_time = end_time;
TimeSpan min_time = end_time;
for (WaitableHolderBase &holder_base : this->waitable_list) {
if (const u64 cur_time = holder_base.GetWakeupTime(); cur_time < min_time) {
if (const TimeSpan cur_time = holder_base.GetAbsoluteWakeupTime(); cur_time < min_time) {
min_timeout_holder = &holder_base;
min_time = cur_time;
}
@ -166,11 +154,11 @@ namespace ams::os::impl{
}
void WaitableManagerImpl::SignalAndWakeupThread(WaitableHolderBase *holder_base) {
std::scoped_lock lk(this->lock);
std::scoped_lock lk(this->cs_wait);
if (this->signaled_holder == nullptr) {
this->signaled_holder = holder_base;
R_ABORT_UNLESS(svcCancelSynchronization(this->waiting_thread_handle));
this->target_impl.CancelWait();
}
}

View file

@ -16,11 +16,17 @@
#pragma once
#include "os_waitable_holder_base.hpp"
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_waitable_manager_target_impl.os.horizon.hpp"
#else
#error "Unknown OS for ams::os::WaitableManagerTargetImpl"
#endif
namespace ams::os::impl {
class WaitableManagerImpl {
public:
static constexpr size_t MaximumHandleCount = 0x40;
static constexpr size_t MaximumHandleCount = WaitableManagerTargetImpl::MaximumHandleCount;
static constexpr s32 WaitInvalid = -3;
static constexpr s32 WaitCancelled = -2;
static constexpr s32 WaitTimedOut = -1;
@ -28,31 +34,30 @@ namespace ams::os::impl {
private:
ListType waitable_list;
WaitableHolderBase *signaled_holder;
u64 current_time;
Mutex lock;
Handle waiting_thread_handle;
TimeSpan current_time;
InternalCriticalSection cs_wait;
WaitableManagerTargetImpl target_impl;
private:
WaitableHolderBase *WaitAnyImpl(bool infinite, u64 timeout);
WaitableHolderBase *WaitAnyHandleImpl(bool infinite, u64 timeout);
s32 WaitSynchronization(Handle *handles, size_t count, u64 timeout);
size_t BuildHandleArray(Handle *out_handles, WaitableHolderBase **out_objects);
WaitableHolderBase *WaitAnyImpl(bool infinite, TimeSpan timeout);
WaitableHolderBase *WaitAnyHandleImpl(bool infinite, TimeSpan timeout);
s32 BuildHandleArray(Handle out_handles[], WaitableHolderBase *out_objects[], s32 num);
WaitableHolderBase *LinkHoldersToObjectList();
void UnlinkHoldersFromObjectList();
WaitableHolderBase *RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time);
WaitableHolderBase *RecalculateNextTimeout(TimeSpan *out_min_timeout, TimeSpan end_time);
public:
/* Wait. */
WaitableHolderBase *WaitAny() {
return this->WaitAnyImpl(true, std::numeric_limits<u64>::max());
return this->WaitAnyImpl(true, TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max()));
}
WaitableHolderBase *TryWaitAny() {
return this->WaitAnyImpl(false, 0);
return this->WaitAnyImpl(false, TimeSpan(0));
}
WaitableHolderBase *TimedWaitAny(u64 timeout) {
return this->WaitAnyImpl(false, timeout);
WaitableHolderBase *TimedWaitAny(TimeSpan ts) {
return this->WaitAnyImpl(false, ts);
}
/* List management. */
@ -84,7 +89,7 @@ namespace ams::os::impl {
}
/* Other. */
u64 GetCurrentTime() const {
TimeSpan GetCurrentTime() const {
return this->current_time;
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_waitable_holder_base.hpp"
#include "os_waitable_manager_impl.hpp"
namespace ams::os::impl {
s32 WaitableManagerHorizonImpl::WaitSynchronizationN(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; }
/* All other results are critical errors. */
/* svc::ResultThreadTerminating */
/* svc::ResultInvalidHandle. */
/* svc::ResultInvalidPointer */
/* svc::ResultOutOfRange */
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return index;
}
void WaitableManagerHorizonImpl::CancelWait() {
R_ABORT_UNLESS(svc::CancelSynchronization(this->handle));
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "os_thread_manager.hpp"
namespace ams::os::impl {
class WaitableManagerHorizonImpl {
public:
static constexpr size_t MaximumHandleCount = svc::MaxWaitSynchronizationHandleCount;
private:
Handle handle;
private:
s32 WaitSynchronizationN(s32 num, Handle arr[], s32 array_size, s64 ns);
public:
void CancelWait();
s32 WaitAny(Handle arr[], s32 array_size, s32 num) {
return this->WaitSynchronizationN(num, arr, array_size, svc::WaitInfinite);
}
s32 TryWaitAny(Handle arr[], s32 array_size, s32 num) {
return this->WaitSynchronizationN(num, arr, array_size, 0);
}
s32 TimedWaitAny(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);
}
void SetCurrentThreadHandleForCancelWait() {
this->handle = GetCurrentThreadHandle();
}
void ClearCurrentThreadHandleForCancelWait() {
this->handle = svc::InvalidHandle;
}
};
using WaitableManagerTargetImpl = WaitableManagerHorizonImpl;
}