From 3cf9c3c9d076d5822595834bdbf5153d4e923c67 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Mon, 28 Oct 2024 18:09:51 +0100 Subject: Renamed lane.h → lane.hpp, linda.h → linda.hpp, threading.h → threading.hpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cancel.cpp | 2 +- src/intercopycontext.cpp | 4 +- src/keeper.cpp | 4 +- src/lane.cpp | 4 +- src/lane.h | 209 ----------------------------------------------- src/lane.hpp | 209 +++++++++++++++++++++++++++++++++++++++++++++++ src/lanes.cpp | 4 +- src/linda.cpp | 4 +- src/linda.h | 103 ----------------------- src/linda.hpp | 103 +++++++++++++++++++++++ src/lindafactory.cpp | 2 +- src/state.cpp | 2 +- src/threading.cpp | 2 +- src/threading.h | 73 ----------------- src/threading.hpp | 73 +++++++++++++++++ src/tracker.cpp | 2 +- src/universe.cpp | 2 +- 17 files changed, 401 insertions(+), 401 deletions(-) delete mode 100644 src/lane.h create mode 100644 src/lane.hpp delete mode 100644 src/linda.h create mode 100644 src/linda.hpp delete mode 100644 src/threading.h create mode 100644 src/threading.hpp (limited to 'src') diff --git a/src/cancel.cpp b/src/cancel.cpp index 2ef5b5c..40f03f8 100644 --- a/src/cancel.cpp +++ b/src/cancel.cpp @@ -36,7 +36,7 @@ THE SOFTWARE. #include "cancel.hpp" #include "debugspew.h" -#include "lane.h" +#include "lane.hpp" // ################################################################################################# // ################################################################################################# diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp index bd09a3c..ff03e3c 100644 --- a/src/intercopycontext.cpp +++ b/src/intercopycontext.cpp @@ -29,8 +29,8 @@ THE SOFTWARE. #include "debugspew.h" #include "deep.hpp" #include "keeper.hpp" -#include "lane.h" -#include "linda.h" +#include "lane.hpp" +#include "linda.hpp" #include "nameof.hpp" #include "universe.hpp" diff --git a/src/keeper.cpp b/src/keeper.cpp index 67c5a38..d7ed77c 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp @@ -41,8 +41,8 @@ #include "keeper.hpp" #include "intercopycontext.h" -#include "lane.h" -#include "linda.h" +#include "lane.hpp" +#include "linda.hpp" #include "state.h" // There is a table at _R[kLindasRegKey] (aka LindasDB) diff --git a/src/lane.cpp b/src/lane.cpp index 0456d98..fbd47b6 100644 --- a/src/lane.cpp +++ b/src/lane.cpp @@ -24,11 +24,11 @@ THE SOFTWARE. =============================================================================== */ #include "_pch.hpp" -#include "lane.h" +#include "lane.hpp" #include "debugspew.h" #include "intercopycontext.h" -#include "threading.h" +#include "threading.hpp" #include "tools.hpp" // ################################################################################################# diff --git a/src/lane.h b/src/lane.h deleted file mode 100644 index 7821112..0000000 --- a/src/lane.h +++ /dev/null @@ -1,209 +0,0 @@ -#pragma once - -#include "cancel.hpp" -#include "uniquekey.hpp" -#include "universe.hpp" - -// ################################################################################################# - -// xxh64 of string "kExtendedStackTraceRegKey" generated at https://www.pelock.com/products/hash-calculator -static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2ull }; // used as registry key - -/* - * registry[FINALIZER_REG_KEY] is either nil (no finalizers) or a table - * of functions that Lanes will call after the executing 'pcall' has ended. - * - * We're NOT using the GC system for finalizer mainly because providing the - * error (and maybe stack trace) arguments to the finalizer functions would - * anyways complicate that approach. - */ -// xxh64 of string "kCoroutineRegKey" generated at https://www.pelock.com/products/hash-calculator -static constexpr RegistryUniqueKey kCoroutineRegKey{ 0x72B049B0D130F009ull }; - -// xxh64 of string "kFinalizerRegKey" generated at https://www.pelock.com/products/hash-calculator -static constexpr RegistryUniqueKey kFinalizerRegKey{ 0xFE936BFAA718FEEAull }; - -// xxh64 of string "kLaneGC" generated at https://www.pelock.com/products/hash-calculator -static constexpr UniqueKey kLaneGC{ 0x5D6122141727F960ull }; - -// xxh64 of string "kLanePointerRegKey" generated at https://www.pelock.com/products/hash-calculator -static constexpr RegistryUniqueKey kLanePointerRegKey{ 0x2D8CF03FE9F0A51Aull }; // used as registry key - - // xxh64 of string "debugName" generated at https://www.pelock.com/products/hash-calculator -static constexpr RegistryUniqueKey kLaneNameRegKey{ 0xA194E2645C57F6DDull }; - -// ################################################################################################# - -// The chain is ended by '(Lane*)(-1)', not nullptr: 'selfdestructFirst -> ... -> ... -> (-1)' -#define SELFDESTRUCT_END ((Lane*) (-1)) - -static constexpr std::string_view kLaneMetatableName{ "Lane" }; - -// must be a #define instead of a constexpr to benefit from compile-time string concatenation -#define kLanesLibName "lanes" -#define kLanesCoreLibName kLanesLibName ".core" - -// for cancel() argument -enum class WakeLane -{ - No, - Yes -}; - -// NOTE: values to be changed by either thread, during execution, without locking, are marked "volatile" -class Lane -{ - public: - /* - Pending: The Lua VM hasn't done anything yet. - Resuming: The user requested the lane to resume execution from Suspended state. - Suspended: returned from lua_resume, waiting for the client to request a lua_resume. - Running, Suspended, Waiting: Thread is inside the Lua VM. - Done, Error, Cancelled: Thread execution is outside the Lua VM. It can be lua_close()d. - */ - enum class Status - { - Pending, - Running, - Suspended, - Resuming, - Waiting, - Done, - Error, - Cancelled - }; - using enum Status; - - enum class ErrorTraceLevel - { - Minimal, // no error handler function when running the lane body - Basic, // lane body errors caught by a error handler - Extended // same as above, but with more data extracted from the debug infos - }; - using enum ErrorTraceLevel; - - // the thread - std::thread thread; // use jthread if we ever need a stop_source -#ifndef __PROSPERO__ - // a latch to wait for the lua_State to be ready - std::latch ready{ 1 }; -#else // __PROSPERO__ - std::atomic_flag ready{}; -#endif // __PROSPERO__ - // to wait for stop requests through thread's stop_source - std::mutex doneMutex; - std::condition_variable doneCondVar; // use condition_variable_any if waiting for a stop_token - // - // M: sub-thread OS thread - // S: not used - - private: - - mutable std::mutex debugNameMutex; - std::string_view debugName{ "" }; - - public: - - Universe* const U{}; - lua_State* S{}; // the master state of the lane - lua_State* L{}; // the state we run things in (either S or a lua_newthread() state if we run in coroutine mode) - int nresults{}; // number of results to extract from the stack (can differ from the actual number of values when in coroutine mode) - // - // M: prepares the state, and reads results - // S: while S is running, M must keep out of modifying the state - - Status volatile status{ Pending }; - // - // M: sets to Pending (before launching) - // S: updates -> Running/Waiting/Suspended -> Done/Error/Cancelled - - std::condition_variable* volatile waiting_on{ nullptr }; - // - // When status is Waiting, points on the linda's signal the thread waits on, else nullptr - - CancelRequest volatile cancelRequest{ CancelRequest::None }; - // - // M: sets to false, flags true for cancel request - // S: reads to see if cancel is requested - - Lane* volatile selfdestruct_next{ nullptr }; - // - // M: sets to non-nullptr if facing lane handle '__gc' cycle but the lane - // is still running - // S: cleans up after itself if non-nullptr at lane exit - - // For tracking only - Lane* volatile tracking_next{ nullptr }; - - ErrorTraceLevel const errorTraceLevel{ Basic }; - - [[nodiscard]] static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); } - // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception - static void operator delete(void* p_, Universe* U_) { U_->internalAllocator.free(p_, sizeof(Lane)); } - // this one is for us, to make sure memory is freed by the correct allocator - static void operator delete(void* p_) { static_cast(p_)->U->internalAllocator.free(p_, sizeof(Lane)); } - - ~Lane(); - Lane(Universe* U_, lua_State* L_, ErrorTraceLevel errorTraceLevel_, bool asCoroutine_); - - // rule of 5 - Lane(Lane const&) = delete; - Lane(Lane&&) = delete; - Lane& operator=(Lane const&) = delete; - Lane& operator=(Lane&&) = delete; - - private: - - [[nodiscard]] CancelResult internalCancel(CancelRequest rq_, std::chrono::time_point until_, WakeLane wakeLane_); - - public: - - CancelResult cancel(CancelOp op_, std::chrono::time_point until_, WakeLane wakeLane_, int hookCount_); - void changeDebugName(StackIndex nameIdx_); - void closeState() - { - lua_State* const _L{ S }; - S = nullptr; - L = nullptr; - nresults = 0; - lua_close(_L); // this collects our coroutine thread at the same time - } - [[nodiscard]] std::string_view errorTraceLevelString() const; - [[nodiscard]] int errorHandlerCount() const noexcept - { - // don't push a error handler when in coroutine mode, as the first lua_resume wants only the function and its arguments on the stack - return ((errorTraceLevel == Lane::Minimal) || isCoroutine()) ? 0 : 1; - } - [[nodiscard]] bool isCoroutine() const noexcept { return S != L; } - [[nodiscard]] std::string_view getDebugName() const - { - std::lock_guard _guard{ debugNameMutex }; - return debugName; - } - static int LuaErrorHandler(lua_State* L_); - [[nodiscard]] int pushErrorHandler() const noexcept { return (errorHandlerCount() == 0) ? 0 : (lua_pushcfunction(L, LuaErrorHandler), 1); } - [[nodiscard]] std::string_view pushErrorTraceLevel(lua_State* L_) const; - static void PushMetatable(lua_State* L_); - void pushStatusString(lua_State* L_) const; - void pushIndexedResult(lua_State* L_, int key_) const; - void resetResultsStorage(lua_State* L_, StackIndex self_idx_); - void selfdestructAdd(); - [[nodiscard]] bool selfdestructRemove(); - void securizeDebugName(lua_State* L_); - void startThread(int priority_); - [[nodiscard]] int storeResults(lua_State* L_); - [[nodiscard]] std::string_view threadStatusString() const; - // wait until the lane stops working with its state (either Suspended or Done+) - [[nodiscard]] bool waitForCompletion(std::chrono::time_point until_); -}; - -// ################################################################################################# - -// To allow free-running threads (longer lifespan than the handle's) -// 'Lane' are malloc/free'd and the handle only carries a pointer. -// This is not deep userdata since the handle is not portable among lanes. -// -[[nodiscard]] inline Lane* ToLane(lua_State* const L_, StackIndex const i_) -{ - return *(static_cast(luaL_checkudata(L_, i_, kLaneMetatableName.data()))); -} diff --git a/src/lane.hpp b/src/lane.hpp new file mode 100644 index 0000000..7821112 --- /dev/null +++ b/src/lane.hpp @@ -0,0 +1,209 @@ +#pragma once + +#include "cancel.hpp" +#include "uniquekey.hpp" +#include "universe.hpp" + +// ################################################################################################# + +// xxh64 of string "kExtendedStackTraceRegKey" generated at https://www.pelock.com/products/hash-calculator +static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2ull }; // used as registry key + +/* + * registry[FINALIZER_REG_KEY] is either nil (no finalizers) or a table + * of functions that Lanes will call after the executing 'pcall' has ended. + * + * We're NOT using the GC system for finalizer mainly because providing the + * error (and maybe stack trace) arguments to the finalizer functions would + * anyways complicate that approach. + */ +// xxh64 of string "kCoroutineRegKey" generated at https://www.pelock.com/products/hash-calculator +static constexpr RegistryUniqueKey kCoroutineRegKey{ 0x72B049B0D130F009ull }; + +// xxh64 of string "kFinalizerRegKey" generated at https://www.pelock.com/products/hash-calculator +static constexpr RegistryUniqueKey kFinalizerRegKey{ 0xFE936BFAA718FEEAull }; + +// xxh64 of string "kLaneGC" generated at https://www.pelock.com/products/hash-calculator +static constexpr UniqueKey kLaneGC{ 0x5D6122141727F960ull }; + +// xxh64 of string "kLanePointerRegKey" generated at https://www.pelock.com/products/hash-calculator +static constexpr RegistryUniqueKey kLanePointerRegKey{ 0x2D8CF03FE9F0A51Aull }; // used as registry key + + // xxh64 of string "debugName" generated at https://www.pelock.com/products/hash-calculator +static constexpr RegistryUniqueKey kLaneNameRegKey{ 0xA194E2645C57F6DDull }; + +// ################################################################################################# + +// The chain is ended by '(Lane*)(-1)', not nullptr: 'selfdestructFirst -> ... -> ... -> (-1)' +#define SELFDESTRUCT_END ((Lane*) (-1)) + +static constexpr std::string_view kLaneMetatableName{ "Lane" }; + +// must be a #define instead of a constexpr to benefit from compile-time string concatenation +#define kLanesLibName "lanes" +#define kLanesCoreLibName kLanesLibName ".core" + +// for cancel() argument +enum class WakeLane +{ + No, + Yes +}; + +// NOTE: values to be changed by either thread, during execution, without locking, are marked "volatile" +class Lane +{ + public: + /* + Pending: The Lua VM hasn't done anything yet. + Resuming: The user requested the lane to resume execution from Suspended state. + Suspended: returned from lua_resume, waiting for the client to request a lua_resume. + Running, Suspended, Waiting: Thread is inside the Lua VM. + Done, Error, Cancelled: Thread execution is outside the Lua VM. It can be lua_close()d. + */ + enum class Status + { + Pending, + Running, + Suspended, + Resuming, + Waiting, + Done, + Error, + Cancelled + }; + using enum Status; + + enum class ErrorTraceLevel + { + Minimal, // no error handler function when running the lane body + Basic, // lane body errors caught by a error handler + Extended // same as above, but with more data extracted from the debug infos + }; + using enum ErrorTraceLevel; + + // the thread + std::thread thread; // use jthread if we ever need a stop_source +#ifndef __PROSPERO__ + // a latch to wait for the lua_State to be ready + std::latch ready{ 1 }; +#else // __PROSPERO__ + std::atomic_flag ready{}; +#endif // __PROSPERO__ + // to wait for stop requests through thread's stop_source + std::mutex doneMutex; + std::condition_variable doneCondVar; // use condition_variable_any if waiting for a stop_token + // + // M: sub-thread OS thread + // S: not used + + private: + + mutable std::mutex debugNameMutex; + std::string_view debugName{ "" }; + + public: + + Universe* const U{}; + lua_State* S{}; // the master state of the lane + lua_State* L{}; // the state we run things in (either S or a lua_newthread() state if we run in coroutine mode) + int nresults{}; // number of results to extract from the stack (can differ from the actual number of values when in coroutine mode) + // + // M: prepares the state, and reads results + // S: while S is running, M must keep out of modifying the state + + Status volatile status{ Pending }; + // + // M: sets to Pending (before launching) + // S: updates -> Running/Waiting/Suspended -> Done/Error/Cancelled + + std::condition_variable* volatile waiting_on{ nullptr }; + // + // When status is Waiting, points on the linda's signal the thread waits on, else nullptr + + CancelRequest volatile cancelRequest{ CancelRequest::None }; + // + // M: sets to false, flags true for cancel request + // S: reads to see if cancel is requested + + Lane* volatile selfdestruct_next{ nullptr }; + // + // M: sets to non-nullptr if facing lane handle '__gc' cycle but the lane + // is still running + // S: cleans up after itself if non-nullptr at lane exit + + // For tracking only + Lane* volatile tracking_next{ nullptr }; + + ErrorTraceLevel const errorTraceLevel{ Basic }; + + [[nodiscard]] static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); } + // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception + static void operator delete(void* p_, Universe* U_) { U_->internalAllocator.free(p_, sizeof(Lane)); } + // this one is for us, to make sure memory is freed by the correct allocator + static void operator delete(void* p_) { static_cast(p_)->U->internalAllocator.free(p_, sizeof(Lane)); } + + ~Lane(); + Lane(Universe* U_, lua_State* L_, ErrorTraceLevel errorTraceLevel_, bool asCoroutine_); + + // rule of 5 + Lane(Lane const&) = delete; + Lane(Lane&&) = delete; + Lane& operator=(Lane const&) = delete; + Lane& operator=(Lane&&) = delete; + + private: + + [[nodiscard]] CancelResult internalCancel(CancelRequest rq_, std::chrono::time_point until_, WakeLane wakeLane_); + + public: + + CancelResult cancel(CancelOp op_, std::chrono::time_point until_, WakeLane wakeLane_, int hookCount_); + void changeDebugName(StackIndex nameIdx_); + void closeState() + { + lua_State* const _L{ S }; + S = nullptr; + L = nullptr; + nresults = 0; + lua_close(_L); // this collects our coroutine thread at the same time + } + [[nodiscard]] std::string_view errorTraceLevelString() const; + [[nodiscard]] int errorHandlerCount() const noexcept + { + // don't push a error handler when in coroutine mode, as the first lua_resume wants only the function and its arguments on the stack + return ((errorTraceLevel == Lane::Minimal) || isCoroutine()) ? 0 : 1; + } + [[nodiscard]] bool isCoroutine() const noexcept { return S != L; } + [[nodiscard]] std::string_view getDebugName() const + { + std::lock_guard _guard{ debugNameMutex }; + return debugName; + } + static int LuaErrorHandler(lua_State* L_); + [[nodiscard]] int pushErrorHandler() const noexcept { return (errorHandlerCount() == 0) ? 0 : (lua_pushcfunction(L, LuaErrorHandler), 1); } + [[nodiscard]] std::string_view pushErrorTraceLevel(lua_State* L_) const; + static void PushMetatable(lua_State* L_); + void pushStatusString(lua_State* L_) const; + void pushIndexedResult(lua_State* L_, int key_) const; + void resetResultsStorage(lua_State* L_, StackIndex self_idx_); + void selfdestructAdd(); + [[nodiscard]] bool selfdestructRemove(); + void securizeDebugName(lua_State* L_); + void startThread(int priority_); + [[nodiscard]] int storeResults(lua_State* L_); + [[nodiscard]] std::string_view threadStatusString() const; + // wait until the lane stops working with its state (either Suspended or Done+) + [[nodiscard]] bool waitForCompletion(std::chrono::time_point until_); +}; + +// ################################################################################################# + +// To allow free-running threads (longer lifespan than the handle's) +// 'Lane' are malloc/free'd and the handle only carries a pointer. +// This is not deep userdata since the handle is not portable among lanes. +// +[[nodiscard]] inline Lane* ToLane(lua_State* const L_, StackIndex const i_) +{ + return *(static_cast(luaL_checkudata(L_, i_, kLaneMetatableName.data()))); +} diff --git a/src/lanes.cpp b/src/lanes.cpp index 23ab400..82977ca 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp @@ -85,10 +85,10 @@ THE SOFTWARE. #include "deep.hpp" #include "intercopycontext.h" #include "keeper.hpp" -#include "lane.h" +#include "lane.hpp" #include "nameof.hpp" #include "state.h" -#include "threading.h" +#include "threading.hpp" #include "tools.hpp" #if !(defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC)) diff --git a/src/linda.cpp b/src/linda.cpp index 753e761..1910a99 100644 --- a/src/linda.cpp +++ b/src/linda.cpp @@ -31,9 +31,9 @@ THE SOFTWARE. */ #include "_pch.hpp" -#include "linda.h" +#include "linda.hpp" -#include "lane.h" +#include "lane.hpp" #include "lindafactory.h" #include "tools.hpp" diff --git a/src/linda.h b/src/linda.h deleted file mode 100644 index 5b5f683..0000000 --- a/src/linda.h +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -#include "cancel.hpp" -#include "deep.hpp" -#include "universe.hpp" - -struct Keeper; - -// ################################################################################################# - -// xxh64 of string "kLindaBatched" generated at https://www.pelock.com/products/hash-calculator -static constexpr UniqueKey kLindaBatched{ 0xB8234DF772646567ull, "linda.batched" }; - -// ################################################################################################# - -DECLARE_UNIQUE_TYPE(LindaGroup, int); - -class Linda -: public DeepPrelude // Deep userdata MUST start with this header -{ - public: - class KeeperOperationInProgress - { - private: - Linda& linda; - [[maybe_unused]] lua_State* const L; // just here for inspection while debugging - - public: - KeeperOperationInProgress(Linda& linda_, lua_State* const L_) - : linda{ linda_ } - , L{ L_ } - { - [[maybe_unused]] UnusedInt const _prev{ linda.keeperOperationCount.fetch_add(1, std::memory_order_seq_cst) }; - } - - public: - ~KeeperOperationInProgress() - { - [[maybe_unused]] UnusedInt const _prev{ linda.keeperOperationCount.fetch_sub(1, std::memory_order_seq_cst) }; - } - }; - - enum class Status - { - Active, - Cancelled - }; - using enum Status; - - private: - - static constexpr size_t kEmbeddedNameLength = 24; - using EmbeddedName = std::array; - // depending on the name length, it is either embedded inside the Linda, or allocated separately - std::variant nameVariant{}; - // counts the keeper operations in progress - std::atomic keeperOperationCount{}; - - public: - std::condition_variable readHappened{}; - std::condition_variable writeHappened{}; - Universe* const U{ nullptr }; // the universe this linda belongs to - KeeperIndex const keeperIndex{ -1 }; // the keeper associated to this linda - Status cancelStatus{ Status::Active }; - - public: - [[nodiscard]] static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); } - // always embedded somewhere else or "in-place constructed" as a full userdata - // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception - static void operator delete(void* p_, Universe* U_) { U_->internalAllocator.free(p_, sizeof(Linda)); } - // this one is for us, to make sure memory is freed by the correct allocator - static void operator delete(void* p_) { static_cast(p_)->U->internalAllocator.free(p_, sizeof(Linda)); } - - ~Linda(); - Linda(Universe* U_, LindaGroup group_, std::string_view const& name_); - Linda() = delete; - // non-copyable, non-movable - Linda(Linda const&) = delete; - Linda(Linda const&&) = delete; - Linda& operator=(Linda const&) = delete; - Linda& operator=(Linda const&&) = delete; - - private: - void freeAllocatedName(); - void setName(std::string_view const& name_); - - public: - [[nodiscard]] Keeper* acquireKeeper() const; - [[nodiscard]] std::string_view getName() const; - [[nodiscard]] bool inKeeperOperation() const { return keeperOperationCount.load(std::memory_order_seq_cst) != 0; } - template - [[nodiscard]] T obfuscated() const - { - // xxh64 of string "kObfuscator" generated at https://www.pelock.com/products/hash-calculator - static constexpr UniqueKey kObfuscator{ 0x7B8AA1F99A3BD782ull }; - return std::bit_cast(std::bit_cast(this) ^ kObfuscator.storage); - }; - void releaseKeeper(Keeper* keeper_) const; - [[nodiscard]] static int ProtectedCall(lua_State* L_, lua_CFunction f_); - void pushCancelString(lua_State* L_) const; - [[nodiscard]] KeeperOperationInProgress startKeeperOperation(lua_State* const L_) { return KeeperOperationInProgress{ *this, L_ }; }; - [[nodiscard]] Keeper* whichKeeper() const { return U->keepers.getKeeper(keeperIndex); } -}; diff --git a/src/linda.hpp b/src/linda.hpp new file mode 100644 index 0000000..5b5f683 --- /dev/null +++ b/src/linda.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include "cancel.hpp" +#include "deep.hpp" +#include "universe.hpp" + +struct Keeper; + +// ################################################################################################# + +// xxh64 of string "kLindaBatched" generated at https://www.pelock.com/products/hash-calculator +static constexpr UniqueKey kLindaBatched{ 0xB8234DF772646567ull, "linda.batched" }; + +// ################################################################################################# + +DECLARE_UNIQUE_TYPE(LindaGroup, int); + +class Linda +: public DeepPrelude // Deep userdata MUST start with this header +{ + public: + class KeeperOperationInProgress + { + private: + Linda& linda; + [[maybe_unused]] lua_State* const L; // just here for inspection while debugging + + public: + KeeperOperationInProgress(Linda& linda_, lua_State* const L_) + : linda{ linda_ } + , L{ L_ } + { + [[maybe_unused]] UnusedInt const _prev{ linda.keeperOperationCount.fetch_add(1, std::memory_order_seq_cst) }; + } + + public: + ~KeeperOperationInProgress() + { + [[maybe_unused]] UnusedInt const _prev{ linda.keeperOperationCount.fetch_sub(1, std::memory_order_seq_cst) }; + } + }; + + enum class Status + { + Active, + Cancelled + }; + using enum Status; + + private: + + static constexpr size_t kEmbeddedNameLength = 24; + using EmbeddedName = std::array; + // depending on the name length, it is either embedded inside the Linda, or allocated separately + std::variant nameVariant{}; + // counts the keeper operations in progress + std::atomic keeperOperationCount{}; + + public: + std::condition_variable readHappened{}; + std::condition_variable writeHappened{}; + Universe* const U{ nullptr }; // the universe this linda belongs to + KeeperIndex const keeperIndex{ -1 }; // the keeper associated to this linda + Status cancelStatus{ Status::Active }; + + public: + [[nodiscard]] static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); } + // always embedded somewhere else or "in-place constructed" as a full userdata + // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception + static void operator delete(void* p_, Universe* U_) { U_->internalAllocator.free(p_, sizeof(Linda)); } + // this one is for us, to make sure memory is freed by the correct allocator + static void operator delete(void* p_) { static_cast(p_)->U->internalAllocator.free(p_, sizeof(Linda)); } + + ~Linda(); + Linda(Universe* U_, LindaGroup group_, std::string_view const& name_); + Linda() = delete; + // non-copyable, non-movable + Linda(Linda const&) = delete; + Linda(Linda const&&) = delete; + Linda& operator=(Linda const&) = delete; + Linda& operator=(Linda const&&) = delete; + + private: + void freeAllocatedName(); + void setName(std::string_view const& name_); + + public: + [[nodiscard]] Keeper* acquireKeeper() const; + [[nodiscard]] std::string_view getName() const; + [[nodiscard]] bool inKeeperOperation() const { return keeperOperationCount.load(std::memory_order_seq_cst) != 0; } + template + [[nodiscard]] T obfuscated() const + { + // xxh64 of string "kObfuscator" generated at https://www.pelock.com/products/hash-calculator + static constexpr UniqueKey kObfuscator{ 0x7B8AA1F99A3BD782ull }; + return std::bit_cast(std::bit_cast(this) ^ kObfuscator.storage); + }; + void releaseKeeper(Keeper* keeper_) const; + [[nodiscard]] static int ProtectedCall(lua_State* L_, lua_CFunction f_); + void pushCancelString(lua_State* L_) const; + [[nodiscard]] KeeperOperationInProgress startKeeperOperation(lua_State* const L_) { return KeeperOperationInProgress{ *this, L_ }; }; + [[nodiscard]] Keeper* whichKeeper() const { return U->keepers.getKeeper(keeperIndex); } +}; diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp index 1233edf..ae5f2f6 100644 --- a/src/lindafactory.cpp +++ b/src/lindafactory.cpp @@ -33,7 +33,7 @@ THE SOFTWARE. #include "_pch.hpp" #include "lindafactory.h" -#include "linda.h" +#include "linda.hpp" static constexpr std::string_view kLindaMetatableName{ "Linda" }; diff --git a/src/state.cpp b/src/state.cpp index deea1fa..e6900d3 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -35,7 +35,7 @@ THE SOFTWARE. #include "state.h" #include "intercopycontext.h" -#include "lane.h" +#include "lane.hpp" #include "lanes.hpp" #include "tools.hpp" #include "universe.hpp" diff --git a/src/threading.cpp b/src/threading.cpp index bc0bc23..9c0d0fb 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -49,7 +49,7 @@ THE SOFTWARE. #endif // __linux__ -#include "threading.h" +#include "threading.hpp" #if !defined(PLATFORM_XBOX) && !defined(PLATFORM_WIN32) && !defined(PLATFORM_POCKETPC) #include diff --git a/src/threading.h b/src/threading.h deleted file mode 100644 index 044b5a4..0000000 --- a/src/threading.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "platform.h" - -#define THREADAPI_WINDOWS 1 -#define THREADAPI_PTHREAD 2 - -#if (defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC)) -// #pragma message ( "THREADAPI_WINDOWS" ) -#define THREADAPI THREADAPI_WINDOWS -#else // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) -// #pragma message ( "THREADAPI_PTHREAD" ) -#define THREADAPI THREADAPI_PTHREAD -#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) - -static constexpr int kThreadPrioDefault{ -999 }; - -// ################################################################################################# -// ################################################################################################# -#if THREADAPI == THREADAPI_WINDOWS - -#if defined(PLATFORM_XBOX) -#include -#else // !PLATFORM_XBOX -#define WIN32_LEAN_AND_MEAN -// CONDITION_VARIABLE needs version 0x0600+ -// _WIN32_WINNT value is already defined by MinGW, but not by MSVC -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 -#endif // _WIN32_WINNT -#include -#endif // !PLATFORM_XBOX -#include - -/* -#define XSTR(x) STR(x) -#define STR(x) #x -#pragma message( "The value of _WIN32_WINNT: " XSTR(_WIN32_WINNT)) -*/ - -static constexpr int kThreadPrioMin{ -3 }; -static constexpr int kThreadPrioMax{ +3 }; - -// ################################################################################################# -// ################################################################################################# -#else // THREADAPI == THREADAPI_PTHREAD -// ################################################################################################# -// ################################################################################################# - -// PThread (Linux, OS X, ...) - -// looks like some MinGW installations don't support PTW32_INCLUDE_WINDOWS_H, so let's include it ourselves, just in case -#if defined(PLATFORM_WIN32) -#include -#endif // PLATFORM_WIN32 -#include - -#if defined(PLATFORM_LINUX) && !defined(LINUX_SCHED_RR) -static constexpr int kThreadPrioMin{ 0 }; -#else -static constexpr int kThreadPrioMin{ -3 }; -#endif -static constexpr int kThreadPrioMax{ +3 }; - -#endif // THREADAPI == THREADAPI_PTHREAD -// ################################################################################################# -// ################################################################################################# - -void THREAD_SETNAME(std::string_view const& name_); -void THREAD_SET_PRIORITY(int prio_, bool sudo_); -void THREAD_SET_AFFINITY(unsigned int aff_); - -void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, bool sudo_); diff --git a/src/threading.hpp b/src/threading.hpp new file mode 100644 index 0000000..044b5a4 --- /dev/null +++ b/src/threading.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include "platform.h" + +#define THREADAPI_WINDOWS 1 +#define THREADAPI_PTHREAD 2 + +#if (defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC)) +// #pragma message ( "THREADAPI_WINDOWS" ) +#define THREADAPI THREADAPI_WINDOWS +#else // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) +// #pragma message ( "THREADAPI_PTHREAD" ) +#define THREADAPI THREADAPI_PTHREAD +#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) + +static constexpr int kThreadPrioDefault{ -999 }; + +// ################################################################################################# +// ################################################################################################# +#if THREADAPI == THREADAPI_WINDOWS + +#if defined(PLATFORM_XBOX) +#include +#else // !PLATFORM_XBOX +#define WIN32_LEAN_AND_MEAN +// CONDITION_VARIABLE needs version 0x0600+ +// _WIN32_WINNT value is already defined by MinGW, but not by MSVC +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 +#endif // _WIN32_WINNT +#include +#endif // !PLATFORM_XBOX +#include + +/* +#define XSTR(x) STR(x) +#define STR(x) #x +#pragma message( "The value of _WIN32_WINNT: " XSTR(_WIN32_WINNT)) +*/ + +static constexpr int kThreadPrioMin{ -3 }; +static constexpr int kThreadPrioMax{ +3 }; + +// ################################################################################################# +// ################################################################################################# +#else // THREADAPI == THREADAPI_PTHREAD +// ################################################################################################# +// ################################################################################################# + +// PThread (Linux, OS X, ...) + +// looks like some MinGW installations don't support PTW32_INCLUDE_WINDOWS_H, so let's include it ourselves, just in case +#if defined(PLATFORM_WIN32) +#include +#endif // PLATFORM_WIN32 +#include + +#if defined(PLATFORM_LINUX) && !defined(LINUX_SCHED_RR) +static constexpr int kThreadPrioMin{ 0 }; +#else +static constexpr int kThreadPrioMin{ -3 }; +#endif +static constexpr int kThreadPrioMax{ +3 }; + +#endif // THREADAPI == THREADAPI_PTHREAD +// ################################################################################################# +// ################################################################################################# + +void THREAD_SETNAME(std::string_view const& name_); +void THREAD_SET_PRIORITY(int prio_, bool sudo_); +void THREAD_SET_AFFINITY(unsigned int aff_); + +void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, bool sudo_); diff --git a/src/tracker.cpp b/src/tracker.cpp index 3697925..69b0ee0 100644 --- a/src/tracker.cpp +++ b/src/tracker.cpp @@ -26,7 +26,7 @@ THE SOFTWARE. #include "_pch.hpp" #include "tracker.hpp" -#include "lane.h" +#include "lane.hpp" // ################################################################################################# diff --git a/src/universe.cpp b/src/universe.cpp index 5cce9a5..9f28f6a 100644 --- a/src/universe.cpp +++ b/src/universe.cpp @@ -34,7 +34,7 @@ THE SOFTWARE. #include "deep.hpp" #include "intercopycontext.h" #include "keeper.hpp" -#include "lane.h" +#include "lane.hpp" #include "state.h" extern LUAG_FUNC(linda); -- cgit v1.2.3-55-g6feb