aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-12-09 17:39:30 +0100
committerBenoit Germain <benoit.germain@ubisoft.com>2024-12-09 17:39:30 +0100
commit5f9ef9e1b75adc27a4ae4129cc33137aa7aaa565 (patch)
tree2500bc537a045a66ae68288f01bff569decdd80c /src
parenta334c7662a1a7be5b79efe72076c49ab7c714070 (diff)
downloadlanes-5f9ef9e1b75adc27a4ae4129cc33137aa7aaa565.tar.gz
lanes-5f9ef9e1b75adc27a4ae4129cc33137aa7aaa565.tar.bz2
lanes-5f9ef9e1b75adc27a4ae4129cc33137aa7aaa565.zip
Improved DeepPrelude architecture
Diffstat (limited to 'src')
-rw-r--r--src/deep.cpp66
-rw-r--r--src/deep.hpp11
-rw-r--r--src/lanes.cpp1
-rw-r--r--src/linda.cpp34
-rw-r--r--src/linda.hpp6
-rw-r--r--src/macros_and_utils.hpp13
-rw-r--r--src/universe.cpp25
-rw-r--r--src/universe.hpp11
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]]
100int 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()
125void DeepFactory::DeleteDeepObject(lua_State* const L_, DeepPrelude* const o_) 127void 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
384void DeepPrelude::push(lua_State* L_) const 386void 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
23struct DeepPrelude 23class 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
133LUAG_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
170Linda* 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
191void 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
168void Linda::freeAllocatedName() 202void 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
53struct PasskeyToken {};
54constexpr PasskeyToken PK{};
55template <typename T>
56class 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
40extern LUAG_FUNC(linda); 41extern 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
412LUAG_FUNC(universe_gc) 402int 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
13struct DeepPrelude;
14class Lane; 13class Lane;
14class 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
177LUAG_FUNC(universe_gc);