os: implement waitable management.

This implements waitable management for Events (and
implements Events). It also refactors PM to use new
Event/Waitable semantics, and also adds STS_ASSERT
as a macro for asserting a boolean expression. The
rest of stratosphere has been refactored to use
STS_ASSERT whenever possible.
This commit is contained in:
Michael Scire
2019-09-27 18:04:58 -07:00
committed by SciresM
parent e07011be32
commit 609a302e16
108 changed files with 2752 additions and 1223 deletions

View File

@@ -81,9 +81,7 @@ static void _CacheValues(void)
args.X[1] = 0; /* NAND */
args.X[2] = reinterpret_cast<u64>(&paths); /* path output */
R_ASSERT(svcCallSecureMonitor(&args));
if (args.X[0] != 0) {
std::abort();
}
STS_ASSERT(args.X[0] == 0);
std::memcpy(&g_exo_emummc_config, &args.X[1], sizeof(args) - sizeof(args.X[0]));
}

View File

@@ -40,9 +40,7 @@ static void _CacheValues(void)
args.X[0] = 0xC3000002; /* smcGetConfig */
args.X[1] = 65000; /* ConfigItem_ExosphereVersion */
R_ASSERT(svcCallSecureMonitor(&args));
if (args.X[0] != 0) {
std::abort();
}
STS_ASSERT(args.X[0] == 0);
target_fw = (args.X[1] >> 0x08) & 0xFF;
}

View File

