mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-05-28 05:34:11 -04:00
os: refactor/rewrite entire namespace.
This commit is contained in:
parent
6193283f03
commit
065485b971
181 changed files with 5353 additions and 1929 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
24
libraries/libstratosphere/source/os/impl/os_mutex_impl.hpp
Normal file
24
libraries/libstratosphere/source/os/impl/os_mutex_impl.hpp
Normal 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);
|
||||
|
||||
}
|
|
@ -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; }
|
||||
};
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_resource_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
226
libraries/libstratosphere/source/os/impl/os_thread_manager.cpp
Normal file
226
libraries/libstratosphere/source/os/impl/os_thread_manager.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
|
@ -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 {
|
||||
/* ... */
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue