diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/deep.cpp | 66 | ||||
-rw-r--r-- | src/deep.hpp | 11 | ||||
-rw-r--r-- | src/lanes.cpp | 1 | ||||
-rw-r--r-- | src/linda.cpp | 34 | ||||
-rw-r--r-- | src/linda.hpp | 6 | ||||
-rw-r--r-- | src/macros_and_utils.hpp | 13 | ||||
-rw-r--r-- | src/universe.cpp | 25 | ||||
-rw-r--r-- | src/universe.hpp | 11 |
8 files changed, 108 insertions, 59 deletions
diff --git a/src/deep.cpp b/src/deep.cpp index 6f4da9f..7b287f5 100644 --- a/src/deep.cpp +++ b/src/deep.cpp | |||
@@ -62,37 +62,6 @@ namespace { | |||
62 | // ############################################################################################# | 62 | // ############################################################################################# |
63 | // ############################################################################################# | 63 | // ############################################################################################# |
64 | 64 | ||
65 | /* | ||
66 | * void= mt.__gc( proxy_ud ) | ||
67 | * | ||
68 | * End of life for a proxy object; reduce the deep reference count and clean it up if reaches 0. | ||
69 | * | ||
70 | */ | ||
71 | [[nodiscard]] | ||
72 | static int DeepGC(lua_State* const L_) | ||
73 | { | ||
74 | DeepPrelude* const* const _proxy{ luaG_tofulluserdata<DeepPrelude*>(L_, StackIndex{ 1 }) }; | ||
75 | DeepPrelude* const _p{ *_proxy }; | ||
76 | |||
77 | // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded | ||
78 | // in that case, we are not multithreaded and locking isn't necessary anyway | ||
79 | bool const _isLastRef{ _p->refcount.fetch_sub(1, std::memory_order_relaxed) == 1 }; | ||
80 | |||
81 | if (_isLastRef) { | ||
82 | // retrieve wrapped __gc, if any | ||
83 | lua_pushvalue(L_, lua_upvalueindex(1)); // L_: self __gc? | ||
84 | if (!lua_isnil(L_, -1)) { | ||
85 | lua_insert(L_, -2); // L_: __gc self | ||
86 | lua_call(L_, 1, 0); // L_: | ||
87 | } else { | ||
88 | // need an empty stack in case we are GC_ing from a Keeper, so that empty stack checks aren't triggered | ||
89 | lua_pop(L_, 2); // L_: | ||
90 | } | ||
91 | DeepFactory::DeleteDeepObject(L_, _p); | ||
92 | } | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | // ############################################################################################# | 65 | // ############################################################################################# |
97 | 66 | ||
98 | // Pops the key (metatable or factory) off the stack, and replaces with the deep lookup value (factory/metatable/nil). | 67 | // Pops the key (metatable or factory) off the stack, and replaces with the deep lookup value (factory/metatable/nil). |
@@ -121,6 +90,39 @@ namespace { | |||
121 | // ################################################################################################# | 90 | // ################################################################################################# |
122 | // ################################################################################################# | 91 | // ################################################################################################# |
123 | 92 | ||
93 | /* | ||
94 | * void= mt.__gc( proxy_ud ) | ||
95 | * | ||
96 | * End of life for a proxy object; reduce the deep reference count and clean it up if reaches 0. | ||
97 | * | ||
98 | */ | ||
99 | [[nodiscard]] | ||
100 | int DeepFactory::DeepGC(lua_State* const L_) | ||
101 | { | ||
102 | DeepPrelude* const* const _proxy{ luaG_tofulluserdata<DeepPrelude*>(L_, StackIndex{ 1 }) }; | ||
103 | DeepPrelude* const _p{ *_proxy }; | ||
104 | |||
105 | // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded | ||
106 | // in that case, we are not multithreaded and locking isn't necessary anyway | ||
107 | bool const _isLastRef{ _p->refcount.fetch_sub(1, std::memory_order_relaxed) == 1 }; | ||
108 | |||
109 | if (_isLastRef) { | ||
110 | // retrieve wrapped __gc, if any | ||
111 | lua_pushvalue(L_, lua_upvalueindex(1)); // L_: self __gc? | ||
112 | if (!lua_isnil(L_, -1)) { | ||
113 | lua_insert(L_, -2); // L_: __gc self | ||
114 | lua_call(L_, 1, 0); // L_: | ||
115 | } else { | ||
116 | // need an empty stack in case we are GC_ing from a Keeper, so that empty stack checks aren't triggered | ||
117 | lua_pop(L_, 2); // L_: | ||
118 | } | ||
119 | DeepFactory::DeleteDeepObject(L_, _p); | ||
120 | } | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | // ################################################################################################# | ||
125 | |||
124 | // NEVER call deleteDeepObjectInternal by itself, ALWAYS go through DeleteDeepObject() | 126 | // NEVER call deleteDeepObjectInternal by itself, ALWAYS go through DeleteDeepObject() |
125 | void DeepFactory::DeleteDeepObject(lua_State* const L_, DeepPrelude* const o_) | 127 | void DeepFactory::DeleteDeepObject(lua_State* const L_, DeepPrelude* const o_) |
126 | { | 128 | { |
@@ -381,7 +383,7 @@ DeepPrelude* DeepFactory::toDeep(lua_State* const L_, StackIndex const index_) c | |||
381 | 383 | ||
382 | // ################################################################################################# | 384 | // ################################################################################################# |
383 | 385 | ||
384 | void DeepPrelude::push(lua_State* L_) const | 386 | void DeepPrelude::push(lua_State* const L_) const |
385 | { | 387 | { |
386 | STACK_CHECK_START_REL(L_, 0); | 388 | STACK_CHECK_START_REL(L_, 0); |
387 | kDeepProxyCacheRegKey.getSubTableMode(L_, "v"); // L_: DPC | 389 | kDeepProxyCacheRegKey.getSubTableMode(L_, "v"); // L_: DPC |
diff --git a/src/deep.hpp b/src/deep.hpp index 6fa12b1..e4bdaca 100644 --- a/src/deep.hpp +++ b/src/deep.hpp | |||
@@ -20,19 +20,26 @@ static constexpr UniqueKey kDeepVersion{ 0x91171AEC6641E9DBull, "kDeepVersion" } | |||
20 | 20 | ||
21 | // should be used as header for deep userdata | 21 | // should be used as header for deep userdata |
22 | // a deep userdata is a full userdata that stores a single pointer to the actual DeepPrelude-derived object | 22 | // a deep userdata is a full userdata that stores a single pointer to the actual DeepPrelude-derived object |
23 | struct DeepPrelude | 23 | class DeepPrelude |
24 | { | 24 | { |
25 | friend class DeepFactory; | ||
26 | |||
27 | private: | ||
25 | UniqueKey const magic{ kDeepVersion }; | 28 | UniqueKey const magic{ kDeepVersion }; |
26 | // when stored in a keeper state, the full userdata doesn't have a metatable, so we need direct access to the factory | 29 | // when stored in a keeper state, the full userdata doesn't have a metatable, so we need direct access to the factory |
27 | DeepFactory& factory; | 30 | DeepFactory& factory; |
31 | |||
32 | protected: | ||
28 | // data is destroyed when refcount is 0 | 33 | // data is destroyed when refcount is 0 |
29 | std::atomic<int> refcount{ 0 }; | 34 | std::atomic<int> refcount{ 0 }; |
30 | 35 | ||
36 | protected: | ||
31 | DeepPrelude(DeepFactory& factory_) | 37 | DeepPrelude(DeepFactory& factory_) |
32 | : factory{ factory_ } | 38 | : factory{ factory_ } |
33 | { | 39 | { |
34 | } | 40 | } |
35 | 41 | ||
42 | public: | ||
36 | void push(lua_State* L_) const; | 43 | void push(lua_State* L_) const; |
37 | }; | 44 | }; |
38 | 45 | ||
@@ -63,6 +70,8 @@ class DeepFactory | |||
63 | virtual std::string_view moduleName() const = 0; | 70 | virtual std::string_view moduleName() const = 0; |
64 | 71 | ||
65 | private: | 72 | private: |
73 | [[nodiscard]] | ||
74 | static int DeepGC(lua_State* L_); | ||
66 | void storeDeepLookup(lua_State* L_) const; | 75 | void storeDeepLookup(lua_State* L_) const; |
67 | 76 | ||
68 | public: | 77 | public: |
diff --git a/src/lanes.cpp b/src/lanes.cpp index 0b29a8f..83d01fb 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp | |||
@@ -86,6 +86,7 @@ THE SOFTWARE. | |||
86 | #include "intercopycontext.hpp" | 86 | #include "intercopycontext.hpp" |
87 | #include "keeper.hpp" | 87 | #include "keeper.hpp" |
88 | #include "lane.hpp" | 88 | #include "lane.hpp" |
89 | #include "linda.hpp" | ||
89 | #include "nameof.hpp" | 90 | #include "nameof.hpp" |
90 | #include "state.hpp" | 91 | #include "state.hpp" |
91 | #include "threading.hpp" | 92 | #include "threading.hpp" |
diff --git a/src/linda.cpp b/src/linda.cpp index 6ebbc1f..7af387f 100644 --- a/src/linda.cpp +++ b/src/linda.cpp | |||
@@ -130,6 +130,8 @@ namespace { | |||
130 | // ################################################################################################# | 130 | // ################################################################################################# |
131 | // ################################################################################################# | 131 | // ################################################################################################# |
132 | 132 | ||
133 | LUAG_FUNC(linda); | ||
134 | |||
133 | // ################################################################################################# | 135 | // ################################################################################################# |
134 | // ################################################################################################# | 136 | // ################################################################################################# |
135 | // #################################### Linda implementation ####################################### | 137 | // #################################### Linda implementation ####################################### |
@@ -165,6 +167,38 @@ Keeper* Linda::acquireKeeper() const | |||
165 | 167 | ||
166 | // ################################################################################################# | 168 | // ################################################################################################# |
167 | 169 | ||
170 | Linda* Linda::CreateTimerLinda(lua_State* const L_) | ||
171 | { | ||
172 | STACK_CHECK_START_REL(L_, 0); // L_: | ||
173 | // Initialize 'timerLinda'; a common Linda object shared by all states | ||
174 | lua_pushcfunction(L_, LG_linda); // L_: lanes.linda | ||
175 | luaG_pushstring(L_, "lanes-timer"); // L_: lanes.linda "lanes-timer" | ||
176 | lua_pushinteger(L_, 0); // L_: lanes.linda "lanes-timer" 0 | ||
177 | lua_call(L_, 2, 1); // L_: linda | ||
178 | STACK_CHECK(L_, 1); | ||
179 | |||
180 | // Proxy userdata contents is only a 'DeepPrelude*' pointer | ||
181 | auto const _timerLinda{ *luaG_tofulluserdata<Linda*>(L_, kIdxTop) }; | ||
182 | // increment refcount so that this linda remains alive as long as the universe exists. | ||
183 | _timerLinda->refcount.fetch_add(1, std::memory_order_relaxed); | ||
184 | lua_pop(L_, 1); // L_: | ||
185 | STACK_CHECK(L_, 0); | ||
186 | return _timerLinda; | ||
187 | } | ||
188 | |||
189 | // ################################################################################################# | ||
190 | |||
191 | void Linda::DeleteTimerLinda(lua_State* const L_, Linda* const linda_) | ||
192 | { | ||
193 | if (linda_ != nullptr) { // test in case some early internal error prevented Lanes from creating the deep timer | ||
194 | [[maybe_unused]] auto const _prev_ref_count{ linda_->refcount.fetch_sub(1, std::memory_order_relaxed) }; | ||
195 | LUA_ASSERT(L_, _prev_ref_count == 1); // this should be the last reference | ||
196 | DeepFactory::DeleteDeepObject(L_, linda_); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | // ################################################################################################# | ||
201 | |||
168 | void Linda::freeAllocatedName() | 202 | void Linda::freeAllocatedName() |
169 | { | 203 | { |
170 | if (std::holds_alternative<std::string_view>(nameVariant)) { | 204 | if (std::holds_alternative<std::string_view>(nameVariant)) { |
diff --git a/src/linda.hpp b/src/linda.hpp index aa63316..920db1b 100644 --- a/src/linda.hpp +++ b/src/linda.hpp | |||
@@ -82,6 +82,9 @@ class Linda | |||
82 | Linda& operator=(Linda const&&) = delete; | 82 | Linda& operator=(Linda const&&) = delete; |
83 | 83 | ||
84 | private: | 84 | private: |
85 | [[nodiscard]] | ||
86 | static Linda* CreateTimerLinda(lua_State* L_); | ||
87 | static void DeleteTimerLinda(lua_State* L_, Linda* linda_); | ||
85 | void freeAllocatedName(); | 88 | void freeAllocatedName(); |
86 | void setName(std::string_view const& name_); | 89 | void setName(std::string_view const& name_); |
87 | 90 | ||
@@ -89,6 +92,9 @@ class Linda | |||
89 | [[nodiscard]] | 92 | [[nodiscard]] |
90 | Keeper* acquireKeeper() const; | 93 | Keeper* acquireKeeper() const; |
91 | [[nodiscard]] | 94 | [[nodiscard]] |
95 | static Linda* CreateTimerLinda(lua_State* const L_, Passkey<Universe>) { return CreateTimerLinda(L_); } | ||
96 | static void DeleteTimerLinda(lua_State* const L_, Linda* const linda_, Passkey<Universe>) { DeleteTimerLinda(L_, linda_); } | ||
97 | [[nodiscard]] | ||
92 | std::string_view getName() const; | 98 | std::string_view getName() const; |
93 | [[nodiscard]] | 99 | [[nodiscard]] |
94 | bool inKeeperOperation() const { return keeperOperationCount.load(std::memory_order_seq_cst) != 0; } | 100 | bool inKeeperOperation() const { return keeperOperationCount.load(std::memory_order_seq_cst) != 0; } |
diff --git a/src/macros_and_utils.hpp b/src/macros_and_utils.hpp index 6850ddf..0897367 100644 --- a/src/macros_and_utils.hpp +++ b/src/macros_and_utils.hpp | |||
@@ -47,3 +47,16 @@ typename T::value_type const& OptionalValue(T const& x_, Ts... args_) | |||
47 | } | 47 | } |
48 | return x_.value(); | 48 | return x_.value(); |
49 | } | 49 | } |
50 | |||
51 | // ################################################################################################# | ||
52 | |||
53 | struct PasskeyToken {}; | ||
54 | constexpr PasskeyToken PK{}; | ||
55 | template <typename T> | ||
56 | class Passkey | ||
57 | { | ||
58 | private: | ||
59 | friend T; | ||
60 | Passkey(PasskeyToken) {} | ||
61 | // rule of 5 ignored out of laziness here | ||
62 | }; | ||
diff --git a/src/universe.cpp b/src/universe.cpp index ec7d043..b7d11d8 100644 --- a/src/universe.cpp +++ b/src/universe.cpp | |||
@@ -35,6 +35,7 @@ THE SOFTWARE. | |||
35 | #include "intercopycontext.hpp" | 35 | #include "intercopycontext.hpp" |
36 | #include "keeper.hpp" | 36 | #include "keeper.hpp" |
37 | #include "lane.hpp" | 37 | #include "lane.hpp" |
38 | #include "linda.hpp" | ||
38 | #include "state.hpp" | 39 | #include "state.hpp" |
39 | 40 | ||
40 | extern LUAG_FUNC(linda); | 41 | extern LUAG_FUNC(linda); |
@@ -147,7 +148,7 @@ Universe* Universe::Create(lua_State* const L_) | |||
147 | DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); | 148 | DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); |
148 | lua_createtable(L_, 0, 1); // L_: settings universe {mt} | 149 | lua_createtable(L_, 0, 1); // L_: settings universe {mt} |
149 | std::ignore = luaG_getfield(L_, kIdxSettings, "shutdown_timeout"); // L_: settings universe {mt} shutdown_timeout | 150 | std::ignore = luaG_getfield(L_, kIdxSettings, "shutdown_timeout"); // L_: settings universe {mt} shutdown_timeout |
150 | lua_pushcclosure(L_, LG_universe_gc, 1); // L_: settings universe {mt} LG_universe_gc | 151 | lua_pushcclosure(L_, UniverseGC, 1); // L_: settings universe {mt} UniverseGC |
151 | lua_setfield(L_, -2, "__gc"); // L_: settings universe {mt} | 152 | lua_setfield(L_, -2, "__gc"); // L_: settings universe {mt} |
152 | lua_setmetatable(L_, -2); // L_: settings universe | 153 | lua_setmetatable(L_, -2); // L_: settings universe |
153 | lua_pop(L_, 1); // L_: settings | 154 | lua_pop(L_, 1); // L_: settings |
@@ -175,18 +176,7 @@ Universe* Universe::Create(lua_State* const L_) | |||
175 | STACK_CHECK(L_, 0); | 176 | STACK_CHECK(L_, 0); |
176 | 177 | ||
177 | // Initialize 'timerLinda'; a common Linda object shared by all states | 178 | // Initialize 'timerLinda'; a common Linda object shared by all states |
178 | lua_pushcfunction(L_, LG_linda); // L_: settings lanes.linda | 179 | _U->timerLinda = Linda::CreateTimerLinda(L_, PK); |
179 | luaG_pushstring(L_, "lanes-timer"); // L_: settings lanes.linda "lanes-timer" | ||
180 | lua_pushinteger(L_, 0); // L_: settings lanes.linda "lanes-timer" 0 | ||
181 | lua_call(L_, 2, 1); // L_: settings linda | ||
182 | STACK_CHECK(L_, 1); | ||
183 | |||
184 | // Proxy userdata contents is only a 'DeepPrelude*' pointer | ||
185 | _U->timerLinda = *luaG_tofulluserdata<DeepPrelude*>(L_, kIdxTop); | ||
186 | // increment refcount so that this linda remains alive as long as the universe exists. | ||
187 | _U->timerLinda->refcount.fetch_add(1, std::memory_order_relaxed); | ||
188 | lua_pop(L_, 1); // L_: settings | ||
189 | STACK_CHECK(L_, 0); | ||
190 | return _U; | 180 | return _U; |
191 | } | 181 | } |
192 | 182 | ||
@@ -409,7 +399,7 @@ bool Universe::terminateFreeRunningLanes(lua_Duration const shutdownTimeout_, Ca | |||
409 | // ################################################################################################# | 399 | // ################################################################################################# |
410 | 400 | ||
411 | // process end: cancel any still free-running threads | 401 | // process end: cancel any still free-running threads |
412 | LUAG_FUNC(universe_gc) | 402 | int Universe::UniverseGC(lua_State* const L_) |
413 | { | 403 | { |
414 | lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; | 404 | lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; |
415 | STACK_CHECK_START_ABS(L_, 1); | 405 | STACK_CHECK_START_ABS(L_, 1); |
@@ -444,12 +434,7 @@ LUAG_FUNC(universe_gc) | |||
444 | } | 434 | } |
445 | 435 | ||
446 | // no need to mutex-protect this as all lanes in the universe are gone at that point | 436 | // no need to mutex-protect this as all lanes in the universe are gone at that point |
447 | if (_U->timerLinda != nullptr) { // test in case some early internal error prevented Lanes from creating the deep timer | 437 | Linda::DeleteTimerLinda(L_, std::exchange(_U->timerLinda, nullptr), PK); |
448 | [[maybe_unused]] int const _prev_ref_count{ _U->timerLinda->refcount.fetch_sub(1, std::memory_order_relaxed) }; | ||
449 | LUA_ASSERT(L_, _prev_ref_count == 1); // this should be the last reference | ||
450 | DeepFactory::DeleteDeepObject(L_, _U->timerLinda); | ||
451 | _U->timerLinda = nullptr; | ||
452 | } | ||
453 | 438 | ||
454 | _U->keepers.close(); | 439 | _U->keepers.close(); |
455 | 440 | ||
diff --git a/src/universe.hpp b/src/universe.hpp index 639dcdb..dbf0ece 100644 --- a/src/universe.hpp +++ b/src/universe.hpp | |||
@@ -10,8 +10,8 @@ | |||
10 | // ################################################################################################# | 10 | // ################################################################################################# |
11 | 11 | ||
12 | // forwards | 12 | // forwards |
13 | struct DeepPrelude; | ||
14 | class Lane; | 13 | class Lane; |
14 | class Linda; | ||
15 | 15 | ||
16 | // ################################################################################################# | 16 | // ################################################################################################# |
17 | 17 | ||
@@ -99,7 +99,7 @@ class Universe | |||
99 | 99 | ||
100 | // Initialized by 'init_once_LOCKED()': the deep userdata Linda object | 100 | // Initialized by 'init_once_LOCKED()': the deep userdata Linda object |
101 | // used for timers (each lane will get a proxy to this) | 101 | // used for timers (each lane will get a proxy to this) |
102 | DeepPrelude* timerLinda{ nullptr }; | 102 | Linda* timerLinda{ nullptr }; |
103 | 103 | ||
104 | LaneTracker tracker; | 104 | LaneTracker tracker; |
105 | 105 | ||
@@ -122,6 +122,9 @@ class Universe | |||
122 | // The terminal desinit sequence should wait for all such processing to terminate before force-killing threads | 122 | // The terminal desinit sequence should wait for all such processing to terminate before force-killing threads |
123 | std::atomic<int> selfdestructingCount{ 0 }; | 123 | std::atomic<int> selfdestructingCount{ 0 }; |
124 | 124 | ||
125 | private: | ||
126 | static int UniverseGC(lua_State* L_); | ||
127 | |||
125 | public: | 128 | public: |
126 | [[nodiscard]] | 129 | [[nodiscard]] |
127 | static void* operator new([[maybe_unused]] size_t size_, lua_State* L_) noexcept { return luaG_newuserdatauv<Universe>(L_, UserValueCount{ 0 }); }; | 130 | static void* operator new([[maybe_unused]] size_t size_, lua_State* L_) noexcept { return luaG_newuserdatauv<Universe>(L_, UserValueCount{ 0 }); }; |
@@ -171,7 +174,3 @@ inline void Universe::Store(lua_State* L_, Universe* U_) | |||
171 | kUniverseLightRegKey.setValue(L_, [U = U_](lua_State* L_) { U ? lua_pushlightuserdata(L_, U) : lua_pushnil(L_); }); | 174 | kUniverseLightRegKey.setValue(L_, [U = U_](lua_State* L_) { U ? lua_pushlightuserdata(L_, U) : lua_pushnil(L_); }); |
172 | STACK_CHECK(L_, 0); | 175 | STACK_CHECK(L_, 0); |
173 | } | 176 | } |
174 | |||
175 | // ################################################################################################# | ||
176 | |||
177 | LUAG_FUNC(universe_gc); | ||