os: refactor/rewrite entire namespace.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -13,167 +13,139 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_inter_process_event.hpp"
|
||||
#include "os_inter_process_event_impl.hpp"
|
||||
#include "os_waitable_object_list.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
Result CreateEventHandles(Handle *out_readable, Handle *out_writable) {
|
||||
/* Create the event handles. */
|
||||
R_TRY_CATCH(svcCreateEvent(out_writable, out_readable)) {
|
||||
R_CONVERT(svc::ResultOutOfResource, ResultOutOfResource());
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
inline void SetupInterProcessEventType(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode) {
|
||||
/* Set handles. */
|
||||
event->readable_handle = read_handle;
|
||||
event->is_readable_handle_managed = read_handle_managed;
|
||||
event->writable_handle = write_handle;
|
||||
event->is_writable_handle_managed = write_handle_managed;
|
||||
|
||||
return ResultSuccess();
|
||||
/* Set auto clear. */
|
||||
event->auto_clear = (clear_mode == EventClearMode_AutoClear);
|
||||
|
||||
/* Create the waitlist node. */
|
||||
new (GetPointer(event->waitable_object_list_storage)) impl::WaitableObjectList;
|
||||
|
||||
/* Set state. */
|
||||
event->state = InterProcessEventType::State_Initialized;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
InterProcessEvent::InterProcessEvent(bool autoclear) : is_initialized(false) {
|
||||
R_ABORT_UNLESS(this->Initialize(autoclear));
|
||||
}
|
||||
|
||||
InterProcessEvent::~InterProcessEvent() {
|
||||
this->Finalize();
|
||||
}
|
||||
|
||||
Result InterProcessEvent::Initialize(bool autoclear) {
|
||||
AMS_ABORT_UNLESS(!this->is_initialized);
|
||||
Result CreateInterProcessEvent(InterProcessEventType *event, EventClearMode clear_mode) {
|
||||
Handle rh, wh;
|
||||
R_TRY(CreateEventHandles(&rh, &wh));
|
||||
this->Initialize(rh, true, wh, true, autoclear);
|
||||
R_TRY(impl::InterProcessEventImpl::Create(std::addressof(wh), std::addressof(rh)));
|
||||
|
||||
SetupInterProcessEventType(event, rh, true, wh, true, clear_mode);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void InterProcessEvent::Initialize(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) {
|
||||
AMS_ABORT_UNLESS(!this->is_initialized);
|
||||
AMS_ABORT_UNLESS(read_handle != INVALID_HANDLE || write_handle != INVALID_HANDLE);
|
||||
this->read_handle = read_handle;
|
||||
this->manage_read_handle = manage_read_handle;
|
||||
this->write_handle = write_handle;
|
||||
this->manage_write_handle = manage_write_handle;
|
||||
this->auto_clear = autoclear;
|
||||
this->is_initialized = true;
|
||||
void DestroyInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
|
||||
|
||||
/* Clear the state. */
|
||||
event->state = InterProcessEventType::State_NotInitialized;
|
||||
|
||||
/* Close handles if required. */
|
||||
if (event->is_readable_handle_managed) {
|
||||
if (event->readable_handle != svc::InvalidHandle) {
|
||||
impl::InterProcessEventImpl::Close(event->readable_handle);
|
||||
}
|
||||
event->is_readable_handle_managed = false;
|
||||
}
|
||||
|
||||
if (event->is_writable_handle_managed) {
|
||||
if (event->writable_handle != svc::InvalidHandle) {
|
||||
impl::InterProcessEventImpl::Close(event->writable_handle);
|
||||
}
|
||||
event->is_writable_handle_managed = false;
|
||||
}
|
||||
|
||||
/* Destroy the waitlist. */
|
||||
GetReference(event->waitable_object_list_storage).~WaitableObjectList();
|
||||
}
|
||||
|
||||
Handle InterProcessEvent::DetachReadableHandle() {
|
||||
AMS_ABORT_UNLESS(this->is_initialized);
|
||||
const Handle handle = this->read_handle;
|
||||
AMS_ABORT_UNLESS(handle != INVALID_HANDLE);
|
||||
this->read_handle = INVALID_HANDLE;
|
||||
this->manage_read_handle = false;
|
||||
void AttachInterProcessEvent(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode) {
|
||||
AMS_ASSERT(read_handle != svc::InvalidHandle || write_handle != svc::InvalidHandle);
|
||||
|
||||
return SetupInterProcessEventType(event, read_handle, read_handle_managed, write_handle, write_handle_managed, clear_mode);
|
||||
}
|
||||
|
||||
Handle DetachReadableHandleOfInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
|
||||
|
||||
const Handle handle = event->readable_handle;
|
||||
|
||||
event->readable_handle = svc::InvalidHandle;
|
||||
event->is_readable_handle_managed = false;
|
||||
|
||||
return handle;
|
||||
}
|
||||
Handle DetachWritableHandleOfInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
|
||||
|
||||
const Handle handle = event->writable_handle;
|
||||
|
||||
event->writable_handle = svc::InvalidHandle;
|
||||
event->is_writable_handle_managed = false;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
Handle InterProcessEvent::DetachWritableHandle() {
|
||||
AMS_ABORT_UNLESS(this->is_initialized);
|
||||
const Handle handle = this->write_handle;
|
||||
AMS_ABORT_UNLESS(handle != INVALID_HANDLE);
|
||||
this->write_handle = INVALID_HANDLE;
|
||||
this->manage_write_handle = false;
|
||||
return handle;
|
||||
void WaitInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
|
||||
|
||||
return impl::InterProcessEventImpl::Wait(event->readable_handle, event->auto_clear);
|
||||
}
|
||||
|
||||
Handle InterProcessEvent::GetReadableHandle() const {
|
||||
AMS_ABORT_UNLESS(this->is_initialized);
|
||||
return this->read_handle;
|
||||
bool TryWaitInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
|
||||
|
||||
return impl::InterProcessEventImpl::TryWait(event->readable_handle, event->auto_clear);
|
||||
}
|
||||
|
||||
Handle InterProcessEvent::GetWritableHandle() const {
|
||||
AMS_ABORT_UNLESS(this->is_initialized);
|
||||
return this->write_handle;
|
||||
bool TimedWaitInterProcessEvent(InterProcessEventType *event, TimeSpan timeout) {
|
||||
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
|
||||
AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
|
||||
|
||||
return impl::InterProcessEventImpl::TimedWait(event->readable_handle, event->auto_clear, timeout);
|
||||
}
|
||||
|
||||
void InterProcessEvent::Finalize() {
|
||||
if (this->is_initialized) {
|
||||
if (this->manage_read_handle && this->read_handle != INVALID_HANDLE) {
|
||||
R_ABORT_UNLESS(svcCloseHandle(this->read_handle));
|
||||
}
|
||||
if (this->manage_write_handle && this->write_handle != INVALID_HANDLE) {
|
||||
R_ABORT_UNLESS(svcCloseHandle(this->write_handle));
|
||||
}
|
||||
void SignalInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
|
||||
|
||||
return impl::InterProcessEventImpl::Signal(event->writable_handle);
|
||||
}
|
||||
|
||||
void ClearInterProcessEvent(InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
|
||||
|
||||
auto handle = event->readable_handle;
|
||||
if (handle == svc::InvalidHandle) {
|
||||
handle = event->writable_handle;
|
||||
}
|
||||
this->read_handle = INVALID_HANDLE;
|
||||
this->manage_read_handle = false;
|
||||
this->write_handle = INVALID_HANDLE;
|
||||
this->manage_write_handle = false;
|
||||
this->is_initialized = false;
|
||||
return impl::InterProcessEventImpl::Clear(handle);
|
||||
}
|
||||
|
||||
void InterProcessEvent::Signal() {
|
||||
R_ABORT_UNLESS(svcSignalEvent(this->GetWritableHandle()));
|
||||
Handle GetReadableHandleOfInterProcessEvent(const InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
|
||||
|
||||
return event->readable_handle;
|
||||
}
|
||||
|
||||
void InterProcessEvent::Reset() {
|
||||
Handle handle = this->GetReadableHandle();
|
||||
if (handle == INVALID_HANDLE) {
|
||||
handle = this->GetWritableHandle();
|
||||
}
|
||||
R_ABORT_UNLESS(svcClearEvent(handle));
|
||||
Handle GetWritableHandleOfInterProcessEvent(const InterProcessEventType *event) {
|
||||
AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
|
||||
|
||||
return event->writable_handle;
|
||||
}
|
||||
|
||||
void InterProcessEvent::Wait() {
|
||||
const Handle handle = this->GetReadableHandle();
|
||||
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, std::numeric_limits<u64>::max())) {
|
||||
R_CATCH(svc::ResultCancelled) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
/* Clear, if we must. */
|
||||
if (this->auto_clear) {
|
||||
R_TRY_CATCH(svcResetSignal(handle)) {
|
||||
/* Some other thread might have caught this before we did. */
|
||||
R_CATCH(svc::ResultInvalidState) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool InterProcessEvent::TryWait() {
|
||||
const Handle handle = this->GetReadableHandle();
|
||||
|
||||
if (this->auto_clear) {
|
||||
/* Auto-clear. Just try to reset. */
|
||||
return R_SUCCEEDED(svcResetSignal(handle));
|
||||
} else {
|
||||
/* Not auto-clear. */
|
||||
while (true) {
|
||||
/* Continuously wait, until success or timeout. */
|
||||
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, 0)) {
|
||||
R_CATCH(svc::ResultTimedOut) { return false; }
|
||||
R_CATCH(svc::ResultCancelled) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
/* We succeeded, so we're signaled. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InterProcessEvent::TimedWait(u64 ns) {
|
||||
const Handle handle = this->GetReadableHandle();
|
||||
|
||||
TimeoutHelper timeout_helper(ns);
|
||||
while (true) {
|
||||
/* Continuously wait, until success or timeout. */
|
||||
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, timeout_helper.NsUntilTimeout())) {
|
||||
R_CATCH(svc::ResultTimedOut) { return false; }
|
||||
R_CATCH(svc::ResultCancelled) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
/* Clear, if we must. */
|
||||
if (this->auto_clear) {
|
||||
R_TRY_CATCH(svcResetSignal(handle)) {
|
||||
/* Some other thread might have caught this before we did. */
|
||||
R_CATCH(svc::ResultInvalidState) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,37 +18,24 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class WaitableHolderOfInterProcessEvent;
|
||||
Result CreateInterProcessEvent(InterProcessEventType *event, EventClearMode clear_mode);
|
||||
void DestroyInterProcessEvent(InterProcessEventType *event);
|
||||
|
||||
class InterProcessEvent {
|
||||
friend class WaitableHolderOfInterProcessEvent;
|
||||
NON_COPYABLE(InterProcessEvent);
|
||||
NON_MOVEABLE(InterProcessEvent);
|
||||
private:
|
||||
Handle read_handle;
|
||||
Handle write_handle;
|
||||
bool manage_read_handle;
|
||||
bool manage_write_handle;
|
||||
bool auto_clear;
|
||||
bool is_initialized;
|
||||
public:
|
||||
InterProcessEvent() : is_initialized(false) { /* ... */ }
|
||||
InterProcessEvent(bool autoclear);
|
||||
~InterProcessEvent();
|
||||
void AttachInterProcessEvent(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode);
|
||||
|
||||
Result Initialize(bool autoclear = true);
|
||||
void Initialize(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true);
|
||||
Handle DetachReadableHandle();
|
||||
Handle DetachWritableHandle();
|
||||
Handle GetReadableHandle() const;
|
||||
Handle GetWritableHandle() const;
|
||||
void Finalize();
|
||||
Handle DetachReadableHandleOfInterProcessEvent(InterProcessEventType *event);
|
||||
Handle DetachWritableHandleOfInterProcessEvent(InterProcessEventType *event);
|
||||
|
||||
void Signal();
|
||||
void Reset();
|
||||
void Wait();
|
||||
bool TryWait();
|
||||
bool TimedWait(u64 ns);
|
||||
};
|
||||
void WaitInterProcessEvent(InterProcessEventType *event);
|
||||
bool TryWaitInterProcessEvent(InterProcessEventType *event);
|
||||
bool TimedWaitInterProcessEvent(InterProcessEventType *event, TimeSpan timeout);
|
||||
|
||||
void SignalInterProcessEvent(InterProcessEventType *event);
|
||||
void ClearInterProcessEvent(InterProcessEventType *event);
|
||||
|
||||
Handle GetReadableHandleOfInterProcessEvent(const InterProcessEventType *event);
|
||||
Handle GetWritableHandleOfInterProcessEvent(const InterProcessEventType *event);
|
||||
|
||||
void InitializeWaitableHolder(WaitableHolderType *waitable_holder, InterProcessEventType *event);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_inter_process_event_impl.os.horizon.hpp"
|
||||
#else
|
||||
#error "Unknown OS for ams::os::InterProcessEventImpl"
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_inter_process_event.hpp"
|
||||
#include "os_inter_process_event_impl.os.horizon.hpp"
|
||||
#include "os_timeout_helper.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
Result InterProcessEventImpl::Create(Handle *out_write, Handle *out_read) {
|
||||
/* Create the event handles. */
|
||||
svc::Handle wh, rh;
|
||||
R_TRY_CATCH(svc::CreateEvent(std::addressof(wh), std::addressof(rh))) {
|
||||
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
*out_write = wh;
|
||||
*out_read = rh;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void InterProcessEventImpl::Close(Handle handle) {
|
||||
if (handle != svc::InvalidHandle) {
|
||||
R_ABORT_UNLESS(svc::CloseHandle(svc::Handle(handle)));
|
||||
}
|
||||
}
|
||||
|
||||
void InterProcessEventImpl::Signal(Handle handle) {
|
||||
R_ABORT_UNLESS(svc::SignalEvent(svc::Handle(handle)));
|
||||
}
|
||||
|
||||
void InterProcessEventImpl::Clear(Handle handle) {
|
||||
R_ABORT_UNLESS(svc::ClearEvent(svc::Handle(handle)));
|
||||
}
|
||||
|
||||
void InterProcessEventImpl::Wait(Handle handle, bool auto_clear) {
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), reinterpret_cast<svc::Handle *>(std::addressof(handle)), 1, svc::WaitInfinite);
|
||||
if (R_SUCCEEDED(res)) {
|
||||
/* Clear, if we must. */
|
||||
if (auto_clear) {
|
||||
R_TRY_CATCH(svc::ResetSignal(svc::Handle(handle))) {
|
||||
/* Some other thread might have caught this before we did. */
|
||||
R_CATCH(svc::ResultInvalidState) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
AMS_ASSERT(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
bool InterProcessEventImpl::TryWait(Handle handle, bool auto_clear) {
|
||||
/* If we're auto clear, just try to reset. */
|
||||
if (auto_clear) {
|
||||
return R_SUCCEEDED(svc::ResetSignal(svc::Handle(handle)));
|
||||
}
|
||||
|
||||
/* Not auto-clear. */
|
||||
while (true) {
|
||||
/* Continuously wait, until success or timeout. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), reinterpret_cast<svc::Handle *>(std::addressof(handle)), 1, 0);
|
||||
|
||||
/* If we succeeded, we're signaled. */
|
||||
if (R_SUCCEEDED(res)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If we timed out, we're not signaled. */
|
||||
if (svc::ResultTimedOut::Includes(res)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AMS_ASSERT(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
bool InterProcessEventImpl::TimedWait(Handle handle, bool auto_clear, TimeSpan timeout) {
|
||||
TimeoutHelper timeout_helper(timeout);
|
||||
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), reinterpret_cast<svc::Handle *>(std::addressof(handle)), 1, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds());
|
||||
if (R_SUCCEEDED(res)) {
|
||||
/* Clear, if we must. */
|
||||
if (auto_clear) {
|
||||
R_TRY_CATCH(svc::ResetSignal(svc::Handle(handle))) {
|
||||
/* Some other thread might have caught this before we did. */
|
||||
R_CATCH(svc::ResultInvalidState) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (svc::ResultTimedOut::Includes(res)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AMS_ASSERT(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InterProcessEventImpl {
|
||||
public:
|
||||
static Result Create(Handle *out_write, Handle *out_read);
|
||||
static void Close(Handle handle);
|
||||
static void Signal(Handle handle);
|
||||
static void Clear(Handle handle);
|
||||
static void Wait(Handle handle, bool auto_clear);
|
||||
static bool TryWait(Handle handle, bool auto_clear);
|
||||
static bool TimedWait(Handle handle, bool auto_clear, TimeSpan timeout);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_timeout_helper.hpp"
|
||||
#include "os_thread_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void InternalConditionVariableImpl::Signal() {
|
||||
ams::svc::SignalProcessWideKey(reinterpret_cast<uintptr_t>(std::addressof(this->value)), 1);
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Broadcast() {
|
||||
ams::svc::SignalProcessWideKey(reinterpret_cast<uintptr_t>(std::addressof(this->value)), -1);
|
||||
}
|
||||
|
||||
void InternalConditionVariableImpl::Wait(InternalCriticalSection *cs) {
|
||||
const auto cur_handle = GetCurrentThreadHandle();
|
||||
AMS_ASSERT((cs->Get()->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle);
|
||||
|
||||
R_ABORT_UNLESS(ams::svc::WaitProcessWideKeyAtomic(reinterpret_cast<uintptr_t>(std::addressof(cs->Get()->thread_handle)), reinterpret_cast<uintptr_t>(std::addressof(this->value)), cur_handle, -1));
|
||||
}
|
||||
|
||||
ConditionVariableStatus InternalConditionVariableImpl::TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper) {
|
||||
const auto cur_handle = GetCurrentThreadHandle();
|
||||
AMS_ASSERT((cs->Get()->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle);
|
||||
|
||||
const TimeSpan left = timeout_helper.GetTimeLeftOnTarget();
|
||||
if (left > 0) {
|
||||
R_TRY_CATCH(ams::svc::WaitProcessWideKeyAtomic(reinterpret_cast<uintptr_t>(std::addressof(cs->Get()->thread_handle)), reinterpret_cast<uintptr_t>(std::addressof(this->value)), cur_handle, left.GetNanoSeconds())) {
|
||||
R_CATCH(svc::ResultTimedOut) {
|
||||
cs->Enter();
|
||||
return ConditionVariableStatus::TimedOut;
|
||||
}
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
return ConditionVariableStatus::Success;
|
||||
} else {
|
||||
return ConditionVariableStatus::TimedOut;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_timeout_helper.hpp"
|
||||
#include "os_thread_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE void DataMemoryBarrierForCriticalSection() {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* ... */
|
||||
#else
|
||||
#error "Unknown architecture for os::impl::InternalCriticalSectionImpl DataMemoryBarrier"
|
||||
#endif
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 LoadExclusive(u32 *ptr) {
|
||||
u32 value;
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
__asm__ __volatile__("ldaxr %w[value], [%[ptr]]" : [value]"=&r"(value) : [ptr]"r"(ptr) : "memory");
|
||||
#else
|
||||
#error "Unknown architecture for os::impl::InternalCriticalSectionImpl LoadExclusive"
|
||||
#endif
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE int StoreExclusive(u32 *ptr, u32 value) {
|
||||
int result;
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
__asm__ __volatile__("stlxr %w[result], %w[value], [%[ptr]]" : [result]"=&r"(result) : [value]"r"(value), [ptr]"r"(ptr) : "memory");
|
||||
#else
|
||||
#error "Unknown architecture for os::impl::InternalCriticalSectionImpl StoreExclusive"
|
||||
#endif
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void ClearExclusive() {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
__asm__ __volatile__("clrex" ::: "memory");
|
||||
#else
|
||||
#error "Unknown architecture for os::impl::InternalCriticalSectionImpl ClearExclusive"
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InternalCriticalSectionImpl::Enter() {
|
||||
AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0);
|
||||
|
||||
const auto cur_handle = GetCurrentThreadHandle();
|
||||
AMS_ASSERT((this->thread_handle & ~ams::svc::HandleWaitMask) != cur_handle);
|
||||
|
||||
u32 value = LoadExclusive(std::addressof(this->thread_handle));
|
||||
while (true) {
|
||||
if (AMS_LIKELY(value == svc::InvalidHandle)) {
|
||||
if (AMS_UNLIKELY(StoreExclusive(std::addressof(this->thread_handle), cur_handle) != 0)) {
|
||||
value = LoadExclusive(std::addressof(this->thread_handle));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (AMS_LIKELY((value & ams::svc::HandleWaitMask) == 0)) {
|
||||
if (AMS_UNLIKELY(StoreExclusive(std::addressof(this->thread_handle), value | ams::svc::HandleWaitMask) != 0)) {
|
||||
value = LoadExclusive(std::addressof(this->thread_handle));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
R_ABORT_UNLESS(ams::svc::ArbitrateLock(value & ~ams::svc::HandleWaitMask, reinterpret_cast<uintptr_t>(std::addressof(this->thread_handle)), cur_handle));
|
||||
|
||||
value = LoadExclusive(std::addressof(this->thread_handle));
|
||||
if (AMS_LIKELY((value & ~ams::svc::HandleWaitMask) == cur_handle)) {
|
||||
ClearExclusive();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DataMemoryBarrierForCriticalSection();
|
||||
}
|
||||
|
||||
bool InternalCriticalSectionImpl::TryEnter() {
|
||||
AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0);
|
||||
|
||||
const auto cur_handle = GetCurrentThreadHandle();
|
||||
|
||||
while (true) {
|
||||
u32 value = LoadExclusive(std::addressof(this->thread_handle));
|
||||
if (AMS_UNLIKELY(value != svc::InvalidHandle)) {
|
||||
break;
|
||||
}
|
||||
|
||||
DataMemoryBarrierForCriticalSection();
|
||||
|
||||
if (AMS_LIKELY(StoreExclusive(std::addressof(this->thread_handle), cur_handle) == 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ClearExclusive();
|
||||
DataMemoryBarrierForCriticalSection();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InternalCriticalSectionImpl::Leave() {
|
||||
AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0);
|
||||
|
||||
const auto cur_handle = GetCurrentThreadHandle();
|
||||
u32 value = LoadExclusive(std::addressof(this->thread_handle));
|
||||
|
||||
while (true) {
|
||||
if (AMS_UNLIKELY(value != cur_handle)) {
|
||||
ClearExclusive();
|
||||
break;
|
||||
}
|
||||
|
||||
DataMemoryBarrierForCriticalSection();
|
||||
|
||||
if (AMS_LIKELY(StoreExclusive(std::addressof(this->thread_handle), 0) == 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
value = LoadExclusive(std::addressof(this->thread_handle));
|
||||
}
|
||||
|
||||
DataMemoryBarrierForCriticalSection();
|
||||
|
||||
AMS_ASSERT((value | ams::svc::HandleWaitMask) == (cur_handle | ams::svc::HandleWaitMask));
|
||||
if (value & ams::svc::HandleWaitMask) {
|
||||
R_ABORT_UNLESS(ams::svc::ArbitrateUnlock(reinterpret_cast<uintptr_t>(std::addressof(this->thread_handle))));
|
||||
}
|
||||
}
|
||||
|
||||
bool InternalCriticalSectionImpl::IsLockedByCurrentThread() const {
|
||||
const auto cur_handle = GetCurrentThreadHandle();
|
||||
return (this->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_interrupt_event_target_impl.os.horizon.hpp"
|
||||
#else
|
||||
#error "Unknown OS for ams::os::InterruptEventImpl"
|
||||
#endif
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InterruptEventImpl {
|
||||
private:
|
||||
InterruptEventTargetImpl impl;
|
||||
public:
|
||||
explicit InterruptEventImpl(InterruptName name, EventClearMode clear_mode) : impl(name, clear_mode) { /* ... */ }
|
||||
|
||||
void Clear() {
|
||||
return this->impl.Clear();
|
||||
}
|
||||
|
||||
void Wait() {
|
||||
return this->impl.Wait();
|
||||
}
|
||||
|
||||
bool TryWait() {
|
||||
return this->impl.TryWait();
|
||||
}
|
||||
|
||||
bool TimedWait(TimeSpan timeout) {
|
||||
return this->impl.TimedWait(timeout);
|
||||
}
|
||||
|
||||
TriBool IsSignaled() {
|
||||
return this->impl.IsSignaled();
|
||||
}
|
||||
|
||||
Handle GetHandle() const {
|
||||
return this->impl.GetHandle();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_interrupt_event_target_impl.os.horizon.hpp"
|
||||
#include "os_timeout_helper.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
InterruptEventHorizonImpl::InterruptEventHorizonImpl(InterruptName name, EventClearMode clear_mode) {
|
||||
this->manual_clear = (clear_mode == EventClearMode_ManualClear);
|
||||
|
||||
auto interrupt_type = this->manual_clear ? svc::InterruptType_Level : svc::InterruptType_Edge;
|
||||
svc::Handle handle;
|
||||
R_ABORT_UNLESS(svc::CreateInterruptEvent(std::addressof(handle), static_cast<s32>(name), interrupt_type));
|
||||
|
||||
this->handle = handle;
|
||||
}
|
||||
|
||||
InterruptEventHorizonImpl::~InterruptEventHorizonImpl() {
|
||||
R_ABORT_UNLESS(svc::CloseHandle(this->handle));
|
||||
}
|
||||
|
||||
void InterruptEventHorizonImpl::Clear() {
|
||||
R_ABORT_UNLESS(svc::ClearEvent(this->handle));
|
||||
}
|
||||
|
||||
void InterruptEventHorizonImpl::Wait() {
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(this->handle), 1, svc::WaitInfinite);
|
||||
if (R_SUCCEEDED(res)) {
|
||||
/* Clear, if we must. */
|
||||
if (!this->manual_clear) {
|
||||
R_TRY_CATCH(svc::ResetSignal(this->handle)) {
|
||||
/* Some other thread might have caught this before we did. */
|
||||
R_CATCH(svc::ResultInvalidState) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
AMS_ASSERT(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
bool InterruptEventHorizonImpl::TryWait() {
|
||||
/* If we're auto clear, just try to reset. */
|
||||
if (!this->manual_clear) {
|
||||
return R_SUCCEEDED(svc::ResetSignal(this->handle));
|
||||
}
|
||||
|
||||
/* Not auto-clear. */
|
||||
while (true) {
|
||||
/* Continuously wait, until success or timeout. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(this->handle), 1, 0);
|
||||
|
||||
/* If we succeeded, we're signaled. */
|
||||
if (R_SUCCEEDED(res)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If we timed out, we're not signaled. */
|
||||
if (svc::ResultTimedOut::Includes(res)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AMS_ASSERT(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
bool InterruptEventHorizonImpl::TimedWait(TimeSpan timeout) {
|
||||
TimeoutHelper timeout_helper(timeout);
|
||||
|
||||
while (true) {
|
||||
/* Continuously wait, until success. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(this->handle), 1, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds());
|
||||
if (R_SUCCEEDED(res)) {
|
||||
/* Clear, if we must. */
|
||||
if (!this->manual_clear) {
|
||||
R_TRY_CATCH(svc::ResetSignal(this->handle)) {
|
||||
/* Some other thread might have caught this before we did. */
|
||||
R_CATCH(svc::ResultInvalidState) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (svc::ResultTimedOut::Includes(res)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AMS_ASSERT(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class InterruptEventHorizonImpl {
|
||||
private:
|
||||
svc::Handle handle;
|
||||
bool manual_clear;
|
||||
public:
|
||||
explicit InterruptEventHorizonImpl(InterruptName name, EventClearMode mode);
|
||||
~InterruptEventHorizonImpl();
|
||||
|
||||
void Clear();
|
||||
void Wait();
|
||||
bool TryWait();
|
||||
bool TimedWait(TimeSpan timeout);
|
||||
|
||||
TriBool IsSignaled() {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
Handle GetHandle() const {
|
||||
return this->handle;
|
||||
}
|
||||
};
|
||||
|
||||
using InterruptEventTargetImpl = InterruptEventHorizonImpl;
|
||||
|
||||
}
|
||||
24
libraries/libstratosphere/source/os/impl/os_mutex_impl.hpp
Normal file
24
libraries/libstratosphere/source/os/impl/os_mutex_impl.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void PushAndCheckLockLevel(MutexType *mutex);
|
||||
void PopAndCheckLockLevel(MutexType *mutex);
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_rng_manager_impl.hpp"
|
||||
#include "os_thread_manager_types.hpp"
|
||||
#include "os_tick_manager_impl.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
@@ -24,12 +25,15 @@ namespace ams::os::impl {
|
||||
private:
|
||||
RngManager rng_manager{};
|
||||
/* TODO */
|
||||
ThreadManager thread_manager{};
|
||||
/* TODO */
|
||||
TickManager tick_manager{};
|
||||
/* TODO */
|
||||
public:
|
||||
constexpr OsResourceManager() = default;
|
||||
OsResourceManager() = default;
|
||||
|
||||
constexpr ALWAYS_INLINE RngManager &GetRngManager() { return this->rng_manager; }
|
||||
constexpr ALWAYS_INLINE ThreadManager &GetThreadManager() { return this->thread_manager; }
|
||||
constexpr ALWAYS_INLINE TickManager &GetTickManager() { return this->tick_manager; }
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_resource_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace ams::os::impl {
|
||||
private:
|
||||
void Initialize();
|
||||
public:
|
||||
constexpr RngManager() : mt(), lock(), initialized() { /* ... */ }
|
||||
constexpr RngManager() : mt(), lock(false), initialized() { /* ... */ }
|
||||
public:
|
||||
u64 GenerateRandomU64();
|
||||
};
|
||||
|
||||
226
libraries/libstratosphere/source/os/impl/os_thread_manager.cpp
Normal file
226
libraries/libstratosphere/source/os/impl/os_thread_manager.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_thread_manager.hpp"
|
||||
#include "os_waitable_manager_impl.hpp"
|
||||
#include "os_waitable_holder_base.hpp"
|
||||
#include "os_waitable_holder_impl.hpp"
|
||||
#include "os_waitable_object_list.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void SetupThreadObjectUnsafe(ThreadType *thread, ThreadImpl *thread_impl, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority) {
|
||||
/* Setup objects. */
|
||||
new (GetPointer(thread->cs_thread)) impl::InternalCriticalSection;
|
||||
new (GetPointer(thread->cv_thread)) impl::InternalConditionVariable;
|
||||
|
||||
new (GetPointer(thread->all_threads_node)) util::IntrusiveListNode;
|
||||
new (GetPointer(thread->waitlist)) WaitableObjectList;
|
||||
|
||||
/* Set member variables. */
|
||||
thread->thread_impl = (thread_impl != nullptr) ? thread_impl : std::addressof(thread->thread_impl_storage);
|
||||
thread->function = function;
|
||||
thread->argument = arg;
|
||||
thread->stack = stack;
|
||||
thread->stack_size = stack_size;
|
||||
thread->base_priority = priority;
|
||||
thread->suspend_count = 0;
|
||||
thread->name_buffer[0] = '\x00';
|
||||
thread->name_pointer = thread->name_buffer;
|
||||
|
||||
/* Mark initialized. */
|
||||
thread->state = ThreadType::State_Initialized;
|
||||
}
|
||||
|
||||
void ThreadManager::InvokeThread(ThreadType *thread) {
|
||||
auto &manager = GetThreadManager();
|
||||
|
||||
manager.SetCurrentThread(thread);
|
||||
manager.NotifyThreadNameChanged(thread);
|
||||
|
||||
{
|
||||
std::unique_lock lk(GetReference(thread->cs_thread));
|
||||
while (thread->state == ThreadType::State_Initialized) {
|
||||
GetReference(thread->cv_thread).Wait(GetPointer(thread->cs_thread));
|
||||
}
|
||||
|
||||
if (thread->state == ThreadType::State_Started) {
|
||||
lk.unlock();
|
||||
|
||||
thread->function(thread->argument);
|
||||
}
|
||||
}
|
||||
|
||||
manager.CleanupThread();
|
||||
}
|
||||
|
||||
ThreadManager::ThreadManager() : impl(std::addressof(main_thread)), total_thread_stack_size(0), num_created_threads(0) {
|
||||
this->main_thread.state = ThreadType::State_Started;
|
||||
|
||||
this->SetCurrentThread(std::addressof(this->main_thread));
|
||||
|
||||
this->PlaceThreadObjectUnderThreadManagerSafe(std::addressof(this->main_thread));
|
||||
}
|
||||
|
||||
void ThreadManager::CleanupThread() {
|
||||
ThreadType *thread = this->GetCurrentThread();
|
||||
|
||||
{
|
||||
std::scoped_lock lk(GetReference(thread->cs_thread));
|
||||
|
||||
thread->state = ThreadType::State_Terminated;
|
||||
|
||||
GetReference(thread->cv_thread).Broadcast();
|
||||
GetReference(thread->waitlist).SignalAllThreads();
|
||||
}
|
||||
}
|
||||
|
||||
Result ThreadManager::CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority, s32 ideal_core) {
|
||||
SetupThreadObjectUnsafe(thread, nullptr, function, argument, stack, stack_size, priority);
|
||||
|
||||
auto guard = SCOPE_GUARD { thread->state = ThreadType::State_NotInitialized; };
|
||||
R_TRY(this->impl.CreateThread(thread, ideal_core));
|
||||
guard.Cancel();
|
||||
|
||||
this->PlaceThreadObjectUnderThreadManagerSafe(thread);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ThreadManager::CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority) {
|
||||
return this->CreateThread(thread, function, argument, stack, stack_size, priority, this->impl.GetDefaultCoreNumber());
|
||||
}
|
||||
|
||||
void ThreadManager::DestroyThread(ThreadType *thread) {
|
||||
{
|
||||
std::scoped_lock lk(GetReference(thread->cs_thread));
|
||||
|
||||
if (thread->state == ThreadType::State_Initialized) {
|
||||
thread->state = ThreadType::State_DestroyedBeforeStarted;
|
||||
this->impl.StartThread(thread);
|
||||
GetReference(thread->cv_thread).Signal();
|
||||
}
|
||||
}
|
||||
|
||||
this->impl.WaitForThreadExit(thread);
|
||||
|
||||
AMS_ASSERT(thread->state == ThreadType::State_Initialized);
|
||||
|
||||
{
|
||||
std::scoped_lock lk(GetReference(thread->cs_thread));
|
||||
|
||||
/* NOTE: Here Nintendo would cleanup the alias stack. */
|
||||
|
||||
this->impl.DestroyThreadUnsafe(thread);
|
||||
|
||||
thread->state = ThreadType::State_NotInitialized;
|
||||
|
||||
GetReference(thread->waitlist).~WaitableObjectList();
|
||||
|
||||
thread->name_buffer[0] = '\x00';
|
||||
|
||||
{
|
||||
std::scoped_lock tlk(this->cs);
|
||||
this->EraseFromAllThreadsListUnsafe(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadManager::StartThread(ThreadType *thread) {
|
||||
std::scoped_lock lk(GetReference(thread->cs_thread));
|
||||
|
||||
AMS_ASSERT(thread->state == ThreadType::State_Initialized);
|
||||
|
||||
this->impl.StartThread(thread);
|
||||
thread->state = ThreadType::State_Started;
|
||||
|
||||
GetReference(thread->cv_thread).Signal();
|
||||
}
|
||||
|
||||
void ThreadManager::WaitThread(ThreadType *thread) {
|
||||
this->impl.WaitForThreadExit(thread);
|
||||
|
||||
{
|
||||
std::scoped_lock lk(GetReference(thread->cs_thread));
|
||||
|
||||
/* Note: Here Nintendo would cleanup the alias stack. */
|
||||
}
|
||||
}
|
||||
|
||||
bool ThreadManager::TryWaitThread(ThreadType *thread) {
|
||||
const bool result = this->impl.TryWaitForThreadExit(thread);
|
||||
|
||||
if (result) {
|
||||
std::scoped_lock lk(GetReference(thread->cs_thread));
|
||||
|
||||
/* Note: Here Nintendo would cleanup the alias stack. */
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 ThreadManager::SuspendThread(ThreadType *thread) {
|
||||
std::scoped_lock lk(GetReference(thread->cs_thread));
|
||||
|
||||
auto prev_suspend_count = thread->suspend_count;
|
||||
AMS_ASSERT(prev_suspend_count < ThreadSuspendCountMax);
|
||||
thread->suspend_count = prev_suspend_count + 1;
|
||||
|
||||
if (prev_suspend_count == 0) {
|
||||
this->impl.SuspendThreadUnsafe(thread);
|
||||
}
|
||||
return prev_suspend_count;
|
||||
}
|
||||
|
||||
s32 ThreadManager::ResumeThread(ThreadType *thread) {
|
||||
std::scoped_lock lk(GetReference(thread->cs_thread));
|
||||
|
||||
auto prev_suspend_count = thread->suspend_count;
|
||||
if (prev_suspend_count > 0) {
|
||||
thread->suspend_count = prev_suspend_count - 1;
|
||||
if (prev_suspend_count == 1) {
|
||||
this->impl.ResumeThreadUnsafe(thread);
|
||||
}
|
||||
}
|
||||
return prev_suspend_count;
|
||||
}
|
||||
|
||||
void ThreadManager::CancelThreadSynchronization(ThreadType *thread) {
|
||||
std::scoped_lock lk(GetReference(thread->cs_thread));
|
||||
|
||||
this->impl.CancelThreadSynchronizationUnsafe(thread);
|
||||
}
|
||||
|
||||
/* TODO void ThreadManager::GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */
|
||||
|
||||
void ThreadManager::SetInitialThreadNameUnsafe(ThreadType *thread) {
|
||||
if (thread == std::addressof(this->main_thread)) {
|
||||
constexpr const char MainThreadName[] = "MainThread";
|
||||
static_assert(sizeof(thread->name_buffer) >= sizeof(MainThreadName));
|
||||
static_assert(MainThreadName[sizeof(MainThreadName) - 1] == '\x00');
|
||||
std::memcpy(thread->name_buffer, MainThreadName, sizeof(MainThreadName));
|
||||
} else {
|
||||
constexpr const char ThreadNamePrefix[] = "Thread_0x";
|
||||
constexpr size_t ThreadNamePrefixSize = sizeof(ThreadNamePrefix) - 1;
|
||||
const u64 func = reinterpret_cast<u64>(thread->function);
|
||||
static_assert(ThreadNamePrefixSize + sizeof(func) * 2 + 1 <= sizeof(thread->name_buffer));
|
||||
std::snprintf(thread->name_buffer, sizeof(thread->name_buffer), "%s%016lX", ThreadNamePrefix, func);
|
||||
}
|
||||
|
||||
thread->name_pointer = thread->name_buffer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_thread_manager_types.hpp"
|
||||
#include "os_resource_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
constexpr inline s32 CoreAffinityMaskBitWidth = BITSIZEOF(u64);
|
||||
|
||||
ALWAYS_INLINE ThreadManager &GetThreadManager() {
|
||||
return GetResourceManager().GetThreadManager();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ThreadType *GetCurrentThread() {
|
||||
return GetThreadManager().GetCurrentThread();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Handle GetCurrentThreadHandle() {
|
||||
/* return GetCurrentThread()->thread_impl->handle; */
|
||||
return ::threadGetCurHandle();
|
||||
}
|
||||
|
||||
void SetupThreadObjectUnsafe(ThreadType *thread, ThreadImpl *thread_impl, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_thread_manager_impl.os.horizon.hpp"
|
||||
#include "os_thread_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
thread_local ThreadType *g_current_thread_pointer;
|
||||
|
||||
namespace {
|
||||
|
||||
s32 ConvertToHorizonPriority(s32 user_priority) {
|
||||
const s32 horizon_priority = user_priority + UserThreadPriorityOffset;
|
||||
AMS_ASSERT(HighestTargetThreadPriority <= horizon_priority && horizon_priority <= LowestTargetThreadPriority);
|
||||
return horizon_priority;
|
||||
}
|
||||
|
||||
s32 ConvertToUserPriority(s32 horizon_priority) {
|
||||
AMS_ASSERT(HighestTargetThreadPriority <= horizon_priority && horizon_priority <= LowestTargetThreadPriority);
|
||||
return horizon_priority - UserThreadPriorityOffset;
|
||||
}
|
||||
|
||||
void InvokeThread(uintptr_t _thread) {
|
||||
ThreadType *thread = reinterpret_cast<ThreadType *>(_thread);
|
||||
|
||||
/* Set the thread's id. */
|
||||
u64 thread_id;
|
||||
R_ABORT_UNLESS(svc::GetThreadId(std::addressof(thread_id), svc::Handle(thread->thread_impl->handle)));
|
||||
thread->thread_id = thread_id;
|
||||
|
||||
/* Invoke the thread. */
|
||||
ThreadManager::InvokeThread(thread);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ThreadManagerHorizonImpl::ThreadManagerHorizonImpl(ThreadType *main_thread) {
|
||||
/* Get the thread impl object from libnx. */
|
||||
ThreadImpl *thread_impl = ::threadGetSelf();
|
||||
|
||||
/* Get the thread priority. */
|
||||
s32 horizon_priority;
|
||||
R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(horizon_priority), thread_impl->handle));
|
||||
|
||||
SetupThreadObjectUnsafe(main_thread, thread_impl, nullptr, nullptr, thread_impl->stack_mirror, thread_impl->stack_sz, ConvertToUserPriority(horizon_priority));
|
||||
|
||||
/* Set the thread id. */
|
||||
u64 thread_id;
|
||||
R_ABORT_UNLESS(svc::GetThreadId(std::addressof(thread_id), svc::Handle(thread_impl->handle)));
|
||||
main_thread->thread_id = thread_id;
|
||||
|
||||
/* NOTE: Here Nintendo would set the thread pointer in TLS. */
|
||||
}
|
||||
|
||||
Result ThreadManagerHorizonImpl::CreateThread(ThreadType *thread, s32 ideal_core) {
|
||||
/* Note: Here Nintendo would set the stack args to point to ThreadManager::InvokeThread. */
|
||||
|
||||
s32 count = 0;
|
||||
while (true) {
|
||||
R_TRY_CATCH(::threadCreate(thread->thread_impl, reinterpret_cast<::ThreadFunc>(&InvokeThread), thread, thread->stack, thread->stack_size, ConvertToHorizonPriority(thread->base_priority), ideal_core)) {
|
||||
R_CATCH(svc::ResultOutOfResource) {
|
||||
if ((++count) < 10) {
|
||||
os::SleepThread(TimeSpan::FromMilliSeconds(10));
|
||||
continue;
|
||||
}
|
||||
return os::ResultOutOfResource();
|
||||
}
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadManagerHorizonImpl::DestroyThreadUnsafe(ThreadType *thread) {
|
||||
R_ABORT_UNLESS(::threadClose(thread->thread_impl));
|
||||
}
|
||||
|
||||
void ThreadManagerHorizonImpl::StartThread(const ThreadType *thread) {
|
||||
R_ABORT_UNLESS(::threadStart(thread->thread_impl));
|
||||
}
|
||||
|
||||
void ThreadManagerHorizonImpl::WaitForThreadExit(ThreadType *thread) {
|
||||
const svc::Handle handle(thread->thread_impl->handle);
|
||||
|
||||
while (true) {
|
||||
s32 index;
|
||||
R_TRY_CATCH(svc::WaitSynchronization(std::addressof(index), std::addressof(handle), 1, svc::WaitInfinite)) {
|
||||
R_CATCH(svc::ResultCancelled) { continue; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool ThreadManagerHorizonImpl::TryWaitForThreadExit(ThreadType *thread) {
|
||||
const svc::Handle handle(thread->thread_impl->handle);
|
||||
|
||||
while (true) {
|
||||
/* Continuously wait, until success or timeout. */
|
||||
s32 index;
|
||||
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(handle), 1, 0);
|
||||
|
||||
/* If we succeeded, we're signaled. */
|
||||
if (R_SUCCEEDED(res)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If we timed out, we're not signaled. */
|
||||
if (svc::ResultTimedOut::Includes(res)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AMS_ABORT_UNLESS(svc::ResultCancelled::Includes(res));
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadManagerHorizonImpl::YieldThread() {
|
||||
if (hos::GetVersion() >= hos::Version_400) {
|
||||
svc::SleepThread(svc::YieldType_WithCoreMigration);
|
||||
} else {
|
||||
svc::SleepThread(svc::YieldType_WithoutCoreMigration);
|
||||
}
|
||||
}
|
||||
|
||||
bool ThreadManagerHorizonImpl::ChangePriority(ThreadType *thread, s32 priority) {
|
||||
const svc::Handle handle(thread->thread_impl->handle);
|
||||
|
||||
auto res = svc::SetThreadPriority(handle, ConvertToHorizonPriority(priority));
|
||||
if (svc::ResultInvalidPriority::Includes(res)) {
|
||||
AMS_ABORT("Invalid thread priority");
|
||||
}
|
||||
|
||||
return R_SUCCEEDED(res);
|
||||
}
|
||||
|
||||
s32 ThreadManagerHorizonImpl::GetCurrentPriority(const ThreadType *thread) const {
|
||||
const svc::Handle handle(thread->thread_impl->handle);
|
||||
s32 priority;
|
||||
|
||||
R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(priority), handle));
|
||||
|
||||
return ConvertToUserPriority(priority);
|
||||
}
|
||||
|
||||
ThreadId ThreadManagerHorizonImpl::GetThreadId(const ThreadType *thread) const {
|
||||
return thread->thread_id;
|
||||
}
|
||||
|
||||
void ThreadManagerHorizonImpl::SuspendThreadUnsafe(ThreadType *thread) {
|
||||
const svc::Handle handle(thread->thread_impl->handle);
|
||||
|
||||
R_ABORT_UNLESS(svc::SetThreadActivity(handle, svc::ThreadActivity_Paused));
|
||||
}
|
||||
|
||||
void ThreadManagerHorizonImpl::ResumeThreadUnsafe(ThreadType *thread) {
|
||||
const svc::Handle handle(thread->thread_impl->handle);
|
||||
|
||||
R_ABORT_UNLESS(svc::SetThreadActivity(handle, svc::ThreadActivity_Runnable));
|
||||
}
|
||||
|
||||
void ThreadManagerHorizonImpl::CancelThreadSynchronizationUnsafe(ThreadType *thread) {
|
||||
const svc::Handle handle(thread->thread_impl->handle);
|
||||
|
||||
R_ABORT_UNLESS(svc::CancelSynchronization(handle));
|
||||
}
|
||||
|
||||
/* TODO: void GetThreadContextUnsafe(ThreadContextInfo *out_context, const ThreadType *thread); */
|
||||
|
||||
s32 ThreadManagerHorizonImpl::GetCurrentCoreNumber() const {
|
||||
return svc::GetCurrentProcessorNumber();
|
||||
}
|
||||
|
||||
void ThreadManagerHorizonImpl::SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const {
|
||||
const svc::Handle handle(thread->thread_impl->handle);
|
||||
R_ABORT_UNLESS(svc::SetThreadCoreMask(handle, ideal_core, affinity_mask));
|
||||
}
|
||||
|
||||
void ThreadManagerHorizonImpl::GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const {
|
||||
s32 ideal_core;
|
||||
u64 affinity_mask;
|
||||
|
||||
const svc::Handle handle(thread->thread_impl->handle);
|
||||
R_ABORT_UNLESS(svc::GetThreadCoreMask(std::addressof(ideal_core), std::addressof(affinity_mask), handle));
|
||||
|
||||
if (out_ideal_core) {
|
||||
*out_ideal_core = ideal_core;
|
||||
}
|
||||
if (out_affinity_mask) {
|
||||
*out_affinity_mask = affinity_mask;
|
||||
}
|
||||
}
|
||||
|
||||
u64 ThreadManagerHorizonImpl::GetThreadAvailableCoreMask() const {
|
||||
u64 core_mask;
|
||||
R_ABORT_UNLESS(svc::GetInfo(std::addressof(core_mask), svc::InfoType_CoreMask, svc::PseudoHandle::CurrentProcess, 0));
|
||||
return core_mask;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
constexpr inline s32 TargetThreadPriorityRangeSize = svc::LowestThreadPriority - svc::HighestThreadPriority + 1;
|
||||
constexpr inline s32 ReservedThreadPriorityRangeSize = svc::SystemThreadPriorityHighest;
|
||||
constexpr inline s32 SystemThreadPriorityRangeSize = TargetThreadPriorityRangeSize - ReservedThreadPriorityRangeSize;
|
||||
constexpr inline s32 UserThreadPriorityOffset = 28;
|
||||
|
||||
constexpr inline s32 HighestTargetThreadPriority = 0;
|
||||
constexpr inline s32 LowestTargetThreadPriority = TargetThreadPriorityRangeSize - 1;
|
||||
|
||||
extern thread_local ThreadType *g_current_thread_pointer;
|
||||
|
||||
class ThreadManagerHorizonImpl {
|
||||
NON_COPYABLE(ThreadManagerHorizonImpl);
|
||||
NON_MOVEABLE(ThreadManagerHorizonImpl);
|
||||
public:
|
||||
explicit ThreadManagerHorizonImpl(ThreadType *main_thread);
|
||||
|
||||
Result CreateThread(ThreadType *thread, s32 ideal_core);
|
||||
void DestroyThreadUnsafe(ThreadType *thread);
|
||||
void StartThread(const ThreadType *thread);
|
||||
void WaitForThreadExit(ThreadType *thread);
|
||||
bool TryWaitForThreadExit(ThreadType *thread);
|
||||
void YieldThread();
|
||||
bool ChangePriority(ThreadType *thread, s32 priority);
|
||||
s32 GetCurrentPriority(const ThreadType *thread) const;
|
||||
ThreadId GetThreadId(const ThreadType *thread) const;
|
||||
|
||||
void SuspendThreadUnsafe(ThreadType *thread);
|
||||
void ResumeThreadUnsafe(ThreadType *thread);
|
||||
|
||||
void CancelThreadSynchronizationUnsafe(ThreadType *thread);
|
||||
|
||||
/* TODO: void GetThreadContextUnsafe(ThreadContextInfo *out_context, const ThreadType *thread); */
|
||||
|
||||
void NotifyThreadNameChangedImpl(const ThreadType *thread) const { /* ... */ }
|
||||
|
||||
void SetCurrentThread(ThreadType *thread) const {
|
||||
g_current_thread_pointer = thread;
|
||||
}
|
||||
|
||||
ThreadType *GetCurrentThread() const {
|
||||
return g_current_thread_pointer;
|
||||
}
|
||||
|
||||
s32 GetCurrentCoreNumber() const;
|
||||
s32 GetDefaultCoreNumber() const { return svc::IdealCoreUseProcessValue; }
|
||||
|
||||
void SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const;
|
||||
void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const;
|
||||
u64 GetThreadAvailableCoreMask() const;
|
||||
|
||||
NORETURN void ExitProcessImpl() {
|
||||
svc::ExitProcess();
|
||||
AMS_ABORT("Process was exited");
|
||||
}
|
||||
};
|
||||
|
||||
using ThreadManagerImpl = ThreadManagerHorizonImpl;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#ifdef ATMOSPHERE_OS_HORIZON
|
||||
#include "os_thread_manager_impl.os.horizon.hpp"
|
||||
#else
|
||||
#error "Unknown OS for ThreadManagerImpl"
|
||||
#endif
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class ThreadManager {
|
||||
NON_COPYABLE(ThreadManager);
|
||||
NON_MOVEABLE(ThreadManager);
|
||||
private:
|
||||
class ThreadListTraits {
|
||||
public:
|
||||
using ListType = util::IntrusiveList<ThreadType, ThreadListTraits>;
|
||||
private:
|
||||
friend class util::IntrusiveList<ThreadType, ThreadListTraits>;
|
||||
|
||||
static constexpr util::IntrusiveListNode &GetNode(ThreadType &parent) {
|
||||
return GetReference(parent.all_threads_node);
|
||||
}
|
||||
|
||||
static constexpr util::IntrusiveListNode const &GetNode(ThreadType const &parent) {
|
||||
return GetReference(parent.all_threads_node);
|
||||
}
|
||||
|
||||
static ThreadType &GetParent(util::IntrusiveListNode &node) {
|
||||
return *reinterpret_cast<ThreadType *>(reinterpret_cast<char *>(std::addressof(node)) - OFFSETOF(ThreadType, all_threads_node));
|
||||
}
|
||||
|
||||
static ThreadType const &GetParent(util::IntrusiveListNode const &node) {
|
||||
return *reinterpret_cast<const ThreadType *>(reinterpret_cast<const char *>(std::addressof(node)) - OFFSETOF(ThreadType, all_threads_node));
|
||||
}
|
||||
};
|
||||
|
||||
using AllThreadsList = ThreadListTraits::ListType;
|
||||
private:
|
||||
ThreadManagerImpl impl;
|
||||
ThreadType main_thread;
|
||||
InternalCriticalSection cs;
|
||||
AllThreadsList all_threads_list;
|
||||
size_t total_thread_stack_size;
|
||||
s32 num_created_threads;
|
||||
public:
|
||||
ThreadManager();
|
||||
|
||||
void CleanupThread();
|
||||
s32 GetThreadCountForDebug() const { return this->num_created_threads; }
|
||||
|
||||
Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority, s32 ideal_core);
|
||||
Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority);
|
||||
|
||||
void DestroyThread(ThreadType *thread);
|
||||
void StartThread(ThreadType *thread);
|
||||
void WaitThread(ThreadType *thread);
|
||||
bool TryWaitThread(ThreadType *thread);
|
||||
|
||||
void YieldThread() { return this->impl.YieldThread(); }
|
||||
|
||||
bool ChangePriority(ThreadType *thread, s32 priority) { return this->impl.ChangePriority(thread, priority); }
|
||||
s32 GetCurrentPriority(const ThreadType *thread) const { return this->impl.GetCurrentPriority(thread); }
|
||||
ThreadType *GetCurrentThread() const { return this->impl.GetCurrentThread(); }
|
||||
|
||||
s32 SuspendThread(ThreadType *thread);
|
||||
s32 ResumeThread(ThreadType *thread);
|
||||
|
||||
void CancelThreadSynchronization(ThreadType *thread);
|
||||
|
||||
/* TODO void GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */
|
||||
|
||||
void SetInitialThreadNameUnsafe(ThreadType *thread);
|
||||
|
||||
void NotifyThreadNameChanged(const ThreadType *thread) const { return this->impl.NotifyThreadNameChangedImpl(thread); }
|
||||
void SetCurrentThread(ThreadType *thread) const { return this->impl.SetCurrentThread(thread); }
|
||||
s32 GetCurrentCoreNumber() const { return this->impl.GetCurrentCoreNumber(); }
|
||||
void SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const { return this->impl.SetThreadCoreMask(thread, ideal_core, affinity_mask); }
|
||||
void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const { return this->impl.GetThreadCoreMask(out_ideal_core, out_affinity_mask, thread); }
|
||||
u64 GetThreadAvailableCoreMask() const { return this->impl.GetThreadAvailableCoreMask(); }
|
||||
|
||||
void PushBackToAllThreadsListUnsafe(ThreadType *thread) {
|
||||
this->all_threads_list.push_back(*thread);
|
||||
++this->num_created_threads;
|
||||
this->total_thread_stack_size += thread->stack_size;
|
||||
}
|
||||
|
||||
void EraseFromAllThreadsListUnsafe(ThreadType *thread) {
|
||||
this->all_threads_list.erase(this->all_threads_list.iterator_to(*thread));
|
||||
--this->num_created_threads;
|
||||
this->total_thread_stack_size -= thread->stack_size;
|
||||
}
|
||||
|
||||
void PushBackToAllThreadsListSafe(ThreadType *thread) {
|
||||
std::scoped_lock lk(this->cs);
|
||||
this->PushBackToAllThreadsListUnsafe(thread);
|
||||
}
|
||||
|
||||
void EraseFromAllThreadsListSafe(ThreadType *thread) {
|
||||
std::scoped_lock lk(this->cs);
|
||||
this->EraseFromAllThreadsListUnsafe(thread);
|
||||
}
|
||||
|
||||
void PlaceThreadObjectUnderThreadManagerSafe(ThreadType *thread) {
|
||||
SetInitialThreadNameUnsafe(thread);
|
||||
{
|
||||
std::scoped_lock lk(this->cs);
|
||||
this->PushBackToAllThreadsListUnsafe(thread);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadType *AllocateThreadType() const {
|
||||
return reinterpret_cast<ThreadType *>(std::malloc(sizeof(ThreadType)));
|
||||
}
|
||||
|
||||
void FreeThreadType(ThreadType *thread) const {
|
||||
std::free(thread);
|
||||
}
|
||||
|
||||
const ThreadType *GetMainThread() const {
|
||||
return std::addressof(this->main_thread);
|
||||
}
|
||||
|
||||
size_t GetTotalThreadStackSize() const {
|
||||
return this->total_thread_stack_size;
|
||||
}
|
||||
|
||||
ThreadId GetThreadId(const ThreadType *thread) {
|
||||
return this->impl.GetThreadId(thread);
|
||||
}
|
||||
public:
|
||||
static void InvokeThread(ThreadType *thread);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_timeout_helper.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
TargetTimeSpan TimeoutHelper::GetTimeLeftOnTarget() const {
|
||||
/* If the absolute tick is zero, we're expired. */
|
||||
if (this->absolute_end_tick.GetInt64Value() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if we've expired. */
|
||||
const Tick cur_tick = impl::GetTickManager().GetTick();
|
||||
if (cur_tick >= this->absolute_end_tick) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the converted difference as a timespan. */
|
||||
return TimeoutHelperImpl::ConvertToImplTime(this->absolute_end_tick - cur_tick);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_tick_manager.hpp"
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_timeout_helper_impl.os.horizon.hpp"
|
||||
#else
|
||||
#error "Unknown OS for ams::os::TimeoutHelper"
|
||||
#endif
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class TimeoutHelper {
|
||||
private:
|
||||
Tick absolute_end_tick;
|
||||
public:
|
||||
explicit TimeoutHelper(TimeSpan timeout) {
|
||||
if (timeout == 0) {
|
||||
/* If timeout is zero, don't do relative tick calculations. */
|
||||
this->absolute_end_tick = Tick(0);
|
||||
} else {
|
||||
const auto &tick_manager = impl::GetTickManager();
|
||||
|
||||
const u64 cur_tick = tick_manager.GetTick().GetInt64Value();
|
||||
const u64 timeout_tick = tick_manager.ConvertToTick(timeout).GetInt64Value();
|
||||
const u64 end_tick = cur_tick + timeout_tick + 1;
|
||||
|
||||
this->absolute_end_tick = Tick(std::min<u64>(std::numeric_limits<s64>::max(), end_tick));
|
||||
}
|
||||
}
|
||||
|
||||
static void Sleep(TimeSpan tm) {
|
||||
TimeoutHelperImpl::Sleep(tm);
|
||||
}
|
||||
|
||||
bool TimedOut() const {
|
||||
if (this->absolute_end_tick.GetInt64Value() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const Tick cur_tick = impl::GetTickManager().GetTick();
|
||||
|
||||
return cur_tick >= this->absolute_end_tick;
|
||||
}
|
||||
|
||||
TargetTimeSpan GetTimeLeftOnTarget() const;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_timeout_helper_impl.os.horizon.hpp"
|
||||
#include "os_thread_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
void TimeoutHelperImpl::Sleep(TimeSpan tm) {
|
||||
if (tm == TimeSpan(0)) {
|
||||
GetThreadManager().YieldThread();
|
||||
} else {
|
||||
ams::svc::SleepThread(tm.GetNanoSeconds());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_tick_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
using TargetTimeSpan = ::ams::TimeSpan;
|
||||
|
||||
class TimeoutHelperImpl {
|
||||
public:
|
||||
static TargetTimeSpan ConvertToImplTime(Tick tick) {
|
||||
return impl::GetTickManager().ConvertToTimeSpan(tick);
|
||||
}
|
||||
|
||||
static void Sleep(TimeSpan tm);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -37,8 +37,8 @@ namespace ams::os::impl {
|
||||
/* Gets handle to output, returns INVALID_HANDLE on failure. */
|
||||
virtual Handle GetHandle() const = 0;
|
||||
/* Gets the amount of time remaining until this wakes up. */
|
||||
virtual u64 GetWakeupTime() const {
|
||||
return std::numeric_limits<u64>::max();
|
||||
virtual TimeSpan GetAbsoluteWakeupTime() const {
|
||||
return TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max());
|
||||
}
|
||||
|
||||
/* Interface with manager. */
|
||||
@@ -58,18 +58,18 @@ namespace ams::os::impl {
|
||||
class WaitableHolderOfUserObject : public WaitableHolderBase {
|
||||
public:
|
||||
/* All user objects have no handle to wait on. */
|
||||
virtual Handle GetHandle() const override {
|
||||
return INVALID_HANDLE;
|
||||
virtual Handle GetHandle() const override final {
|
||||
return svc::InvalidHandle;
|
||||
}
|
||||
};
|
||||
|
||||
class WaitableHolderOfKernelObject : public WaitableHolderBase {
|
||||
public:
|
||||
/* All kernel objects have native handles, and thus don't have object list semantics. */
|
||||
virtual TriBool LinkToObjectList() override {
|
||||
virtual TriBool LinkToObjectList() override final {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
virtual void UnlinkFromObjectList() override {
|
||||
virtual void UnlinkFromObjectList() override final {
|
||||
/* ... */
|
||||
}
|
||||
};
|
||||
|
||||
@@ -51,6 +51,6 @@ namespace ams::os::impl {
|
||||
|
||||
#undef CHECK_HOLDER
|
||||
|
||||
static_assert(std::is_trivial<WaitableHolderImpl>::value && std::is_trivially_destructible<WaitableHolderImpl>::value, "WaitableHolderImpl");
|
||||
static_assert(sizeof(WaitableHolderImpl) == WaitableHolder::ImplStorageSize, "WaitableHolderImpl size");
|
||||
static_assert(std::is_trivial<WaitableHolderImpl>::value && std::is_trivially_destructible<WaitableHolderImpl>::value);
|
||||
static_assert(sizeof(WaitableHolderImpl) == sizeof(os::WaitableHolderType::impl_storage));
|
||||
}
|
||||
|
||||
@@ -21,29 +21,29 @@ namespace ams::os::impl {
|
||||
|
||||
class WaitableHolderOfEvent : public WaitableHolderOfUserObject {
|
||||
private:
|
||||
Event *event;
|
||||
EventType *event;
|
||||
private:
|
||||
TriBool IsSignaledImpl() const {
|
||||
return this->event->signaled ? TriBool::True : TriBool::False;
|
||||
}
|
||||
public:
|
||||
explicit WaitableHolderOfEvent(Event *e) : event(e) { /* ... */ }
|
||||
explicit WaitableHolderOfEvent(EventType *e) : event(e) { /* ... */ }
|
||||
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
std::scoped_lock lk(this->event->lock);
|
||||
std::scoped_lock lk(GetReference(this->event->cs_event));
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual TriBool LinkToObjectList() override {
|
||||
std::scoped_lock lk(this->event->lock);
|
||||
std::scoped_lock lk(GetReference(this->event->cs_event));
|
||||
|
||||
GetReference(this->event->waitable_object_list_storage).LinkWaitableHolder(*this);
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual void UnlinkFromObjectList() override {
|
||||
std::scoped_lock lk(this->event->lock);
|
||||
std::scoped_lock lk(GetReference(this->event->cs_event));
|
||||
|
||||
GetReference(this->event->waitable_object_list_storage).UnlinkWaitableHolder(*this);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ namespace ams::os::impl {
|
||||
|
||||
class WaitableHolderOfInterProcessEvent : public WaitableHolderOfKernelObject {
|
||||
private:
|
||||
InterProcessEvent *event;
|
||||
InterProcessEventType *event;
|
||||
public:
|
||||
explicit WaitableHolderOfInterProcessEvent(InterProcessEvent *e) : event(e) { /* ... */ }
|
||||
explicit WaitableHolderOfInterProcessEvent(InterProcessEventType *e) : event(e) { /* ... */ }
|
||||
|
||||
/* IsSignaled, GetHandle both implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
@@ -31,8 +31,7 @@ namespace ams::os::impl {
|
||||
}
|
||||
|
||||
virtual Handle GetHandle() const override {
|
||||
AMS_ABORT_UNLESS(this->event->is_initialized);
|
||||
return this->event->GetReadableHandle();
|
||||
return this->event->readable_handle;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_waitable_holder_of_interrupt_event.hpp"
|
||||
#include "os_interrupt_event_impl.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
Handle WaitableHolderOfInterruptEvent::GetHandle() const {
|
||||
return GetReference(event->impl).GetHandle();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,19 +20,16 @@ namespace ams::os::impl {
|
||||
|
||||
class WaitableHolderOfInterruptEvent : public WaitableHolderOfKernelObject {
|
||||
private:
|
||||
InterruptEvent *event;
|
||||
InterruptEventType *event;
|
||||
public:
|
||||
explicit WaitableHolderOfInterruptEvent(InterruptEvent *e) : event(e) { /* ... */ }
|
||||
explicit WaitableHolderOfInterruptEvent(InterruptEventType *e) : event(e) { /* ... */ }
|
||||
|
||||
/* IsSignaled, GetHandle both implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
return TriBool::Undefined;
|
||||
}
|
||||
|
||||
virtual Handle GetHandle() const override {
|
||||
AMS_ABORT_UNLESS(this->event->is_initialized);
|
||||
return this->event->handle.Get();
|
||||
}
|
||||
virtual Handle GetHandle() const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -19,57 +19,57 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
template<MessageQueueWaitKind WaitKind>
|
||||
template<MessageQueueWaitType WaitType>
|
||||
class WaitableHolderOfMessageQueue : public WaitableHolderOfUserObject {
|
||||
static_assert(WaitKind == MessageQueueWaitKind::ForNotEmpty || WaitKind == MessageQueueWaitKind::ForNotFull, "MessageQueueHolder WaitKind!");
|
||||
static_assert(WaitType == MessageQueueWaitType::ForNotEmpty || WaitType == MessageQueueWaitType::ForNotFull);
|
||||
private:
|
||||
MessageQueue *message_queue;
|
||||
MessageQueueType *mq;
|
||||
private:
|
||||
constexpr inline TriBool IsSignaledImpl() const {
|
||||
if constexpr (WaitKind == MessageQueueWaitKind::ForNotEmpty) {
|
||||
if constexpr (WaitType == MessageQueueWaitType::ForNotEmpty) {
|
||||
/* ForNotEmpty. */
|
||||
return this->message_queue->IsEmpty() ? TriBool::False : TriBool::True;
|
||||
} else if constexpr (WaitKind == MessageQueueWaitKind::ForNotFull) {
|
||||
return this->mq->count > 0 ? TriBool::True : TriBool::False;
|
||||
} else if constexpr (WaitType == MessageQueueWaitType::ForNotFull) {
|
||||
/* ForNotFull */
|
||||
return this->message_queue->IsFull() ? TriBool::False : TriBool::True;
|
||||
return this->mq->count < this->mq->capacity ? TriBool::True : TriBool::False;
|
||||
} else {
|
||||
static_assert(WaitKind != WaitKind);
|
||||
static_assert(WaitType != WaitType);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline WaitableObjectList &GetObjectList() const {
|
||||
if constexpr (WaitKind == MessageQueueWaitKind::ForNotEmpty) {
|
||||
return GetReference(this->message_queue->waitlist_not_empty);
|
||||
} else if constexpr (WaitKind == MessageQueueWaitKind::ForNotFull) {
|
||||
return GetReference(this->message_queue->waitlist_not_full);
|
||||
if constexpr (WaitType == MessageQueueWaitType::ForNotEmpty) {
|
||||
return GetReference(this->mq->waitlist_not_empty);
|
||||
} else if constexpr (WaitType == MessageQueueWaitType::ForNotFull) {
|
||||
return GetReference(this->mq->waitlist_not_full);
|
||||
} else {
|
||||
static_assert(WaitKind != WaitKind);
|
||||
static_assert(WaitType != WaitType);
|
||||
}
|
||||
}
|
||||
public:
|
||||
explicit WaitableHolderOfMessageQueue(MessageQueue *mq) : message_queue(mq) { /* ... */ }
|
||||
explicit WaitableHolderOfMessageQueue(MessageQueueType *mq) : mq(mq) { /* ... */ }
|
||||
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
std::scoped_lock lk(this->message_queue->queue_lock);
|
||||
std::scoped_lock lk(GetReference(this->mq->cs_queue));
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual TriBool LinkToObjectList() override {
|
||||
std::scoped_lock lk(this->message_queue->queue_lock);
|
||||
std::scoped_lock lk(GetReference(this->mq->cs_queue));
|
||||
|
||||
this->GetObjectList().LinkWaitableHolder(*this);
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual void UnlinkFromObjectList() override {
|
||||
std::scoped_lock lk(this->message_queue->queue_lock);
|
||||
std::scoped_lock lk(GetReference(this->mq->cs_queue));
|
||||
|
||||
this->GetObjectList().UnlinkWaitableHolder(*this);
|
||||
}
|
||||
};
|
||||
|
||||
using WaitableHolderOfMessageQueueForNotEmpty = WaitableHolderOfMessageQueue<MessageQueueWaitKind::ForNotEmpty>;
|
||||
using WaitableHolderOfMessageQueueForNotFull = WaitableHolderOfMessageQueue<MessageQueueWaitKind::ForNotFull>;
|
||||
using WaitableHolderOfMessageQueueForNotEmpty = WaitableHolderOfMessageQueue<MessageQueueWaitType::ForNotEmpty>;
|
||||
using WaitableHolderOfMessageQueueForNotFull = WaitableHolderOfMessageQueue<MessageQueueWaitType::ForNotFull>;
|
||||
|
||||
}
|
||||
|
||||
@@ -21,29 +21,29 @@ namespace ams::os::impl {
|
||||
|
||||
class WaitableHolderOfSemaphore : public WaitableHolderOfUserObject {
|
||||
private:
|
||||
Semaphore *semaphore;
|
||||
SemaphoreType *semaphore;
|
||||
private:
|
||||
TriBool IsSignaledImpl() const {
|
||||
return this->semaphore->count > 0 ? TriBool::True : TriBool::False;
|
||||
}
|
||||
public:
|
||||
explicit WaitableHolderOfSemaphore(Semaphore *s) : semaphore(s) { /* ... */ }
|
||||
explicit WaitableHolderOfSemaphore(SemaphoreType *s) : semaphore(s) { /* ... */ }
|
||||
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
std::scoped_lock lk(this->semaphore->mutex);
|
||||
std::scoped_lock lk(GetReference(this->semaphore->cs_sema));
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual TriBool LinkToObjectList() override {
|
||||
std::scoped_lock lk(this->semaphore->mutex);
|
||||
std::scoped_lock lk(GetReference(this->semaphore->cs_sema));
|
||||
|
||||
GetReference(this->semaphore->waitlist).LinkWaitableHolder(*this);
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual void UnlinkFromObjectList() override {
|
||||
std::scoped_lock lk(this->semaphore->mutex);
|
||||
std::scoped_lock lk(GetReference(this->semaphore->cs_sema));
|
||||
|
||||
GetReference(this->semaphore->waitlist).UnlinkWaitableHolder(*this);
|
||||
}
|
||||
|
||||
@@ -18,21 +18,33 @@
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
/* Nintendo implements this as a user wait object, operating on Thread state. */
|
||||
/* Libnx doesn't have an equivalent, so we'll use the thread's handle for kernel semantics. */
|
||||
class WaitableHolderOfThread : public WaitableHolderOfKernelObject {
|
||||
class WaitableHolderOfThread : public WaitableHolderOfUserObject {
|
||||
private:
|
||||
Thread *thread;
|
||||
ThreadType *thread;
|
||||
private:
|
||||
TriBool IsSignaledImpl() const {
|
||||
return this->thread->state == ThreadType::State_Terminated ? TriBool::True : TriBool::False;
|
||||
}
|
||||
public:
|
||||
explicit WaitableHolderOfThread(Thread *t) : thread(t) { /* ... */ }
|
||||
explicit WaitableHolderOfThread(ThreadType *t) : thread(t) { /* ... */ }
|
||||
|
||||
/* IsSignaled, GetHandle both implemented. */
|
||||
/* IsSignaled, Link, Unlink implemented. */
|
||||
virtual TriBool IsSignaled() const override {
|
||||
return TriBool::Undefined;
|
||||
std::scoped_lock lk(GetReference(this->thread->cs_thread));
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual Handle GetHandle() const override {
|
||||
return this->thread->GetHandle();
|
||||
virtual TriBool LinkToObjectList() override {
|
||||
std::scoped_lock lk(GetReference(this->thread->cs_thread));
|
||||
|
||||
GetReference(this->thread->waitlist).LinkWaitableHolder(*this);
|
||||
return this->IsSignaledImpl();
|
||||
}
|
||||
|
||||
virtual void UnlinkFromObjectList() override {
|
||||
std::scoped_lock lk(GetReference(this->thread->cs_thread));
|
||||
|
||||
GetReference(this->thread->waitlist).UnlinkWaitableHolder(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -15,21 +15,19 @@
|
||||
*/
|
||||
#include "os_waitable_manager_impl.hpp"
|
||||
#include "os_waitable_object_list.hpp"
|
||||
#include "os_tick_manager.hpp"
|
||||
|
||||
namespace ams::os::impl{
|
||||
|
||||
WaitableHolderBase *WaitableManagerImpl::WaitAnyImpl(bool infinite, u64 timeout) {
|
||||
/* Set processing thread handle while in scope. */
|
||||
this->waiting_thread_handle = threadGetCurHandle();
|
||||
ON_SCOPE_EXIT { this->waiting_thread_handle = INVALID_HANDLE; };
|
||||
namespace ams::os::impl {
|
||||
|
||||
WaitableHolderBase *WaitableManagerImpl::WaitAnyImpl(bool infinite, TimeSpan timeout) {
|
||||
/* Prepare for processing. */
|
||||
this->signaled_holder = nullptr;
|
||||
this->target_impl.SetCurrentThreadHandleForCancelWait();
|
||||
WaitableHolderBase *result = this->LinkHoldersToObjectList();
|
||||
|
||||
/* Check if we've been signaled. */
|
||||
{
|
||||
std::scoped_lock lk(this->lock);
|
||||
std::scoped_lock lk(this->cs_wait);
|
||||
if (this->signaled_holder != nullptr) {
|
||||
result = this->signaled_holder;
|
||||
}
|
||||
@@ -43,36 +41,42 @@ namespace ams::os::impl{
|
||||
/* Unlink holders from the current object list. */
|
||||
this->UnlinkHoldersFromObjectList();
|
||||
|
||||
this->target_impl.ClearCurrentThreadHandleForCancelWait();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
WaitableHolderBase *WaitableManagerImpl::WaitAnyHandleImpl(bool infinite, u64 timeout) {
|
||||
WaitableHolderBase *WaitableManagerImpl::WaitAnyHandleImpl(bool infinite, TimeSpan timeout) {
|
||||
Handle object_handles[MaximumHandleCount];
|
||||
WaitableHolderBase *objects[MaximumHandleCount];
|
||||
|
||||
const size_t count = this->BuildHandleArray(object_handles, objects);
|
||||
const u64 end_time = infinite ? std::numeric_limits<u64>::max() : armTicksToNs(armGetSystemTick());
|
||||
const s32 count = this->BuildHandleArray(object_handles, objects, MaximumHandleCount);
|
||||
const TimeSpan end_time = infinite ? TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max()) : GetCurrentTick().ToTimeSpan() + timeout;
|
||||
|
||||
while (true) {
|
||||
this->current_time = armTicksToNs(armGetSystemTick());
|
||||
this->current_time = GetCurrentTick().ToTimeSpan();
|
||||
|
||||
u64 min_timeout = 0;
|
||||
TimeSpan min_timeout = 0;
|
||||
WaitableHolderBase *min_timeout_object = this->RecalculateNextTimeout(&min_timeout, end_time);
|
||||
|
||||
s32 index;
|
||||
if (count == 0 && min_timeout == 0) {
|
||||
index = WaitTimedOut;
|
||||
if (infinite && min_timeout_object == nullptr) {
|
||||
index = this->target_impl.WaitAny(object_handles, MaximumHandleCount, count);
|
||||
} else {
|
||||
index = this->WaitSynchronization(object_handles, count, min_timeout);
|
||||
AMS_ABORT_UNLESS(index != WaitInvalid);
|
||||
if (count == 0 && min_timeout == 0) {
|
||||
index = WaitTimedOut;
|
||||
} else {
|
||||
index = this->target_impl.TimedWaitAny(object_handles, MaximumHandleCount, count, min_timeout);
|
||||
AMS_ABORT_UNLESS(index != WaitInvalid);
|
||||
}
|
||||
}
|
||||
|
||||
switch (index) {
|
||||
case WaitTimedOut:
|
||||
if (min_timeout_object) {
|
||||
this->current_time = armTicksToNs(armGetSystemTick());
|
||||
this->current_time = GetCurrentTick().ToTimeSpan();
|
||||
if (min_timeout_object->IsSignaled() == TriBool::True) {
|
||||
std::scoped_lock lk(this->lock);
|
||||
std::scoped_lock lk(this->cs_wait);
|
||||
this->signaled_holder = min_timeout_object;
|
||||
return this->signaled_holder;
|
||||
}
|
||||
@@ -86,7 +90,7 @@ namespace ams::os::impl{
|
||||
continue;
|
||||
default: /* 0 - 0x3F, valid. */
|
||||
{
|
||||
std::scoped_lock lk(this->lock);
|
||||
std::scoped_lock lk(this->cs_wait);
|
||||
this->signaled_holder = objects[index];
|
||||
return this->signaled_holder;
|
||||
}
|
||||
@@ -94,28 +98,12 @@ namespace ams::os::impl{
|
||||
}
|
||||
}
|
||||
|
||||
s32 WaitableManagerImpl::WaitSynchronization(Handle *handles, size_t count, u64 timeout) {
|
||||
s32 index = WaitInvalid;
|
||||
|
||||
R_TRY_CATCH(svcWaitSynchronization(&index, handles, count, timeout)) {
|
||||
R_CATCH(svc::ResultTimedOut) { return WaitTimedOut; }
|
||||
R_CATCH(svc::ResultCancelled) { return WaitCancelled; }
|
||||
/* All other results are critical errors. */
|
||||
/* svc::ResultThreadTerminating */
|
||||
/* svc::ResultInvalidHandle. */
|
||||
/* svc::ResultInvalidPointer */
|
||||
/* svc::ResultOutOfRange */
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
size_t WaitableManagerImpl::BuildHandleArray(Handle *out_handles, WaitableHolderBase **out_objects) {
|
||||
size_t count = 0;
|
||||
s32 WaitableManagerImpl::BuildHandleArray(Handle out_handles[], WaitableHolderBase *out_objects[], s32 num) {
|
||||
s32 count = 0;
|
||||
|
||||
for (WaitableHolderBase &holder_base : this->waitable_list) {
|
||||
if (Handle handle = holder_base.GetHandle(); handle != INVALID_HANDLE) {
|
||||
AMS_ABORT_UNLESS(count < MaximumHandleCount);
|
||||
if (Handle handle = holder_base.GetHandle(); handle != svc::InvalidHandle) {
|
||||
AMS_ASSERT(count < num);
|
||||
|
||||
out_handles[count] = handle;
|
||||
out_objects[count] = &holder_base;
|
||||
@@ -146,12 +134,12 @@ namespace ams::os::impl{
|
||||
}
|
||||
}
|
||||
|
||||
WaitableHolderBase *WaitableManagerImpl::RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time) {
|
||||
WaitableHolderBase *WaitableManagerImpl::RecalculateNextTimeout(TimeSpan *out_min_timeout, TimeSpan end_time) {
|
||||
WaitableHolderBase *min_timeout_holder = nullptr;
|
||||
u64 min_time = end_time;
|
||||
TimeSpan min_time = end_time;
|
||||
|
||||
for (WaitableHolderBase &holder_base : this->waitable_list) {
|
||||
if (const u64 cur_time = holder_base.GetWakeupTime(); cur_time < min_time) {
|
||||
if (const TimeSpan cur_time = holder_base.GetAbsoluteWakeupTime(); cur_time < min_time) {
|
||||
min_timeout_holder = &holder_base;
|
||||
min_time = cur_time;
|
||||
}
|
||||
@@ -166,11 +154,11 @@ namespace ams::os::impl{
|
||||
}
|
||||
|
||||
void WaitableManagerImpl::SignalAndWakeupThread(WaitableHolderBase *holder_base) {
|
||||
std::scoped_lock lk(this->lock);
|
||||
std::scoped_lock lk(this->cs_wait);
|
||||
|
||||
if (this->signaled_holder == nullptr) {
|
||||
this->signaled_holder = holder_base;
|
||||
R_ABORT_UNLESS(svcCancelSynchronization(this->waiting_thread_handle));
|
||||
this->target_impl.CancelWait();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,17 @@
|
||||
#pragma once
|
||||
#include "os_waitable_holder_base.hpp"
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "os_waitable_manager_target_impl.os.horizon.hpp"
|
||||
#else
|
||||
#error "Unknown OS for ams::os::WaitableManagerTargetImpl"
|
||||
#endif
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class WaitableManagerImpl {
|
||||
public:
|
||||
static constexpr size_t MaximumHandleCount = 0x40;
|
||||
static constexpr size_t MaximumHandleCount = WaitableManagerTargetImpl::MaximumHandleCount;
|
||||
static constexpr s32 WaitInvalid = -3;
|
||||
static constexpr s32 WaitCancelled = -2;
|
||||
static constexpr s32 WaitTimedOut = -1;
|
||||
@@ -28,31 +34,30 @@ namespace ams::os::impl {
|
||||
private:
|
||||
ListType waitable_list;
|
||||
WaitableHolderBase *signaled_holder;
|
||||
u64 current_time;
|
||||
Mutex lock;
|
||||
Handle waiting_thread_handle;
|
||||
TimeSpan current_time;
|
||||
InternalCriticalSection cs_wait;
|
||||
WaitableManagerTargetImpl target_impl;
|
||||
private:
|
||||
WaitableHolderBase *WaitAnyImpl(bool infinite, u64 timeout);
|
||||
WaitableHolderBase *WaitAnyHandleImpl(bool infinite, u64 timeout);
|
||||
s32 WaitSynchronization(Handle *handles, size_t count, u64 timeout);
|
||||
size_t BuildHandleArray(Handle *out_handles, WaitableHolderBase **out_objects);
|
||||
WaitableHolderBase *WaitAnyImpl(bool infinite, TimeSpan timeout);
|
||||
WaitableHolderBase *WaitAnyHandleImpl(bool infinite, TimeSpan timeout);
|
||||
s32 BuildHandleArray(Handle out_handles[], WaitableHolderBase *out_objects[], s32 num);
|
||||
|
||||
WaitableHolderBase *LinkHoldersToObjectList();
|
||||
void UnlinkHoldersFromObjectList();
|
||||
|
||||
WaitableHolderBase *RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time);
|
||||
WaitableHolderBase *RecalculateNextTimeout(TimeSpan *out_min_timeout, TimeSpan end_time);
|
||||
public:
|
||||
/* Wait. */
|
||||
WaitableHolderBase *WaitAny() {
|
||||
return this->WaitAnyImpl(true, std::numeric_limits<u64>::max());
|
||||
return this->WaitAnyImpl(true, TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max()));
|
||||
}
|
||||
|
||||
WaitableHolderBase *TryWaitAny() {
|
||||
return this->WaitAnyImpl(false, 0);
|
||||
return this->WaitAnyImpl(false, TimeSpan(0));
|
||||
}
|
||||
|
||||
WaitableHolderBase *TimedWaitAny(u64 timeout) {
|
||||
return this->WaitAnyImpl(false, timeout);
|
||||
WaitableHolderBase *TimedWaitAny(TimeSpan ts) {
|
||||
return this->WaitAnyImpl(false, ts);
|
||||
}
|
||||
|
||||
/* List management. */
|
||||
@@ -84,7 +89,7 @@ namespace ams::os::impl {
|
||||
}
|
||||
|
||||
/* Other. */
|
||||
u64 GetCurrentTime() const {
|
||||
TimeSpan GetCurrentTime() const {
|
||||
return this->current_time;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_waitable_holder_base.hpp"
|
||||
#include "os_waitable_manager_impl.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
s32 WaitableManagerHorizonImpl::WaitSynchronizationN(s32 num, Handle arr[], s32 array_size, s64 ns) {
|
||||
AMS_ASSERT(!(num == 0 && ns == 0));
|
||||
s32 index = WaitableManagerImpl::WaitInvalid;
|
||||
|
||||
R_TRY_CATCH(svc::WaitSynchronization(std::addressof(index), static_cast<const svc::Handle *>(arr), num, ns)) {
|
||||
R_CATCH(svc::ResultTimedOut) { return WaitableManagerImpl::WaitTimedOut; }
|
||||
R_CATCH(svc::ResultCancelled) { return WaitableManagerImpl::WaitCancelled; }
|
||||
/* All other results are critical errors. */
|
||||
/* svc::ResultThreadTerminating */
|
||||
/* svc::ResultInvalidHandle. */
|
||||
/* svc::ResultInvalidPointer */
|
||||
/* svc::ResultOutOfRange */
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void WaitableManagerHorizonImpl::CancelWait() {
|
||||
R_ABORT_UNLESS(svc::CancelSynchronization(this->handle));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "os_thread_manager.hpp"
|
||||
|
||||
namespace ams::os::impl {
|
||||
|
||||
class WaitableManagerHorizonImpl {
|
||||
public:
|
||||
static constexpr size_t MaximumHandleCount = svc::MaxWaitSynchronizationHandleCount;
|
||||
private:
|
||||
Handle handle;
|
||||
private:
|
||||
s32 WaitSynchronizationN(s32 num, Handle arr[], s32 array_size, s64 ns);
|
||||
public:
|
||||
void CancelWait();
|
||||
|
||||
s32 WaitAny(Handle arr[], s32 array_size, s32 num) {
|
||||
return this->WaitSynchronizationN(num, arr, array_size, svc::WaitInfinite);
|
||||
}
|
||||
|
||||
s32 TryWaitAny(Handle arr[], s32 array_size, s32 num) {
|
||||
return this->WaitSynchronizationN(num, arr, array_size, 0);
|
||||
}
|
||||
|
||||
s32 TimedWaitAny(Handle arr[], s32 array_size, s32 num, TimeSpan ts) {
|
||||
s64 timeout = ts.GetNanoSeconds();
|
||||
if (timeout < 0) {
|
||||
timeout = 0;
|
||||
}
|
||||
return this->WaitSynchronizationN(num, arr, array_size, timeout);
|
||||
}
|
||||
|
||||
void SetCurrentThreadHandleForCancelWait() {
|
||||
this->handle = GetCurrentThreadHandle();
|
||||
}
|
||||
|
||||
void ClearCurrentThreadHandleForCancelWait() {
|
||||
this->handle = svc::InvalidHandle;
|
||||
}
|
||||
};
|
||||
|
||||
using WaitableManagerTargetImpl = WaitableManagerHorizonImpl;
|
||||
|
||||
}
|
||||
107
libraries/libstratosphere/source/os/os_condition_variable.cpp
Normal file
107
libraries/libstratosphere/source/os/os_condition_variable.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
160
libraries/libstratosphere/source/os/os_mutex.cpp
Normal file
160
libraries/libstratosphere/source/os/os_mutex.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
209
libraries/libstratosphere/source/os/os_thread.cpp
Normal file
209
libraries/libstratosphere/source/os/os_thread.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
151
libraries/libstratosphere/source/os/os_waitable.cpp
Normal file
151
libraries/libstratosphere/source/os/os_waitable.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user