os: refactor/rewrite entire namespace.

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

View File

@@ -58,7 +58,7 @@ namespace ams::emummc {
};
/* Globals. */
os::Mutex g_lock;
os::Mutex g_lock(false);
ExosphereConfig g_exo_config;
bool g_is_emummc;
bool g_has_cached;

View File

@@ -25,7 +25,7 @@ namespace ams::cfg {
constexpr os::ProcessId InitialProcessIdMaxDeprecated = {0x50};
/* Privileged process globals. */
os::Mutex g_lock;
os::Mutex g_lock(false);
bool g_got_privileged_process_status = false;
os::ProcessId g_min_initial_process_id = os::InvalidProcessId, g_max_initial_process_id = os::InvalidProcessId;
os::ProcessId g_cur_process_id = os::InvalidProcessId;

View File

@@ -30,7 +30,7 @@ namespace ams::cfg {
constexpr size_t NumRequiredServicesForSdCardAccess = util::size(RequiredServicesForSdCardAccess);
/* SD card globals. */
os::Mutex g_sd_card_lock;
os::Mutex g_sd_card_lock(false);
bool g_sd_card_initialized = false;
FsFileSystem g_sd_card_filesystem = {};

View File

@@ -30,7 +30,7 @@ namespace ams::fs {
std::free(ptr);
}
os::Mutex g_lock;
os::Mutex g_lock(false);
AllocateFunction g_allocate_func = DefaultAllocate;
DeallocateFunction g_deallocate_func = DefaultDeallocate;

View File

@@ -60,7 +60,7 @@ namespace ams::fs::impl {
}
FileSystemAccessor::FileSystemAccessor(const char *n, std::unique_ptr<fsa::IFileSystem> &&fs, std::unique_ptr<fsa::ICommonMountNameGenerator> &&generator)
: impl(std::move(fs)), mount_name_generator(std::move(generator)),
: impl(std::move(fs)), open_list_lock(false), mount_name_generator(std::move(generator)),
access_log_enabled(false), data_cache_attachable(false), path_cache_attachable(false), path_cache_attached(false), multi_commit_supported(false)
{
R_ABORT_UNLESS(ValidateMountName(n));

View File

@@ -28,7 +28,7 @@ namespace ams::fs::impl {
FileSystemList fs_list;
os::Mutex mutex;
public:
constexpr MountTable() : fs_list(), mutex() { /* ... */ }
constexpr MountTable() : fs_list(), mutex(false) { /* ... */ }
private:
bool CanAcceptMountName(const char *name);
public:

View File

@@ -17,7 +17,7 @@
namespace ams::fssystem {
AesXtsStorage::AesXtsStorage(IStorage *base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size) : base_storage(base), block_size(block_size), mutex() {
AesXtsStorage::AesXtsStorage(IStorage *base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size) : base_storage(base), block_size(block_size), mutex(false) {
AMS_ASSERT(base != nullptr);
AMS_ASSERT(key1 != nullptr);
AMS_ASSERT(key2 != nullptr);

View File

@@ -74,13 +74,13 @@ namespace ams::fssystem {
}
DirectorySaveDataFileSystem::DirectorySaveDataFileSystem(std::shared_ptr<fs::fsa::IFileSystem> fs)
: PathResolutionFileSystem(fs), open_writable_files(0)
: PathResolutionFileSystem(fs), accessor_mutex(false), open_writable_files(0)
{
/* ... */
}
DirectorySaveDataFileSystem::DirectorySaveDataFileSystem(std::unique_ptr<fs::fsa::IFileSystem> fs)
: PathResolutionFileSystem(std::move(fs)), open_writable_files(0)
: PathResolutionFileSystem(std::move(fs)), accessor_mutex(false), open_writable_files(0)
{
/* ... */
}

View File

@@ -27,7 +27,7 @@ namespace ams::fssystem {
uintptr_t address;
size_t size;
public:
constexpr AdditionalDeviceAddressEntry() : mutex(), is_registered(), address(), size() { /* ... */ }
constexpr AdditionalDeviceAddressEntry() : mutex(false), is_registered(), address(), size() { /* ... */ }
void Register(uintptr_t addr, size_t sz) {
std::scoped_lock lk(this->mutex);
@@ -78,7 +78,7 @@ namespace ams::fssystem {
constexpr size_t HeapAllocatableSizeMaxForLarge = HeapBlockSize * (static_cast<size_t>(1) << HeapOrderMaxForLarge);
/* TODO: SdkMutex */
os::Mutex g_heap_mutex;
os::Mutex g_heap_mutex(false);
FileSystemBuddyHeap g_heap;
std::atomic<size_t> g_retry_count;

View File

@@ -20,7 +20,7 @@ namespace ams::hid {
namespace {
/* Global lock. */
os::Mutex g_hid_lock;
os::Mutex g_hid_lock(false);
bool g_initialized_hid = false;
/* Helper. */

View File

@@ -22,7 +22,7 @@ namespace ams::hos {
hos::Version g_hos_version;
bool g_has_cached;
os::Mutex g_mutex;
os::Mutex g_mutex(false);
void CacheValues() {
if (__atomic_load_n(&g_has_cached, __ATOMIC_SEQ_CST)) {

View File

@@ -33,12 +33,6 @@ namespace ams::lmem::impl {
new (&out->list_node) util::IntrusiveListNode;
new (&out->child_list) decltype(out->child_list);
/* Only initialize mutex if option requires it. */
if (option & CreateOption_ThreadSafe) {
static_assert(std::is_trivially_destructible<os::Mutex>::value);
new (&out->mutex) os::Mutex;
}
/* Set fields. */
out->magic = magic;
out->heap_start = start;

View File

@@ -30,13 +30,13 @@ namespace ams::lmem::impl {
public:
explicit ScopedHeapLock(HeapHandle h) : handle(h) {
if (this->handle->option & CreateOption_ThreadSafe) {
this->handle->mutex.Lock();
os::LockMutex(std::addressof(this->handle->mutex));
}
}
~ScopedHeapLock() {
if (this->handle->option & CreateOption_ThreadSafe) {
this->handle->mutex.Unlock();
os::UnlockMutex(std::addressof(this->handle->mutex));
}
}
};

View File

@@ -19,10 +19,17 @@
namespace ams::lmem {
HeapHandle CreateExpHeap(void *address, size_t size, u32 option) {
return impl::CreateExpHeap(address, size, option);
HeapHandle handle = impl::CreateExpHeap(address, size, option);
if (option & CreateOption_ThreadSafe) {
os::InitializeMutex(std::addressof(handle->mutex), false, 0);
}
return handle;
}
void DestroyExpHeap(HeapHandle handle) {
if (handle->option & CreateOption_ThreadSafe) {
os::FinalizeMutex(std::addressof(handle->mutex));
}
impl::DestroyExpHeap(handle);
}

View File

@@ -19,18 +19,33 @@
namespace ams::lmem {
HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option) {
return impl::CreateUnitHeap(address, size, unit_size, DefaultAlignment, static_cast<u16>(option), InfoPlacement_Head, nullptr);
HeapHandle handle = impl::CreateUnitHeap(address, size, unit_size, DefaultAlignment, static_cast<u16>(option), InfoPlacement_Head, nullptr);
if (option & CreateOption_ThreadSafe) {
os::InitializeMutex(std::addressof(handle->mutex), false, 0);
}
return handle;
}
HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, InfoPlacement info_placement) {
return impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast<u16>(option), info_placement, nullptr);
HeapHandle handle = impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast<u16>(option), info_placement, nullptr);
if (option & CreateOption_ThreadSafe) {
os::InitializeMutex(std::addressof(handle->mutex), false, 0);
}
return handle;
}
HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, HeapCommonHead *heap_head) {
return impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast<u16>(option), InfoPlacement_Head, heap_head);
HeapHandle handle = impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast<u16>(option), InfoPlacement_Head, heap_head);
if (option & CreateOption_ThreadSafe) {
os::InitializeMutex(std::addressof(handle->mutex), false, 0);
}
return handle;
}
void DestroyUnitHeap(HeapHandle handle) {
if (handle->option & CreateOption_ThreadSafe) {
os::FinalizeMutex(std::addressof(handle->mutex));
}
impl::DestroyUnitHeap(handle);
}

View File

@@ -201,14 +201,14 @@ namespace ams::mem::impl::heap {
s32 static_thread_quota;
s32 dynamic_thread_quota;
bool use_virtual_memory;
os::RecursiveMutex lock;
os::Mutex lock;
ListHeader<SpanPage> spanpage_list;
ListHeader<SpanPage> full_spanpage_list;
ListHeader<Span> freelists[FreeListCount];
FreeListAvailableWord freelists_bitmap[NumFreeListBitmaps];
ListHeader<Span> smallmem_lists[TlsHeapStatic::NumClassInfo];
public:
TlsHeapCentral() {
TlsHeapCentral() : lock(true) {
this->span_table.total_pages = 0;
}

View File

@@ -20,7 +20,7 @@ namespace ams::mem::impl {
namespace {
os::Mutex g_virt_mem_enabled_lock;
os::Mutex g_virt_mem_enabled_lock(false);
bool g_virt_mem_enabled_detected;
bool g_virt_mem_enabled;

View File

@@ -626,7 +626,6 @@ namespace ams::ncm {
}
Result InstallTaskBase::PreparePlaceHolder() {
static os::Mutex placeholder_mutex;
size_t total_size = 0;
/* Count the number of content meta entries. */
@@ -635,7 +634,9 @@ namespace ams::ncm {
for (s32 i = 0; i < count; i++) {
R_UNLESS(!this->IsCancelRequested(), ncm::ResultCreatePlaceHolderCancelled());
std::scoped_lock lk(placeholder_mutex);
static os::Mutex s_placeholder_mutex(false);
std::scoped_lock lk(s_placeholder_mutex);
InstallContentMeta content_meta;
R_TRY(this->data->Get(std::addressof(content_meta), i));

View File

@@ -43,7 +43,7 @@ namespace ams::ncm {
CacheEntry *FindInCache(PlaceHolderId placeholder_id);
CacheEntry *GetFreeEntry();;
public:
PlaceHolderAccessor() : cur_counter(0), delay_flush(false) {
PlaceHolderAccessor() : cur_counter(0), cache_mutex(false), delay_flush(false) {
for (size_t i = 0; i < MaxCacheEntries; i++) {
caches[i].id = InvalidPlaceHolderId;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,107 @@
/*
* 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 "impl/os_thread_manager.hpp"
#include "impl/os_timeout_helper.hpp"
#include "impl/os_mutex_impl.hpp"
namespace ams::os {
void InitializeConditionVariable(ConditionVariableType *cv) {
/* Construct object. */
new (GetPointer(cv->_storage)) impl::InternalConditionVariable;
/* Mark initialized. */
cv->state = ConditionVariableType::State_Initialized;
}
void FinalizeConditionVariable(ConditionVariableType *cv) {
AMS_ASSERT(cv->state == ConditionVariableType::State_Initialized);
/* Mark not initialized. */
cv->state = ConditionVariableType::State_NotInitialized;
/* Destroy objects. */
GetReference(cv->_storage).~InternalConditionVariable();
}
void SignalConditionVariable(ConditionVariableType *cv) {
AMS_ASSERT(cv->state == ConditionVariableType::State_Initialized);
GetReference(cv->_storage).Signal();
}
void BroadcastConditionVariable(ConditionVariableType *cv) {
AMS_ASSERT(cv->state == ConditionVariableType::State_Initialized);
GetReference(cv->_storage).Broadcast();
}
void WaitConditionVariable(ConditionVariableType *cv, MutexType *m) {
AMS_ASSERT(cv->state == ConditionVariableType::State_Initialized);
AMS_ASSERT(m->state == MutexType::State_Initialized);
AMS_ASSERT(m->owner_thread == impl::GetCurrentThread());
AMS_ASSERT(m->nest_count == 1);
impl::PopAndCheckLockLevel(m);
if ((--m->nest_count) == 0) {
m->owner_thread = nullptr;
}
GetReference(cv->_storage).Wait(GetPointer(m->_storage));
impl::PushAndCheckLockLevel(m);
++m->nest_count;
m->owner_thread = impl::GetCurrentThread();
}
ConditionVariableStatus TimedWaitConditionVariable(ConditionVariableType *cv, MutexType *m, TimeSpan timeout) {
AMS_ASSERT(cv->state == ConditionVariableType::State_Initialized);
AMS_ASSERT(m->state == MutexType::State_Initialized);
AMS_ASSERT(m->owner_thread == impl::GetCurrentThread());
AMS_ASSERT(m->nest_count == 1);
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
impl::PopAndCheckLockLevel(m);
if ((--m->nest_count) == 0) {
m->owner_thread = nullptr;
}
ConditionVariableStatus status;
if (timeout == TimeSpan(0)) {
GetReference(m->_storage).Leave();
GetReference(m->_storage).Enter();
status = ConditionVariableStatus::TimedOut;
} else {
impl::TimeoutHelper timeout_helper(timeout);
status = GetReference(cv->_storage).TimedWait(GetPointer(m->_storage), timeout_helper);
}
impl::PushAndCheckLockLevel(m);
++m->nest_count;
m->owner_thread = impl::GetCurrentThread();
return status;
}
}

View File

@@ -13,93 +13,158 @@
* 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 "impl/os_timeout_helper.hpp"
#include "impl/os_waitable_object_list.hpp"
#include "impl/os_waitable_holder_impl.hpp"
namespace ams::os {
Event::Event(bool a, bool s) : auto_clear(a), signaled(s) {
new (GetPointer(this->waitable_object_list_storage)) impl::WaitableObjectList();
namespace {
ALWAYS_INLINE u64 GetBroadcastCounterUnsafe(EventType *event) {
const u64 upper = event->broadcast_counter_high;
return (upper << BITSIZEOF(event->broadcast_counter_low)) | event->broadcast_counter_low;
}
ALWAYS_INLINE void IncrementBroadcastCounterUnsafe(EventType *event) {
if ((++event->broadcast_counter_low) == 0) {
++event->broadcast_counter_high;
}
}
}
Event::~Event() {
GetReference(this->waitable_object_list_storage).~WaitableObjectList();
void InitializeEvent(EventType *event, bool signaled, EventClearMode clear_mode) {
/* Initialize internal variables. */
new (GetPointer(event->cs_event)) impl::InternalCriticalSection;
new (GetPointer(event->cv_signaled)) impl::InternalConditionVariable;
/* Initialize the waitable object list. */
new (GetPointer(event->waitable_object_list_storage)) impl::WaitableObjectList();
/* Initialize member variables. */
event->signaled = signaled;
event->initially_signaled = signaled;
event->clear_mode = static_cast<u8>(clear_mode);
event->broadcast_counter_low = 0;
event->broadcast_counter_high = 0;
/* Mark initialized. */
event->state = EventType::State_Initialized;
}
void Event::Signal() {
std::scoped_lock lk(this->lock);
void FinalizeEvent(EventType *event) {
AMS_ASSERT(event->state == EventType::State_Initialized);
/* Mark uninitialized. */
event->state = EventType::State_NotInitialized;
/* Destroy objects. */
GetReference(event->waitable_object_list_storage).~WaitableObjectList();
GetReference(event->cv_signaled).~InternalConditionVariable();
GetReference(event->cs_event).~InternalCriticalSection();
}
void SignalEvent(EventType *event) {
AMS_ASSERT(event->state == EventType::State_Initialized);
std::scoped_lock lk(GetReference(event->cs_event));
/* If we're already signaled, nothing more to do. */
if (this->signaled) {
if (event->signaled) {
return;
}
this->signaled = true;
event->signaled = true;
/* Signal! */
if (this->auto_clear) {
/* If we're auto clear, signal one thread, which will clear. */
this->cv.Signal();
} else {
if (event->clear_mode == EventClearMode_ManualClear) {
/* If we're manual clear, increment counter and wake all. */
this->counter++;
this->cv.Broadcast();
IncrementBroadcastCounterUnsafe(event);
GetReference(event->cv_signaled).Broadcast();
} else {
/* If we're auto clear, signal one thread, which will clear. */
GetReference(event->cv_signaled).Signal();
}
/* Wake up whatever manager, if any. */
GetReference(this->waitable_object_list_storage).SignalAllThreads();
GetReference(event->waitable_object_list_storage).SignalAllThreads();
}
void Event::Reset() {
std::scoped_lock lk(this->lock);
this->signaled = false;
}
void WaitEvent(EventType *event) {
AMS_ASSERT(event->state == EventType::State_Initialized);
void Event::Wait() {
std::scoped_lock lk(this->lock);
std::scoped_lock lk(GetReference(event->cs_event));
u64 cur_counter = this->counter;
while (!this->signaled) {
if (this->counter != cur_counter) {
const auto cur_counter = GetBroadcastCounterUnsafe(event);
while (!event->signaled) {
if (cur_counter != GetBroadcastCounterUnsafe(event)) {
break;
}
this->cv.Wait(&this->lock);
GetReference(event->cv_signaled).Wait(GetPointer(event->cs_event));
}
if (this->auto_clear) {
this->signaled = false;
if (event->clear_mode == EventClearMode_AutoClear) {
event->signaled = false;
}
}
bool Event::TryWait() {
std::scoped_lock lk(this->lock);
bool TryWaitEvent(EventType *event) {
AMS_ASSERT(event->state == EventType::State_Initialized);
const bool success = this->signaled;
if (this->auto_clear) {
this->signaled = false;
std::scoped_lock lk(GetReference(event->cs_event));
const bool signaled = event->signaled;
if (event->clear_mode == EventClearMode_AutoClear) {
event->signaled = false;
}
return success;
return signaled;
}
bool Event::TimedWait(u64 ns) {
TimeoutHelper timeout_helper(ns);
std::scoped_lock lk(this->lock);
bool TimedWaitEvent(EventType *event, TimeSpan timeout) {
AMS_ASSERT(event->state == EventType::State_Initialized);
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
u64 cur_counter = this->counter;
while (!this->signaled) {
if (this->counter != cur_counter) {
break;
}
if (this->cv.TimedWait(&this->lock, timeout_helper.NsUntilTimeout()) == ConditionVariableStatus::TimedOut) {
return false;
}
}
{
impl::TimeoutHelper timeout_helper(timeout);
std::scoped_lock lk(GetReference(event->cs_event));
if (this->auto_clear) {
this->signaled = false;
const auto cur_counter = GetBroadcastCounterUnsafe(event);
while (!event->signaled) {
if (cur_counter != GetBroadcastCounterUnsafe(event)) {
break;
}
auto wait_res = GetReference(event->cv_signaled).TimedWait(GetPointer(event->cs_event), timeout_helper);
if (wait_res == ConditionVariableStatus::TimedOut) {
return false;
}
}
if (event->clear_mode == EventClearMode_AutoClear) {
event->signaled = false;
}
}
return true;
}
void ClearEvent(EventType *event) {
AMS_ASSERT(event->state == EventType::State_Initialized);
std::scoped_lock lk(GetReference(event->cs_event));
/* Clear the signaled state. */
event->signaled = false;
}
void InitializeWaitableHolder(WaitableHolderType *waitable_holder, EventType *event) {
AMS_ASSERT(event->state == EventType::State_Initialized);
new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfEvent(event);
waitable_holder->user_data = 0;
}
}

View File

@@ -13,99 +13,51 @@
* 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 "impl/os_interrupt_event_impl.hpp"
#include "impl/os_waitable_object_list.hpp"
namespace ams::os {
Result InterruptEvent::Initialize(u32 interrupt_id, bool autoclear) {
AMS_ABORT_UNLESS(!this->is_initialized);
this->auto_clear = autoclear;
void InitializeInterruptEvent(InterruptEventType *event, InterruptName name, EventClearMode clear_mode) {
/* Initialize member variables. */
event->clear_mode = static_cast<u8>(clear_mode);
const auto type = this->auto_clear ? svc::InterruptType_Edge : svc::InterruptType_Level;
R_TRY(svcCreateInterruptEvent(this->handle.GetPointer(), interrupt_id, type));
/* Initialize implementation. */
new (GetPointer(event->impl)) impl::InterruptEventImpl(name, clear_mode);
this->is_initialized = true;
return ResultSuccess();
/* Mark initialized. */
event->state = InterruptEventType::State_Initialized;
}
void InterruptEvent::Finalize() {
AMS_ABORT_UNLESS(this->is_initialized);
R_ABORT_UNLESS(svcCloseHandle(this->handle.Move()));
this->auto_clear = true;
this->is_initialized = false;
void FinalizeInterruptEvent(InterruptEventType *event) {
AMS_ASSERT(event->state == InterruptEventType::State_Initialized);
/* Mark uninitialized. */
event->state = InterruptEventType::State_NotInitialized;
/* Destroy objects. */
GetReference(event->impl).~InterruptEventImpl();
}
InterruptEvent::InterruptEvent(u32 interrupt_id, bool autoclear) {
this->is_initialized = false;
R_ABORT_UNLESS(this->Initialize(interrupt_id, autoclear));
void WaitInterruptEvent(InterruptEventType *event) {
AMS_ASSERT(event->state == InterruptEventType::State_Initialized);
return GetReference(event->impl).Wait();
}
void InterruptEvent::Reset() {
R_ABORT_UNLESS(svcClearEvent(this->handle.Get()));
bool TryWaitInterruptEvent(InterruptEventType *event) {
AMS_ASSERT(event->state == InterruptEventType::State_Initialized);
return GetReference(event->impl).TryWait();
}
void InterruptEvent::Wait() {
AMS_ABORT_UNLESS(this->is_initialized);
while (true) {
/* Continuously wait, until success. */
R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), 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(this->handle.Get())) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return;
}
bool TimedWaitInterruptEvent(InterruptEventType *event, TimeSpan timeout) {
AMS_ASSERT(event->state == InterruptEventType::State_Initialized);
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
return GetReference(event->impl).TimedWait(timeout);
}
bool InterruptEvent::TryWait() {
AMS_ABORT_UNLESS(this->is_initialized);
if (this->auto_clear) {
/* Auto-clear. Just try to reset. */
return R_SUCCEEDED(svcResetSignal(this->handle.Get()));
} else {
/* Not auto-clear. */
while (true) {
/* Continuously wait, until success or timeout. */
R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), 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 InterruptEvent::TimedWait(u64 ns) {
AMS_ABORT_UNLESS(this->is_initialized);
TimeoutHelper timeout_helper(ns);
while (true) {
/* Continuously wait, until success or timeout. */
R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), 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(this->handle.Get())) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return true;
}
void ClearInterruptEvent(InterruptEventType *event) {
AMS_ASSERT(event->state == InterruptEventType::State_Initialized);
return GetReference(event->impl).Clear();
}
}

View File

@@ -14,233 +14,387 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "impl/os_waitable_object_list.hpp"
#include "impl/os_timeout_helper.hpp"
namespace ams::os {
MessageQueue::MessageQueue(std::unique_ptr<uintptr_t[]> buf, size_t c): buffer(std::move(buf)), capacity(c), count(0), offset(0) {
new (GetPointer(this->waitlist_not_empty)) impl::WaitableObjectList();
new (GetPointer(this->waitlist_not_full)) impl::WaitableObjectList();
}
namespace {
MessageQueue::~MessageQueue() {
GetReference(this->waitlist_not_empty).~WaitableObjectList();
GetReference(this->waitlist_not_full).~WaitableObjectList();
}
void MessageQueue::SendInternal(uintptr_t data) {
/* Ensure we don't corrupt the queue, but this should never happen. */
AMS_ABORT_UNLESS(this->count < this->capacity);
/* Write data to tail of queue. */
this->buffer[(this->count++ + this->offset) % this->capacity] = data;
}
void MessageQueue::SendNextInternal(uintptr_t data) {
/* Ensure we don't corrupt the queue, but this should never happen. */
AMS_ABORT_UNLESS(this->count < this->capacity);
/* Write data to head of queue. */
this->offset = (this->offset + this->capacity - 1) % this->capacity;
this->buffer[this->offset] = data;
this->count++;
}
uintptr_t MessageQueue::ReceiveInternal() {
/* Ensure we don't corrupt the queue, but this should never happen. */
AMS_ABORT_UNLESS(this->count > 0);
uintptr_t data = this->buffer[this->offset];
this->offset = (this->offset + 1) % this->capacity;
this->count--;
return data;
}
inline uintptr_t MessageQueue::PeekInternal() {
/* Ensure we don't corrupt the queue, but this should never happen. */
AMS_ABORT_UNLESS(this->count > 0);
return this->buffer[this->offset];
}
void MessageQueue::Send(uintptr_t data) {
/* Acquire mutex, wait sendable. */
std::scoped_lock lock(this->queue_lock);
while (this->IsFull()) {
this->cv_not_full.Wait(&this->queue_lock);
ALWAYS_INLINE bool IsMessageQueueFull(const MessageQueueType *mq) {
return mq->count >= mq->capacity;
}
/* Send, signal. */
this->SendInternal(data);
this->cv_not_empty.Broadcast();
GetReference(this->waitlist_not_empty).SignalAllThreads();
}
bool MessageQueue::TrySend(uintptr_t data) {
std::scoped_lock lock(this->queue_lock);
if (this->IsFull()) {
return false;
ALWAYS_INLINE bool IsMessageQueueEmpty(const MessageQueueType *mq) {
return mq->count == 0;
}
void SendUnsafe(MessageQueueType *mq, uintptr_t data) {
/* Ensure our limits are correct. */
auto count = mq->count;
auto capacity = mq->capacity;
AMS_ASSERT(count < capacity);
/* Determine where we're writing. */
auto ind = mq->offset + count;
if (ind >= capacity) {
ind -= capacity;
}
AMS_ASSERT(0 <= ind && ind < capacity);
/* Write the data. */
mq->buffer[ind] = data;
++count;
/* Update tracking. */
mq->count = count;
}
void SendNextUnsafe(MessageQueueType *mq, uintptr_t data) {
/* Ensure our limits are correct. */
auto count = mq->count;
auto capacity = mq->capacity;
AMS_ASSERT(count < capacity);
/* Determine where we're writing. */
auto offset = mq->offset - 1;
if (offset < 0) {
offset += capacity;
}
AMS_ASSERT(0 <= offset && offset < capacity);
/* Write the data. */
mq->buffer[offset] = data;
++count;
/* Update tracking. */
mq->offset = offset;
mq->count = count;
}
uintptr_t ReceiveUnsafe(MessageQueueType *mq) {
/* Ensure our limits are correct. */
auto count = mq->count;
auto offset = mq->offset;
auto capacity = mq->capacity;
AMS_ASSERT(count > 0);
AMS_ASSERT(offset >= 0 && offset < capacity);
/* Get the data. */
auto data = mq->buffer[offset];
/* Calculate new tracking variables. */
if ((++offset) >= capacity) {
offset -= capacity;
}
--count;
/* Update tracking. */
mq->offset = offset;
mq->count = count;
return data;
}
uintptr_t PeekUnsafe(const MessageQueueType *mq) {
/* Ensure our limits are correct. */
auto count = mq->count;
auto offset = mq->offset;
AMS_ASSERT(count > 0);
return mq->buffer[offset];
}
/* Send, signal. */
this->SendInternal(data);
this->cv_not_empty.Broadcast();
GetReference(this->waitlist_not_empty).SignalAllThreads();
return true;
}
bool MessageQueue::TimedSend(uintptr_t data, u64 timeout) {
std::scoped_lock lock(this->queue_lock);
TimeoutHelper timeout_helper(timeout);
void InitializeMessageQueue(MessageQueueType *mq, uintptr_t *buffer, size_t count) {
AMS_ASSERT(buffer != nullptr);
AMS_ASSERT(count >= 1);
while (this->IsFull()) {
if (timeout_helper.TimedOut()) {
/* Setup objects. */
new (GetPointer(mq->cs_queue)) impl::InternalCriticalSection;
new (GetPointer(mq->cv_not_full)) impl::InternalConditionVariable;
new (GetPointer(mq->cv_not_empty)) impl::InternalConditionVariable;
/* Setup wait lists. */
new (GetPointer(mq->waitlist_not_empty)) impl::WaitableObjectList;
new (GetPointer(mq->waitlist_not_full)) impl::WaitableObjectList;
/* Set member variables. */
mq->buffer = buffer;
mq->capacity = static_cast<s32>(count);
mq->count = 0;
mq->offset = 0;
/* Mark initialized. */
mq->state = MessageQueueType::State_Initialized;
}
void FinalizeMessageQueue(MessageQueueType *mq) {
AMS_ASSERT(mq->state = MessageQueueType::State_Initialized);
AMS_ASSERT(GetReference(mq->waitlist_not_empty).IsEmpty());
AMS_ASSERT(GetReference(mq->waitlist_not_full).IsEmpty());
/* Mark uninitialized. */
mq->state = MessageQueueType::State_NotInitialized;
/* Destroy wait lists. */
GetReference(mq->waitlist_not_empty).~WaitableObjectList();
GetReference(mq->waitlist_not_full).~WaitableObjectList();
/* Destroy objects. */
GetReference(mq->cv_not_empty).~InternalConditionVariable();
GetReference(mq->cv_not_full).~InternalConditionVariable();
GetReference(mq->cs_queue).~InternalCriticalSection();
}
/* Sending (FIFO functionality) */
void SendMessageQueue(MessageQueueType *mq, uintptr_t data) {
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
{
/* Acquire mutex, wait sendable. */
std::scoped_lock lk(GetReference(mq->cs_queue));
while (IsMessageQueueFull(mq)) {
GetReference(mq->cv_not_full).Wait(GetPointer(mq->cs_queue));
}
/* Send, signal. */
SendUnsafe(mq, data);
GetReference(mq->cv_not_empty).Broadcast();
GetReference(mq->waitlist_not_empty).SignalAllThreads();
}
}
bool TrySendMessageQueue(MessageQueueType *mq, uintptr_t data) {
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
{
/* Acquire mutex, check sendable. */
std::scoped_lock lk(GetReference(mq->cs_queue));
if (IsMessageQueueFull(mq)) {
return false;
}
this->cv_not_full.TimedWait(&this->queue_lock, timeout_helper.NsUntilTimeout());
/* Send, signal. */
SendUnsafe(mq, data);
GetReference(mq->cv_not_empty).Broadcast();
GetReference(mq->waitlist_not_empty).SignalAllThreads();
}
/* Send, signal. */
this->SendInternal(data);
this->cv_not_empty.Broadcast();
GetReference(this->waitlist_not_empty).SignalAllThreads();
return true;
}
void MessageQueue::SendNext(uintptr_t data) {
/* Acquire mutex, wait sendable. */
std::scoped_lock lock(this->queue_lock);
bool TimedSendMessageQueue(MessageQueueType *mq, uintptr_t data, TimeSpan timeout) {
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
while (this->IsFull()) {
this->cv_not_full.Wait(&this->queue_lock);
{
/* Acquire mutex, wait sendable. */
impl::TimeoutHelper timeout_helper(timeout);
std::scoped_lock lk(GetReference(mq->cs_queue));
while (IsMessageQueueFull(mq)) {
if (timeout_helper.TimedOut()) {
return false;
}
GetReference(mq->cv_not_full).TimedWait(GetPointer(mq->cs_queue), timeout_helper);
}
/* Send, signal. */
SendUnsafe(mq, data);
GetReference(mq->cv_not_empty).Broadcast();
GetReference(mq->waitlist_not_empty).SignalAllThreads();
}
/* Send, signal. */
this->SendNextInternal(data);
this->cv_not_empty.Broadcast();
GetReference(this->waitlist_not_empty).SignalAllThreads();
}
bool MessageQueue::TrySendNext(uintptr_t data) {
std::scoped_lock lock(this->queue_lock);
if (this->IsFull()) {
return false;
}
/* Send, signal. */
this->SendNextInternal(data);
this->cv_not_empty.Broadcast();
GetReference(this->waitlist_not_empty).SignalAllThreads();
return true;
}
bool MessageQueue::TimedSendNext(uintptr_t data, u64 timeout) {
std::scoped_lock lock(this->queue_lock);
TimeoutHelper timeout_helper(timeout);
/* Sending (LIFO functionality) */
void SendNextMessageQueue(MessageQueueType *mq, uintptr_t data) {
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
while (this->IsFull()) {
if (timeout_helper.TimedOut()) {
{
/* Acquire mutex, wait sendable. */
std::scoped_lock lk(GetReference(mq->cs_queue));
while (IsMessageQueueFull(mq)) {
GetReference(mq->cv_not_full).Wait(GetPointer(mq->cs_queue));
}
/* Send, signal. */
SendNextUnsafe(mq, data);
GetReference(mq->cv_not_empty).Broadcast();
GetReference(mq->waitlist_not_empty).SignalAllThreads();
}
}
bool TrySendNextMessageQueue(MessageQueueType *mq, uintptr_t data) {
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
{
/* Acquire mutex, check sendable. */
std::scoped_lock lk(GetReference(mq->cs_queue));
if (IsMessageQueueFull(mq)) {
return false;
}
this->cv_not_full.TimedWait(&this->queue_lock, timeout_helper.NsUntilTimeout());
/* Send, signal. */
SendNextUnsafe(mq, data);
GetReference(mq->cv_not_empty).Broadcast();
GetReference(mq->waitlist_not_empty).SignalAllThreads();
}
/* Send, signal. */
this->SendNextInternal(data);
this->cv_not_empty.Broadcast();
GetReference(this->waitlist_not_empty).SignalAllThreads();
return true;
}
void MessageQueue::Receive(uintptr_t *out) {
/* Acquire mutex, wait receivable. */
std::scoped_lock lock(this->queue_lock);
bool TimedSendNextMessageQueue(MessageQueueType *mq, uintptr_t data, TimeSpan timeout) {
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
while (this->IsEmpty()) {
this->cv_not_empty.Wait(&this->queue_lock);
{
/* Acquire mutex, wait sendable. */
impl::TimeoutHelper timeout_helper(timeout);
std::scoped_lock lk(GetReference(mq->cs_queue));
while (IsMessageQueueFull(mq)) {
if (timeout_helper.TimedOut()) {
return false;
}
GetReference(mq->cv_not_full).TimedWait(GetPointer(mq->cs_queue), timeout_helper);
}
/* Send, signal. */
SendNextUnsafe(mq, data);
GetReference(mq->cv_not_empty).Broadcast();
GetReference(mq->waitlist_not_empty).SignalAllThreads();
}
/* Receive, signal. */
*out = this->ReceiveInternal();
this->cv_not_full.Broadcast();
GetReference(this->waitlist_not_full).SignalAllThreads();
}
bool MessageQueue::TryReceive(uintptr_t *out) {
/* Acquire mutex, wait receivable. */
std::scoped_lock lock(this->queue_lock);
if (this->IsEmpty()) {
return false;
}
/* Receive, signal. */
*out = this->ReceiveInternal();
this->cv_not_full.Broadcast();
GetReference(this->waitlist_not_full).SignalAllThreads();
return true;
}
bool MessageQueue::TimedReceive(uintptr_t *out, u64 timeout) {
std::scoped_lock lock(this->queue_lock);
TimeoutHelper timeout_helper(timeout);
/* Receive functionality */
void ReceiveMessageQueue(uintptr_t *out, MessageQueueType *mq) {
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
while (this->IsEmpty()) {
if (timeout_helper.TimedOut()) {
{
/* Acquire mutex, wait receivable. */
std::scoped_lock lk(GetReference(mq->cs_queue));
while (IsMessageQueueEmpty(mq)) {
GetReference(mq->cv_not_empty).Wait(GetPointer(mq->cs_queue));
}
/* Receive, signal. */
*out = ReceiveUnsafe(mq);
GetReference(mq->cv_not_full).Broadcast();
GetReference(mq->waitlist_not_full).SignalAllThreads();
}
}
bool TryReceiveMessageQueue(uintptr_t *out, MessageQueueType *mq) {
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
{
/* Acquire mutex, check receivable. */
std::scoped_lock lk(GetReference(mq->cs_queue));
if (IsMessageQueueEmpty(mq)) {
return false;
}
this->cv_not_empty.TimedWait(&this->queue_lock, timeout_helper.NsUntilTimeout());
/* Receive, signal. */
*out = ReceiveUnsafe(mq);
GetReference(mq->cv_not_full).Broadcast();
GetReference(mq->waitlist_not_full).SignalAllThreads();
}
/* Receive, signal. */
*out = this->ReceiveInternal();
this->cv_not_full.Broadcast();
GetReference(this->waitlist_not_full).SignalAllThreads();
return true;
}
void MessageQueue::Peek(uintptr_t *out) {
/* Acquire mutex, wait receivable. */
std::scoped_lock lock(this->queue_lock);
bool TimedReceiveMessageQueue(uintptr_t *out, MessageQueueType *mq, TimeSpan timeout) {
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
while (this->IsEmpty()) {
this->cv_not_empty.Wait(&this->queue_lock);
{
/* Acquire mutex, wait receivable. */
impl::TimeoutHelper timeout_helper(timeout);
std::scoped_lock lk(GetReference(mq->cs_queue));
while (IsMessageQueueEmpty(mq)) {
if (timeout_helper.TimedOut()) {
return false;
}
GetReference(mq->cv_not_empty).TimedWait(GetPointer(mq->cs_queue), timeout_helper);
}
/* Receive, signal. */
*out = ReceiveUnsafe(mq);
GetReference(mq->cv_not_full).Broadcast();
GetReference(mq->waitlist_not_full).SignalAllThreads();
}
/* Peek. */
*out = this->PeekInternal();
}
bool MessageQueue::TryPeek(uintptr_t *out) {
/* Acquire mutex, wait receivable. */
std::scoped_lock lock(this->queue_lock);
if (this->IsEmpty()) {
return false;
}
/* Peek. */
*out = this->PeekInternal();
return true;
}
bool MessageQueue::TimedPeek(uintptr_t *out, u64 timeout) {
std::scoped_lock lock(this->queue_lock);
TimeoutHelper timeout_helper(timeout);
/* Peek functionality */
void PeekMessageQueue(uintptr_t *out, const MessageQueueType *mq) {
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
while (this->IsEmpty()) {
if (timeout_helper.TimedOut()) {
{
/* Acquire mutex, wait receivable. */
std::scoped_lock lk(GetReference(mq->cs_queue));
while (IsMessageQueueEmpty(mq)) {
GetReference(mq->cv_not_empty).Wait(GetPointer(mq->cs_queue));
}
/* Peek. */
*out = PeekUnsafe(mq);
}
}
bool TryPeekMessageQueue(uintptr_t *out, const MessageQueueType *mq) {
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
{
/* Acquire mutex, check receivable. */
std::scoped_lock lk(GetReference(mq->cs_queue));
if (IsMessageQueueEmpty(mq)) {
return false;
}
this->cv_not_empty.TimedWait(&this->queue_lock, timeout_helper.NsUntilTimeout());
/* Peek. */
*out = PeekUnsafe(mq);
}
return true;
}
bool TimedPeekMessageQueue(uintptr_t *out, const MessageQueueType *mq, TimeSpan timeout) {
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
{
/* Acquire mutex, wait receivable. */
impl::TimeoutHelper timeout_helper(timeout);
std::scoped_lock lk(GetReference(mq->cs_queue));
while (IsMessageQueueEmpty(mq)) {
if (timeout_helper.TimedOut()) {
return false;
}
GetReference(mq->cv_not_empty).TimedWait(GetPointer(mq->cs_queue), timeout_helper);
}
/* Peek. */
*out = PeekUnsafe(mq);
}
/* Peek. */
*out = this->PeekInternal();
return true;
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/os_thread_manager.hpp"
#include "impl/os_mutex_impl.hpp"
namespace ams::os {
namespace impl {
#ifdef ATMOSPHERE_BUILD_FOR_AUDITING
void PushAndCheckLockLevel(MutexType *mutex) {
/* If auditing isn't specified, don't bother. */
if (mutex->lock_level == 0) {
return;
}
/* TODO: Implement mutex level auditing. */
}
void PopAndCheckLockLevel(MutexType *mutex) {
/* If auditing isn't specified, don't bother. */
if (mutex->lock_level == 0) {
return;
}
/* TODO: Implement mutex level auditing. */
}
#else
void PushAndCheckLockLevel(MutexType *mutex) {
/* ... */
}
void PopAndCheckLockLevel(MutexType *mutex) {
/* ... */
}
#endif
}
namespace {
ALWAYS_INLINE void AfterLockMutex(MutexType *mutex, ThreadType *cur_thread) {
AMS_ASSERT(mutex->nest_count < MutexRecursiveLockCountMax);
impl::PushAndCheckLockLevel(mutex);
++mutex->nest_count;
mutex->owner_thread = cur_thread;
}
}
void InitializeMutex(MutexType *mutex, bool recursive, int lock_level) {
AMS_ASSERT((lock_level == 0) || (MutexLockLevelMin <= lock_level && lock_level <= MutexLockLevelMax));
/* Create object. */
new (GetPointer(mutex->_storage)) impl::InternalCriticalSection;
/* Set member variables. */
mutex->is_recursive = recursive;
mutex->lock_level = lock_level;
mutex->nest_count = 0;
mutex->owner_thread = nullptr;
/* Mark initialized. */
mutex->state = MutexType::State_Initialized;
}
void FinalizeMutex(MutexType *mutex) {
AMS_ASSERT(mutex->state == MutexType::State_Initialized);
/* Mark not intialized. */
mutex->state = MutexType::State_NotInitialized;
/* Destroy object. */
GetReference(mutex->_storage).~InternalCriticalSection();
}
void LockMutex(MutexType *mutex) {
AMS_ASSERT(mutex->state == MutexType::State_Initialized);
ThreadType *current = impl::GetCurrentThread();
if (!mutex->is_recursive) {
AMS_ASSERT(mutex->owner_thread != current);
GetReference(mutex->_storage).Enter();
} else {
if (mutex->owner_thread == current) {
AMS_ASSERT(mutex->nest_count >= 1);
} else {
GetReference(mutex->_storage).Enter();
}
}
AfterLockMutex(mutex, current);
}
bool TryLockMutex(MutexType *mutex) {
AMS_ASSERT(mutex->state == MutexType::State_Initialized);
ThreadType *current = impl::GetCurrentThread();
if (!mutex->is_recursive) {
AMS_ASSERT(mutex->owner_thread != current);
if (!GetReference(mutex->_storage).TryEnter()) {
return false;
}
} else {
if (mutex->owner_thread == current) {
AMS_ASSERT(mutex->nest_count >= 1);
} else {
if (!GetReference(mutex->_storage).TryEnter()) {
return false;
}
}
}
AfterLockMutex(mutex, current);
return true;
}
void UnlockMutex(MutexType *mutex) {
AMS_ASSERT(mutex->state == MutexType::State_Initialized);
AMS_ASSERT(mutex->nest_count > 0);
AMS_ASSERT(mutex->owner_thread == impl::GetCurrentThread());
impl::PopAndCheckLockLevel(mutex);
if ((--mutex->nest_count) == 0) {
mutex->owner_thread = nullptr;
GetReference(mutex->_storage).Leave();
}
}
bool IsMutexLockedByCurrentThread(const MutexType *mutex) {
AMS_ASSERT(mutex->state == MutexType::State_Initialized);
return mutex->owner_thread == impl::GetCurrentThread();
}
}

View File

@@ -21,7 +21,7 @@ namespace ams::os {
namespace {
util::TinyMT g_random;
os::Mutex g_random_mutex;
os::Mutex g_random_mutex(false);
bool g_initialized_random;
template<typename T>

View File

@@ -14,72 +14,132 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "impl/os_waitable_object_list.hpp"
#include "impl/os_timeout_helper.hpp"
namespace ams::os {
Semaphore::Semaphore(int c, int mc) : count(c), max_count(mc) {
new (GetPointer(this->waitlist)) impl::WaitableObjectList();
void InitializeSemaphore(SemaphoreType *sema, s32 count, s32 max_count) {
AMS_ASSERT(max_count >= 1);
AMS_ASSERT(count >= 0);
/* Setup objects. */
new (GetPointer(sema->cs_sema)) impl::InternalCriticalSection;
new (GetPointer(sema->cv_not_zero)) impl::InternalConditionVariable;
/* Setup wait lists. */
new (GetPointer(sema->waitlist)) impl::WaitableObjectList;
/* Set member variables. */
sema->count = count;
sema->max_count = max_count;
/* Mark initialized. */
sema->state = SemaphoreType::State_Initialized;
}
Semaphore::~Semaphore() {
GetReference(this->waitlist).~WaitableObjectList();
void FinalizeSemaphore(SemaphoreType *sema) {
AMS_ASSERT(sema->state = SemaphoreType::State_Initialized);
AMS_ASSERT(GetReference(sema->waitlist).IsEmpty());
/* Mark uninitialized. */
sema->state = SemaphoreType::State_NotInitialized;
/* Destroy wait lists. */
GetReference(sema->waitlist).~WaitableObjectList();
/* Destroy objects. */
GetReference(sema->cv_not_zero).~InternalConditionVariable();
GetReference(sema->cs_sema).~InternalCriticalSection();
}
void Semaphore::Acquire() {
std::scoped_lock lk(this->mutex);
void AcquireSemaphore(SemaphoreType *sema) {
AMS_ASSERT(sema->state == SemaphoreType::State_Initialized);
while (this->count == 0) {
this->condvar.Wait(&this->mutex);
{
std::scoped_lock lk(GetReference(sema->cs_sema));
while (sema->count == 0) {
GetReference(sema->cv_not_zero).Wait(GetPointer(sema->cs_sema));
}
--sema->count;
}
this->count--;
}
bool Semaphore::TryAcquire() {
std::scoped_lock lk(this->mutex);
bool TryAcquireSemaphore(SemaphoreType *sema) {
AMS_ASSERT(sema->state == SemaphoreType::State_Initialized);
if (this->count == 0) {
return false;
}
{
std::scoped_lock lk(GetReference(sema->cs_sema));
this->count--;
return true;
}
bool Semaphore::TimedAcquire(u64 timeout) {
std::scoped_lock lk(this->mutex);
TimeoutHelper timeout_helper(timeout);
while (this->count == 0) {
if (timeout_helper.TimedOut()) {
if (sema->count == 0) {
return false;
}
this->condvar.TimedWait(&this->mutex, timeout_helper.NsUntilTimeout());
--sema->count;
}
this->count--;
return true;
}
void Semaphore::Release() {
std::scoped_lock lk(this->mutex);
bool TimedAcquireSemaphore(SemaphoreType *sema, TimeSpan timeout) {
AMS_ASSERT(sema->state == SemaphoreType::State_Initialized);
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
AMS_ABORT_UNLESS(this->count + 1 <= this->max_count);
this->count++;
{
impl::TimeoutHelper timeout_helper(timeout);
std::scoped_lock lk(GetReference(sema->cs_sema));
this->condvar.Signal();
GetReference(this->waitlist).SignalAllThreads();
while (sema->count == 0) {
if (timeout_helper.TimedOut()) {
return false;
}
GetReference(sema->cv_not_zero).TimedWait(GetPointer(sema->cs_sema), timeout_helper);
}
--sema->count;
}
return true;
}
void Semaphore::Release(int count) {
std::scoped_lock lk(this->mutex);
void ReleaseSemaphore(SemaphoreType *sema) {
AMS_ASSERT(sema->state == SemaphoreType::State_Initialized);
AMS_ABORT_UNLESS(this->count + count <= this->max_count);
this->count += count;
{
std::scoped_lock lk(GetReference(sema->cs_sema));
this->condvar.Broadcast();
GetReference(this->waitlist).SignalAllThreads();
AMS_ASSERT(sema->count + 1 <= sema->max_count);
++sema->count;
GetReference(sema->cv_not_zero).Signal();
GetReference(sema->waitlist).SignalAllThreads();
}
}
void ReleaseSemaphore(SemaphoreType *sema, s32 count) {
AMS_ASSERT(sema->state == SemaphoreType::State_Initialized);
{
std::scoped_lock lk(GetReference(sema->cs_sema));
AMS_ASSERT(sema->count + count <= sema->max_count);
sema->count += count;
GetReference(sema->cv_not_zero).Signal();
GetReference(sema->waitlist).SignalAllThreads();
}
}
s32 GetCurrentSemaphoreCount(const SemaphoreType *sema) {
AMS_ASSERT(sema->state == SemaphoreType::State_Initialized);
return sema->count;
}
// void InitializeWaitableHolder(WaitableHolderType *waitable_holder, SemaphoreType *sema);
}

View File

@@ -15,174 +15,120 @@
*/
#include <stratosphere.hpp>
#include "impl/os_waitable_holder_impl.hpp"
#include "impl/os_inter_process_event.hpp"
#include "impl/os_timeout_helper.hpp"
namespace ams::os {
SystemEvent::SystemEvent(bool inter_process, bool autoclear) : state(SystemEventState::Uninitialized) {
Result CreateSystemEvent(SystemEventType *event, EventClearMode clear_mode, bool inter_process) {
if (inter_process) {
R_ABORT_UNLESS(this->InitializeAsInterProcessEvent(autoclear));
R_TRY(impl::CreateInterProcessEvent(std::addressof(event->inter_process_event), clear_mode));
event->state = SystemEventType::State_InitializedAsInterProcessEvent;
} else {
R_ABORT_UNLESS(this->InitializeAsEvent(autoclear));
InitializeEvent(std::addressof(event->event), false, clear_mode);
event->state = SystemEventType::State_InitializedAsEvent;
}
}
SystemEvent::SystemEvent(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) : state(SystemEventState::Uninitialized) {
this->AttachHandles(read_handle, manage_read_handle, write_handle, manage_write_handle, autoclear);
}
SystemEvent::~SystemEvent() {
this->Finalize();
}
Event &SystemEvent::GetEvent() {
AMS_ABORT_UNLESS(this->state == SystemEventState::Event);
return GetReference(this->storage_for_event);
}
const Event &SystemEvent::GetEvent() const {
AMS_ABORT_UNLESS(this->state == SystemEventState::Event);
return GetReference(this->storage_for_event);
}
impl::InterProcessEvent &SystemEvent::GetInterProcessEvent() {
AMS_ABORT_UNLESS(this->state == SystemEventState::InterProcessEvent);
return GetReference(this->storage_for_inter_process_event);
}
const impl::InterProcessEvent &SystemEvent::GetInterProcessEvent() const {
AMS_ABORT_UNLESS(this->state == SystemEventState::InterProcessEvent);
return GetReference(this->storage_for_inter_process_event);
}
Result SystemEvent::InitializeAsEvent(bool autoclear) {
AMS_ABORT_UNLESS(this->state == SystemEventState::Uninitialized);
new (GetPointer(this->storage_for_event)) Event(autoclear);
this->state = SystemEventState::Event;
return ResultSuccess();
}
Result SystemEvent::InitializeAsInterProcessEvent(bool autoclear) {
AMS_ABORT_UNLESS(this->state == SystemEventState::Uninitialized);
new (GetPointer(this->storage_for_inter_process_event)) impl::InterProcessEvent();
this->state = SystemEventState::InterProcessEvent;
void DestroySystemEvent(SystemEventType *event) {
auto state = event->state;
event->state = SystemEventType::State_NotInitialized;
/* Ensure we end up in a correct state if initialization fails. */
{
auto guard = SCOPE_GUARD { this->Finalize(); };
R_TRY(this->GetInterProcessEvent().Initialize(autoclear));
guard.Cancel();
}
return ResultSuccess();
}
void SystemEvent::AttachHandles(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) {
AMS_ABORT_UNLESS(this->state == SystemEventState::Uninitialized);
new (GetPointer(this->storage_for_inter_process_event)) impl::InterProcessEvent();
this->state = SystemEventState::InterProcessEvent;
this->GetInterProcessEvent().Initialize(read_handle, manage_read_handle, write_handle, manage_write_handle, autoclear);
}
void SystemEvent::AttachReadableHandle(Handle read_handle, bool manage_read_handle, bool autoclear) {
this->AttachHandles(read_handle, manage_read_handle, INVALID_HANDLE, false, autoclear);
}
void SystemEvent::AttachWritableHandle(Handle write_handle, bool manage_write_handle, bool autoclear) {
this->AttachHandles(INVALID_HANDLE, false, write_handle, manage_write_handle, autoclear);
}
Handle SystemEvent::DetachReadableHandle() {
AMS_ABORT_UNLESS(this->state == SystemEventState::InterProcessEvent);
return this->GetInterProcessEvent().DetachReadableHandle();
}
Handle SystemEvent::DetachWritableHandle() {
AMS_ABORT_UNLESS(this->state == SystemEventState::InterProcessEvent);
return this->GetInterProcessEvent().DetachWritableHandle();
}
Handle SystemEvent::GetReadableHandle() const {
AMS_ABORT_UNLESS(this->state == SystemEventState::InterProcessEvent);
return this->GetInterProcessEvent().GetReadableHandle();
}
Handle SystemEvent::GetWritableHandle() const {
AMS_ABORT_UNLESS(this->state == SystemEventState::InterProcessEvent);
return this->GetInterProcessEvent().GetWritableHandle();
}
void SystemEvent::Finalize() {
switch (this->state) {
case SystemEventState::Uninitialized:
break;
case SystemEventState::Event:
this->GetEvent().~Event();
break;
case SystemEventState::InterProcessEvent:
this->GetInterProcessEvent().~InterProcessEvent();
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
this->state = SystemEventState::Uninitialized;
}
void SystemEvent::Signal() {
switch (this->state) {
case SystemEventState::Event:
this->GetEvent().Signal();
break;
case SystemEventState::InterProcessEvent:
this->GetInterProcessEvent().Signal();
break;
case SystemEventState::Uninitialized:
switch (state) {
case SystemEventType::State_InitializedAsInterProcessEvent: impl::DestroyInterProcessEvent(std::addressof(event->inter_process_event)); break;
case SystemEventType::State_InitializedAsEvent: FinalizeEvent(std::addressof(event->event)); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void SystemEvent::Reset() {
switch (this->state) {
case SystemEventState::Event:
this->GetEvent().Reset();
break;
case SystemEventState::InterProcessEvent:
this->GetInterProcessEvent().Reset();
break;
case SystemEventState::Uninitialized:
AMS_UNREACHABLE_DEFAULT_CASE();
}
void AttachSystemEvent(SystemEventType *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);
impl::AttachInterProcessEvent(std::addressof(event->inter_process_event), read_handle, read_handle_managed, write_handle, write_handle_managed, clear_mode);
event->state = SystemEventType::State_InitializedAsInterProcessEvent;
}
void SystemEvent::Wait() {
switch (this->state) {
case SystemEventState::Event:
this->GetEvent().Wait();
break;
case SystemEventState::InterProcessEvent:
this->GetInterProcessEvent().Wait();
break;
case SystemEventState::Uninitialized:
void AttachReadableHandleToSystemEvent(SystemEventType *event, Handle read_handle, bool manage_read_handle, EventClearMode clear_mode) {
return AttachSystemEvent(event, read_handle, manage_read_handle, svc::InvalidHandle, false, clear_mode);
}
void AttachWritableHandleToSystemEvent(SystemEventType *event, Handle write_handle, bool manage_write_handle, EventClearMode clear_mode) {
return AttachSystemEvent(event, svc::InvalidHandle, false, write_handle, manage_write_handle, clear_mode);
}
Handle DetachReadableHandleOfSystemEvent(SystemEventType *event) {
AMS_ASSERT(event->state == SystemEventType::State_InitializedAsInterProcessEvent);
return impl::DetachReadableHandleOfInterProcessEvent(std::addressof(event->inter_process_event));
}
Handle DetachWritableHandleOfSystemEvent(SystemEventType *event) {
AMS_ASSERT(event->state == SystemEventType::State_InitializedAsInterProcessEvent);
return impl::DetachWritableHandleOfInterProcessEvent(std::addressof(event->inter_process_event));
}
Handle GetReadableHandleOfSystemEvent(const SystemEventType *event) {
AMS_ASSERT(event->state == SystemEventType::State_InitializedAsInterProcessEvent);
return impl::GetReadableHandleOfInterProcessEvent(std::addressof(event->inter_process_event));
}
Handle GetWritableHandleOfSystemEvent(const SystemEventType *event) {
AMS_ASSERT(event->state == SystemEventType::State_InitializedAsInterProcessEvent);
return impl::GetWritableHandleOfInterProcessEvent(std::addressof(event->inter_process_event));
}
void SignalSystemEvent(SystemEventType *event) {
switch (event->state) {
case SystemEventType::State_InitializedAsInterProcessEvent: return impl::SignalInterProcessEvent(std::addressof(event->inter_process_event));
case SystemEventType::State_InitializedAsEvent: return SignalEvent(std::addressof(event->event));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
bool SystemEvent::TryWait() {
switch (this->state) {
case SystemEventState::Event:
return this->GetEvent().TryWait();
case SystemEventState::InterProcessEvent:
return this->GetInterProcessEvent().TryWait();
case SystemEventState::Uninitialized:
void WaitSystemEvent(SystemEventType *event) {
switch (event->state) {
case SystemEventType::State_InitializedAsInterProcessEvent: return impl::WaitInterProcessEvent(std::addressof(event->inter_process_event));
case SystemEventType::State_InitializedAsEvent: return WaitEvent(std::addressof(event->event));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
bool SystemEvent::TimedWait(u64 ns) {
switch (this->state) {
case SystemEventState::Event:
return this->GetEvent().TimedWait(ns);
case SystemEventState::InterProcessEvent:
return this->GetInterProcessEvent().TimedWait(ns);
case SystemEventState::Uninitialized:
bool TryWaitSystemEvent(SystemEventType *event) {
switch (event->state) {
case SystemEventType::State_InitializedAsInterProcessEvent: return impl::TryWaitInterProcessEvent(std::addressof(event->inter_process_event));
case SystemEventType::State_InitializedAsEvent: return TryWaitEvent(std::addressof(event->event));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
bool TimedWaitSystemEvent(SystemEventType *event, TimeSpan timeout) {
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
switch (event->state) {
case SystemEventType::State_InitializedAsInterProcessEvent: return impl::TimedWaitInterProcessEvent(std::addressof(event->inter_process_event), timeout);
case SystemEventType::State_InitializedAsEvent: return TimedWaitEvent(std::addressof(event->event), timeout);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void ClearSystemEvent(SystemEventType *event) {
switch (event->state) {
case SystemEventType::State_InitializedAsInterProcessEvent: return impl::ClearInterProcessEvent(std::addressof(event->inter_process_event));
case SystemEventType::State_InitializedAsEvent: return ClearEvent(std::addressof(event->event));
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void InitializeWaitableHolder(WaitableHolderType *waitable_holder, SystemEventType *event) {
switch (event->state) {
case SystemEventType::State_InitializedAsInterProcessEvent:
new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfInterProcessEvent(std::addressof(event->inter_process_event));
break;
case SystemEventType::State_InitializedAsEvent:
new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfEvent(std::addressof(event->event));
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}

View File

@@ -0,0 +1,209 @@
/*
* 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 "impl/os_thread_manager.hpp"
#include "impl/os_timeout_helper.hpp"
#include "impl/os_waitable_holder_impl.hpp"
namespace ams::os {
namespace {
size_t CheckThreadNameLength(const char *name) {
const char *cur = name;
for (size_t len = 0; len < ThreadNameLengthMax; ++len) {
if (*(cur++) == 0) {
return len;
}
}
AMS_ABORT("ThreadNameLength too large");
}
void ValidateThreadArguments(ThreadType *thread, void *stack, size_t stack_size, s32 priority) {
AMS_ASSERT(stack != nullptr);
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(stack), ThreadStackAlignment));
AMS_ASSERT(stack_size > 0);
AMS_ASSERT(util::IsAligned(stack_size, ThreadStackAlignment));
}
}
Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority, s32 ideal_core) {
ValidateThreadArguments(thread, stack, stack_size, priority);
AMS_ASSERT(GetThreadAvailableCoreMask() & (1ul << ideal_core));
return impl::GetThreadManager().CreateThread(thread, function, argument, stack, stack_size, priority, ideal_core);
}
Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority) {
ValidateThreadArguments(thread, stack, stack_size, priority);
return impl::GetThreadManager().CreateThread(thread, function, argument, stack, stack_size, priority);
}
void DestroyThread(ThreadType *thread) {
auto &manager = impl::GetThreadManager();
AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated);
AMS_ASSERT(thread != manager.GetMainThread());
manager.DestroyThread(thread);
}
void StartThread(ThreadType *thread) {
AMS_ASSERT(thread->state == ThreadType::State_Initialized);
impl::GetThreadManager().StartThread(thread);
}
ThreadType *GetCurrentThread() {
return impl::GetCurrentThread();
}
void WaitThread(ThreadType *thread) {
AMS_ASSERT(thread != impl::GetCurrentThread());
AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated);
return impl::GetThreadManager().WaitThread(thread);
}
bool TryWaitThread(ThreadType *thread) {
AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated);
return impl::GetThreadManager().TryWaitThread(thread);
}
void YieldThread() {
return impl::GetThreadManager().YieldThread();
}
void SleepThread(TimeSpan time) {
impl::TimeoutHelper::Sleep(time);
}
s32 SuspendThread(ThreadType *thread) {
AMS_ASSERT(thread->state == ThreadType::State_Started);
AMS_ASSERT(thread != impl::GetCurrentThread());
return impl::GetThreadManager().SuspendThread(thread);
}
s32 ResumeThread(ThreadType *thread) {
AMS_ASSERT(thread->state == ThreadType::State_Started);
return impl::GetThreadManager().ResumeThread(thread);
}
s32 GetThreadSuspendCount(const ThreadType *thread) {
return thread->suspend_count;
}
void CancelThreadSynchronization(ThreadType *thread) {
AMS_ASSERT(thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated);
return impl::GetThreadManager().CancelThreadSynchronization(thread);
}
/* TODO: void GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */
s32 ChangeThreadPriority(ThreadType *thread, s32 priority) {
AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_DestroyedBeforeStarted || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated);
{
std::scoped_lock lk(GetReference(thread->cs_thread));
const s32 prev_prio = thread->base_priority;
bool success = impl::GetThreadManager().ChangePriority(thread, priority);
AMS_ASSERT(success);
thread->base_priority = priority;
return prev_prio;
}
}
s32 GetThreadPriority(const ThreadType *thread) {
AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_DestroyedBeforeStarted || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated);
return thread->base_priority;
}
s32 GetThreadCurrentPriority(const ThreadType *thread) {
AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_DestroyedBeforeStarted || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated);
return impl::GetThreadManager().GetCurrentPriority(thread);
}
void SetThreadName(ThreadType *thread, const char *name) {
AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_DestroyedBeforeStarted || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated);
if (name == nullptr) {
impl::GetThreadManager().SetInitialThreadNameUnsafe(thread);
return;
}
const size_t name_size = CheckThreadNameLength(name) + 1;
std::memcpy(thread->name_buffer, name, name_size);
SetThreadNamePointer(thread, thread->name_buffer);
}
void SetThreadNamePointer(ThreadType *thread, const char *name) {
AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_DestroyedBeforeStarted || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated);
if (name == nullptr) {
impl::GetThreadManager().SetInitialThreadNameUnsafe(thread);
return;
}
thread->name_pointer = name;
impl::GetThreadManager().NotifyThreadNameChanged(thread);
}
const char *GetThreadNamePointer(const ThreadType *thread) {
AMS_ASSERT(thread->state == ThreadType::State_Initialized || thread->state == ThreadType::State_DestroyedBeforeStarted || thread->state == ThreadType::State_Started || thread->state == ThreadType::State_Terminated);
return thread->name_pointer;
}
s32 GetCurrentCoreNumber() {
return impl::GetThreadManager().GetCurrentCoreNumber();
}
s32 GetCurrentProcessorNumber() {
return GetCurrentCoreNumber();
}
void SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) {
AMS_ASSERT(ideal_core == IdealCoreDontCare || ideal_core == IdealCoreUseDefault || ideal_core == IdealCoreNoUpdate || (0 <= ideal_core && ideal_core < impl::CoreAffinityMaskBitWidth));
if (ideal_core != IdealCoreUseDefault) {
AMS_ASSERT(affinity_mask != 0);
AMS_ASSERT((affinity_mask & ~GetThreadAvailableCoreMask()) == 0);
}
if (ideal_core >= 0) {
AMS_ASSERT((affinity_mask & (1ul << ideal_core)) != 0);
}
return impl::GetThreadManager().SetThreadCoreMask(thread, ideal_core, affinity_mask);
}
void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) {
return impl::GetThreadManager().GetThreadCoreMask(out_ideal_core, out_affinity_mask, thread);
}
u64 GetThreadAvailableCoreMask() {
return impl::GetThreadManager().GetThreadAvailableCoreMask();
}
ThreadId GetThreadId(const ThreadType *thread) {
return impl::GetThreadManager().GetThreadId(thread);
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/os_waitable_manager_impl.hpp"
#include "impl/os_waitable_holder_base.hpp"
#include "impl/os_waitable_holder_impl.hpp"
namespace ams::os {
namespace {
ALWAYS_INLINE impl::WaitableManagerImpl &GetWaitableManagerImpl(WaitableManagerType *manager) {
return GetReference(manager->impl_storage);
}
ALWAYS_INLINE WaitableHolderType *ReinterpretAsWaitableHolder(impl::WaitableHolderBase *base) {
return reinterpret_cast<WaitableHolderType *>(base);
}
}
void InitializeWaitableManager(WaitableManagerType *manager) {
/* Initialize storage. */
new (std::addressof(GetWaitableManagerImpl(manager))) impl::WaitableManagerImpl;
/* Mark initialized. */
manager->state = WaitableManagerType::State_Initialized;
}
void FinalizeWaitableManager(WaitableManagerType *manager) {
auto &impl = GetWaitableManagerImpl(manager);
AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized);
AMS_ASSERT(impl.IsEmpty());
/* Mark not initialized. */
manager->state = WaitableManagerType::State_NotInitialized;
/* Destroy. */
impl.~WaitableManagerImpl();
}
WaitableHolderType *WaitAny(WaitableManagerType *manager) {
auto &impl = GetWaitableManagerImpl(manager);
AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized);
AMS_ASSERT(!impl.IsEmpty());
auto *holder = ReinterpretAsWaitableHolder(impl.WaitAny());
AMS_ASSERT(holder != nullptr);
return holder;
}
WaitableHolderType *TryWaitAny(WaitableManagerType *manager) {
auto &impl = GetWaitableManagerImpl(manager);
AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized);
AMS_ASSERT(!impl.IsEmpty());
auto *holder = ReinterpretAsWaitableHolder(impl.TryWaitAny());
return holder;
}
WaitableHolderType *TimedWaitAny(WaitableManagerType *manager, TimeSpan timeout) {
auto &impl = GetWaitableManagerImpl(manager);
AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized);
AMS_ASSERT(!impl.IsEmpty());
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
auto *holder = ReinterpretAsWaitableHolder(impl.TimedWaitAny(timeout));
return holder;
}
void FinalizeWaitableHolder(WaitableHolderType *holder) {
auto *holder_base = reinterpret_cast<impl::WaitableHolderBase *>(GetPointer(holder->impl_storage));
AMS_ASSERT(!holder_base->IsLinkedToManager());
holder_base->~WaitableHolderBase();
}
void LinkWaitableHolder(WaitableManagerType *manager, WaitableHolderType *holder) {
auto &impl = GetWaitableManagerImpl(manager);
auto *holder_base = reinterpret_cast<impl::WaitableHolderBase *>(GetPointer(holder->impl_storage));
AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized);
AMS_ASSERT(!holder_base->IsLinkedToManager());
impl.LinkWaitableHolder(*holder_base);
holder_base->SetManager(&impl);
}
void UnlinkWaitableHolder(WaitableHolderType *holder) {
auto *holder_base = reinterpret_cast<impl::WaitableHolderBase *>(GetPointer(holder->impl_storage));
/* Don't allow unlinking of an unlinked holder. */
AMS_ABORT_UNLESS(holder_base->IsLinkedToManager());
holder_base->GetManager()->UnlinkWaitableHolder(*holder_base);
holder_base->SetManager(nullptr);
}
void UnlinkAllWaitableHolder(WaitableManagerType *manager) {
auto &impl = GetWaitableManagerImpl(manager);
AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized);
return impl.UnlinkAll();
}
void MoveAllWaitableHolder(WaitableManagerType *_dst, WaitableManagerType *_src) {
auto &dst = GetWaitableManagerImpl(_dst);
auto &src = GetWaitableManagerImpl(_src);
AMS_ASSERT(_dst->state == WaitableManagerType::State_Initialized);
AMS_ASSERT(_src->state == WaitableManagerType::State_Initialized);
return dst.MoveAllFrom(src);
}
void SetWaitableHolderUserData(WaitableHolderType *holder, uintptr_t user_data) {
holder->user_data = user_data;
}
uintptr_t GetWaitableHolderUserData(const WaitableHolderType *holder) {
return holder->user_data;
}
void InitializeWaitableHolder(WaitableHolderType *holder, Handle handle) {
AMS_ASSERT(handle != svc::InvalidHandle);
new (GetPointer(holder->impl_storage)) impl::WaitableHolderOfHandle(handle);
holder->user_data = 0;
}
}

View File

@@ -1,116 +0,0 @@
/*
* 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 "impl/os_waitable_holder_impl.hpp"
#include "impl/os_waitable_manager_impl.hpp"
namespace ams::os {
WaitableHolder::WaitableHolder(Handle handle) {
/* Don't allow invalid handles. */
AMS_ABORT_UNLESS(handle != INVALID_HANDLE);
/* Initialize appropriate holder. */
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfHandle(handle);
/* Set user-data. */
this->user_data = 0;
}
WaitableHolder::WaitableHolder(Event *event) {
/* Initialize appropriate holder. */
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfEvent(event);
/* Set user-data. */
this->user_data = 0;
}
WaitableHolder::WaitableHolder(SystemEvent *event) {
/* Initialize appropriate holder. */
switch (event->GetState()) {
case SystemEventState::Event:
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfEvent(&event->GetEvent());
break;
case SystemEventState::InterProcessEvent:
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfInterProcessEvent(&event->GetInterProcessEvent());
break;
case SystemEventState::Uninitialized:
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Set user-data. */
this->user_data = 0;
}
WaitableHolder::WaitableHolder(InterruptEvent *event) {
/* Initialize appropriate holder. */
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfInterruptEvent(event);
/* Set user-data. */
this->user_data = 0;
}
WaitableHolder::WaitableHolder(Thread *thread) {
/* Initialize appropriate holder. */
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfThread(thread);
/* Set user-data. */
this->user_data = 0;
}
WaitableHolder::WaitableHolder(Semaphore *semaphore) {
/* Initialize appropriate holder. */
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfSemaphore(semaphore);
/* Set user-data. */
this->user_data = 0;
}
WaitableHolder::WaitableHolder(MessageQueue *message_queue, MessageQueueWaitKind wait_kind) {
/* Initialize appropriate holder. */
switch (wait_kind) {
case MessageQueueWaitKind::ForNotFull:
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfMessageQueueForNotFull(message_queue);
break;
case MessageQueueWaitKind::ForNotEmpty:
new (GetPointer(this->impl_storage)) impl::WaitableHolderOfMessageQueueForNotEmpty(message_queue);
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Set user-data. */
this->user_data = 0;
}
WaitableHolder::~WaitableHolder() {
auto holder_base = reinterpret_cast<impl::WaitableHolderBase *>(GetPointer(this->impl_storage));
/* Don't allow destruction of a linked waitable holder. */
AMS_ABORT_UNLESS(!holder_base->IsLinkedToManager());
holder_base->~WaitableHolderBase();
}
void WaitableHolder::UnlinkFromWaitableManager() {
auto holder_base = reinterpret_cast<impl::WaitableHolderBase *>(GetPointer(this->impl_storage));
/* Don't allow unlinking of an unlinked holder. */
AMS_ABORT_UNLESS(holder_base->IsLinkedToManager());
holder_base->GetManager()->UnlinkWaitableHolder(*holder_base);
holder_base->SetManager(nullptr);
}
}

View File

@@ -1,88 +0,0 @@
/*
* 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 "impl/os_waitable_holder_impl.hpp"
#include "impl/os_waitable_manager_impl.hpp"
namespace ams::os {
WaitableManager::WaitableManager() {
/* Initialize storage. */
new (GetPointer(this->impl_storage)) impl::WaitableManagerImpl();
}
WaitableManager::~WaitableManager() {
auto &impl = GetReference(this->impl_storage);
/* Don't allow destruction of a non-empty waitable holder. */
AMS_ABORT_UNLESS(impl.IsEmpty());
impl.~WaitableManagerImpl();
}
/* Wait. */
WaitableHolder *WaitableManager::WaitAny() {
auto &impl = GetReference(this->impl_storage);
/* Don't allow waiting on empty list. */
AMS_ABORT_UNLESS(!impl.IsEmpty());
return reinterpret_cast<WaitableHolder *>(impl.WaitAny());
}
WaitableHolder *WaitableManager::TryWaitAny() {
auto &impl = GetReference(this->impl_storage);
/* Don't allow waiting on empty list. */
AMS_ABORT_UNLESS(!impl.IsEmpty());
return reinterpret_cast<WaitableHolder *>(impl.TryWaitAny());
}
WaitableHolder *WaitableManager::TimedWaitAny(u64 timeout) {
auto &impl = GetReference(this->impl_storage);
/* Don't allow waiting on empty list. */
AMS_ABORT_UNLESS(!impl.IsEmpty());
return reinterpret_cast<WaitableHolder *>(impl.TimedWaitAny(timeout));
}
/* Link. */
void WaitableManager::LinkWaitableHolder(WaitableHolder *holder) {
auto &impl = GetReference(this->impl_storage);
auto holder_base = reinterpret_cast<impl::WaitableHolderBase *>(GetPointer(holder->impl_storage));
/* Don't allow double-linking a holder. */
AMS_ABORT_UNLESS(!holder_base->IsLinkedToManager());
impl.LinkWaitableHolder(*holder_base);
holder_base->SetManager(&impl);
}
void WaitableManager::UnlinkAll() {
auto &impl = GetReference(this->impl_storage);
impl.UnlinkAll();
}
void WaitableManager::MoveAllFrom(WaitableManager *other) {
auto &dst_impl = GetReference(this->impl_storage);
auto &src_impl = GetReference(other->impl_storage);
dst_impl.MoveAllFrom(src_impl);
}
}

View File

@@ -31,7 +31,7 @@ namespace ams::patcher {
constexpr size_t ModuleIpsPatchLength = 2 * sizeof(ro::ModuleId) + IpsFileExtensionLength;
/* Global data. */
os::Mutex apply_patch_lock;
os::Mutex apply_patch_lock(false);
u8 g_patch_read_buffer[os::MemoryPageSize];
/* Helpers. */

View File

@@ -21,7 +21,7 @@ namespace ams::pm::info {
namespace {
/* Global lock. */
os::Mutex g_info_lock;
os::Mutex g_info_lock(false);
/* TODO: Less memory-intensive storage? */
std::set<u64> g_cached_launched_programs;

View File

@@ -106,7 +106,7 @@ namespace ams::sf::cmif {
return entry->object.Clone();
}
ServerDomainManager::EntryManager::EntryManager(DomainEntryStorage *entry_storage, size_t entry_count) {
ServerDomainManager::EntryManager::EntryManager(DomainEntryStorage *entry_storage, size_t entry_count) : lock(false) {
this->entries = reinterpret_cast<Entry *>(entry_storage);
this->num_entries = entry_count;
for (size_t i = 0; i < this->num_entries; i++) {

View File

@@ -19,7 +19,7 @@ namespace ams::sf::hipc {
namespace {
NX_INLINE Result ReceiveImpl(Handle session_handle, void *message_buf, size_t message_buf_size) {
ALWAYS_INLINE Result ReceiveImpl(Handle session_handle, void *message_buf, size_t message_buf_size) {
s32 unused_index;
if (message_buf == armGetTls()) {
/* Consider: AMS_ABORT_UNLESS(message_buf_size == TlsMessageBufferSize); */
@@ -29,7 +29,7 @@ namespace ams::sf::hipc {
}
}
NX_INLINE Result ReplyImpl(Handle session_handle, void *message_buf, size_t message_buf_size) {
ALWAYS_INLINE Result ReplyImpl(Handle session_handle, void *message_buf, size_t message_buf_size) {
s32 unused_index;
if (message_buf == armGetTls()) {
/* Consider: AMS_ABORT_UNLESS(message_buf_size == TlsMessageBufferSize); */
@@ -41,6 +41,14 @@ namespace ams::sf::hipc {
}
void AttachWaitableHolderForAccept(os::WaitableHolderType *holder, Handle port) {
return os::InitializeWaitableHolder(holder, port);
}
void AttachWaitableHolderForReply(os::WaitableHolderType *holder, Handle request) {
return os::InitializeWaitableHolder(holder, request);
}
Result Receive(ReceiveResult *out_recv_result, Handle session_handle, const cmif::PointerAndSize &message_buffer) {
R_TRY_CATCH(ReceiveImpl(session_handle, message_buffer.GetPointer(), message_buffer.GetSize())) {
R_CATCH(svc::ResultSessionClosed) {

View File

@@ -40,7 +40,7 @@ namespace ams::sf::hipc::impl {
};
/* Globals. */
os::Mutex g_query_server_lock;
os::Mutex g_query_server_lock(false);
bool g_constructed_server = false;
bool g_registered_any = false;
@@ -49,8 +49,9 @@ namespace ams::sf::hipc::impl {
}
constexpr size_t QueryServerProcessThreadStackSize = 0x4000;
constexpr int QueryServerProcessThreadPriority = 27;
os::StaticThread<QueryServerProcessThreadStackSize> g_query_server_process_thread;
constexpr s32 QueryServerProcessThreadPriority = -1;
alignas(os::ThreadStackAlignment) u8 g_server_process_thread_stack[QueryServerProcessThreadStackSize];
os::ThreadType g_query_server_process_thread;
constexpr size_t MaxServers = 0;
TYPED_STORAGE(sf::hipc::ServerManager<MaxServers>) g_query_server_storage;
@@ -61,16 +62,16 @@ namespace ams::sf::hipc::impl {
std::scoped_lock lk(g_query_server_lock);
if (!g_constructed_server) {
if (AMS_UNLIKELY(!g_constructed_server)) {
new (GetPointer(g_query_server_storage)) sf::hipc::ServerManager<MaxServers>();
g_constructed_server = true;
}
R_ABORT_UNLESS(GetPointer(g_query_server_storage)->RegisterSession(query_handle, cmif::ServiceObjectHolder(std::make_shared<MitmQueryService>(query_func))));
if (!g_registered_any) {
R_ABORT_UNLESS(g_query_server_process_thread.Initialize(&QueryServerProcessThreadMain, GetPointer(g_query_server_storage), QueryServerProcessThreadPriority));
R_ABORT_UNLESS(g_query_server_process_thread.Start());
if (AMS_UNLIKELY(!g_registered_any)) {
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_query_server_process_thread), &QueryServerProcessThreadMain, GetPointer(g_query_server_storage), g_server_process_thread_stack, sizeof(g_server_process_thread_stack), QueryServerProcessThreadPriority));
os::StartThread(std::addressof(g_query_server_process_thread));
g_registered_any = true;
}
}

View File

@@ -34,56 +34,56 @@ namespace ams::sf::hipc {
session->has_received = false;
/* Set user data tag. */
session->SetUserData(static_cast<uintptr_t>(UserDataTag::Session));
os::SetWaitableHolderUserData(session, static_cast<uintptr_t>(UserDataTag::Session));
this->RegisterToWaitList(session);
}
void ServerManagerBase::RegisterToWaitList(os::WaitableHolder *holder) {
void ServerManagerBase::RegisterToWaitList(os::WaitableHolderType *holder) {
std::scoped_lock lk(this->waitlist_mutex);
this->waitlist.LinkWaitableHolder(holder);
os::LinkWaitableHolder(std::addressof(this->waitlist), holder);
this->notify_event.Signal();
}
void ServerManagerBase::ProcessWaitList() {
std::scoped_lock lk(this->waitlist_mutex);
this->waitable_manager.MoveAllFrom(&this->waitlist);
os::MoveAllWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->waitlist));
}
os::WaitableHolder *ServerManagerBase::WaitSignaled() {
os::WaitableHolderType *ServerManagerBase::WaitSignaled() {
std::scoped_lock lk(this->waitable_selection_mutex);
while (true) {
this->ProcessWaitList();
auto selected = this->waitable_manager.WaitAny();
auto selected = os::WaitAny(std::addressof(this->waitable_manager));
if (selected == &this->request_stop_event_holder) {
return nullptr;
} else if (selected == &this->notify_event_holder) {
this->notify_event.Reset();
this->notify_event.Clear();
} else {
selected->UnlinkFromWaitableManager();
os::UnlinkWaitableHolder(selected);
return selected;
}
}
}
void ServerManagerBase::ResumeProcessing() {
this->request_stop_event.Reset();
this->request_stop_event.Clear();
}
void ServerManagerBase::RequestStopProcessing() {
this->request_stop_event.Signal();
}
void ServerManagerBase::AddUserWaitableHolder(os::WaitableHolder *waitable) {
const auto user_data_tag = static_cast<UserDataTag>(waitable->GetUserData());
void ServerManagerBase::AddUserWaitableHolder(os::WaitableHolderType *waitable) {
const auto user_data_tag = static_cast<UserDataTag>(os::GetWaitableHolderUserData(waitable));
AMS_ABORT_UNLESS(user_data_tag != UserDataTag::Server);
AMS_ABORT_UNLESS(user_data_tag != UserDataTag::MitmServer);
AMS_ABORT_UNLESS(user_data_tag != UserDataTag::Session);
this->RegisterToWaitList(waitable);
}
Result ServerManagerBase::ProcessForServer(os::WaitableHolder *holder) {
AMS_ABORT_UNLESS(static_cast<UserDataTag>(holder->GetUserData()) == UserDataTag::Server);
Result ServerManagerBase::ProcessForServer(os::WaitableHolderType *holder) {
AMS_ABORT_UNLESS(static_cast<UserDataTag>(os::GetWaitableHolderUserData(holder)) == UserDataTag::Server);
ServerBase *server = static_cast<ServerBase *>(holder);
ON_SCOPE_EXIT { this->RegisterToWaitList(server); };
@@ -100,8 +100,8 @@ namespace ams::sf::hipc {
return this->AcceptSession(server->port_handle, std::move(obj));
}
Result ServerManagerBase::ProcessForMitmServer(os::WaitableHolder *holder) {
AMS_ABORT_UNLESS(static_cast<UserDataTag>(holder->GetUserData()) == UserDataTag::MitmServer);
Result ServerManagerBase::ProcessForMitmServer(os::WaitableHolderType *holder) {
AMS_ABORT_UNLESS(static_cast<UserDataTag>(os::GetWaitableHolderUserData(holder)) == UserDataTag::MitmServer);
ServerBase *server = static_cast<ServerBase *>(holder);
ON_SCOPE_EXIT { this->RegisterToWaitList(server); };
@@ -118,8 +118,8 @@ namespace ams::sf::hipc {
return this->AcceptMitmSession(server->port_handle, std::move(obj), std::move(fsrv));
}
Result ServerManagerBase::ProcessForSession(os::WaitableHolder *holder) {
AMS_ABORT_UNLESS(static_cast<UserDataTag>(holder->GetUserData()) == UserDataTag::Session);
Result ServerManagerBase::ProcessForSession(os::WaitableHolderType *holder) {
AMS_ABORT_UNLESS(static_cast<UserDataTag>(os::GetWaitableHolderUserData(holder)) == UserDataTag::Session);
ServerSession *session = static_cast<ServerSession *>(holder);
@@ -176,8 +176,8 @@ namespace ams::sf::hipc {
}
}
Result ServerManagerBase::Process(os::WaitableHolder *holder) {
switch (static_cast<UserDataTag>(holder->GetUserData())) {
Result ServerManagerBase::Process(os::WaitableHolderType *holder) {
switch (static_cast<UserDataTag>(os::GetWaitableHolderUserData(holder))) {
case UserDataTag::Server:
return this->ProcessForServer(holder);
break;

View File

@@ -77,6 +77,7 @@ namespace ams::sf::hipc {
void ServerSessionManager::CloseSessionImpl(ServerSession *session) {
const Handle session_handle = session->session_handle;
os::FinalizeWaitableHolder(session);
this->DestroySession(session);
R_ABORT_UNLESS(svcCloseHandle(session_handle));
}

View File

@@ -20,22 +20,22 @@ namespace ams::sm::impl {
namespace {
/* Globals. */
os::RecursiveMutex g_user_session_mutex;
os::RecursiveMutex g_mitm_ack_session_mutex;
os::RecursiveMutex g_per_thread_session_mutex;
os::Mutex g_user_session_mutex(true);
os::Mutex g_mitm_ack_session_mutex(true);
os::Mutex g_per_thread_session_mutex(true);
}
/* Utilities. */
os::RecursiveMutex &GetUserSessionMutex() {
os::Mutex &GetUserSessionMutex() {
return g_user_session_mutex;
}
os::RecursiveMutex &GetMitmAcknowledgementSessionMutex() {
os::Mutex &GetMitmAcknowledgementSessionMutex() {
return g_mitm_ack_session_mutex;
}
os::RecursiveMutex &GetPerThreadSessionMutex() {
os::Mutex &GetPerThreadSessionMutex() {
return g_per_thread_session_mutex;
}

View File

@@ -21,13 +21,13 @@
namespace ams::sm::impl {
/* Utilities. */
os::RecursiveMutex &GetUserSessionMutex();
os::RecursiveMutex &GetMitmAcknowledgementSessionMutex();
os::RecursiveMutex &GetPerThreadSessionMutex();
os::Mutex &GetUserSessionMutex();
os::Mutex &GetMitmAcknowledgementSessionMutex();
os::Mutex &GetPerThreadSessionMutex();
template<typename F>
Result DoWithUserSession(F f) {
std::scoped_lock<os::RecursiveMutex &> lk(GetUserSessionMutex());
std::scoped_lock lk(GetUserSessionMutex());
{
R_ABORT_UNLESS(smInitialize());
ON_SCOPE_EXIT { smExit(); };
@@ -38,7 +38,7 @@ namespace ams::sm::impl {
template<typename F>
Result DoWithMitmAcknowledgementSession(F f) {
std::scoped_lock<os::RecursiveMutex &> lk(GetMitmAcknowledgementSessionMutex());
std::scoped_lock lk(GetMitmAcknowledgementSessionMutex());
{
R_ABORT_UNLESS(smAtmosphereMitmInitialize());
ON_SCOPE_EXIT { smAtmosphereMitmExit(); };
@@ -51,7 +51,7 @@ namespace ams::sm::impl {
Result DoWithPerThreadSession(F f) {
Service srv;
{
std::scoped_lock<os::RecursiveMutex &> lk(GetPerThreadSessionMutex());
std::scoped_lock lk(GetPerThreadSessionMutex());
R_ABORT_UNLESS(smAtmosphereOpenSession(&srv));
}
{