mirror of
https://github.com/zeldaret/botw
synced 2026-09-04 10:12:02 -04:00
Switch to subrepos
git subrepo clone https://github.com/open-ead/sead lib/sead subrepo: subdir: "lib/sead" merged: "1b66e825d" upstream: origin: "https://github.com/open-ead/sead" branch: "master" commit: "1b66e825d" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo" commit: "2f68596" git subrepo clone (merge) https://github.com/open-ead/nnheaders lib/NintendoSDK subrepo: subdir: "lib/NintendoSDK" merged: "9ee21399f" upstream: origin: "https://github.com/open-ead/nnheaders" branch: "master" commit: "9ee21399f" git-subrepo: version: "0.4.3" origin: "ssh://git@github.com/ingydotnet/git-subrepo" commit: "2f68596" git subrepo clone https://github.com/open-ead/agl lib/agl subrepo: subdir: "lib/agl" merged: "7c063271b" upstream: origin: "https://github.com/open-ead/agl" branch: "master" commit: "7c063271b" git-subrepo: version: "0.4.3" origin: "ssh://git@github.com/ingydotnet/git-subrepo" commit: "2f68596" git subrepo clone https://github.com/open-ead/EventFlow lib/EventFlow subrepo: subdir: "lib/EventFlow" merged: "c35d21b34" upstream: origin: "https://github.com/open-ead/EventFlow" branch: "master" commit: "c35d21b34" git-subrepo: version: "0.4.3" origin: "ssh://git@github.com/ingydotnet/git-subrepo" commit: "2f68596"
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
#ifndef SEAD_THREAD_THREAD_LOCAL_STORAGE_H_
|
||||
#include "thread/seadThreadLocalStorage.h"
|
||||
#endif
|
||||
|
||||
#include "basis/seadRawPrint.h"
|
||||
|
||||
namespace sead
|
||||
{
|
||||
inline ThreadLocalStorage::ThreadLocalStorage()
|
||||
{
|
||||
[[maybe_unused]] auto result = nn::os::AllocateTlsSlot(&mTlsSlot, nullptr);
|
||||
SEAD_ASSERT(result.IsSuccess());
|
||||
}
|
||||
|
||||
inline ThreadLocalStorage::~ThreadLocalStorage()
|
||||
{
|
||||
nn::os::FreeTlsSlot(mTlsSlot);
|
||||
}
|
||||
|
||||
inline void ThreadLocalStorage::setValue(uintptr_t value)
|
||||
{
|
||||
static_assert(sizeof(uintptr_t) == sizeof(u64), "uintptr_t and u64 should have the same size");
|
||||
nn::os::SetTlsValue(mTlsSlot, value);
|
||||
}
|
||||
|
||||
inline uintptr_t ThreadLocalStorage::getValue() const
|
||||
{
|
||||
return nn::os::GetTlsValue(mTlsSlot);
|
||||
}
|
||||
} // namespace sead
|
||||
@@ -0,0 +1,287 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NNSDK
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
namespace sead
|
||||
{
|
||||
struct AtomicDirectInitTag
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct AtomicBase
|
||||
{
|
||||
public:
|
||||
AtomicBase(T value = {}); // NOLINT(google-explicit-constructor)
|
||||
/// Directly initialises the underlying atomic with the specified value.
|
||||
/// Note that initialisation is not atomic.
|
||||
AtomicBase(AtomicDirectInitTag, T value);
|
||||
AtomicBase(const AtomicBase& rhs) { *this = rhs; }
|
||||
|
||||
operator T() const { return load(); }
|
||||
|
||||
AtomicBase& operator=(const AtomicBase& rhs)
|
||||
{
|
||||
store(rhs.load());
|
||||
return *this;
|
||||
}
|
||||
|
||||
AtomicBase& operator=(T value)
|
||||
{
|
||||
store(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Load the current value, as if with memory_order_relaxed.
|
||||
T load() const;
|
||||
/// Store a new value, as if with memory_order_relaxed.
|
||||
void store(T value);
|
||||
/// Non-atomically store a new value.
|
||||
void storeNonAtomic(T value);
|
||||
/// Exchange/swap the current value, as if with memory_order_relaxed.
|
||||
/// @return the previous value
|
||||
T exchange(T value);
|
||||
/// Load the current value and if it is equal to `expected`, store `desired`
|
||||
/// as if with memory_order_relaxed.
|
||||
/// Otherwise, this sets `original` to the current value.
|
||||
/// @param expected The value expected to be found in the atomic object, and to be replaced.
|
||||
/// @param desired The new value to store in the atomic object if `expected` was found.
|
||||
/// @param original The value that was found in the atomic object if the comparison fails. May
|
||||
/// be null. Note that this is only updated when false is returned.
|
||||
/// @return true if and only if the value was modified
|
||||
bool compareExchange(T expected, T desired, T* original = nullptr);
|
||||
|
||||
protected:
|
||||
#ifdef NNSDK
|
||||
// Nintendo appears to have manually implemented atomics with volatile and platform specific
|
||||
// intrinsics (e.g. __builtin_arm_ldrex).
|
||||
// For ease of implementation and portability, we will use std::atomic and cast to volatile
|
||||
// when necessary. That is formally undefined behavior, but it should be safe because
|
||||
// sead is built with -fno-strict-aliasing and because of the following static assertions.
|
||||
std::atomic<T> mValue;
|
||||
static_assert(sizeof(mValue) == sizeof(T),
|
||||
"std::atomic<T> and T do not have the same size; unsupported case");
|
||||
static_assert(alignof(decltype(mValue)) == alignof(volatile T),
|
||||
"std::atomic<T> and T do not have the same alignment; unsupported case");
|
||||
static_assert(std::atomic<T>::is_always_lock_free,
|
||||
"std::atomic<T>::is_always_lock_free is not true; unsupported case");
|
||||
|
||||
const volatile T* getValuePtr() const { return reinterpret_cast<const volatile T*>(&mValue); }
|
||||
volatile T* getValuePtr() { return reinterpret_cast<volatile T*>(&mValue); }
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct Atomic : AtomicBase<T>
|
||||
{
|
||||
using AtomicBase<T>::AtomicBase;
|
||||
using AtomicBase<T>::operator=;
|
||||
|
||||
T fetchAdd(T x);
|
||||
T fetchSub(T x);
|
||||
T fetchAnd(T x);
|
||||
T fetchOr(T x);
|
||||
T fetchXor(T x);
|
||||
T increment() { return fetchAdd(1); }
|
||||
T decrement() { return fetchSub(1); }
|
||||
|
||||
bool isBitOn(unsigned int bit) const;
|
||||
/// @return whether the bit was cleared and is now set.
|
||||
bool setBitOn(unsigned int bit);
|
||||
/// @return whether the bit was set and is now cleared.
|
||||
bool setBitOff(unsigned int bit);
|
||||
|
||||
T operator+=(T x) { return fetchAdd(x); }
|
||||
T operator-=(T x) { return fetchSub(x); }
|
||||
T operator&=(T x) { return fetchAnd(x); }
|
||||
T operator|=(T x) { return fetchOr(x); }
|
||||
T operator^=(T x) { return fetchXor(x); }
|
||||
T operator++() { return fetchAdd(1) + 1; }
|
||||
T operator++(int) { return fetchAdd(1); }
|
||||
T operator--() { return fetchSub(1) - 1; }
|
||||
T operator--(int) { return fetchSub(1); }
|
||||
};
|
||||
|
||||
/// Specialization for pointer types.
|
||||
template <class T>
|
||||
struct Atomic<T*> : AtomicBase<T*>
|
||||
{
|
||||
using AtomicBase<T*>::AtomicBase;
|
||||
using AtomicBase<T*>::operator=;
|
||||
|
||||
T& operator*() const { return *this->load(); }
|
||||
T* operator->() const { return this->load(); }
|
||||
};
|
||||
|
||||
// Implementation.
|
||||
|
||||
#ifdef NNSDK
|
||||
template <class T>
|
||||
inline AtomicBase<T>::AtomicBase(T value)
|
||||
{
|
||||
storeNonAtomic(value);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline AtomicBase<T>::AtomicBase(AtomicDirectInitTag, T value) : mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T AtomicBase<T>::load() const
|
||||
{
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
// Using std::atomic<T>::load prevents LLVM from folding ldr+sext into ldrsw.
|
||||
return *getValuePtr();
|
||||
#else
|
||||
return mValue.load(std::memory_order_relaxed);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void AtomicBase<T>::store(T value)
|
||||
{
|
||||
mValue.store(value, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void AtomicBase<T>::storeNonAtomic(T value)
|
||||
{
|
||||
*getValuePtr() = value;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T AtomicBase<T>::exchange(T value)
|
||||
{
|
||||
return mValue.exchange(value, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool AtomicBase<T>::compareExchange(T expected, T desired, T* original)
|
||||
{
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
// Unlike Clang (https://reviews.llvm.org/D13033), Nintendo's implementation does not use clrex.
|
||||
do
|
||||
{
|
||||
T value = __builtin_arm_ldrex(getValuePtr());
|
||||
if (value != expected)
|
||||
{
|
||||
if (original)
|
||||
*original = value;
|
||||
return false;
|
||||
}
|
||||
} while (__builtin_arm_strex(desired, getValuePtr()));
|
||||
return true;
|
||||
#else
|
||||
T value = expected;
|
||||
if (mValue.compare_exchange_strong(value, desired, std::memory_order_relaxed))
|
||||
return true;
|
||||
if (original)
|
||||
*original = value;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
namespace detail
|
||||
{
|
||||
// To match Nintendo's implementation of atomics.
|
||||
template <typename T, typename F>
|
||||
inline T atomicReadModifyWrite(volatile T* value_ptr, F op)
|
||||
{
|
||||
T value;
|
||||
do
|
||||
{
|
||||
value = __builtin_arm_ldrex(value_ptr);
|
||||
} while (__builtin_arm_strex(op(value), value_ptr));
|
||||
return value;
|
||||
}
|
||||
} // namespace detail
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
inline T Atomic<T>::fetchAdd(T x)
|
||||
{
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
return detail::atomicReadModifyWrite(this->getValuePtr(), [&](T val) { return val + x; });
|
||||
#else
|
||||
return this->mValue.fetch_add(x, std::memory_order_relaxed);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T Atomic<T>::fetchSub(T x)
|
||||
{
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
return detail::atomicReadModifyWrite(this->getValuePtr(), [&](T val) { return val - x; });
|
||||
#else
|
||||
return this->mValue.fetch_sub(x, std::memory_order_relaxed);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T Atomic<T>::fetchAnd(T x)
|
||||
{
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
return detail::atomicReadModifyWrite(this->getValuePtr(), [&](T val) { return val & x; });
|
||||
#else
|
||||
return this->mValue.fetch_and(x, std::memory_order_relaxed);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T Atomic<T>::fetchOr(T x)
|
||||
{
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
return detail::atomicReadModifyWrite(this->getValuePtr(), [&](T val) { return val | x; });
|
||||
#else
|
||||
return this->mValue.fetch_or(x, std::memory_order_relaxed);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T Atomic<T>::fetchXor(T x)
|
||||
{
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
return detail::atomicReadModifyWrite(this->getValuePtr(), [&](T val) { return val ^ x; });
|
||||
#else
|
||||
return this->mValue.fetch_xor(x, std::memory_order_relaxed);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool Atomic<T>::isBitOn(unsigned int bit) const
|
||||
{
|
||||
return (this->load() & (1 << bit)) != 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool Atomic<T>::setBitOn(unsigned int bit)
|
||||
{
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
const auto old = detail::atomicReadModifyWrite(this->getValuePtr(),
|
||||
[bit](T val) { return val | (1 << bit); });
|
||||
#else
|
||||
const auto old = this->mValue.fetch_or(1 << bit, std::memory_order_relaxed);
|
||||
#endif
|
||||
return (old & (1 << bit)) == 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool Atomic<T>::setBitOff(unsigned int bit)
|
||||
{
|
||||
#ifdef MATCHING_HACK_NX_CLANG
|
||||
const auto old = detail::atomicReadModifyWrite(this->getValuePtr(),
|
||||
[bit](T val) { return val & ~(1 << bit); });
|
||||
#else
|
||||
const auto old = this->mValue.fetch_and(~(1 << bit), std::memory_order_relaxed);
|
||||
#endif
|
||||
return (old & (1 << bit)) != 0;
|
||||
}
|
||||
#else // NNSDK
|
||||
#error "Unknown platform"
|
||||
#endif
|
||||
} // namespace sead
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef SEAD_CRITICAL_SECTION_H_
|
||||
#define SEAD_CRITICAL_SECTION_H_
|
||||
|
||||
#if defined(cafe)
|
||||
#include <cafe.h>
|
||||
#elif defined(NNSDK)
|
||||
#include <nn/os.h>
|
||||
#endif
|
||||
|
||||
#include <basis/seadTypes.h>
|
||||
#include <heap/seadDisposer.h>
|
||||
|
||||
namespace sead
|
||||
{
|
||||
class Heap;
|
||||
|
||||
class CriticalSection : public IDisposer
|
||||
{
|
||||
public:
|
||||
CriticalSection();
|
||||
explicit CriticalSection(Heap* disposer_heap);
|
||||
CriticalSection(Heap* disposer_heap, HeapNullOption heap_null_option);
|
||||
~CriticalSection() override;
|
||||
|
||||
CriticalSection(const CriticalSection&) = delete;
|
||||
CriticalSection& operator=(const CriticalSection&) = delete;
|
||||
|
||||
void lock();
|
||||
bool tryLock();
|
||||
void unlock();
|
||||
|
||||
// For compatibility with the standard Lockable concept.
|
||||
bool try_lock() { return tryLock(); }
|
||||
|
||||
#if defined(cafe)
|
||||
OSMutex mCriticalSectionInner;
|
||||
#elif defined(NNSDK)
|
||||
nn::os::MutexType mCriticalSectionInner;
|
||||
#else
|
||||
#error "Unknown platform"
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace sead
|
||||
|
||||
#endif // SEAD_CRITICAL_SECTION_H_
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread/seadMessageQueue.h>
|
||||
#include <thread/seadThread.h>
|
||||
|
||||
namespace sead
|
||||
{
|
||||
template <typename A1, typename A2>
|
||||
class IDelegate2;
|
||||
|
||||
class DelegateThread : public Thread
|
||||
{
|
||||
public:
|
||||
DelegateThread(const SafeString& name, IDelegate2<Thread*, MessageQueue::Element>* delegate,
|
||||
Heap* heap, s32 priority, MessageQueue::BlockType block_type,
|
||||
MessageQueue::Element quit_msg, s32 stack_size, s32 message_queue_size);
|
||||
~DelegateThread() override;
|
||||
|
||||
protected:
|
||||
void calc_(MessageQueue::Element msg) override;
|
||||
|
||||
IDelegate2<Thread*, MessageQueue::Element>* mDelegate;
|
||||
};
|
||||
|
||||
} // namespace sead
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NNSDK
|
||||
#include <nn/os.h>
|
||||
#endif
|
||||
|
||||
#include "heap/seadDisposer.h"
|
||||
#include "time/seadTickSpan.h"
|
||||
|
||||
namespace sead
|
||||
{
|
||||
class Heap;
|
||||
|
||||
class Event : public IDisposer
|
||||
{
|
||||
public:
|
||||
Event();
|
||||
explicit Event(bool manual_reset);
|
||||
explicit Event(Heap* disposer_heap);
|
||||
Event(Heap* disposer_heap, bool manual_reset);
|
||||
Event(Heap* disposer_heap, IDisposer::HeapNullOption heap_null_option);
|
||||
Event(Heap* disposer_heap, IDisposer::HeapNullOption heap_null_option, bool manual_reset);
|
||||
~Event() override;
|
||||
|
||||
Event(const Event&) = delete;
|
||||
Event& operator=(const Event&) = delete;
|
||||
|
||||
void initialize(bool manual_reset);
|
||||
void wait();
|
||||
bool wait(TickSpan duration);
|
||||
void setSignal();
|
||||
void resetSignal();
|
||||
|
||||
private:
|
||||
void setInitialized([[maybe_unused]] bool initialized)
|
||||
{
|
||||
#ifdef SEAD_DEBUG
|
||||
mInitialized = initialized;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NNSDK
|
||||
nn::os::LightEventType mEventInner;
|
||||
#else
|
||||
#error "Unknown platform"
|
||||
#endif
|
||||
|
||||
#ifdef SEAD_DEBUG
|
||||
bool mInitialized = false;
|
||||
#endif
|
||||
};
|
||||
} // namespace sead
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NNSDK
|
||||
#include <nn/os.h>
|
||||
#endif
|
||||
|
||||
namespace sead
|
||||
{
|
||||
class Heap;
|
||||
|
||||
class MessageQueue
|
||||
{
|
||||
public:
|
||||
#ifdef NNSDK
|
||||
using Element = s64;
|
||||
#else
|
||||
#error "Unknown platform"
|
||||
using Element = u64;
|
||||
#endif
|
||||
|
||||
enum class BlockType
|
||||
{
|
||||
Blocking = 0,
|
||||
NonBlocking = 1,
|
||||
};
|
||||
|
||||
MessageQueue();
|
||||
~MessageQueue();
|
||||
|
||||
void allocate(s32 size, Heap* heap);
|
||||
void free();
|
||||
bool push(Element message, BlockType block_type);
|
||||
Element pop(BlockType block_type);
|
||||
Element peek(BlockType block_type) const;
|
||||
bool jam(Element message, BlockType block_type);
|
||||
|
||||
static constexpr Element cNullElement = 0;
|
||||
|
||||
private:
|
||||
#ifdef NNSDK
|
||||
nn::os::MessageQueueType mMessageQueueInner;
|
||||
Element* mBuffer = nullptr;
|
||||
#else
|
||||
#error "Unknown platform"
|
||||
#endif
|
||||
};
|
||||
} // namespace sead
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(cafe)
|
||||
#include <cafe.h>
|
||||
#elif defined(NNSDK)
|
||||
#include <nn/os.h>
|
||||
#endif
|
||||
|
||||
#include <basis/seadTypes.h>
|
||||
#include <heap/seadDisposer.h>
|
||||
|
||||
namespace sead
|
||||
{
|
||||
class Heap;
|
||||
|
||||
class Mutex : public IDisposer
|
||||
{
|
||||
public:
|
||||
Mutex();
|
||||
explicit Mutex(Heap* disposer_heap);
|
||||
Mutex(Heap* disposer_heap, HeapNullOption heap_null_option);
|
||||
~Mutex() override;
|
||||
|
||||
Mutex(const Mutex&) = delete;
|
||||
Mutex& operator=(const Mutex&) = delete;
|
||||
|
||||
void lock();
|
||||
bool tryLock();
|
||||
void unlock();
|
||||
|
||||
// For compatibility with the standard Lockable concept.
|
||||
bool try_lock() { return tryLock(); }
|
||||
|
||||
#if defined(cafe)
|
||||
OSMutex mMutexInner;
|
||||
#elif defined(NNSDK)
|
||||
nn::os::MutexType mMutexInner;
|
||||
#else
|
||||
#error "Unknown platform"
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace sead
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "thread/seadAtomic.h"
|
||||
#include "thread/seadSemaphore.h"
|
||||
|
||||
namespace sead
|
||||
{
|
||||
class Thread;
|
||||
|
||||
class ReadWriteLock
|
||||
{
|
||||
public:
|
||||
ReadWriteLock();
|
||||
~ReadWriteLock();
|
||||
void readLock();
|
||||
void readUnlock();
|
||||
void writeLock();
|
||||
void writeUnlock();
|
||||
|
||||
private:
|
||||
class SemaphoreLock
|
||||
{
|
||||
public:
|
||||
SemaphoreLock();
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
Semaphore mSemaphore{0, 0x7FFF};
|
||||
Atomic<u32> mLockCount{0};
|
||||
};
|
||||
|
||||
Atomic<u32> mNumReaders{0};
|
||||
Atomic<u32> mNumWriters{0};
|
||||
SemaphoreLock mReadLock;
|
||||
SemaphoreLock mWriteLock;
|
||||
Thread* mWriterThread = nullptr;
|
||||
u32 mWritingThreadCount{0};
|
||||
};
|
||||
} // namespace sead
|
||||
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NNSDK
|
||||
#include <nn/os.h>
|
||||
#endif
|
||||
|
||||
#include "heap/seadDisposer.h"
|
||||
|
||||
namespace sead
|
||||
{
|
||||
class Heap;
|
||||
|
||||
class Semaphore : public IDisposer
|
||||
{
|
||||
public:
|
||||
Semaphore();
|
||||
explicit Semaphore(s32 initial_count);
|
||||
Semaphore(s32 initial_count, s32 max_count);
|
||||
|
||||
explicit Semaphore(Heap* heap);
|
||||
Semaphore(Heap* heap, s32 initial_count);
|
||||
Semaphore(Heap* heap, s32 initial_count, s32 max_count);
|
||||
|
||||
Semaphore(Heap* heap, HeapNullOption heap_null_option);
|
||||
Semaphore(Heap* heap, HeapNullOption heap_null_option, s32 initial_count);
|
||||
Semaphore(Heap* heap, HeapNullOption heap_null_option, s32 initial_count, s32 max_count);
|
||||
|
||||
~Semaphore() override;
|
||||
|
||||
Semaphore(const Semaphore&) = delete;
|
||||
Semaphore& operator=(const Semaphore&) = delete;
|
||||
|
||||
void initialize(s32 initial_count) { initialize(initial_count, initial_count); }
|
||||
void initialize(s32 initial_count, s32 max_count);
|
||||
void lock();
|
||||
bool tryLock();
|
||||
void unlock();
|
||||
|
||||
bool try_lock() { return tryLock(); }
|
||||
|
||||
private:
|
||||
void setInitialized([[maybe_unused]] bool initialized)
|
||||
{
|
||||
#ifdef SEAD_DEBUG
|
||||
mInitialized = initialized;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NNSDK
|
||||
nn::os::SemaphoreType mSemaphoreInner;
|
||||
#else
|
||||
#error "Unknown platform"
|
||||
#endif
|
||||
|
||||
#ifdef SEAD_DEBUG
|
||||
bool mInitialized = false;
|
||||
#endif
|
||||
};
|
||||
} // namespace sead
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "basis/seadTypes.h"
|
||||
#include "thread/seadAtomic.h"
|
||||
|
||||
namespace sead
|
||||
{
|
||||
class Thread;
|
||||
|
||||
class SpinLock
|
||||
{
|
||||
public:
|
||||
SpinLock();
|
||||
~SpinLock();
|
||||
|
||||
SpinLock(const SpinLock&) = delete;
|
||||
SpinLock& operator=(const SpinLock&) = delete;
|
||||
|
||||
void lock();
|
||||
bool tryLock();
|
||||
void unlock();
|
||||
|
||||
bool try_lock() { return tryLock(); }
|
||||
|
||||
private:
|
||||
Atomic<Thread*> mOwnerThread;
|
||||
u32 mCount = 0;
|
||||
};
|
||||
} // namespace sead
|
||||
@@ -0,0 +1,199 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NNSDK
|
||||
#include <nn/os.h>
|
||||
#endif
|
||||
|
||||
#include <basis/seadRawPrint.h>
|
||||
#include <container/seadTList.h>
|
||||
#include <heap/seadDisposer.h>
|
||||
#include <heap/seadHeapMgr.h>
|
||||
#include <hostio/seadHostIONode.h>
|
||||
#include <hostio/seadHostIOReflexible.h>
|
||||
#include <mc/seadCoreInfo.h>
|
||||
#include <prim/seadEnum.h>
|
||||
#include <prim/seadNamable.h>
|
||||
#include <prim/seadSafeString.h>
|
||||
#include <prim/seadScopedLock.h>
|
||||
#include <thread/seadMessageQueue.h>
|
||||
#include <thread/seadThreadLocalStorage.h>
|
||||
#include <time/seadTickSpan.h>
|
||||
|
||||
namespace sead
|
||||
{
|
||||
class Heap;
|
||||
class Thread;
|
||||
|
||||
using ThreadList = TList<Thread*>;
|
||||
using ThreadListNode = TListNode<Thread*>;
|
||||
|
||||
class Thread : public IDisposer, public INamable, public hostio::Reflexible
|
||||
{
|
||||
public:
|
||||
SEAD_ENUM(State, cInitialized, cRunning, cQuitting, cTerminated, cReleased);
|
||||
|
||||
Thread(const SafeString& name, Heap* heap, s32 priority, MessageQueue::BlockType block_type,
|
||||
MessageQueue::Element quit_msg, s32 stack_size, s32 message_queue_size);
|
||||
~Thread() override;
|
||||
|
||||
Thread(const Thread&) = delete;
|
||||
Thread& operator=(const Thread&) = delete;
|
||||
|
||||
virtual void destroy() { waitDone(); }
|
||||
|
||||
virtual bool sendMessage(MessageQueue::Element msg, MessageQueue::BlockType block_type);
|
||||
virtual MessageQueue::Element recvMessage(MessageQueue::BlockType block_type);
|
||||
virtual const MessageQueue& getMessageQueue() const { return mMessageQueue; }
|
||||
|
||||
virtual bool start();
|
||||
virtual void quit(bool is_jam);
|
||||
virtual void waitDone();
|
||||
virtual void quitAndDestroySingleThread(bool is_jam) { quitAndWaitDoneSingleThread(is_jam); }
|
||||
virtual void quitAndWaitDoneSingleThread(bool is_jam);
|
||||
|
||||
virtual void setPriority(s32 prio);
|
||||
virtual s32 getPriority() const;
|
||||
virtual MessageQueue::BlockType getBlockType() const { return mBlockType; }
|
||||
virtual s32 getStackSize() const { return mStackSize; }
|
||||
virtual s32 calcStackUsedSizePeak() const;
|
||||
|
||||
u32 getId() const { return mId; }
|
||||
State getState() const { return mState; }
|
||||
bool isDone() const { return mState == State::cTerminated || mState == State::cReleased; }
|
||||
bool isActive() const { return mState == State::cRunning || mState == State::cQuitting; }
|
||||
|
||||
const CoreIdMask& getAffinity() const { return mAffinity; }
|
||||
void setAffinity(const CoreIdMask& affinity);
|
||||
|
||||
static void yield();
|
||||
static void sleep(TickSpan howLong);
|
||||
|
||||
void checkStackOverFlow(const char* source_file, s32 source_line) const;
|
||||
void checkStackEndCorruption(const char* source_file, s32 source_line) const;
|
||||
void checkStackPointerOverFlow(const char* source_file, s32 source_line) const;
|
||||
void setStackOverflowExceptionEnable(bool);
|
||||
|
||||
ThreadListNode* getThreadListNode() { return &mListNode; }
|
||||
|
||||
#ifdef SEAD_DEBUG
|
||||
void listenPropertyEvent(const hostio::PropertyEvent* event) override;
|
||||
void genMessage(hostio::Context* context) override;
|
||||
#endif
|
||||
|
||||
bool isDefaultPriority() const { return getPriority() == cDefaultPriority; }
|
||||
|
||||
static const s32 cDefaultPriority;
|
||||
|
||||
protected:
|
||||
#ifdef NNSDK
|
||||
Thread(Heap* heap, nn::os::ThreadType*, u32);
|
||||
#endif
|
||||
|
||||
virtual void run_();
|
||||
virtual void calc_(MessageQueue::Element msg) = 0;
|
||||
virtual uintptr_t getStackCheckStartAddress_() const;
|
||||
|
||||
void initStackCheck_();
|
||||
void initStackCheckWithCurrentStackPointer_();
|
||||
|
||||
#ifdef NNSDK
|
||||
static void ninThreadFunc_(void*);
|
||||
#endif
|
||||
|
||||
MessageQueue mMessageQueue;
|
||||
s32 mStackSize = 0;
|
||||
ThreadListNode mListNode;
|
||||
Heap* mCurrentHeap = nullptr;
|
||||
FindContainHeapCache mFindContainHeapCache;
|
||||
MessageQueue::BlockType mBlockType = MessageQueue::BlockType::Blocking;
|
||||
MessageQueue::Element mQuitMsg = 0;
|
||||
u32 mId = 0;
|
||||
State mState = State::cInitialized;
|
||||
CoreIdMask mAffinity{CoreId::cMain};
|
||||
#ifdef NNSDK
|
||||
nn::os::ThreadType* mThreadInner = nullptr;
|
||||
#endif
|
||||
void* mStackTop = nullptr;
|
||||
void* mStackTopForCheck = nullptr;
|
||||
s32 mPriority = 0;
|
||||
};
|
||||
|
||||
class ThreadMgr : public hostio::Node
|
||||
{
|
||||
SEAD_SINGLETON_DISPOSER(ThreadMgr)
|
||||
public:
|
||||
ThreadMgr();
|
||||
virtual ~ThreadMgr();
|
||||
|
||||
void initialize(Heap* heap);
|
||||
void destroy();
|
||||
|
||||
bool isMainThread() const;
|
||||
Thread* getMainThread() const { return mMainThread; }
|
||||
Thread* getCurrentThread() const { return reinterpret_cast<Thread*>(mThreadPtrTLS.getValue()); }
|
||||
|
||||
static void waitDoneMultipleThread(Thread* const* threads, s32 num);
|
||||
static void quitAndWaitDoneMultipleThread(Thread** threads, s32 num, bool is_jam);
|
||||
|
||||
static void checkCurrentThreadStackOverFlow(const char* source_file, s32 source_line);
|
||||
static void checkCurrentThreadStackEndCorruption(const char* source_file, s32 source_line);
|
||||
static void checkCurrentThreadStackPointerOverFlow(const char* source_file, s32 source_line);
|
||||
|
||||
CriticalSection* getListCS() { return &mListCS; }
|
||||
|
||||
#ifdef SEAD_DEBUG
|
||||
void initHostIO();
|
||||
void genMessage(hostio::Context* context) override;
|
||||
void listenPropertyEvent(const hostio::PropertyEvent* event) override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
friend class Thread;
|
||||
|
||||
void addThread_(Thread* thread)
|
||||
{
|
||||
ScopedLock<CriticalSection> lock(getListCS());
|
||||
mList.pushBack(thread->getThreadListNode());
|
||||
}
|
||||
|
||||
void removeThread_(Thread* thread)
|
||||
{
|
||||
ScopedLock<CriticalSection> lock(getListCS());
|
||||
mList.erase(thread->getThreadListNode());
|
||||
}
|
||||
|
||||
void initMainThread_(Heap* heap);
|
||||
void destroyMainThread_();
|
||||
static u32 getCurrentThreadID_();
|
||||
|
||||
private:
|
||||
ThreadList mList;
|
||||
CriticalSection mListCS;
|
||||
Thread* mMainThread = nullptr;
|
||||
ThreadLocalStorage mThreadPtrTLS;
|
||||
};
|
||||
|
||||
class MainThread : public Thread
|
||||
{
|
||||
public:
|
||||
#ifdef NNSDK
|
||||
MainThread(Heap* heap, nn::os::ThreadType* nn_thread, u32 thread_id)
|
||||
: Thread(heap, nn_thread, thread_id)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
~MainThread() override { mState = State::cTerminated; }
|
||||
|
||||
void destroy() override { SEAD_ASSERT_MSG(false, "Main thread can not destroy"); }
|
||||
void quit(bool) override { SEAD_ASSERT_MSG(false, "Main thread can not quit"); }
|
||||
void waitDone() override { SEAD_ASSERT_MSG(false, "Main thread can not waitDone"); }
|
||||
void quitAndDestroySingleThread(bool) override
|
||||
{
|
||||
SEAD_ASSERT_MSG(false, "Main thread can not quit");
|
||||
}
|
||||
void setPriority(s32) override { SEAD_ASSERT_MSG(false, "Main thread can not set priority"); }
|
||||
|
||||
protected:
|
||||
void calc_(MessageQueue::Element) override {}
|
||||
};
|
||||
} // namespace sead
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef NNSDK
|
||||
#include <nn/os.h>
|
||||
#endif
|
||||
|
||||
#include "basis/seadTypes.h"
|
||||
|
||||
namespace sead
|
||||
{
|
||||
class ThreadLocalStorage
|
||||
{
|
||||
public:
|
||||
ThreadLocalStorage();
|
||||
~ThreadLocalStorage();
|
||||
|
||||
ThreadLocalStorage(const ThreadLocalStorage&) = delete;
|
||||
ThreadLocalStorage& operator=(const ThreadLocalStorage&) = delete;
|
||||
|
||||
void setValue(uintptr_t value);
|
||||
uintptr_t getValue() const;
|
||||
|
||||
private:
|
||||
#ifdef NNSDK
|
||||
nn::os::TlsSlot mTlsSlot;
|
||||
#endif
|
||||
};
|
||||
} // namespace sead
|
||||
|
||||
#define SEAD_THREAD_THREAD_LOCAL_STORAGE_H_
|
||||
#ifdef NNSDK
|
||||
#include "thread/nin/seadThreadLocalStorageNin.hpp"
|
||||
#else
|
||||
#error "Unknown platform"
|
||||
#endif
|
||||
#undef SEAD_THREAD_THREAD_LOCAL_STORAGE_H_
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "basis/seadTypes.h"
|
||||
|
||||
namespace sead
|
||||
{
|
||||
class ThreadUtil
|
||||
{
|
||||
public:
|
||||
static s32 ConvertPrioritySeadToPlatform(s32 prio);
|
||||
static s32 ConvertPriorityPlatformToSead(s32 prio);
|
||||
static uintptr_t GetCurrentStackPointer();
|
||||
};
|
||||
} // namespace sead
|
||||
Reference in New Issue
Block a user