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:
@@ -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"
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -202,4 +202,10 @@ namespace sts::svc {
|
||||
CreateProcessFlag_OptimizeMemoryAllocation = (1 << 11),
|
||||
};
|
||||
|
||||
/* Type for svcCreateInterruptEvent. */
|
||||
enum InterruptType : u32 {
|
||||
InterruptType_Edge = 0,
|
||||
InterruptType_Level = 1,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user