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

@@ -21,13 +21,9 @@
#include "stratosphere/utilities.hpp"
#include "stratosphere/emummc_utilities.hpp"
#include "stratosphere/scope_guard.hpp"
#include "stratosphere/version_check.hpp"
#include "stratosphere/auto_handle.hpp"
#include "stratosphere/iwaitable.hpp"
#include "stratosphere/event.hpp"
#include "stratosphere/waitable_manager.hpp"

View File

@@ -1,80 +0,0 @@
/*
* 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 <switch.h>
#include "defines.hpp"
class AutoHandle {
NON_COPYABLE(AutoHandle);
private:
Handle hnd;
public:
AutoHandle() : hnd(INVALID_HANDLE) { /* ... */ }
AutoHandle(Handle h) : hnd(h) { /* ... */ }
~AutoHandle() {
if (this->hnd != INVALID_HANDLE) {
svcCloseHandle(this->hnd);
this->hnd = INVALID_HANDLE;
}
}
AutoHandle(AutoHandle&& rhs) {
this->hnd = rhs.hnd;
rhs.hnd = INVALID_HANDLE;
}
AutoHandle& operator=(AutoHandle&& rhs) {
rhs.Swap(*this);
return *this;
}
explicit operator bool() const {
return this->hnd != INVALID_HANDLE;
}
void Swap(AutoHandle& rhs) {
std::swap(this->hnd, rhs.hnd);
}
Handle Get() const {
return this->hnd;
}
Handle *GetPointer() {
return &this->hnd;
}
Handle *GetPointerAndClear() {
this->Clear();
return this->GetPointer();
}
Handle Move() {
const Handle h = this->hnd;
this->hnd = INVALID_HANDLE;
return h;
}
void Reset(Handle h) {
AutoHandle(h).Swap(*this);
}
void Clear() {
this->Reset(INVALID_HANDLE);
}
};

View File

@@ -19,6 +19,8 @@
/* Any broadly useful language defines should go here. */
#define STS_ASSERT(expr) do { if (!(expr)) { std::abort(); } } while (0)
#define NON_COPYABLE(cls) \
cls(const cls&) = delete; \
cls& operator=(const cls&) = delete
@@ -30,21 +32,12 @@
#define ALIGNED(algn) __attribute__((aligned(algn)))
#define WEAK __attribute__((weak))
namespace sts::util {
/* std::size() does not support zero-size C arrays. We're fixing that. */
template<class C>
constexpr auto size(const C& c) -> decltype(c.size()) {
return std::size(c);
}
#define CONCATENATE_IMPL(S1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
template<class C>
constexpr std::size_t size(const C& c) {
if constexpr (sizeof(C) == 0) {
return 0;
} else {
return std::size(c);
}
}
}
#ifdef __COUNTER__
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__)
#else
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__)
#endif

View File

