diff options
Diffstat (limited to 'src/universe.hpp')
-rw-r--r-- | src/universe.hpp | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/src/universe.hpp b/src/universe.hpp new file mode 100644 index 0000000..18e125f --- /dev/null +++ b/src/universe.hpp | |||
@@ -0,0 +1,168 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "allocator.hpp" | ||
4 | #include "keeper.hpp" | ||
5 | #include "lanesconf.h" | ||
6 | #include "tracker.hpp" | ||
7 | #include "uniquekey.hpp" | ||
8 | |||
9 | // ################################################################################################# | ||
10 | |||
11 | // forwards | ||
12 | enum class CancelOp; | ||
13 | struct DeepPrelude; | ||
14 | class Lane; | ||
15 | |||
16 | // ################################################################################################# | ||
17 | |||
18 | // mutex-protected allocator for use with Lua states that share a non-threadsafe allocator | ||
19 | class ProtectedAllocator | ||
20 | : public lanes::AllocatorDefinition | ||
21 | { | ||
22 | private: | ||
23 | std::mutex mutex; | ||
24 | |||
25 | [[nodiscard]] static void* protected_lua_Alloc(void* ud_, void* ptr_, size_t osize_, size_t nsize_) | ||
26 | { | ||
27 | ProtectedAllocator* const allocator{ static_cast<ProtectedAllocator*>(ud_) }; | ||
28 | std::lock_guard<std::mutex> guard{ allocator->mutex }; | ||
29 | return allocator->allocF(allocator->allocUD, ptr_, osize_, nsize_); | ||
30 | } | ||
31 | |||
32 | public: | ||
33 | // 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) | ||
34 | [[nodiscard]] static void* operator new(size_t size_, lua_State* L_) noexcept = delete; | ||
35 | static void operator delete(void* p_, lua_State* L_) = delete; | ||
36 | |||
37 | AllocatorDefinition makeDefinition() | ||
38 | { | ||
39 | return AllocatorDefinition{ version, protected_lua_Alloc, this }; | ||
40 | } | ||
41 | |||
42 | void installIn(lua_State* L_) | ||
43 | { | ||
44 | lua_setallocf(L_, protected_lua_Alloc, this); | ||
45 | } | ||
46 | |||
47 | void removeFrom(lua_State* L_) | ||
48 | { | ||
49 | // remove the protected allocator, if any | ||
50 | if (allocF != nullptr) { | ||
51 | // install the non-protected allocator | ||
52 | lua_setallocf(L_, allocF, allocUD); | ||
53 | } | ||
54 | } | ||
55 | }; | ||
56 | |||
57 | // ################################################################################################# | ||
58 | |||
59 | // xxh64 of string "kUniverseLightRegKey" generated at https://www.pelock.com/products/hash-calculator | ||
60 | static constexpr RegistryUniqueKey kUniverseLightRegKey{ 0x48BBE9CEAB0BA04Full }; | ||
61 | |||
62 | // ################################################################################################# | ||
63 | |||
64 | // everything regarding the Lanes universe is stored in that global structure | ||
65 | // held as a full userdata in the master Lua state that required it for the first time | ||
66 | class Universe | ||
67 | { | ||
68 | public: | ||
69 | static constexpr char const* kFinally{ "finally" }; // update lanes.lua if the name changes! | ||
70 | |||
71 | #ifdef PLATFORM_LINUX | ||
72 | // Linux needs to check, whether it's been run as root | ||
73 | bool const sudo{ geteuid() == 0 }; | ||
74 | #else | ||
75 | bool const sudo{ false }; | ||
76 | #endif // PLATFORM_LINUX | ||
77 | |||
78 | // for verbose errors | ||
79 | bool verboseErrors{ false }; | ||
80 | |||
81 | bool stripFunctions{ true }; | ||
82 | |||
83 | // before a state is created, this function will be called to obtain the allocator | ||
84 | lua_CFunction provideAllocator{ nullptr }; | ||
85 | |||
86 | // after a state is created, this function will be called right after the bases libraries are loaded | ||
87 | std::variant<std::nullptr_t, uintptr_t, lua_CFunction> onStateCreateFunc; | ||
88 | |||
89 | // if allocator="protected" is found in the configuration settings, a wrapper allocator will protect all allocator calls with a mutex | ||
90 | // contains a mutex and the original allocator definition | ||
91 | ProtectedAllocator protectedAllocator; | ||
92 | |||
93 | lanes::AllocatorDefinition internalAllocator; | ||
94 | |||
95 | Keepers keepers; | ||
96 | |||
97 | // Initialized by 'init_once_LOCKED()': the deep userdata Linda object | ||
98 | // used for timers (each lane will get a proxy to this) | ||
99 | DeepPrelude* timerLinda{ nullptr }; | ||
100 | |||
101 | LaneTracker tracker; | ||
102 | |||
103 | // Protects modifying the selfdestruct chain | ||
104 | std::mutex selfdestructMutex; | ||
105 | |||
106 | // require() serialization | ||
107 | std::recursive_mutex requireMutex; | ||
108 | |||
109 | // metatable unique identifiers | ||
110 | std::atomic<lua_Integer> nextMetatableId{ 1 }; | ||
111 | |||
112 | #if USE_DEBUG_SPEW() | ||
113 | std::atomic<int> debugspewIndentDepth{ 0 }; | ||
114 | #endif // USE_DEBUG_SPEW() | ||
115 | |||
116 | Lane* volatile selfdestructFirst{ nullptr }; | ||
117 | // After a lane has removed itself from the chain, it still performs some processing. | ||
118 | // The terminal desinit sequence should wait for all such processing to terminate before force-killing threads | ||
119 | std::atomic<int> selfdestructingCount{ 0 }; | ||
120 | |||
121 | public: | ||
122 | [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, lua_State* L_) noexcept { return luaG_newuserdatauv<Universe>(L_, UserValueCount{ 0 }); }; | ||
123 | // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception | ||
124 | static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] lua_State* L_) {} // nothing to do, as nothing is allocated independently | ||
125 | |||
126 | Universe(); | ||
127 | ~Universe() = default; | ||
128 | // non-copyable, non-movable | ||
129 | Universe(Universe const&) = delete; | ||
130 | Universe(Universe&&) = delete; | ||
131 | Universe& operator=(Universe const&) = delete; | ||
132 | Universe& operator=(Universe&&) = delete; | ||
133 | |||
134 | void callOnStateCreate(lua_State* const L_, lua_State* const from_, LookupMode const mode_); | ||
135 | [[nodiscard]] static Universe* Create(lua_State* L_); | ||
136 | [[nodiscard]] static inline Universe* Get(lua_State* L_); | ||
137 | void initializeAllocatorFunction(lua_State* L_); | ||
138 | static int InitializeFinalizer(lua_State* L_); | ||
139 | void initializeOnStateCreate(lua_State* const L_); | ||
140 | lanes::AllocatorDefinition resolveAllocator(lua_State* const L_, std::string_view const& hint_) const; | ||
141 | static inline void Store(lua_State* L_, Universe* U_); | ||
142 | [[nodiscard]] bool terminateFreeRunningLanes(lua_Duration shutdownTimeout_, CancelOp op_); | ||
143 | }; | ||
144 | |||
145 | // ################################################################################################# | ||
146 | |||
147 | inline Universe* Universe::Get(lua_State* L_) | ||
148 | { | ||
149 | STACK_CHECK_START_REL(L_, 0); | ||
150 | Universe* const _universe{ kUniverseLightRegKey.readLightUserDataValue<Universe>(L_) }; | ||
151 | STACK_CHECK(L_, 0); | ||
152 | return _universe; | ||
153 | } | ||
154 | |||
155 | // ################################################################################################# | ||
156 | |||
157 | inline void Universe::Store(lua_State* L_, Universe* U_) | ||
158 | { | ||
159 | // TODO: check if we actually ever call Store with a null universe | ||
160 | LUA_ASSERT(L_, !U_ || Universe::Get(L_) == nullptr); | ||
161 | STACK_CHECK_START_REL(L_, 0); | ||
162 | kUniverseLightRegKey.setValue(L_, [U = U_](lua_State* L_) { U ? lua_pushlightuserdata(L_, U) : lua_pushnil(L_); }); | ||
163 | STACK_CHECK(L_, 0); | ||
164 | } | ||
165 | |||
166 | // ################################################################################################# | ||
167 | |||
168 | LUAG_FUNC(universe_gc); | ||