@@ -90,9 +90,7 @@ namespace sts::kvdb {
Result ArchiveReader::ReadEntryCount(size_t *out) {
/* This should only be called at the start of reading stream. */
if (this->offset != 0) {
std::abort();
}
STS_ASSERT(this->offset == 0);
/* Read and validate header. */
ArchiveHeader header;
@@ -105,9 +103,7 @@ namespace sts::kvdb {
Result ArchiveReader::GetEntrySize(size_t *out_key_size, size_t *out_value_size) {
/* This should only be called after ReadEntryCount. */
if (this->offset == 0) {
std::abort();
}
STS_ASSERT(this->offset != 0);
/* Peek the next entry header. */
ArchiveEntryHeader header;
@@ -121,9 +117,7 @@ namespace sts::kvdb {
Result ArchiveReader::ReadEntry(void *out_key, size_t key_size, void *out_value, size_t value_size) {
/* This should only be called after ReadEntryCount. */
if (this->offset == 0) {
std::abort();
}
STS_ASSERT(this->offset != 0);
/* Read the next entry header. */
ArchiveEntryHeader header;
@@ -131,9 +125,8 @@ namespace sts::kvdb {
R_TRY(header.Validate());
/* Key size and Value size must be correct. */
if (key_size != header.key_size || value_size != header.value_size) {
std::abort();
}
STS_ASSERT(key_size == header.key_size);
STS_ASSERT(value_size == header.value_size);
R_ASSERT(this->Read(out_key, key_size));
R_ASSERT(this->Read(out_value, value_size));
@@ -154,9 +147,7 @@ namespace sts::kvdb {
void ArchiveWriter::WriteHeader(size_t entry_count) {
/* This should only be called at start of write. */
if (this->offset != 0) {
std::abort();
}
STS_ASSERT(this->offset == 0);
ArchiveHeader header = ArchiveHeader::Make(entry_count);
R_ASSERT(this->Write(&header, sizeof(header)));
@@ -164,9 +155,7 @@ namespace sts::kvdb {
void ArchiveWriter::WriteEntry(const void *key, size_t key_size, const void *value, size_t value_size) {
/* This should only be called after writing header. */
if (this->offset == 0) {
std::abort();
}
STS_ASSERT(this->offset != 0);
ArchiveEntryHeader header = ArchiveEntryHeader::Make(key_size, value_size);
R_ASSERT(this->Write(&header, sizeof(header)));

View File

@@ -57,9 +57,7 @@ namespace sts::kvdb {
this->backing_buffer_free_offset = 0;
this->count = 0;
this->entries = static_cast<decltype(this->entries)>(this->Allocate(sizeof(*this->entries) * this->capacity));
if (this->entries == nullptr) {
std::abort();
}
STS_ASSERT(this->entries != nullptr);
}
std::optional<size_t> FileKeyValueStore::Cache::TryGet(void *out_value, size_t max_out_size, const void *key, size_t key_size) {
@@ -106,9 +104,7 @@ namespace sts::kvdb {
}
/* Ensure key size is small enough. */
if (key_size > MaxKeySize) {
std::abort();
}
STS_ASSERT(key_size <= MaxKeySize);
/* If we're at capacity, invalidate the cache. */
if (this->count == this->capacity) {

View File

@@ -0,0 +1,182 @@
/*
* Copyright (c) 2018-2019 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"
namespace sts::os::impl {
namespace {
Result CreateEventHandles(Handle *out_readable, Handle *out_writable) {
/* Create the event handles. */
R_TRY_CATCH(svcCreateEvent(out_writable, out_readable)) {
R_CATCH(ResultKernelResourceExhausted) {
return ResultOsResourceExhausted;
}
} R_END_TRY_CATCH_WITH_ASSERT;
return ResultSuccess;
}
}
InterProcessEvent::InterProcessEvent(bool autoclear) : is_initialized(false) {
R_ASSERT(this->Initialize(autoclear));
}
InterProcessEvent::~InterProcessEvent() {
this->Finalize();
}
Result InterProcessEvent::Initialize(bool autoclear) {
STS_ASSERT(!this->is_initialized);
Handle rh, wh;
R_TRY(CreateEventHandles(&rh, &wh));
this->Initialize(rh, true, wh, true, autoclear);
return ResultSuccess;
}
void InterProcessEvent::Initialize(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) {
STS_ASSERT(!this->is_initialized);
STS_ASSERT(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;
}
Handle InterProcessEvent::DetachReadableHandle() {
STS_ASSERT(this->is_initialized);
const Handle handle = this->read_handle;
STS_ASSERT(handle != INVALID_HANDLE);
this->read_handle = INVALID_HANDLE;
this->manage_read_handle = false;
return handle;
}
Handle InterProcessEvent::DetachWritableHandle() {
STS_ASSERT(this->is_initialized);
const Handle handle = this->write_handle;
STS_ASSERT(handle != INVALID_HANDLE);
this->write_handle = INVALID_HANDLE;
this->manage_write_handle = false;
return handle;
}
Handle InterProcessEvent::GetReadableHandle() const {
STS_ASSERT(this->is_initialized);
return this->read_handle;
}
Handle InterProcessEvent::GetWritableHandle() const {
STS_ASSERT(this->is_initialized);
return this->write_handle;
}
void InterProcessEvent::Finalize() {
if (this->is_initialized) {
if (this->manage_read_handle && this->read_handle != INVALID_HANDLE) {
R_ASSERT(svcCloseHandle(this->read_handle));
}
if (this->manage_write_handle && this->write_handle != INVALID_HANDLE) {
R_ASSERT(svcCloseHandle(this->write_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;
}
void InterProcessEvent::Signal() {
R_ASSERT(svcSignalEvent(this->GetWritableHandle()));
}
void InterProcessEvent::Reset() {
Handle handle = this->GetReadableHandle();
if (handle == INVALID_HANDLE) {
handle = this->GetWritableHandle();
}
R_ASSERT(svcClearEvent(handle));
}
void InterProcessEvent::Wait() {
const Handle handle = this->GetReadableHandle();
while (true) {
/* Continuously wait, until success. */
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, U64_MAX)) {
R_CATCH(ResultKernelCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ASSERT;
/* 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(ResultKernelInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ASSERT;
}
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(ResultKernelTimedOut) { return false; }
R_CATCH(ResultKernelCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ASSERT;
/* 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(ResultKernelTimedOut) { return false; }
R_CATCH(ResultKernelCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ASSERT;
/* 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(ResultKernelInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ASSERT;
}
return true;
}
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2018-2019 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 sts::os::impl {
class WaitableHolderOfInterProcessEvent;
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();
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();
void Signal();
void Reset();
void Wait();
bool TryWait();
bool TimedWait(u64 ns);
};
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) 2018-2019 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 sts::os::impl {
class WaitableObjectList;
class WaitableManagerImpl;
class WaitableHolderBase {
public:
util::IntrusiveListNode manager_node;
util::IntrusiveListNode object_list_node;
private:
WaitableManagerImpl *manager = nullptr;
public:
/* Gets whether the held waitable is currently signaled. */
virtual TriBool IsSignaled() const = 0;
/* Adds to manager's object list, returns is signaled. */
virtual TriBool LinkToObjectList() = 0;
/* Removes from the manager's object list. */
virtual void UnlinkFromObjectList() = 0;
/* 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 U64_MAX;
}
/* Interface with manager. */
void SetManager(WaitableManagerImpl *m) {
this->manager = m;
}
WaitableManagerImpl *GetManager() const {
return this->manager;
}
bool IsLinkedToManager() const {
return this->manager != nullptr;
}
};
class WaitableHolderOfUserObject : public WaitableHolderBase {
public:
/* All user objects have no handle to wait on. */
virtual Handle GetHandle() const override {
return INVALID_HANDLE;
}
};
class WaitableHolderOfKernelObject : public WaitableHolderBase {
public:
/* All kernel objects have native handles, and thus don't have object list semantics. */
virtual TriBool LinkToObjectList() override {
return TriBool::Undefined;
}
virtual void UnlinkFromObjectList() override {
/* ... */
}
};
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2018-2019 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 "os_waitable_holder_of_handle.hpp"
#include "os_waitable_holder_of_event.hpp"
#include "os_waitable_holder_of_inter_process_event.hpp"
#include "os_waitable_holder_of_interrupt_event.hpp"
#include "os_waitable_holder_of_thread.hpp"
#include "os_waitable_holder_of_message_queue.hpp"
namespace sts::os::impl {
struct WaitableHolderImpl {
union {
TYPED_STORAGE(WaitableHolderOfHandle) holder_of_handle_storage;
TYPED_STORAGE(WaitableHolderOfEvent) holder_of_event_storage;
TYPED_STORAGE(WaitableHolderOfInterProcessEvent) holder_of_inter_process_event_storage;
TYPED_STORAGE(WaitableHolderOfInterruptEvent) holder_of_interrupt_event_storage;
TYPED_STORAGE(WaitableHolderOfThread) holder_of_thread_storage;
TYPED_STORAGE(WaitableHolderOfMessageQueueForNotFull) holder_of_mq_for_not_full_storage;
TYPED_STORAGE(WaitableHolderOfMessageQueueForNotEmpty) holder_of_mq_for_not_empty_storage;
};
};
#define CHECK_HOLDER(T) \
static_assert(std::is_base_of<::sts::os::impl::WaitableHolderBase, T>::value && std::is_trivially_destructible<T>::value, #T)
CHECK_HOLDER(WaitableHolderOfHandle);
CHECK_HOLDER(WaitableHolderOfEvent);
CHECK_HOLDER(WaitableHolderOfInterProcessEvent);
CHECK_HOLDER(WaitableHolderOfInterruptEvent);
CHECK_HOLDER(WaitableHolderOfThread);
CHECK_HOLDER(WaitableHolderOfMessageQueueForNotFull);
CHECK_HOLDER(WaitableHolderOfMessageQueueForNotEmpty);
#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");
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2018-2019 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 "os_waitable_holder_base.hpp"
#include "os_waitable_object_list.hpp"
namespace sts::os::impl {
class WaitableHolderOfEvent : public WaitableHolderOfUserObject {
private:
Event *event;
private:
TriBool IsSignaledImpl() const {
return this->event->signaled ? TriBool::True : TriBool::False;
}
public:
explicit WaitableHolderOfEvent(Event *e) : event(e) { /* ... */ }
/* IsSignaled, Link, Unlink implemented. */
virtual TriBool IsSignaled() const override {
std::scoped_lock lk(this->event->lock);
return this->IsSignaledImpl();
}
virtual TriBool LinkToObjectList() override {
std::scoped_lock lk(this->event->lock);
GetReference(this->event->waitable_object_list_storage).LinkWaitableHolder(*this);
return this->IsSignaledImpl();
}
virtual void UnlinkFromObjectList() override {
std::scoped_lock lk(this->event->lock);
GetReference(this->event->waitable_object_list_storage).UnlinkWaitableHolder(*this);
}
};
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2019 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 "os_waitable_holder_base.hpp"
namespace sts::os::impl {
class WaitableHolderOfHandle : public WaitableHolderOfKernelObject {
private:
Handle handle;
public:
explicit WaitableHolderOfHandle(Handle h) : handle(h) { /* ... */ }
/* IsSignaled, GetHandle both implemented. */
virtual TriBool IsSignaled() const override {
return TriBool::Undefined;
}
virtual Handle GetHandle() const override {
return this->handle;
}
};
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2019 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 "os_waitable_holder_base.hpp"
#include "os_inter_process_event.hpp"
namespace sts::os::impl {
class WaitableHolderOfInterProcessEvent : public WaitableHolderOfKernelObject {
private:
InterProcessEvent *event;
public:
explicit WaitableHolderOfInterProcessEvent(InterProcessEvent *e) : event(e) { /* ... */ }
/* IsSignaled, GetHandle both implemented. */
virtual TriBool IsSignaled() const override {
return TriBool::Undefined;
}
virtual Handle GetHandle() const override {
STS_ASSERT(this->event->is_initialized);
return this->event->GetReadableHandle();
}
};
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-2019 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 "os_waitable_holder_base.hpp"
namespace sts::os::impl {
class WaitableHolderOfInterruptEvent : public WaitableHolderOfKernelObject {
private:
InterruptEvent *event;
public:
explicit WaitableHolderOfInterruptEvent(InterruptEvent *e) : event(e) { /* ... */ }
/* IsSignaled, GetHandle both implemented. */
virtual TriBool IsSignaled() const override {
return TriBool::Undefined;
}
virtual Handle GetHandle() const override {
STS_ASSERT(this->event->is_initialized);
return this->event->handle.Get();
}
};
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2019 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 "os_waitable_holder_base.hpp"
#include "os_waitable_object_list.hpp"
namespace sts::os::impl {
template<MessageQueueWaitKind WaitKind>
class WaitableHolderOfMessageQueue : public WaitableHolderOfUserObject {
static_assert(WaitKind == MessageQueueWaitKind::ForNotEmpty || WaitKind == MessageQueueWaitKind::ForNotFull, "MessageQueueHolder WaitKind!");
private:
MessageQueue *message_queue;
private:
TriBool IsSignaledImpl() const {
if constexpr (WaitKind == MessageQueueWaitKind::ForNotEmpty) {
/* ForNotEmpty. */
return this->message_queue->IsEmpty() ? TriBool::False : TriBool::True;
} else /* if constexpr (WaitKind == MessageQueueWaitKind::ForNotFull) */ {
/* ForNotFull */
return this->message_queue->IsFull() ? TriBool::False : TriBool::True;
}
}
public:
explicit WaitableHolderOfMessageQueue(MessageQueue *mq) : message_queue(mq) { /* ... */ }
/* IsSignaled, Link, Unlink implemented. */
virtual TriBool IsSignaled() const override {
std::scoped_lock lk(this->message_queue->queue_lock);
return this->IsSignaledImpl();
}
virtual TriBool LinkToObjectList() override {
std::scoped_lock lk(this->message_queue->queue_lock);
GetReference(this->message_queue->waitable_object_list_storage).LinkWaitableHolder(*this);
return this->IsSignaledImpl();
}
virtual void UnlinkFromObjectList() override {
std::scoped_lock lk(this->message_queue->queue_lock);
GetReference(this->message_queue->waitable_object_list_storage).UnlinkWaitableHolder(*this);
}
};
using WaitableHolderOfMessageQueueForNotEmpty = WaitableHolderOfMessageQueue<MessageQueueWaitKind::ForNotEmpty>;
using WaitableHolderOfMessageQueueForNotFull = WaitableHolderOfMessageQueue<MessageQueueWaitKind::ForNotFull>;
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2019 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 "os_waitable_holder_base.hpp"
namespace sts::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 {
private:
Thread *thread;
public:
explicit WaitableHolderOfThread(Thread *t) : thread(t) { /* ... */ }
/* IsSignaled, GetHandle both implemented. */
virtual TriBool IsSignaled() const override {
return TriBool::Undefined;
}
virtual Handle GetHandle() const override {
return this->thread->GetHandle();
}
};
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright (c) 2018-2019 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 "os_waitable_manager_impl.hpp"
#include "os_waitable_object_list.hpp"
namespace sts::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; };
/* Prepare for processing. */
this->signaled_holder = nullptr;
WaitableHolderBase *result = this->LinkHoldersToObjectList();
/* Check if we've been signaled. */
{
std::scoped_lock lk(this->lock);
if (this->signaled_holder != nullptr) {
result = this->signaled_holder;
}
}
/* Process object array. */
if (result == nullptr) {
result = this->WaitAnyHandleImpl(infinite, timeout);
}
/* Unlink holders from the current object list. */
this->UnlinkHoldersFromObjectList();
return result;
}
WaitableHolderBase *WaitableManagerImpl::WaitAnyHandleImpl(bool infinite, u64 timeout) {
Handle object_handles[MaximumHandleCount];
WaitableHolderBase *objects[MaximumHandleCount];
const size_t count = this->BuildHandleArray(object_handles, objects);
const u64 end_time = infinite ? U64_MAX : armTicksToNs(armGetSystemTick());
while (true) {
this->current_time = armTicksToNs(armGetSystemTick());
u64 min_timeout = 0;
WaitableHolderBase *min_timeout_object = this->RecalculateNextTimeout(&min_timeout, end_time);
s32 index;
if (count == 0 && min_timeout == 0) {
index = WaitTimedOut;
} else {
index = this->WaitSynchronization(object_handles, count, min_timeout);
STS_ASSERT(index != WaitInvalid);
}
switch (index) {
case WaitTimedOut:
if (min_timeout_object) {
this->current_time = armTicksToNs(armGetSystemTick());
if (min_timeout_object->IsSignaled() == TriBool::True) {
std::scoped_lock lk(this->lock);
this->signaled_holder = min_timeout_object;
return this->signaled_holder;
}
continue;
}
return nullptr;
case WaitCancelled:
if (this->signaled_holder) {
return this->signaled_holder;
}
continue;
default: /* 0 - 0x3F, valid. */
{
std::scoped_lock lk(this->lock);
this->signaled_holder = objects[index];
return this->signaled_holder;
}
}
}
}
s32 WaitableManagerImpl::WaitSynchronization(Handle *handles, size_t count, u64 timeout) {
s32 index = WaitInvalid;
R_TRY_CATCH(svcWaitSynchronization(&index, handles, count, timeout)) {
R_CATCH(ResultKernelTimedOut) { return WaitTimedOut; }
R_CATCH(ResultKernelCancelled) { return WaitCancelled; }
/* All other results are critical errors. */
/* 7601: Thread termination requested. */
/* E401: Handle is dead. */
/* E601: Handle list address invalid. */
/* EE01: Too many handles. */
} R_END_TRY_CATCH_WITH_ASSERT;
return index;
}
size_t WaitableManagerImpl::BuildHandleArray(Handle *out_handles, WaitableHolderBase **out_objects) {
size_t count = 0;
for (WaitableHolderBase &holder_base : this->waitable_list) {
if (Handle handle = holder_base.GetHandle(); handle != INVALID_HANDLE) {
STS_ASSERT(count < MaximumHandleCount);
out_handles[count] = handle;
out_objects[count] = &holder_base;
count++;
}
}
return count;
}
WaitableHolderBase *WaitableManagerImpl::LinkHoldersToObjectList() {
WaitableHolderBase *signaled_holder = nullptr;
for (WaitableHolderBase &holder_base : this->waitable_list) {
TriBool is_signaled = holder_base.LinkToObjectList();
if (signaled_holder == nullptr && is_signaled == TriBool::True) {
signaled_holder = &holder_base;
}
}
return signaled_holder;
}
void WaitableManagerImpl::UnlinkHoldersFromObjectList() {
for (WaitableHolderBase &holder_base : this->waitable_list) {
holder_base.UnlinkFromObjectList();
}
}
WaitableHolderBase *WaitableManagerImpl::RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time) {
WaitableHolderBase *min_timeout_holder = nullptr;
u64 min_time = end_time;
for (WaitableHolderBase &holder_base : this->waitable_list) {
if (const u64 cur_time = holder_base.GetWakeupTime(); cur_time < min_time) {
min_timeout_holder = &holder_base;
min_time = cur_time;
}
}
if (min_time < this->current_time) {
*out_min_timeout = 0;
} else {
*out_min_timeout = min_time - this->current_time;
}
return min_timeout_holder;
}
void WaitableManagerImpl::SignalAndWakeupThread(WaitableHolderBase *holder_base) {
std::scoped_lock lk(this->lock);
if (this->signaled_holder == nullptr) {
this->signaled_holder = holder_base;
R_ASSERT(svcCancelSynchronization(this->waiting_thread_handle));
}
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (c) 2018-2019 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 "os_waitable_holder_base.hpp"
namespace sts::os::impl {
class WaitableManagerImpl {
public:
static constexpr size_t MaximumHandleCount = 0x40;
static constexpr s32 WaitInvalid = -3;
static constexpr s32 WaitCancelled = -2;
static constexpr s32 WaitTimedOut = -1;
using ListType = util::IntrusiveListMemberTraits<&WaitableHolderBase::manager_node>::ListType;
private:
ListType waitable_list;
WaitableHolderBase *signaled_holder;
u64 current_time;
Mutex lock;
Handle waiting_thread_handle;
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 *LinkHoldersToObjectList();
void UnlinkHoldersFromObjectList();
WaitableHolderBase *RecalculateNextTimeout(u64 *out_min_timeout, u64 end_time);
public:
/* Wait. */
WaitableHolderBase *WaitAny() {
return this->WaitAnyImpl(true, U64_MAX);
}
WaitableHolderBase *TryWaitAny() {
return this->WaitAnyImpl(false, 0);
}
WaitableHolderBase *TimedWaitAny(u64 timeout) {
return this->WaitAnyImpl(false, timeout);
}
/* List management. */
bool IsEmpty() const {
return this->waitable_list.empty();
}
void LinkWaitableHolder(WaitableHolderBase &holder_base) {
this->waitable_list.push_back(holder_base);
}
void UnlinkWaitableHolder(WaitableHolderBase &holder_base) {
this->waitable_list.erase(this->waitable_list.iterator_to(holder_base));
}
void UnlinkAll() {
while (!this->IsEmpty()) {
this->waitable_list.front().SetManager(nullptr);
this->waitable_list.pop_front();
}
}
void MoveAllFrom(WaitableManagerImpl &other) {
/* Set manager for all of the other's waitables. */
for (auto &w : other.waitable_list) {
w.SetManager(this);
}
this->waitable_list.splice(this->waitable_list.end(), other.waitable_list);
}
/* Other. */
u64 GetCurrentTime() const {
return this->current_time;
}
void SignalAndWakeupThread(WaitableHolderBase *holder_base);
};
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2018-2019 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 "os_waitable_holder_base.hpp"
#include "os_waitable_manager_impl.hpp"
namespace sts::os::impl {
class WaitableObjectList {
public:
using ListType = util::IntrusiveListMemberTraits<&WaitableHolderBase::object_list_node>::ListType;
private:
ListType object_list;
public:
void SignalAllThreads() {
for (WaitableHolderBase &holder_base : this->object_list) {
holder_base.GetManager()->SignalAndWakeupThread(&holder_base);
}
}
void WakeupAllThreads() {
for (WaitableHolderBase &holder_base : this->object_list) {
holder_base.GetManager()->SignalAndWakeupThread(nullptr);
}
}
bool IsEmpty() const {
return this->object_list.empty();
}
void LinkWaitableHolder(WaitableHolderBase &holder_base) {
this->object_list.push_back(holder_base);
}
void UnlinkWaitableHolder(WaitableHolderBase &holder_base) {
this->object_list.erase(this->object_list.iterator_to(holder_base));
}
};
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (c) 2018-2019 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_object_list.hpp"
namespace sts::os {
Event::Event(bool a, bool s) : auto_clear(a), signaled(s) {
new (GetPointer(this->waitable_object_list_storage)) impl::WaitableObjectList();
}
Event::~Event() {
GetReference(this->waitable_object_list_storage).~WaitableObjectList();
}
void Event::Signal() {
std::scoped_lock lk(this->lock);
/* If we're already signaled, nothing more to do. */
if (this->signaled) {
return;
}
this->signaled = true;
/* Signal! */
if (this->auto_clear) {
/* If we're auto clear, signal one thread, which will clear. */
this->cv.Signal();
} else {
/* If we're manual clear, increment counter and wake all. */
this->counter++;
this->cv.Broadcast();
}
/* Wake up whatever manager, if any. */
GetReference(this->waitable_object_list_storage).SignalAllThreads();
}
void Event::Reset() {
std::scoped_lock lk(this->lock);
this->signaled = false;
}
void Event::Wait() {
std::scoped_lock lk(this->lock);
u64 cur_counter = this->counter;
while (!this->signaled) {
if (this->counter != cur_counter) {
break;
}
this->cv.Wait(&this->lock);
}
if (this->auto_clear) {
this->signaled = false;
}
}
bool Event::TryWait() {
std::scoped_lock lk(this->lock);
const bool success = this->signaled;
if (this->auto_clear) {
this->signaled = false;
}
return success;
}
bool Event::TimedWait(u64 ns) {
TimeoutHelper timeout_helper(ns);
std::scoped_lock lk(this->lock);
u64 cur_counter = this->counter;
while (!this->signaled) {
if (this->counter != cur_counter) {
break;
}
if (R_FAILED(this->cv.TimedWait(&this->lock, timeout_helper.NsUntilTimeout()))) {
return false;
}
}
if (this->auto_clear) {
this->signaled = false;
}
return true;
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (c) 2018-2019 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_object_list.hpp"
namespace sts::os {
Result InterruptEvent::Initialize(u32 interrupt_id, bool autoclear) {
STS_ASSERT(!this->is_initialized);
this->auto_clear = autoclear;
const auto type = this->auto_clear ? svc::InterruptType_Edge : svc::InterruptType_Level;
R_TRY(svcCreateInterruptEvent(this->handle.GetPointer(), interrupt_id, type));
this->is_initialized = true;
return ResultSuccess;
}
void InterruptEvent::Finalize() {
STS_ASSERT(this->is_initialized);
R_ASSERT(svcCloseHandle(this->handle.Move()));
this->auto_clear = true;
this->is_initialized = false;
}
InterruptEvent::InterruptEvent(u32 interrupt_id, bool autoclear) {
this->is_initialized = false;
R_ASSERT(this->Initialize(interrupt_id, autoclear));
}
void InterruptEvent::Reset() {
R_ASSERT(svcClearEvent(this->handle.Get()));
}
void InterruptEvent::Wait() {
STS_ASSERT(this->is_initialized);
while (true) {
/* Continuously wait, until success. */
R_TRY_CATCH(svcWaitSynchronizationSingle(this->handle.Get(), U64_MAX)) {
R_CATCH(ResultKernelCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ASSERT;
/* 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(ResultKernelInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ASSERT;
}
return;
}
}
bool InterruptEvent::TryWait() {
STS_ASSERT(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(ResultKernelTimedOut) { return false; }
R_CATCH(ResultKernelCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ASSERT;
/* We succeeded, so we're signaled. */
return true;
}
}
}
bool InterruptEvent::TimedWait(u64 ns) {
STS_ASSERT(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(ResultKernelTimedOut) { return false; }
R_CATCH(ResultKernelCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ASSERT;
/* 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(ResultKernelInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ASSERT;
}
return true;
}
}
}

View File

@@ -14,14 +14,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/os_waitable_object_list.hpp"
namespace sts::os {
MessageQueue::MessageQueue(std::unique_ptr<uintptr_t[]> buf, size_t c): buffer(std::move(buf)), capacity(c) {
new (GetPointer(this->waitable_object_list_storage)) impl::WaitableObjectList();
}
MessageQueue::~MessageQueue() {
GetReference(this->waitable_object_list_storage).~WaitableObjectList();
}
void MessageQueue::SendInternal(uintptr_t data) {
/* Ensure we don't corrupt the queue, but this should never happen. */
if (this->count >= this->capacity) {
std::abort();
}
STS_ASSERT(this->count < this->capacity);
/* Write data to tail of queue. */
this->buffer[(this->count++ + this->offset) % this->capacity] = data;
@@ -29,9 +36,7 @@ namespace sts::os {
void MessageQueue::SendNextInternal(uintptr_t data) {
/* Ensure we don't corrupt the queue, but this should never happen. */
if (this->count >= this->capacity) {
std::abort();
}
STS_ASSERT(this->count < this->capacity);
/* Write data to head of queue. */
this->offset = (this->offset + this->capacity - 1) % this->capacity;
@@ -41,9 +46,7 @@ namespace sts::os {
uintptr_t MessageQueue::ReceiveInternal() {
/* Ensure we don't corrupt the queue, but this should never happen. */
if (this->count == 0) {
std::abort();
}
STS_ASSERT(this->count > 0);
uintptr_t data = this->buffer[this->offset];
this->offset = (this->offset + 1) % this->capacity;
@@ -53,9 +56,7 @@ namespace sts::os {
uintptr_t MessageQueue::PeekInternal() {
/* Ensure we don't corrupt the queue, but this should never happen. */
if (this->count == 0) {
std::abort();
}
STS_ASSERT(this->count > 0);
return this->buffer[this->offset];
}

View File

@@ -0,0 +1,189 @@
/*
* Copyright (c) 2018-2019 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_holder_impl.hpp"
namespace sts::os {
SystemEvent::SystemEvent(bool inter_process, bool autoclear) : state(SystemEventState::Uninitialized) {
if (inter_process) {
R_ASSERT(this->InitializeAsInterProcessEvent(autoclear));
} else {
R_ASSERT(this->InitializeAsEvent(autoclear));
}
}
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() {
STS_ASSERT(this->state == SystemEventState::Event);
return GetReference(this->storage_for_event);
}
const Event &SystemEvent::GetEvent() const {
STS_ASSERT(this->state == SystemEventState::Event);
return GetReference(this->storage_for_event);
}
impl::InterProcessEvent &SystemEvent::GetInterProcessEvent() {
STS_ASSERT(this->state == SystemEventState::InterProcessEvent);
return GetReference(this->storage_for_inter_process_event);
}
const impl::InterProcessEvent &SystemEvent::GetInterProcessEvent() const {
STS_ASSERT(this->state == SystemEventState::InterProcessEvent);
return GetReference(this->storage_for_inter_process_event);
}
Result SystemEvent::InitializeAsEvent(bool autoclear) {
STS_ASSERT(this->state == SystemEventState::Uninitialized);
new (GetPointer(this->storage_for_event)) Event(autoclear);
this->state = SystemEventState::Event;
return ResultSuccess;
}
Result SystemEvent::InitializeAsInterProcessEvent(bool autoclear) {
STS_ASSERT(this->state == SystemEventState::Uninitialized);
new (GetPointer(this->storage_for_inter_process_event)) impl::InterProcessEvent();
this->state = SystemEventState::InterProcessEvent;
R_TRY_CLEANUP(this->GetInterProcessEvent().Initialize(autoclear), {
this->Finalize();
});
return ResultSuccess;
}
void SystemEvent::AttachHandles(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) {
STS_ASSERT(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() {
STS_ASSERT(this->state == SystemEventState::InterProcessEvent);
return this->GetInterProcessEvent().DetachReadableHandle();
}
Handle SystemEvent::DetachWritableHandle() {
STS_ASSERT(this->state == SystemEventState::InterProcessEvent);
return this->GetInterProcessEvent().DetachWritableHandle();
}
Handle SystemEvent::GetReadableHandle() const {
STS_ASSERT(this->state == SystemEventState::InterProcessEvent);
return this->GetInterProcessEvent().GetReadableHandle();
}
Handle SystemEvent::GetWritableHandle() const {
STS_ASSERT(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;
default:
std::abort();
}
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:
default:
std::abort();
}
}
void SystemEvent::Reset() {
switch (this->state) {
case SystemEventState::Event:
this->GetEvent().Reset();
break;
case SystemEventState::InterProcessEvent:
this->GetInterProcessEvent().Reset();
break;
case SystemEventState::Uninitialized:
default:
std::abort();
}
}
void SystemEvent::Wait() {
switch (this->state) {
case SystemEventState::Event:
this->GetEvent().Wait();
break;
case SystemEventState::InterProcessEvent:
this->GetInterProcessEvent().Wait();
break;
case SystemEventState::Uninitialized:
default:
std::abort();
}
}
bool SystemEvent::TryWait() {
switch (this->state) {
case SystemEventState::Event:
return this->GetEvent().TryWait();
case SystemEventState::InterProcessEvent:
return this->GetInterProcessEvent().TryWait();
case SystemEventState::Uninitialized:
default:
std::abort();
}
}
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:
default:
std::abort();
}
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (c) 2018-2019 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_holder_impl.hpp"
#include "impl/os_waitable_manager_impl.hpp"
namespace sts::os {
WaitableHolder::WaitableHolder(Handle handle) {
/* Don't allow invalid handles. */
STS_ASSERT(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:
default:
std::abort();
}
/* 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(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;
default:
std::abort();
}
/* 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. */
STS_ASSERT(!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. */
STS_ASSERT(holder_base->IsLinkedToManager());
holder_base->GetManager()->UnlinkWaitableHolder(*holder_base);
holder_base->SetManager(nullptr);
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (c) 2018-2019 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_holder_impl.hpp"
#include "impl/os_waitable_manager_impl.hpp"
namespace sts::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. */
STS_ASSERT(impl.IsEmpty());
impl.~WaitableManagerImpl();
}
/* Wait. */
WaitableHolder *WaitableManager::WaitAny() {
auto &impl = GetReference(this->impl_storage);
/* Don't allow waiting on empty list. */
STS_ASSERT(!impl.IsEmpty());
return reinterpret_cast<WaitableHolder *>(impl.WaitAny());
}
WaitableHolder *WaitableManager::TryWaitAny() {
auto &impl = GetReference(this->impl_storage);
/* Don't allow waiting on empty list. */
STS_ASSERT(!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. */
STS_ASSERT(!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. */
STS_ASSERT(!holder_base->IsLinkedToManager());
impl.LinkWaitableHolder(*holder_base);
holder_base->SetManager(&impl);
}
void WaitableManager::UnlinkAll() {
auto &impl = GetReference(this->impl_storage);
impl.UnlinkAll();
}
void WaitableManager::MoveAllFrom(WaitableManager *other) {
auto &dst_impl = GetReference(this->impl_storage);
auto &src_impl = GetReference(other->impl_storage);
dst_impl.MoveAllFrom(src_impl);
}
}

View File

@@ -101,15 +101,11 @@ namespace sts::patcher {
void ApplyIpsPatch(u8 *mapped_module, size_t mapped_size, size_t protected_size, size_t offset, bool is_ips32, FILE *f_ips) {
/* Validate offset/protected size. */
if (offset > protected_size) {
std::abort();
}
STS_ASSERT(offset <= protected_size);
u8 buffer[sizeof(Ips32TailMagic)];
while (true) {
if (fread(buffer, is_ips32 ? sizeof(Ips32TailMagic) : sizeof(IpsTailMagic), 1, f_ips) != 1) {
std::abort();
}
STS_ASSERT(fread(buffer, is_ips32 ? sizeof(Ips32TailMagic) : sizeof(IpsTailMagic), 1, f_ips) == 1);
if (IsIpsTail(is_ips32, buffer)) {
break;
@@ -119,24 +115,18 @@ namespace sts::patcher {
u32 patch_offset = GetIpsPatchOffset(is_ips32, buffer);
/* Size of patch. */
if (fread(buffer, 2, 1, f_ips) != 1) {
std::abort();
}
STS_ASSERT(fread(buffer, 2, 1, f_ips) == 1);
u32 patch_size = GetIpsPatchSize(is_ips32, buffer);
/* Check for RLE encoding. */
if (patch_size == 0) {
/* Size of RLE. */
if (fread(buffer, 2, 1, f_ips) != 1) {
std::abort();
}
STS_ASSERT(fread(buffer, 2, 1, f_ips) == 1);
u32 rle_size = (buffer[0] << 8) | (buffer[1]);
/* Value for RLE. */
if (fread(buffer, 1, 1, f_ips) != 1) {
std::abort();
}
STS_ASSERT(fread(buffer, 1, 1, f_ips) == 1);
/* Ensure we don't write to protected region. */
if (patch_offset < protected_size) {
@@ -179,9 +169,7 @@ namespace sts::patcher {
if (patch_offset + read_size > mapped_size) {
read_size = mapped_size - patch_offset;
}
if (fread(mapped_module + patch_offset, read_size, 1, f_ips) != 1) {
std::abort();
}
STS_ASSERT(fread(mapped_module + patch_offset, read_size, 1, f_ips) == 1);
if (patch_size > read_size) {
fseek(f_ips, patch_size - read_size, SEEK_CUR);
}

View File

@@ -142,9 +142,7 @@ namespace sts::updater {
Result GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) {
/* Ensure we can read content metas. */
constexpr size_t MaxContentMetas = 0x40;
if (work_buffer_size < sizeof(NcmMetaRecord) * MaxContentMetas) {
std::abort();
}
STS_ASSERT(work_buffer_size >= sizeof(NcmMetaRecord) * MaxContentMetas);
/* Open NAND System meta database, list contents. */
NcmContentMetaDatabase meta_db;
@@ -161,9 +159,7 @@ namespace sts::updater {
return ResultUpdaterBootImagePackageNotFound;
}
if (total_entries != written_entries) {
std::abort();
}
STS_ASSERT(total_entries == written_entries);
/* Output is sorted, return the lowest valid exfat entry. */
if (total_entries > 1) {
@@ -203,7 +199,7 @@ namespace sts::updater {
return ResultUpdaterBootImagePackageNotFound;
}
} R_END_TRY_CATCH;
ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } };
ON_SCOPE_EXIT { R_ASSERT(romfsUnmount(GetBootImagePackageMountPath())); };
/* Read and validate hashes of boot images. */
{
@@ -258,7 +254,7 @@ namespace sts::updater {
return ResultUpdaterBootImagePackageNotFound;
}
} R_END_TRY_CATCH;
ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } };
ON_SCOPE_EXIT { R_ASSERT(romfsUnmount(GetBootImagePackageMountPath())); };
/* Read and validate hashes of boot images. */
{
@@ -329,7 +325,7 @@ namespace sts::updater {
return ResultUpdaterBootImagePackageNotFound;
}
} R_END_TRY_CATCH;
ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } };
ON_SCOPE_EXIT { R_ASSERT(romfsUnmount(GetBootImagePackageMountPath())); };
{
Boot0Accessor boot0_accessor;
@@ -386,7 +382,7 @@ namespace sts::updater {
return ResultUpdaterBootImagePackageNotFound;
}
} R_END_TRY_CATCH;
ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } };
ON_SCOPE_EXIT { R_ASSERT(romfsUnmount(GetBootImagePackageMountPath())); };
{
Boot0Accessor boot0_accessor;
@@ -533,9 +529,7 @@ namespace sts::updater {
/* Get a session to ncm. */
DoWithSmSession([&]() {
if (R_FAILED(ncmInitialize())) {
std::abort();
}
R_ASSERT(ncmInitialize());
});
ON_SCOPE_EXIT { ncmExit(); };

View File

@@ -35,23 +35,18 @@ namespace sts::updater {
}
Result BisAccessor::Read(void *dst, size_t size, u64 offset) {
if (offset % SectorAlignment) {
std::abort();
}
STS_ASSERT((offset % SectorAlignment) == 0);
return fsStorageRead(&this->storage, offset, dst, size);
}
Result BisAccessor::Write(u64 offset, const void *src, size_t size) {
if (offset % SectorAlignment) {
std::abort();
}
STS_ASSERT((offset % SectorAlignment) == 0);
return fsStorageWrite(&this->storage, offset, src, size);
}
Result BisAccessor::Write(u64 offset, size_t size, const char *bip_path, void *work_buffer, size_t work_buffer_size) {
if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) {
std::abort();
}
STS_ASSERT((offset % SectorAlignment) == 0);
STS_ASSERT((work_buffer_size % SectorAlignment) == 0);
FILE *bip_fp = fopen(bip_path, "rb");
if (bip_fp == NULL) {
@@ -68,9 +63,7 @@ namespace sts::updater {
return fsdevGetLastResult();
}
}
if (written + read_size > size) {
std::abort();
}
STS_ASSERT(written + read_size <= size);
size_t aligned_size = ((read_size + SectorAlignment - 1) / SectorAlignment) * SectorAlignment;
R_TRY(this->Write(offset + written, work_buffer, aligned_size));
@@ -84,9 +77,8 @@ namespace sts::updater {
}
Result BisAccessor::Clear(u64 offset, u64 size, void *work_buffer, size_t work_buffer_size) {
if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) {
std::abort();
}
STS_ASSERT((offset % SectorAlignment) == 0);
STS_ASSERT((work_buffer_size % SectorAlignment) == 0);
std::memset(work_buffer, 0, work_buffer_size);
@@ -100,9 +92,8 @@ namespace sts::updater {
}
Result BisAccessor::GetHash(void *dst, u64 offset, u64 size, u64 hash_size, void *work_buffer, size_t work_buffer_size) {
if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) {
std::abort();
}
STS_ASSERT((offset % SectorAlignment) == 0);
STS_ASSERT((work_buffer_size % SectorAlignment) == 0);
Sha256Context sha_ctx;
sha256ContextCreate(&sha_ctx);
@@ -122,18 +113,12 @@ namespace sts::updater {
size_t Boot0Accessor::GetBootloaderVersion(void *bct) {
u32 version = *reinterpret_cast<u32 *>(reinterpret_cast<uintptr_t>(bct) + BctVersionOffset);
if (version > BctVersionMax) {
std::abort();
}
STS_ASSERT(version <= BctVersionMax);
return static_cast<size_t>(version);
}
size_t Boot0Accessor::GetEksIndex(size_t bootloader_version) {
if (bootloader_version > BctVersionMax) {
std::abort();
}
STS_ASSERT(bootloader_version <= BctVersionMax);
return (bootloader_version > 0) ? bootloader_version - 1 : 0;
}

View File

@@ -140,9 +140,7 @@ namespace sts::updater {
public:
Result Read(size_t *out_size, void *dst, size_t size, EnumType which) {
const auto entry = FindEntry(which);
if (size < entry->size) {
std::abort();
}
STS_ASSERT(size >= entry->size);
R_TRY(BisAccessor::Read(dst, entry->size, entry->offset));
@@ -152,10 +150,8 @@ namespace sts::updater {
Result Write(const void *src, size_t size, EnumType which) {
const auto entry = FindEntry(which);
if (size > entry->size || size % BisAccessor::SectorAlignment != 0) {
std::abort();
}
STS_ASSERT(size <= entry->size);
STS_ASSERT((size % BisAccessor::SectorAlignment) == 0);
return BisAccessor::Write(entry->offset, src, size);
}

View File

@@ -33,9 +33,9 @@ namespace sts::updater {
}
Result BisSave::Initialize(void *work_buffer, size_t work_buffer_size) {
if (work_buffer_size < SaveSize || reinterpret_cast<uintptr_t>(work_buffer) & 0xFFF || work_buffer_size & 0x1FF) {
std::abort();
}
STS_ASSERT(work_buffer_size >= SaveSize);
STS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(work_buffer), 0x1000));
STS_ASSERT(util::IsAligned(work_buffer_size, 0x200));
R_TRY(this->accessor.Initialize());
this->save_buffer = work_buffer;

View File

@@ -34,9 +34,7 @@ namespace sts::updater {
constexpr const char *Package2PathA = "bip:/a/package2";
const char *ChooseCandidatePath(const char * const *candidates, size_t num_candidates) {
if (num_candidates == 0) {
std::abort();
}
STS_ASSERT(num_candidates > 0);
for (size_t i = 0; i < num_candidates; i++) {
struct stat buf;

View File

@@ -25,9 +25,8 @@ namespace sts::util {
/* Compression utilities. */
int CompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) {
/* Size checks. */
if (dst_size > std::numeric_limits<int>::max() || src_size > std::numeric_limits<int>::max()) {
std::abort();
}
STS_ASSERT(dst_size <= std::numeric_limits<int>::max());
STS_ASSERT(src_size <= std::numeric_limits<int>::max());
/* This is just a thin wrapper around LZ4. */
return LZ4_compress_default(reinterpret_cast<const char *>(src), reinterpret_cast<char *>(dst), static_cast<int>(src_size), static_cast<int>(dst_size));
@@ -36,9 +35,8 @@ namespace sts::util {
/* Decompression utilities. */
int DecompressLZ4(void *dst, size_t dst_size, const void *src, size_t src_size) {
/* Size checks. */
if (dst_size > std::numeric_limits<int>::max() || src_size > std::numeric_limits<int>::max()) {
std::abort();
}
STS_ASSERT(dst_size <= std::numeric_limits<int>::max());
STS_ASSERT(src_size <= std::numeric_limits<int>::max());
/* This is just a thin wrapper around LZ4. */
return LZ4_decompress_safe(reinterpret_cast<const char *>(src), reinterpret_cast<char *>(dst), static_cast<int>(src_size), static_cast<int>(dst_size));

View File

@@ -50,9 +50,7 @@ namespace sts::util::ini {
size_t try_read = std::min(size_t(num - 1), ctx->num_left);
size_t actually_read;
R_ASSERT(fsFileRead(ctx->f, ctx->offset, str, try_read, FS_READOPTION_NONE, &actually_read));
if (actually_read != try_read) {
std::abort();
}
STS_ASSERT(actually_read == try_read);
/* Only "read" up to the first \n. */
size_t offset = actually_read;