#pragma once #ifdef __cplusplus extern "C" { #endif // __cplusplus #include "lua.h" #ifdef __cplusplus } #endif // __cplusplus #include "compat.h" #include "macros_and_utils.h" #include // forwards struct DeepPrelude; struct Keepers; struct Lane; // ################################################################################################ /* * Do we want to activate full lane tracking feature? (EXPERIMENTAL) */ #define HAVE_LANE_TRACKING() 1 // ################################################################################################ // everything we need to provide to lua_newstate() class AllocatorDefinition { public: lua_Alloc m_allocF{ nullptr }; void* m_allocUD{ nullptr }; static void* operator new(size_t size_, lua_State* L) noexcept { return lua_newuserdatauv(L, size_, 0); } // 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_, lua_State* L) { ASSERT_L(!"should never be called") }; AllocatorDefinition(lua_Alloc allocF_, void* allocUD_) noexcept : m_allocF{ allocF_ } , m_allocUD{ allocUD_ } { } AllocatorDefinition() = default; AllocatorDefinition(AllocatorDefinition const& rhs_) = default; AllocatorDefinition(AllocatorDefinition&& rhs_) = default; AllocatorDefinition& operator=(AllocatorDefinition const& rhs_) = default; AllocatorDefinition& operator=(AllocatorDefinition&& rhs_) = default; void initFrom(lua_State* L) { m_allocF = lua_getallocf(L, &m_allocUD); } void* lua_alloc(void* ptr_, size_t osize_, size_t nsize_) { m_allocF(m_allocUD, ptr_, osize_, nsize_); } void* alloc(size_t nsize_) { return m_allocF(m_allocUD, nullptr, 0, nsize_); } void free(void* ptr_, size_t osize_) { std::ignore = m_allocF(m_allocUD, ptr_, osize_, 0); } }; // ################################################################################################ // mutex-protected allocator for use with Lua states that share a non-threadsafe allocator class ProtectedAllocator : public AllocatorDefinition { private: std::mutex m_lock; static void* protected_lua_Alloc(void* ud_, void* ptr_, size_t osize_, size_t nsize_) { ProtectedAllocator* const s{ static_cast(ud_) }; std::lock_guard guard{ s->m_lock }; return s->m_allocF(s->m_allocUD, ptr_, osize_, nsize_); } public: // we are not like our base class: we can't be created inside a full userdata (or we would have to install a metatable and __gc handler to destroy ourselves properly) static void* operator new(size_t size_, lua_State* L) noexcept = delete; static void operator delete(void* p_, lua_State* L) = delete; AllocatorDefinition makeDefinition() { return AllocatorDefinition{ protected_lua_Alloc, this}; } void installIn(lua_State* L) { lua_setallocf(L, protected_lua_Alloc, this); } void removeFrom(lua_State* L) { // remove the protected allocator, if any if (m_allocF != nullptr) { // install the non-protected allocator lua_setallocf(L, m_allocF, m_allocUD); } } }; // ################################################################################################ // everything regarding the Lanes universe is stored in that global structure // held as a full userdata in the master Lua state that required it for the first time // don't forget to initialize all members in LG_configure() struct Universe { // for verbose errors bool verboseErrors{ false }; bool demoteFullUserdata{ false }; // before a state is created, this function will be called to obtain the allocator lua_CFunction provide_allocator{ nullptr }; // after a state is created, this function will be called right after the bases libraries are loaded lua_CFunction on_state_create_func{ nullptr }; // if allocator="protected" is found in the configuration settings, a wrapper allocator will protect all allocator calls with a mutex // contains a mutex and the original allocator definition ProtectedAllocator protected_allocator; AllocatorDefinition internal_allocator; Keepers* keepers{ nullptr }; // Initialized by 'init_once_LOCKED()': the deep userdata Linda object // used for timers (each lane will get a proxy to this) DeepPrelude* timer_deep{ nullptr }; #if HAVE_LANE_TRACKING() std::mutex tracking_cs; Lane* volatile tracking_first{ nullptr }; // will change to TRACKING_END if we want to activate tracking #endif // HAVE_LANE_TRACKING() std::mutex selfdestruct_cs; // require() serialization std::recursive_mutex require_cs; std::atomic next_mt_id{ 1 }; #if USE_DEBUG_SPEW() int debugspew_indent_depth{ 0 }; #endif // USE_DEBUG_SPEW() Lane* volatile selfdestruct_first{ nullptr }; // After a lane has removed itself from the chain, it still performs some processing. // The terminal desinit sequence should wait for all such processing to terminate before force-killing threads int volatile selfdestructing_count{ 0 }; }; Universe* universe_get(lua_State* L); Universe* universe_create(lua_State* L); void universe_store(lua_State* L, Universe* U);