@@ -1,139 +0,0 @@
/*
* 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 <switch.h>
#include <algorithm>
#include <vector>
#include "iwaitable.hpp"
#include "results.hpp"
class IEvent : public IWaitable {
public:
/* Information members. */
Handle r_h;
Handle w_h;
bool autoclear;
public:
IEvent(bool a = false) : r_h(INVALID_HANDLE), w_h(INVALID_HANDLE), autoclear(a) { }
IEvent(Handle r, bool a = false) : r_h(r), w_h(INVALID_HANDLE), autoclear(a) { }
IEvent(Handle r, Handle w, bool a = false) : r_h(r), w_h(w), autoclear(a) { }
~IEvent() {
if (r_h != INVALID_HANDLE) {
svcCloseHandle(r_h);
}
if (w_h != INVALID_HANDLE) {
svcCloseHandle(w_h);
}
}
/* Make it non-copyable */
IEvent() = delete;
IEvent(const IEvent &) = delete;
IEvent& operator=(const IEvent&) = delete;
bool IsAutoClear() {
return this->autoclear;
}
void Clear() {
std::scoped_lock lock(this->sig_lock);
this->is_signaled = false;
if (this->w_h != INVALID_HANDLE) {
svcClearEvent(this->w_h);
} else if (this->r_h != INVALID_HANDLE) {
svcResetSignal(this->r_h);
}
}
void Signal() {
std::scoped_lock lock(this->sig_lock);
if (this->w_h == INVALID_HANDLE && this->r_h != INVALID_HANDLE) {
/* We can't signal an event if we only have a read handle. */
std::abort();
}
if (this->w_h == INVALID_HANDLE && this->is_signaled) {
return;
}
this->is_signaled = true;
if (this->w_h != INVALID_HANDLE) {
svcSignalEvent(this->w_h);
} else {
this->NotifyManagerSignaled();
}
}
virtual Result HandleSignaled(u64 timeout) = 0;
/* IWaitable */
virtual Handle GetHandle() override {
return this->r_h;
}
};
template<class F>
class HosEvent : public IEvent {
private:
F callback;
public:
HosEvent(F f, bool a = false) : IEvent(a), callback(std::move(f)) { }
HosEvent(Handle r, F f, bool a = false) : IEvent(r, a), callback(std::move(f)) { }
HosEvent(Handle r, Handle w, F f, bool a = false) : IEvent(r, w, a), callback(std::move(f)) { }
virtual Result HandleSignaled(u64 timeout) override {
if (this->IsAutoClear()) {
this->Clear();
}
return this->callback(timeout);
}
};
template <class F>
static IEvent *CreateHosEvent(F f, bool autoclear = false) {
return new HosEvent<F>(INVALID_HANDLE, INVALID_HANDLE, std::move(f), autoclear);
}
template <class F>
static IEvent *CreateSystemEvent(F f, bool autoclear = false) {
Handle w_h, r_h;
R_ASSERT(svcCreateEvent(&w_h, &r_h));
return new HosEvent<F>(r_h, w_h, std::move(f), autoclear);
}
template <class F>
static IEvent *CreateInterruptEvent(F f, u64 irq, bool autoclear = false) {
Handle r_h;
/* flag is "rising edge vs level". */
R_ASSERT(svcCreateInterruptEvent(&r_h, irq, autoclear ? 0 : 1));
return new HosEvent<F>(r_h, INVALID_HANDLE, std::move(f), autoclear);
}
template <bool a = false>
static IEvent *CreateWriteOnlySystemEvent() {
return CreateSystemEvent([](u64 timeout) -> Result { std::abort(); }, a);
}
template <class F>
static IEvent *LoadReadOnlySystemEvent(Handle r_h, F f, bool autoclear = false) {
return new HosEvent<F>(r_h, f, autoclear);
}

View File

@@ -68,9 +68,7 @@ namespace sts::kvdb {
Result Initialize(size_t size) {
/* Check that we're not already initialized. */
if (this->buffer != nullptr) {
std::abort();
}
STS_ASSERT(this->buffer == nullptr);
/* Allocate a buffer. */
this->buffer = static_cast<u8 *>(std::malloc(size));

View File

@@ -32,9 +32,7 @@ namespace sts::kvdb {
private:
/* Utility. */
static inline void CheckLength(size_t len) {
if (len >= N) {
std::abort();
}
STS_ASSERT(len < N);
}
public:
/* Constructors. */
@@ -115,9 +113,8 @@ namespace sts::kvdb {
/* Substring utilities. */
void GetSubstring(char *dst, size_t dst_size, size_t offset, size_t length) const {
/* Make sure output buffer can hold the substring. */
if (offset + length > GetLength() || dst_size <= length) {
std::abort();
}
STS_ASSERT(offset + length <= GetLength());
STS_ASSERT(dst_size > length);
/* Copy substring to dst. */
std::strncpy(dst, this->buffer + offset, length);
dst[length] = 0;

View File

@@ -60,9 +60,7 @@ namespace sts::kvdb {
}
private:
void RemoveIndex(size_t i) {
if (i >= this->GetCount()) {
std::abort();
}
STS_ASSERT(i < this->GetCount());
std::memmove(this->keys + i, this->keys + i + 1, sizeof(*this->keys) * (this->GetCount() - (i + 1)));
this->DecrementCount();
}
@@ -79,9 +77,8 @@ namespace sts::kvdb {
Result Initialize(const char *path, void *buf, size_t size) {
/* Only initialize once, and ensure we have sufficient memory. */
if (this->keys != nullptr || size < BufferSize) {
std::abort();
}
STS_ASSERT(this->keys == nullptr);
SSS_ASSERT(size >= BufferSize);
/* Setup member variables. */
this->keys = static_cast<Key *>(buf);
@@ -148,31 +145,23 @@ namespace sts::kvdb {
}
Key Get(size_t i) const {
if (i >= this->GetCount()) {
std::abort();
}
STS_ASSERT(i < this->GetCount());
return this->keys[i];
}
Key Peek() const {
if (this->IsEmpty()) {
std::abort();
}
STS_ASSERT(!this->IsEmpty());
return this->Get(0);
}
void Push(const Key &key) {
if (this->IsFull()) {
std::abort();
}
STS_ASSERT(!this->IsFull());
this->keys[this->GetCount()] = key;
this->IncrementCount();
}
Key Pop() {
if (this->IsEmpty()) {
std::abort();
}
STS_ASSERT(!this->IsEmpty());
this->RemoveIndex(0);
}

View File

@@ -96,9 +96,7 @@ namespace sts::kvdb {
static_assert(std::is_pod<Value>::value && !std::is_pointer<Value>::value, "Invalid FileKeyValueStore Value!");
size_t size = 0;
R_TRY(this->Get(&size, out_value, sizeof(Value), key));
if (size < sizeof(Value)) {
std::abort();
}
STS_ASSERT(size >= sizeof(Value));
return ResultSuccess;
}

View File

@@ -18,6 +18,9 @@
#include <algorithm>
#include <switch.h>
#include <sys/stat.h>
#include "../defines.hpp"
#include "../results.hpp"
#include "../util.hpp"
#include "kvdb_auto_buffer.hpp"
#include "kvdb_archive.hpp"
#include "kvdb_bounded_string.hpp"
@@ -47,9 +50,7 @@ namespace sts::kvdb {
Value *GetValuePointer() {
/* Size check. Note: Nintendo does not size check. */
if constexpr (!std::is_same<Value, void>::value) {
if (sizeof(Value) > this->value_size) {
std::abort();
}
STS_ASSERT(sizeof(Value) <= this->value_size);
/* Ensure we only get pod. */
static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod");
}
@@ -60,9 +61,7 @@ namespace sts::kvdb {
const Value *GetValuePointer() const {
/* Size check. Note: Nintendo does not size check. */
if constexpr (!std::is_same<Value, void>::value) {
if (sizeof(Value) > this->value_size) {
std::abort();
}
STS_ASSERT(sizeof(Value) <= this->value_size);
/* Ensure we only get pod. */
static_assert(std::is_pod<Value>::value, "KeyValueStore Values must be pod");
}

View File

@@ -17,10 +17,16 @@
#pragma once
#include <switch.h>
#include "os/os_common_types.hpp"
#include "os/os_managed_handle.hpp"
#include "os/os_mutex.hpp"
#include "os/os_condvar.hpp"
#include "os/os_semaphore.hpp"
#include "os/os_timeout_helper.hpp"
#include "os/os_event.hpp"
#include "os/os_system_event.hpp"
#include "os/os_interrupt_event.hpp"
#include "os/os_thread.hpp"
#include "os/os_message_queue.hpp"
#include "os/os_message_queue.hpp"
#include "os/os_waitable_holder.hpp"
#include "os/os_waitable_manager.hpp"

View File

@@ -0,0 +1,32 @@
/*
* 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 <switch.h>
namespace sts::os {
enum class TriBool {
False = 0,
True = 1,
Undefined = 2,
};
enum class MessageQueueWaitKind {
ForNotEmpty,
ForNotFull,
};
}

View File

@@ -21,93 +21,33 @@
namespace sts::os {
namespace impl {
class WaitableObjectList;
class WaitableHolderOfEvent;
}
class Event {
friend class impl::WaitableHolderOfEvent;
NON_COPYABLE(Event);
NON_MOVEABLE(Event);
private:
Mutex m;
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitable_object_list_storage;
Mutex lock;
ConditionVariable cv;
u64 counter = 0;
bool auto_clear;
bool signaled;
u64 counter = 0;
public:
Event(bool a = true, bool s = false) : auto_clear(a), signaled(s) {}
Event(bool a = true, bool s = false);
~Event();
void Signal() {
std::scoped_lock lk(this->m);
/* 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();
}
/* TODO: Waitable auto-wakeup. */
}
void Reset() {
std::scoped_lock lk(this->m);
this->signaled = false;
}
void Wait() {
std::scoped_lock lk(this->m);
u64 cur_counter = this->counter;
while (!this->signaled) {
if (this->counter != cur_counter) {
break;
}
this->cv.Wait(&this->m);
}
if (this->auto_clear) {
this->signaled = false;
}
}
bool TryWait() {
std::scoped_lock lk(this->m);
const bool success = this->signaled;
if (this->auto_clear) {
this->signaled = false;
}
return success;
}
bool TimedWait(u64 ns) {
TimeoutHelper timeout_helper(ns);
std::scoped_lock lk(this->m);
u64 cur_counter = this->counter;
while (!this->signaled) {
if (this->counter != cur_counter) {
break;
}
if (R_FAILED(this->cv.TimedWait(&this->m, timeout_helper.NsUntilTimeout()))) {
return false;
}
}
if (this->auto_clear) {
this->signaled = false;
}
return true;
}
void Signal();
void Reset();
void Wait();
bool TryWait();
bool TimedWait(u64 ns);
};
}

View File

@@ -0,0 +1,50 @@
/*
* 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_common_types.hpp"
#include "os_managed_handle.hpp"
namespace sts::os {
namespace impl {
class WaitableHolderOfInterruptEvent;
}
class InterruptEvent {
friend class impl::WaitableHolderOfInterruptEvent;
NON_COPYABLE(InterruptEvent);
NON_MOVEABLE(InterruptEvent);
private:
ManagedHandle handle;
bool auto_clear;
bool is_initialized;
public:
InterruptEvent() : auto_clear(true), is_initialized(false) { }
InterruptEvent(u32 interrupt_id, bool autoclear = true);
Result Initialize(u32 interrupt_id, bool autoclear = true);
void Finalize();
void Reset();
void Wait();
bool TryWait();
bool TimedWait(u64 ns);
};
}

View File

@@ -0,0 +1,84 @@
/*
* 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 <switch.h>
#include "../defines.hpp"
#include "../results.hpp"
namespace sts::os {
class ManagedHandle {
NON_COPYABLE(ManagedHandle);
private:
Handle hnd;
public:
ManagedHandle() : hnd(INVALID_HANDLE) { /* ... */ }
ManagedHandle(Handle h) : hnd(h) { /* ... */ }
~ManagedHandle() {
if (this->hnd != INVALID_HANDLE) {
R_ASSERT(svcCloseHandle(this->hnd));
this->hnd = INVALID_HANDLE;
}
}
ManagedHandle(ManagedHandle&& rhs) {
this->hnd = rhs.hnd;
rhs.hnd = INVALID_HANDLE;
}
ManagedHandle& operator=(ManagedHandle&& rhs) {
rhs.Swap(*this);
return *this;
}
explicit operator bool() const {
return this->hnd != INVALID_HANDLE;
}
void Swap(ManagedHandle& rhs) {
std::swap(this->hnd, rhs.hnd);
}
Handle Get() const {
return this->hnd;
}
Handle *GetPointer() {
return &this->hnd;
}
Handle *GetPointerAndClear() {
this->Clear();
return this->GetPointer();
}
Handle Move() {
const Handle h = this->hnd;
this->hnd = INVALID_HANDLE;
return h;
}
void Reset(Handle h) {
ManagedHandle(h).Swap(*this);
}
void Clear() {
this->Reset(INVALID_HANDLE);
}
};
}

View File

@@ -16,15 +16,28 @@
#pragma once
#include <memory>
#include "os_common_types.hpp"
#include "os_mutex.hpp"
#include "os_condvar.hpp"
namespace sts::os {
namespace impl {
class WaitableObjectList;
template<MessageQueueWaitKind WaitKind>
class WaitableHolderOfMessageQueue;
}
class MessageQueue {
template<MessageQueueWaitKind WaitKind>
friend class impl::WaitableHolderOfMessageQueue;
NON_COPYABLE(MessageQueue);
NON_MOVEABLE(MessageQueue);
private:
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitable_object_list_storage;
Mutex queue_lock;
ConditionVariable cv_not_full;
ConditionVariable cv_not_empty;
@@ -47,11 +60,11 @@ namespace sts::os {
uintptr_t ReceiveInternal();
uintptr_t PeekInternal();
public:
MessageQueue(size_t c) : capacity(c) {
this->buffer = std::make_unique<uintptr_t[]>(this->capacity);
}
MessageQueue(std::unique_ptr<uintptr_t[]> buf, size_t c);
~MessageQueue();
MessageQueue(std::unique_ptr<uintptr_t[]> buf, size_t c) : buffer(std::move(buf)), capacity(c) { }
/* For convenience. */
MessageQueue(size_t c) : MessageQueue(std::make_unique<uintptr_t[]>(c), c) { /* ... */ }
/* Sending (FIFO functionality) */
void Send(uintptr_t data);

View File

@@ -0,0 +1,81 @@
/*
* 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_common_types.hpp"
#include "os_event.hpp"
namespace sts::os {
class WaitableHolder;
namespace impl {
class InterProcessEvent;
}
enum class SystemEventState {
Uninitialized,
Event,
InterProcessEvent,
};
class SystemEvent {
friend class WaitableHolder;
NON_COPYABLE(SystemEvent);
NON_MOVEABLE(SystemEvent);
private:
union {
util::TypedStorage<Event, sizeof(Event), alignof(Event)> storage_for_event;
util::TypedStorage<impl::InterProcessEvent, 3 * sizeof(Handle), alignof(Handle)> storage_for_inter_process_event;
};
SystemEventState state;
private:
Event &GetEvent();
const Event &GetEvent() const;
impl::InterProcessEvent &GetInterProcessEvent();
const impl::InterProcessEvent &GetInterProcessEvent() const;
public:
SystemEvent() : state(SystemEventState::Uninitialized) { /* ... */ }
SystemEvent(bool inter_process, bool autoclear = true);
SystemEvent(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true);
SystemEvent(Handle read_handle, bool manage_read_handle, bool autoclear = true) : SystemEvent(read_handle, manage_read_handle, INVALID_HANDLE, false, autoclear) { /* ... */ }
~SystemEvent();
Result InitializeAsEvent(bool autoclear = true);
Result InitializeAsInterProcessEvent(bool autoclear = true);
void AttachHandles(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true);
void AttachReadableHandle(Handle read_handle, bool manage_read_handle, bool autoclear = true);
void AttachWritableHandle(Handle write_handle, bool manage_write_handle, bool autoclear = true);
Handle DetachReadableHandle();
Handle DetachWritableHandle();
Handle GetReadableHandle() const;
Handle GetWritableHandle() const;
void Finalize();
SystemEventState GetState() const {
return this->state;
}
void Signal();
void Reset();
void Wait();
bool TryWait();
bool TimedWait(u64 ns);
};
}

View File

@@ -37,6 +37,10 @@ namespace sts::os {
return threadStart(&this->thr);
}
Result Wait() {
return threadWaitForExit(&this->thr);
}
Result Join() {
R_TRY(threadWaitForExit(&this->thr));
R_TRY(threadClose(&this->thr));

View File

@@ -0,0 +1,66 @@
/*
* 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 "../util.hpp"
#include "os_common_types.hpp"
namespace sts::os {
class WaitableManager;
class Event;
class SystemEvent;
class InterruptEvent;
class Thread;
class MessageQueue;
namespace impl {
class WaitableHolderImpl;
}
class WaitableHolder {
friend class WaitableManager;
NON_COPYABLE(WaitableHolder);
NON_MOVEABLE(WaitableHolder);
private:
util::TypedStorage<impl::WaitableHolderImpl, 2 * sizeof(util::IntrusiveListNode) + 3 * sizeof(void *), alignof(void *)> impl_storage;
uintptr_t user_data;
public:
static constexpr size_t ImplStorageSize = sizeof(impl_storage);
public:
WaitableHolder(Handle handle);
WaitableHolder(Event *event);
WaitableHolder(SystemEvent *event);
WaitableHolder(InterruptEvent *event);
WaitableHolder(Thread *thread);
WaitableHolder(MessageQueue *message_queue, MessageQueueWaitKind wait_kind);
~WaitableHolder();
void SetUserData(uintptr_t data) {
this->user_data = data;
}
uintptr_t GetUserData() const {
return this->user_data;
}
void UnlinkFromWaitableManager();
};
}

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 "../util.hpp"
#include "os_common_types.hpp"
#include "os_mutex.hpp"
namespace sts::os {
class WaitableHolder;
namespace impl {
class WaitableManagerImpl;
}
class WaitableManager {
NON_COPYABLE(WaitableManager);
NON_MOVEABLE(WaitableManager);
private:
util::TypedStorage<impl::WaitableManagerImpl, sizeof(util::IntrusiveListNode) + sizeof(Mutex) + 2 * sizeof(void *) + sizeof(Handle), alignof(void *)> impl_storage;
public:
static constexpr size_t ImplStorageSize = sizeof(impl_storage);
public:
WaitableManager();
~WaitableManager();
/* Wait. */
WaitableHolder *WaitAny();
WaitableHolder *TryWaitAny();
WaitableHolder *TimedWaitAny(u64 timeout);
/* Link. */
void LinkWaitableHolder(WaitableHolder *holder);
void UnlinkAll();
void MoveAllFrom(WaitableManager *other);
};
}

View File

@@ -32,6 +32,7 @@
#include "results/kvdb_results.hpp"
#include "results/loader_results.hpp"
#include "results/lr_results.hpp"
#include "results/os_results.hpp"
#include "results/ncm_results.hpp"
#include "results/pm_results.hpp"
#include "results/ro_results.hpp"

View File

@@ -0,0 +1,23 @@
/*
* 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 <switch.h>
static constexpr u32 Module_Os = 3;
static constexpr Result ResultOsOutOfMemory = MAKERESULT(Module_Os, 8);
static constexpr Result ResultOsResourceExhausted = MAKERESULT(Module_Os, 9);

View File

@@ -28,6 +28,14 @@ extern "C" {
#define R_ASSERT_IMPL(res) fatalSimple(res)
#endif
/// Evaluates a boolean expression, and returns a result unless that expression is true.
#define R_UNLESS(expr, res) \
({ \
if (!(expr)) { \
return static_cast<Result>(res); \
} \
})
#define R_ASSERT(res_expr) \
({ \
const Result _tmp_r_assert_rc = res_expr; \
@@ -83,6 +91,14 @@ extern "C" {
} \
})
#define R_END_TRY_CATCH_WITH_ASSERT \
else { \
R_ASSERT(R_TRY_CATCH_RESULT); \
} \
} \
})
/// Evaluates an expression that returns a result, and returns the result (after evaluating a cleanup expression) if it would fail.
#define R_CLEANUP_RESULT _tmp_r_try_cleanup_rc

View File

@@ -17,6 +17,7 @@
#pragma once
#include <switch.h>
#include <cstdlib>
#include "../defines.hpp"
namespace sts::ro {
@@ -61,9 +62,7 @@ namespace sts::ro {
ModuleType GetType() const {
const ModuleType type = static_cast<ModuleType>(this->type);
if (type >= ModuleType::Count) {
std::abort();
}
STS_ASSERT(type < ModuleType::Count);
return type;
}

View File

@@ -1,62 +0,0 @@
/*
* 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/>.
*/
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
#pragma once
#include <utility>
template<class F>
class ScopeGuard {
private:
F f;
bool active;
public:
ScopeGuard(F f) : f(std::move(f)), active(true) { }
~ScopeGuard() { if (active) { f(); } }
void Cancel() { active = false; }
ScopeGuard() = delete;
ScopeGuard(const ScopeGuard &) = delete;
ScopeGuard& operator=(const ScopeGuard&) = delete;
ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
rhs.Cancel();
}
};
template<class F>
ScopeGuard<F> MakeScopeGuard(F f) {
return ScopeGuard<F>(std::move(f));
}
enum class ScopeGuardOnExit {};
template <typename F>
ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
return ScopeGuard<F>(std::forward<F>(f));
}
#define CONCATENATE_IMPL(S1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
#ifdef __COUNTER__
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__)
#else
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__)
#endif
#define SCOPE_GUARD ScopeGuardOnExit() + [&]()
#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD

View File

@@ -114,9 +114,7 @@ namespace sts::sm {
}
Result Initialize() {
if (this->has_initialized) {
std::abort();
}
STS_ASSERT(!this->has_initialized);
DoWithSmSession([&]() {
this->result = Initializer();
@@ -127,9 +125,7 @@ namespace sts::sm {
}
void Finalize() {
if (!this->has_initialized) {
std::abort();
}
STS_ASSERT(this->has_initialized);
Finalizer();
this->has_initialized = false;
}

View File

@@ -202,4 +202,10 @@ namespace sts::svc {
CreateProcessFlag_OptimizeMemoryAllocation = (1 << 11),
};
/* Type for svcCreateInterruptEvent. */
enum InterruptType : u32 {
InterruptType_Edge = 0,
InterruptType_Level = 1,
};
}

View File

@@ -17,7 +17,11 @@
#pragma once
#include <switch.h>
#include "util/util_compression.hpp"
#include "util/util_ini.hpp"
#include "util/util_alignment.hpp"
#include "util/util_size.hpp"
#include "util/util_scope_guard.hpp"
#include "util/util_typed_storage.hpp"
#include "util/util_intrusive_list.hpp"
#include "util/util_intrusive_red_black_tree.hpp"
#include "util/util_compression.hpp"
#include "util/util_ini.hpp"

View File

@@ -0,0 +1,47 @@
/*
* 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 <switch.h>
#include "../defines.hpp"
#include <type_traits>
namespace sts::util {
/* Utilities for alignment to power of two. */
template<typename T>
static constexpr inline T AlignUp(T value, size_t alignment) {
using U = typename std::make_unsigned<T>::type;
const U invmask = static_cast<U>(alignment - 1);
return static_cast<T>((value + invmask) & ~invmask);
}
template<typename T>
static constexpr inline T AlignDown(T value, size_t alignment) {
using U = typename std::make_unsigned<T>::type;
const U invmask = static_cast<U>(alignment - 1);
return static_cast<T>(value & ~invmask);
}
template<typename T>
static constexpr inline bool IsAligned(T value, size_t alignment) {
using U = typename std::make_unsigned<T>::type;
const U invmask = static_cast<U>(alignment - 1);
return (value & invmask) == 0;
}
}

View File

@@ -49,10 +49,8 @@ namespace sts::util {
private:
void LinkPrev(IntrusiveListNode *node) {
/* We can't link an already linked node. */
if (node->IsLinked()) {
std::abort();
}
return this->SplicePrev(node, node);
STS_ASSERT(!node->IsLinked());
this->SplicePrev(node, node);
}
void SplicePrev(IntrusiveListNode *first, IntrusiveListNode *last) {
@@ -66,9 +64,7 @@ namespace sts::util {
void LinkNext(IntrusiveListNode *node) {
/* We can't link an already linked node. */
if (node->IsLinked()) {
std::abort();
}
STS_ASSERT(!node->IsLinked());
return this->SpliceNext(node, node);
}
@@ -214,17 +210,13 @@ namespace sts::util {
iterator iterator_to(reference v) {
/* Only allow iterator_to for values in lists. */
if (!v.IsLinked()) {
std::abort();
}
STS_ASSERT(v.IsLinked());
return iterator(&v);
}
const_iterator iterator_to(const_reference v) const {
/* Only allow iterator_to for values in lists. */
if (!v.IsLinked()) {
std::abort();
}
STS_ASSERT(v.IsLinked());
return const_iterator(&v);
}
@@ -488,23 +480,23 @@ namespace sts::util {
}
reference back() {
if (this->impl.empty()) { std::abort(); }
return this->impl.back();
STS_ASSERT(!this->impl.empty());
return GetParent(this->impl.back());
}
const_reference back() const {
if (this->impl.empty()) { std::abort(); }
return this->impl.back();
STS_ASSERT(!this->impl.empty());
return GetParent(this->impl.back());
}
reference front() {
if (this->impl.empty()) { std::abort(); }
return this->impl.front();
STS_ASSERT(!this->impl.empty());
return GetParent(this->impl.front());
}
const_reference front() const {
if (this->impl.empty()) { std::abort(); }
return this->impl.front();
STS_ASSERT(!this->impl.empty());
return GetParent(this->impl.front());
}
void push_back(reference ref) {
@@ -516,13 +508,13 @@ namespace sts::util {
}
void pop_back() {
if (this->impl.empty()) { std::abort(); }
return this->impl.pop_back();
STS_ASSERT(!this->impl.empty());
this->impl.pop_back();
}
void pop_front() {
if (this->impl.empty()) { std::abort(); }
return this->impl.pop_front();
STS_ASSERT(!this->impl.empty());
this->impl.pop_front();
}
iterator insert(const_iterator pos, reference ref) {

View File

@@ -0,0 +1,59 @@
/*
* 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/>.
*/
/* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++" */
#pragma once
#include <utility>
#include "../defines.hpp"
namespace sts::util {
namespace impl {
template<class F>
class ScopeGuard {
NON_COPYABLE(ScopeGuard);
private:
F f;
bool active;
public:
constexpr ScopeGuard(F f) : f(std::move(f)), active(true) { }
~ScopeGuard() { if (active) { f(); } }
void Cancel() { active = false; }
ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active) {
rhs.Cancel();
}
};
template<class F>
constexpr ScopeGuard<F> MakeScopeGuard(F f) {
return ScopeGuard<F>(std::move(f));
}
enum class ScopeGuardOnExit {};
template <typename F>
constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
return ScopeGuard<F>(std::forward<F>(f));
}
}
}
#define SCOPE_GUARD ::sts::util::impl::ScopeGuardOnExit() + [&]()
#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD

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 <switch.h>
namespace sts::util {
/* std::size() does not support zero-size C arrays. We're fixing that. */
template<class C>
constexpr auto size(const C& c) -> decltype(c.size()) {
return std::size(c);
}
template<class C>
constexpr std::size_t size(const C& c) {
if constexpr (sizeof(C) == 0) {
return 0;
} else {
return std::size(c);
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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 <switch.h>
#include "../defines.hpp"
#include <type_traits>
namespace sts::util {
template<typename T, size_t Size, size_t Align>
struct TypedStorage {
typename std::aligned_storage<Size, Align>::type _storage;
};
#define TYPED_STORAGE(T) ::sts::util::TypedStorage<T, sizeof(T), alignof(T)>
template<typename T>
static constexpr inline __attribute__((always_inline)) T *GetPointer(TYPED_STORAGE(T) &ts) {
return reinterpret_cast<T *>(&ts._storage);
}
template<typename T>
static constexpr inline __attribute__((always_inline)) const T *GetPointer(const TYPED_STORAGE(T) &ts) {
return reinterpret_cast<const T *>(&ts._storage);
}
template<typename T>
static constexpr inline __attribute__((always_inline)) T &GetReference(TYPED_STORAGE(T) &ts) {
return *GetPointer(ts);
}
template<typename T>
static constexpr inline __attribute__((always_inline)) const T &GetReference(const TYPED_STORAGE(T) &ts) {
return *GetPointer(ts);
}
}

View File

@@ -23,11 +23,10 @@
#include "results.hpp"
#include "os.hpp"
#include "waitable_manager_base.hpp"
#include "event.hpp"
#include "ipc.hpp"
#include "servers.hpp"
#include "scope_guard.hpp"
#include "util.hpp"
static inline Handle GetCurrentThreadHandle() {
return threadGetCurHandle();