diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-04-30 11:44:53 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-04-30 11:44:53 +0200 |
commit | 92ea4d16a274b4a7db0206fd74891a555f6501c9 (patch) | |
tree | 358a1b98f6d5c0985c2eaabc3c19b1b7db7327ba /src | |
parent | d60a9fb712886880ec9630e744e1258ec3ed19b1 (diff) | |
download | lanes-92ea4d16a274b4a7db0206fd74891a555f6501c9.tar.gz lanes-92ea4d16a274b4a7db0206fd74891a555f6501c9.tar.bz2 lanes-92ea4d16a274b4a7db0206fd74891a555f6501c9.zip |
Progressively applying the coding rules
Diffstat (limited to 'src')
-rw-r--r-- | src/cancel.cpp | 9 | ||||
-rw-r--r-- | src/deep.cpp | 330 | ||||
-rw-r--r-- | src/keeper.cpp | 655 | ||||
-rw-r--r-- | src/keeper.h | 6 | ||||
-rw-r--r-- | src/lanes.cpp | 1037 | ||||
-rw-r--r-- | src/linda.cpp | 389 | ||||
-rw-r--r-- | src/lindafactory.cpp | 24 | ||||
-rw-r--r-- | src/macros_and_utils.h | 2 | ||||
-rw-r--r-- | src/state.cpp | 327 | ||||
-rw-r--r-- | src/state.h | 10 | ||||
-rw-r--r-- | src/threading.cpp | 351 | ||||
-rw-r--r-- | src/tools.cpp | 1501 | ||||
-rw-r--r-- | src/tools.h | 8 | ||||
-rw-r--r-- | src/universe.cpp | 9 | ||||
-rw-r--r-- | src/universe.h | 2 |
15 files changed, 2060 insertions, 2600 deletions
diff --git a/src/cancel.cpp b/src/cancel.cpp index e80c0b5..9887cba 100644 --- a/src/cancel.cpp +++ b/src/cancel.cpp | |||
@@ -111,8 +111,7 @@ LUAG_FUNC(cancel_test) | |||
111 | { | 111 | { |
112 | lane_->cancel_request = CancelRequest::Soft; // it's now signaled to stop | 112 | lane_->cancel_request = CancelRequest::Soft; // it's now signaled to stop |
113 | // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own | 113 | // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own |
114 | if (wakeLane_) // wake the thread so that execution returns from any pending linda operation if desired | 114 | if (wakeLane_) { // wake the thread so that execution returns from any pending linda operation if desired |
115 | { | ||
116 | std::condition_variable* const waiting_on{ lane_->m_waiting_on }; | 115 | std::condition_variable* const waiting_on{ lane_->m_waiting_on }; |
117 | if (lane_->m_status == Lane::Waiting && waiting_on != nullptr) { | 116 | if (lane_->m_status == Lane::Waiting && waiting_on != nullptr) { |
118 | waiting_on->notify_all(); | 117 | waiting_on->notify_all(); |
@@ -128,8 +127,7 @@ LUAG_FUNC(cancel_test) | |||
128 | { | 127 | { |
129 | lane_->cancel_request = CancelRequest::Hard; // it's now signaled to stop | 128 | lane_->cancel_request = CancelRequest::Hard; // it's now signaled to stop |
130 | // lane_->m_thread.get_stop_source().request_stop(); | 129 | // lane_->m_thread.get_stop_source().request_stop(); |
131 | if (wakeLane_) // wake the thread so that execution returns from any pending linda operation if desired | 130 | if (wakeLane_) { // wake the thread so that execution returns from any pending linda operation if desired |
132 | { | ||
133 | std::condition_variable* waiting_on = lane_->m_waiting_on; | 131 | std::condition_variable* waiting_on = lane_->m_waiting_on; |
134 | if (lane_->m_status == Lane::Waiting && waiting_on != nullptr) { | 132 | if (lane_->m_status == Lane::Waiting && waiting_on != nullptr) { |
135 | waiting_on->notify_all(); | 133 | waiting_on->notify_all(); |
@@ -209,8 +207,7 @@ LUAG_FUNC(thread_cancel) | |||
209 | CancelOp const op{ which_cancel_op(L_, 2) }; // this removes the op string from the stack | 207 | CancelOp const op{ which_cancel_op(L_, 2) }; // this removes the op string from the stack |
210 | 208 | ||
211 | int hook_count{ 0 }; | 209 | int hook_count{ 0 }; |
212 | if (static_cast<int>(op) > static_cast<int>(CancelOp::Soft)) // hook is requested | 210 | if (static_cast<int>(op) > static_cast<int>(CancelOp::Soft)) { // hook is requested |
213 | { | ||
214 | hook_count = static_cast<int>(luaL_checkinteger(L_, 2)); | 211 | hook_count = static_cast<int>(luaL_checkinteger(L_, 2)); |
215 | lua_remove(L_, 2); // argument is processed, remove it | 212 | lua_remove(L_, 2); // argument is processed, remove it |
216 | if (hook_count < 1) { | 213 | if (hook_count < 1) { |
diff --git a/src/deep.cpp b/src/deep.cpp index 13932a4..11e1ea7 100644 --- a/src/deep.cpp +++ b/src/deep.cpp | |||
@@ -46,95 +46,90 @@ THE SOFTWARE. | |||
46 | 46 | ||
47 | /*---=== Deep userdata ===---*/ | 47 | /*---=== Deep userdata ===---*/ |
48 | 48 | ||
49 | /* | 49 | /* |
50 | * 'registry[REGKEY]' is a two-way lookup table for 'factory's and those type's | 50 | * 'registry[REGKEY]' is a two-way lookup table for 'factory's and those type's |
51 | * metatables: | 51 | * metatables: |
52 | * | 52 | * |
53 | * metatable -> factory | 53 | * metatable -> factory |
54 | * factory -> metatable | 54 | * factory -> metatable |
55 | */ | 55 | */ |
56 | // xxh64 of string "kDeepLookupRegKey" generated at https://www.pelock.com/products/hash-calculator | 56 | // xxh64 of string "kDeepLookupRegKey" generated at https://www.pelock.com/products/hash-calculator |
57 | static constexpr RegistryUniqueKey kDeepLookupRegKey{ 0xC6788345703C6059ull }; | 57 | static constexpr RegistryUniqueKey kDeepLookupRegKey{ 0xC6788345703C6059ull }; |
58 | 58 | ||
59 | /* | 59 | /* |
60 | * The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying | 60 | * The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying |
61 | * xxh64 of string "kDeepProxyCacheRegKey" generated at https://www.pelock.com/products/hash-calculator | 61 | * xxh64 of string "kDeepProxyCacheRegKey" generated at https://www.pelock.com/products/hash-calculator |
62 | */ | 62 | */ |
63 | static constexpr RegistryUniqueKey kDeepProxyCacheRegKey{ 0xEBCD49AE1A3DD35Eull }; | 63 | static constexpr RegistryUniqueKey kDeepProxyCacheRegKey{ 0xEBCD49AE1A3DD35Eull }; |
64 | 64 | ||
65 | /* | 65 | /* |
66 | * Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists. | 66 | * Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists. |
67 | * Pops the both values off the stack. | 67 | * Pops the both values off the stack. |
68 | */ | 68 | */ |
69 | static void set_deep_lookup(lua_State* L_) | 69 | static void set_deep_lookup(lua_State* L_) |
70 | { | 70 | { |
71 | STACK_GROW( L_, 3); | 71 | STACK_GROW(L_, 3); |
72 | STACK_CHECK_START_REL(L_, 2); // a b | 72 | STACK_CHECK_START_REL(L_, 2); // L_: a b |
73 | push_registry_subtable( L_, kDeepLookupRegKey); // a b {} | 73 | push_registry_subtable(L_, kDeepLookupRegKey); // L_: a b {} |
74 | STACK_CHECK( L_, 3); | 74 | STACK_CHECK(L_, 3); |
75 | lua_insert( L_, -3); // {} a b | 75 | lua_insert(L_, -3); // L_: {} a b |
76 | lua_pushvalue( L_, -1); // {} a b b | 76 | lua_pushvalue(L_, -1); // L_: {} a b b |
77 | lua_pushvalue( L_,-3); // {} a b b a | 77 | lua_pushvalue(L_, -3); // L_: {} a b b a |
78 | lua_rawset( L_, -5); // {} a b | 78 | lua_rawset(L_, -5); // L_: {} a b |
79 | lua_rawset( L_, -3); // {} | 79 | lua_rawset(L_, -3); // L_: {} |
80 | lua_pop( L_, 1); // | 80 | lua_pop(L_, 1); // L_: |
81 | STACK_CHECK( L_, 0); | 81 | STACK_CHECK(L_, 0); |
82 | } | 82 | } |
83 | 83 | ||
84 | // ################################################################################################# | 84 | // ################################################################################################# |
85 | 85 | ||
86 | /* | 86 | /* |
87 | * Pops the key (metatable or factory) off the stack, and replaces with the | 87 | * Pops the key (metatable or factory) off the stack, and replaces with the |
88 | * deep lookup value (factory/metatable/nil). | 88 | * deep lookup value (factory/metatable/nil). |
89 | */ | 89 | */ |
90 | static void get_deep_lookup(lua_State* L_) | 90 | static void get_deep_lookup(lua_State* L_) |
91 | { | 91 | { |
92 | STACK_GROW( L_, 1); | 92 | STACK_GROW(L_, 1); |
93 | STACK_CHECK_START_REL(L_, 1); // a | 93 | STACK_CHECK_START_REL(L_, 1); // L_: a |
94 | kDeepLookupRegKey.pushValue(L_); // a {} | 94 | kDeepLookupRegKey.pushValue(L_); // L_: a {} |
95 | if (!lua_isnil( L_, -1)) | 95 | if (!lua_isnil(L_, -1)) { |
96 | { | 96 | lua_insert(L_, -2); // L_: {} a |
97 | lua_insert( L_, -2); // {} a | 97 | lua_rawget(L_, -2); // L_: {} b |
98 | lua_rawget( L_, -2); // {} b | ||
99 | } | 98 | } |
100 | lua_remove( L_, -2); // a|b | 99 | lua_remove(L_, -2); // L_: a|b |
101 | STACK_CHECK( L_, 1); | 100 | STACK_CHECK(L_, 1); |
102 | } | 101 | } |
103 | 102 | ||
104 | // ################################################################################################# | 103 | // ################################################################################################# |
105 | 104 | ||
106 | /* | 105 | /* |
107 | * Return the registered factory for 'index' (deep userdata proxy), | 106 | * Return the registered factory for 'index' (deep userdata proxy), |
108 | * or nullptr if 'index' is not a deep userdata proxy. | 107 | * or nullptr if 'index' is not a deep userdata proxy. |
109 | */ | 108 | */ |
110 | [[nodiscard]] static inline DeepFactory* get_factory(lua_State* L_, int index_, LookupMode mode_) | 109 | [[nodiscard]] static inline DeepFactory* get_factory(lua_State* L_, int index_, LookupMode mode_) |
111 | { | 110 | { |
112 | // when looking inside a keeper, we are 100% sure the object is a deep userdata | 111 | // when looking inside a keeper, we are 100% sure the object is a deep userdata |
113 | if (mode_ == LookupMode::FromKeeper) | 112 | if (mode_ == LookupMode::FromKeeper) { |
114 | { | ||
115 | DeepPrelude* const proxy{ *lua_tofulluserdata<DeepPrelude*>(L_, index_) }; | 113 | DeepPrelude* const proxy{ *lua_tofulluserdata<DeepPrelude*>(L_, index_) }; |
116 | // we can (and must) cast and fetch the internally stored factory | 114 | // we can (and must) cast and fetch the internally stored factory |
117 | return &proxy->m_factory; | 115 | return &proxy->m_factory; |
118 | } | 116 | } else { |
119 | else | ||
120 | { | ||
121 | // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/factory database | 117 | // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/factory database |
122 | // it is the only way to ensure that the userdata is indeed a deep userdata! | 118 | // it is the only way to ensure that the userdata is indeed a deep userdata! |
123 | // of course, we could just trust the caller, but we won't | 119 | // of course, we could just trust the caller, but we won't |
124 | STACK_GROW( L_, 1); | 120 | STACK_GROW(L_, 1); |
125 | STACK_CHECK_START_REL(L_, 0); | 121 | STACK_CHECK_START_REL(L_, 0); |
126 | 122 | ||
127 | if (!lua_getmetatable( L_, index_)) // deep ... metatable? | 123 | if (!lua_getmetatable(L_, index_)) { // L_: deep ... metatable? |
128 | { | ||
129 | return nullptr; // no metatable: can't be a deep userdata object! | 124 | return nullptr; // no metatable: can't be a deep userdata object! |
130 | } | 125 | } |
131 | 126 | ||
132 | // replace metatable with the factory pointer, if it is actually a deep userdata | 127 | // replace metatable with the factory pointer, if it is actually a deep userdata |
133 | get_deep_lookup( L_); // deep ... factory|nil | 128 | get_deep_lookup(L_); // L_: deep ... factory|nil |
134 | 129 | ||
135 | DeepFactory* const ret{ lua_tolightuserdata<DeepFactory>(L_, -1) }; // nullptr if not a userdata | 130 | DeepFactory* const ret{ lua_tolightuserdata<DeepFactory>(L_, -1) }; // nullptr if not a userdata |
136 | lua_pop( L_, 1); | 131 | lua_pop(L_, 1); |
137 | STACK_CHECK( L_, 0); | 132 | STACK_CHECK(L_, 0); |
138 | return ret; | 133 | return ret; |
139 | } | 134 | } |
140 | } | 135 | } |
@@ -165,19 +160,15 @@ void DeepFactory::DeleteDeepObject(lua_State* L_, DeepPrelude* o_) | |||
165 | // in that case, we are not multithreaded and locking isn't necessary anyway | 160 | // in that case, we are not multithreaded and locking isn't necessary anyway |
166 | bool const isLastRef{ p->m_refcount.fetch_sub(1, std::memory_order_relaxed) == 1 }; | 161 | bool const isLastRef{ p->m_refcount.fetch_sub(1, std::memory_order_relaxed) == 1 }; |
167 | 162 | ||
168 | if (isLastRef) | 163 | if (isLastRef) { |
169 | { | ||
170 | // retrieve wrapped __gc | 164 | // retrieve wrapped __gc |
171 | lua_pushvalue(L_, lua_upvalueindex( 1)); // self __gc? | 165 | lua_pushvalue(L_, lua_upvalueindex(1)); // L_: self __gc? |
172 | if (!lua_isnil(L_, -1)) | 166 | if (!lua_isnil(L_, -1)) { |
173 | { | 167 | lua_insert(L_, -2); // L_: __gc self |
174 | lua_insert(L_, -2); // __gc self | 168 | lua_call(L_, 1, 0); // L_: |
175 | lua_call(L_, 1, 0); // | 169 | } else { |
176 | } | ||
177 | else | ||
178 | { | ||
179 | // need an empty stack in case we are GC_ing from a Keeper, so that empty stack checks aren't triggered | 170 | // need an empty stack in case we are GC_ing from a Keeper, so that empty stack checks aren't triggered |
180 | lua_pop(L_, 2); | 171 | lua_pop(L_, 2); // L_: |
181 | } | 172 | } |
182 | DeepFactory::DeleteDeepObject(L_, p); | 173 | DeepFactory::DeleteDeepObject(L_, p); |
183 | } | 174 | } |
@@ -198,133 +189,111 @@ void DeepFactory::DeleteDeepObject(lua_State* L_, DeepPrelude* o_) | |||
198 | char const* DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int nuv_, LookupMode mode_) | 189 | char const* DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int nuv_, LookupMode mode_) |
199 | { | 190 | { |
200 | // Check if a proxy already exists | 191 | // Check if a proxy already exists |
201 | push_registry_subtable_mode(L_, kDeepProxyCacheRegKey, "v"); // DPC | 192 | push_registry_subtable_mode(L_, kDeepProxyCacheRegKey, "v"); // L_: DPC |
202 | lua_pushlightuserdata(L_, prelude_); // DPC deep | 193 | lua_pushlightuserdata(L_, prelude_); // L_: DPC deep |
203 | lua_rawget(L_, -2); // DPC proxy | 194 | lua_rawget(L_, -2); // L_: DPC proxy |
204 | if (!lua_isnil(L_, -1)) | 195 | if (!lua_isnil(L_, -1)) { |
205 | { | 196 | lua_remove(L_, -2); // L_: proxy |
206 | lua_remove(L_, -2); // proxy | ||
207 | return nullptr; | 197 | return nullptr; |
208 | } | 198 | } else { |
209 | else | 199 | lua_pop(L_, 1); // L_: DPC |
210 | { | ||
211 | lua_pop(L_, 1); // DPC | ||
212 | } | 200 | } |
213 | 201 | ||
214 | STACK_GROW(L_, 7); | 202 | STACK_GROW(L_, 7); |
215 | STACK_CHECK_START_REL(L_, 0); | 203 | STACK_CHECK_START_REL(L_, 0); |
216 | 204 | ||
217 | // a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4) | 205 | // a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4) |
218 | DeepPrelude** const proxy{ lua_newuserdatauv<DeepPrelude*>(L_, nuv_) }; // DPC proxy | 206 | DeepPrelude** const proxy{ lua_newuserdatauv<DeepPrelude*>(L_, nuv_) }; // L_: DPC proxy |
219 | LUA_ASSERT(L_, proxy); | 207 | LUA_ASSERT(L_, proxy); |
220 | *proxy = prelude_; | 208 | *proxy = prelude_; |
221 | prelude_->m_refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data | 209 | prelude_->m_refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data |
222 | 210 | ||
223 | // Get/create metatable for 'factory' (in this state) | 211 | // Get/create metatable for 'factory' (in this state) |
224 | DeepFactory& factory = prelude_->m_factory; | 212 | DeepFactory& factory = prelude_->m_factory; |
225 | lua_pushlightuserdata(L_, std::bit_cast<void*>(&factory)); // DPC proxy factory | 213 | lua_pushlightuserdata(L_, std::bit_cast<void*>(&factory)); // L_: DPC proxy factory |
226 | get_deep_lookup(L_); // DPC proxy metatable? | 214 | get_deep_lookup(L_); // L_: DPC proxy metatable? |
227 | 215 | ||
228 | if (lua_isnil(L_, -1)) // // No metatable yet. | 216 | if (lua_isnil(L_, -1)) { // No metatable yet. |
229 | { | 217 | lua_pop(L_, 1); // L_: DPC proxy |
230 | lua_pop(L_, 1); // DPC proxy | ||
231 | int const oldtop{ lua_gettop(L_) }; | 218 | int const oldtop{ lua_gettop(L_) }; |
232 | // 1 - make one and register it | 219 | // 1 - make one and register it |
233 | if (mode_ != LookupMode::ToKeeper) | 220 | if (mode_ != LookupMode::ToKeeper) { |
234 | { | 221 | factory.createMetatable(L_); // L_: DPC proxy metatable |
235 | factory.createMetatable(L_); // DPC proxy metatable | 222 | if (lua_gettop(L_) - oldtop != 1 || !lua_istable(L_, -1)) { |
236 | if (lua_gettop(L_) - oldtop != 1 || !lua_istable(L_, -1)) | ||
237 | { | ||
238 | // factory didn't push exactly 1 value, or the value it pushed is not a table: ERROR! | 223 | // factory didn't push exactly 1 value, or the value it pushed is not a table: ERROR! |
239 | lua_settop(L_, oldtop); // DPC proxy X | 224 | lua_settop(L_, oldtop); // L_: DPC proxy X |
240 | lua_pop(L_, 3); // | 225 | lua_pop(L_, 3); // L_: |
241 | return "Bad DeepFactory::createMetatable overload: unexpected pushed value"; | 226 | return "Bad DeepFactory::createMetatable overload: unexpected pushed value"; |
242 | } | 227 | } |
243 | // if the metatable contains a __gc, we will call it from our own | 228 | // if the metatable contains a __gc, we will call it from our own |
244 | lua_getfield(L_, -1, "__gc"); // DPC proxy metatable __gc | 229 | lua_getfield(L_, -1, "__gc"); // L_: DPC proxy metatable __gc |
245 | } | 230 | } else { |
246 | else | ||
247 | { | ||
248 | // keepers need a minimal metatable that only contains our own __gc | 231 | // keepers need a minimal metatable that only contains our own __gc |
249 | lua_newtable(L_); // DPC proxy metatable | 232 | lua_newtable(L_); // L_: DPC proxy metatable |
250 | lua_pushnil(L_); // DPC proxy metatable nil | 233 | lua_pushnil(L_); // L_: DPC proxy metatable nil |
251 | } | 234 | } |
252 | if (lua_isnil(L_, -1)) | 235 | if (lua_isnil(L_, -1)) { |
253 | { | ||
254 | // Add our own '__gc' method | 236 | // Add our own '__gc' method |
255 | lua_pop(L_, 1); // DPC proxy metatable | 237 | lua_pop(L_, 1); // L_: DPC proxy metatable |
256 | lua_pushcfunction(L_, deep_userdata_gc); // DPC proxy metatable deep_userdata_gc | 238 | lua_pushcfunction(L_, deep_userdata_gc); // L_: DPC proxy metatable deep_userdata_gc |
257 | } | 239 | } else { |
258 | else | ||
259 | { | ||
260 | // Add our own '__gc' method wrapping the original | 240 | // Add our own '__gc' method wrapping the original |
261 | lua_pushcclosure(L_, deep_userdata_gc, 1); // DPC proxy metatable deep_userdata_gc | 241 | lua_pushcclosure(L_, deep_userdata_gc, 1); // DPC proxy metatable deep_userdata_gc |
262 | } | 242 | } |
263 | lua_setfield(L_, -2, "__gc"); // DPC proxy metatable | 243 | lua_setfield(L_, -2, "__gc"); // DPC proxy metatable |
264 | 244 | ||
265 | // Memorize for later rounds | 245 | // Memorize for later rounds |
266 | lua_pushvalue(L_, -1); // DPC proxy metatable metatable | 246 | lua_pushvalue(L_, -1); // L_: DPC proxy metatable metatable |
267 | lua_pushlightuserdata(L_, std::bit_cast<void*>(&factory)); // DPC proxy metatable metatable factory | 247 | lua_pushlightuserdata(L_, std::bit_cast<void*>(&factory)); // L_: DPC proxy metatable metatable factory |
268 | set_deep_lookup(L_); // DPC proxy metatable | 248 | set_deep_lookup(L_); // L_: DPC proxy metatable |
269 | 249 | ||
270 | // 2 - cause the target state to require the module that exported the factory | 250 | // 2 - cause the target state to require the module that exported the factory |
271 | if (char const* const modname{ factory.moduleName() }; modname) // we actually got a module name | 251 | if (char const* const modname{ factory.moduleName() }; modname) { // we actually got a module name |
272 | { | ||
273 | // L.registry._LOADED exists without having registered the 'package' library. | 252 | // L.registry._LOADED exists without having registered the 'package' library. |
274 | lua_getglobal(L_, "require"); // DPC proxy metatable require() | 253 | lua_getglobal(L_, "require"); // DPC proxy metatable require() |
275 | // check that the module is already loaded (or being loaded, we are happy either way) | 254 | // check that the module is already loaded (or being loaded, we are happy either way) |
276 | if (lua_isfunction(L_, -1)) | 255 | if (lua_isfunction(L_, -1)) { |
277 | { | 256 | lua_pushstring(L_, modname); // L_: DPC proxy metatable require() "module" |
278 | lua_pushstring(L_, modname); // DPC proxy metatable require() "module" | 257 | lua_getfield(L_, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); // L_: DPC proxy metatable require() "module" _R._LOADED |
279 | lua_getfield(L_, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); // DPC proxy metatable require() "module" _R._LOADED | 258 | if (lua_istable(L_, -1)) { |
280 | if (lua_istable(L_, -1)) | 259 | lua_pushvalue(L_, -2); // L_: DPC proxy metatable require() "module" _R._LOADED "module" |
281 | { | 260 | lua_rawget(L_, -2); // L_: DPC proxy metatable require() "module" _R._LOADED module |
282 | lua_pushvalue(L_, -2); // DPC proxy metatable require() "module" _R._LOADED "module" | ||
283 | lua_rawget(L_, -2); // DPC proxy metatable require() "module" _R._LOADED module | ||
284 | int const alreadyloaded = lua_toboolean(L_, -1); | 261 | int const alreadyloaded = lua_toboolean(L_, -1); |
285 | if (!alreadyloaded) // not loaded | 262 | if (!alreadyloaded) { // not loaded |
286 | { | ||
287 | int require_result; | 263 | int require_result; |
288 | lua_pop(L_, 2); // DPC proxy metatable require() "module" | 264 | lua_pop(L_, 2); // L_: DPC proxy metatable require() "module" |
289 | // require "modname" | 265 | // require "modname" |
290 | require_result = lua_pcall(L_, 1, 0, 0); // DPC proxy metatable error? | 266 | require_result = lua_pcall(L_, 1, 0, 0); // L_: DPC proxy metatable error? |
291 | if (require_result != LUA_OK) | 267 | if (require_result != LUA_OK) { |
292 | { | ||
293 | // failed, return the error message | 268 | // failed, return the error message |
294 | lua_pushfstring(L_, "error while requiring '%s' identified by DeepFactory::moduleName: ", modname); | 269 | lua_pushfstring(L_, "error while requiring '%s' identified by DeepFactory::moduleName: ", modname); |
295 | lua_insert(L_, -2); // DPC proxy metatable prefix error | 270 | lua_insert(L_, -2); // L_: DPC proxy metatable prefix error |
296 | lua_concat(L_, 2); // DPC proxy metatable error | 271 | lua_concat(L_, 2); // L_: DPC proxy metatable error |
297 | return lua_tostring(L_, -1); | 272 | return lua_tostring(L_, -1); |
298 | } | 273 | } |
274 | } else { // already loaded, we are happy | ||
275 | lua_pop(L_, 4); // L_: DPC proxy metatable | ||
299 | } | 276 | } |
300 | else // already loaded, we are happy | 277 | } else { // no L.registry._LOADED; can this ever happen? |
301 | { | 278 | lua_pop(L_, 6); // L_: |
302 | lua_pop(L_, 4); // DPC proxy metatable | ||
303 | } | ||
304 | } | ||
305 | else // no L.registry._LOADED; can this ever happen? | ||
306 | { | ||
307 | lua_pop(L_, 6); // | ||
308 | return "unexpected error while requiring a module identified by DeepFactory::moduleName"; | 279 | return "unexpected error while requiring a module identified by DeepFactory::moduleName"; |
309 | } | 280 | } |
310 | } | 281 | } else { // a module name, but no require() function :-( |
311 | else // a module name, but no require() function :-( | 282 | lua_pop(L_, 4); // L_: |
312 | { | ||
313 | lua_pop(L_, 4); // | ||
314 | return "lanes receiving deep userdata should register the 'package' library"; | 283 | return "lanes receiving deep userdata should register the 'package' library"; |
315 | } | 284 | } |
316 | } | 285 | } |
317 | } | 286 | } |
318 | STACK_CHECK(L_, 2); // DPC proxy metatable | 287 | STACK_CHECK(L_, 2); // DPC proxy metatable |
319 | LUA_ASSERT(L_, lua_type_as_enum(L_, -2) == LuaType::USERDATA); | 288 | LUA_ASSERT(L_, lua_type_as_enum(L_, -2) == LuaType::USERDATA); |
320 | LUA_ASSERT(L_, lua_istable(L_, -1)); | 289 | LUA_ASSERT(L_, lua_istable(L_, -1)); |
321 | lua_setmetatable(L_, -2); // DPC proxy | 290 | lua_setmetatable(L_, -2); // DPC proxy |
322 | 291 | ||
323 | // If we're here, we obviously had to create a new proxy, so cache it. | 292 | // If we're here, we obviously had to create a new proxy, so cache it. |
324 | lua_pushlightuserdata(L_, prelude_); // DPC proxy deep | 293 | lua_pushlightuserdata(L_, prelude_); // L_: DPC proxy deep |
325 | lua_pushvalue(L_, -2); // DPC proxy deep proxy | 294 | lua_pushvalue(L_, -2); // L_: DPC proxy deep proxy |
326 | lua_rawset(L_, -4); // DPC proxy | 295 | lua_rawset(L_, -4); // L_: DPC proxy |
327 | lua_remove(L_, -2); // proxy | 296 | lua_remove(L_, -2); // L_: proxy |
328 | LUA_ASSERT(L_, lua_type_as_enum(L_, -1) == LuaType::USERDATA); | 297 | LUA_ASSERT(L_, lua_type_as_enum(L_, -1) == LuaType::USERDATA); |
329 | STACK_CHECK(L_, 0); | 298 | STACK_CHECK(L_, 0); |
330 | return nullptr; | 299 | return nullptr; |
@@ -333,33 +302,31 @@ char const* DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int | |||
333 | // ################################################################################################# | 302 | // ################################################################################################# |
334 | 303 | ||
335 | /* | 304 | /* |
336 | * Create a deep userdata | 305 | * Create a deep userdata |
337 | * | 306 | * |
338 | * proxy_ud= deep_userdata( [...] ) | 307 | * proxy_ud= deep_userdata( [...] ) |
339 | * | 308 | * |
340 | * Creates a deep userdata entry of the type defined by the factory. | 309 | * Creates a deep userdata entry of the type defined by the factory. |
341 | * Parameters found on the stack are left as is and passed on to DeepFactory::newDeepObjectInternal. | 310 | * Parameters found on the stack are left as is and passed on to DeepFactory::newDeepObjectInternal. |
342 | * | 311 | * |
343 | * Reference counting and true userdata proxying are taken care of for the actual data type. | 312 | * Reference counting and true userdata proxying are taken care of for the actual data type. |
344 | * | 313 | * |
345 | * Types using the deep userdata system (and only those!) can be passed between | 314 | * Types using the deep userdata system (and only those!) can be passed between |
346 | * separate Lua states via 'luaG_inter_move()'. | 315 | * separate Lua states via 'luaG_inter_move()'. |
347 | * | 316 | * |
348 | * Returns: 'proxy' userdata for accessing the deep data via 'DeepFactory::toDeep()' | 317 | * Returns: 'proxy' userdata for accessing the deep data via 'DeepFactory::toDeep()' |
349 | */ | 318 | */ |
350 | int DeepFactory::pushDeepUserdata(DestState L_, int nuv_) const | 319 | int DeepFactory::pushDeepUserdata(DestState L_, int nuv_) const |
351 | { | 320 | { |
352 | STACK_GROW(L_, 1); | 321 | STACK_GROW(L_, 1); |
353 | STACK_CHECK_START_REL(L_, 0); | 322 | STACK_CHECK_START_REL(L_, 0); |
354 | int const oldtop{ lua_gettop(L_) }; | 323 | int const oldtop{ lua_gettop(L_) }; |
355 | DeepPrelude* const prelude{ newDeepObjectInternal(L_) }; | 324 | DeepPrelude* const prelude{ newDeepObjectInternal(L_) }; |
356 | if (prelude == nullptr) | 325 | if (prelude == nullptr) { |
357 | { | ||
358 | raise_luaL_error(L_, "DeepFactory::newDeepObjectInternal failed to create deep userdata (out of memory)"); | 326 | raise_luaL_error(L_, "DeepFactory::newDeepObjectInternal failed to create deep userdata (out of memory)"); |
359 | } | 327 | } |
360 | 328 | ||
361 | if (prelude->m_magic != kDeepVersion) | 329 | if (prelude->m_magic != kDeepVersion) { |
362 | { | ||
363 | // just in case, don't leak the newly allocated deep userdata object | 330 | // just in case, don't leak the newly allocated deep userdata object |
364 | deleteDeepObjectInternal(L_, prelude); | 331 | deleteDeepObjectInternal(L_, prelude); |
365 | raise_luaL_error(L_, "Bad Deep Factory: kDeepVersion is incorrect, rebuild your implementation with the latest deep implementation"); | 332 | raise_luaL_error(L_, "Bad Deep Factory: kDeepVersion is incorrect, rebuild your implementation with the latest deep implementation"); |
@@ -368,16 +335,14 @@ int DeepFactory::pushDeepUserdata(DestState L_, int nuv_) const | |||
368 | LUA_ASSERT(L_, prelude->m_refcount.load(std::memory_order_relaxed) == 0); // 'DeepFactory::PushDeepProxy' will lift it to 1 | 335 | LUA_ASSERT(L_, prelude->m_refcount.load(std::memory_order_relaxed) == 0); // 'DeepFactory::PushDeepProxy' will lift it to 1 |
369 | LUA_ASSERT(L_, &prelude->m_factory == this); | 336 | LUA_ASSERT(L_, &prelude->m_factory == this); |
370 | 337 | ||
371 | if (lua_gettop(L_) - oldtop != 0) | 338 | if (lua_gettop(L_) - oldtop != 0) { |
372 | { | ||
373 | // just in case, don't leak the newly allocated deep userdata object | 339 | // just in case, don't leak the newly allocated deep userdata object |
374 | deleteDeepObjectInternal(L_, prelude); | 340 | deleteDeepObjectInternal(L_, prelude); |
375 | raise_luaL_error(L_, "Bad DeepFactory::newDeepObjectInternal overload: should not push anything on the stack"); | 341 | raise_luaL_error(L_, "Bad DeepFactory::newDeepObjectInternal overload: should not push anything on the stack"); |
376 | } | 342 | } |
377 | 343 | ||
378 | char const* const errmsg{ DeepFactory::PushDeepProxy(L_, prelude, nuv_, LookupMode::LaneBody) }; // proxy | 344 | char const* const errmsg{ DeepFactory::PushDeepProxy(L_, prelude, nuv_, LookupMode::LaneBody) }; // proxy |
379 | if (errmsg != nullptr) | 345 | if (errmsg != nullptr) { |
380 | { | ||
381 | raise_luaL_error(L_, errmsg); | 346 | raise_luaL_error(L_, errmsg); |
382 | } | 347 | } |
383 | STACK_CHECK(L_, 1); | 348 | STACK_CHECK(L_, 1); |
@@ -387,17 +352,16 @@ int DeepFactory::pushDeepUserdata(DestState L_, int nuv_) const | |||
387 | // ################################################################################################# | 352 | // ################################################################################################# |
388 | 353 | ||
389 | /* | 354 | /* |
390 | * Access deep userdata through a proxy. | 355 | * Access deep userdata through a proxy. |
391 | * | 356 | * |
392 | * Reference count is not changed, and access to the deep userdata is not | 357 | * Reference count is not changed, and access to the deep userdata is not |
393 | * serialized. It is the module's responsibility to prevent conflicting usage. | 358 | * serialized. It is the module's responsibility to prevent conflicting usage. |
394 | */ | 359 | */ |
395 | DeepPrelude* DeepFactory::toDeep(lua_State* L_, int index_) const | 360 | DeepPrelude* DeepFactory::toDeep(lua_State* L_, int index_) const |
396 | { | 361 | { |
397 | STACK_CHECK_START_REL(L_, 0); | 362 | STACK_CHECK_START_REL(L_, 0); |
398 | // ensure it is actually a deep userdata we created | 363 | // ensure it is actually a deep userdata we created |
399 | if (get_factory(L_, index_, LookupMode::LaneBody) != this) | 364 | if (get_factory(L_, index_, LookupMode::LaneBody) != this) { |
400 | { | ||
401 | return nullptr; // no metatable, or wrong kind | 365 | return nullptr; // no metatable, or wrong kind |
402 | } | 366 | } |
403 | STACK_CHECK(L_, 0); | 367 | STACK_CHECK(L_, 0); |
@@ -415,12 +379,11 @@ DeepPrelude* DeepFactory::toDeep(lua_State* L_, int index_) const | |||
415 | * the id function of the copied value, or nullptr for non-deep userdata | 379 | * the id function of the copied value, or nullptr for non-deep userdata |
416 | * (not copied) | 380 | * (not copied) |
417 | */ | 381 | */ |
418 | [[nodiscard]] bool InterCopyContext::copydeep() const | 382 | [[nodiscard]] bool InterCopyContext::copyDeep() const |
419 | { | 383 | { |
420 | DeepFactory* const factory { get_factory(L1, L1_i, mode) }; | 384 | DeepFactory* const factory{ get_factory(L1, L1_i, mode) }; |
421 | if (factory == nullptr) | 385 | if (factory == nullptr) { |
422 | { | 386 | return false; // not a deep userdata |
423 | return false; // not a deep userdata | ||
424 | } | 387 | } |
425 | 388 | ||
426 | STACK_CHECK_START_REL(L1, 0); | 389 | STACK_CHECK_START_REL(L1, 0); |
@@ -428,39 +391,36 @@ DeepPrelude* DeepFactory::toDeep(lua_State* L_, int index_) const | |||
428 | 391 | ||
429 | // extract all uservalues of the source | 392 | // extract all uservalues of the source |
430 | int nuv = 0; | 393 | int nuv = 0; |
431 | while (lua_getiuservalue(L1, L1_i, nuv + 1) != LUA_TNONE) // ... u [uv]* nil | 394 | while (lua_getiuservalue(L1, L1_i, nuv + 1) != LUA_TNONE) { // L1: ... u [uv]* nil |
432 | { | 395 | ++nuv; |
433 | ++ nuv; | ||
434 | } | 396 | } |
435 | // last call returned TNONE and pushed nil, that we don't need | 397 | // last call returned TNONE and pushed nil, that we don't need |
436 | lua_pop(L1, 1); // ... u [uv]* | 398 | lua_pop(L1, 1); // L1: ... u [uv]* |
437 | STACK_CHECK(L1, nuv); | 399 | STACK_CHECK(L1, nuv); |
438 | 400 | ||
439 | char const* errmsg{ DeepFactory::PushDeepProxy(L2, *lua_tofulluserdata<DeepPrelude*>(L1, L1_i), nuv, mode) }; // u | 401 | DeepPrelude* const u{ *lua_tofulluserdata<DeepPrelude*>(L1, L1_i) }; |
402 | char const* errmsg{ DeepFactory::PushDeepProxy(L2, u, nuv, mode) }; // L2: u | ||
440 | 403 | ||
441 | // transfer all uservalues of the source in the destination | 404 | // transfer all uservalues of the source in the destination |
442 | { | 405 | { |
443 | InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, name }; | 406 | InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, name }; |
444 | int const clone_i{ lua_gettop(L2) }; | 407 | int const clone_i{ lua_gettop(L2) }; |
445 | while (nuv) | 408 | while (nuv) { |
446 | { | ||
447 | c.L1_i = SourceIndex{ lua_absindex(L1, -1) }; | 409 | c.L1_i = SourceIndex{ lua_absindex(L1, -1) }; |
448 | if (!c.inter_copy_one()) // u uv | 410 | if (!c.inter_copy_one()) { // L2: u uv |
449 | { | ||
450 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); | 411 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); |
451 | } | 412 | } |
452 | lua_pop(L1, 1); // ... u [uv]* | 413 | lua_pop(L1, 1); // L1: ... u [uv]* |
453 | // this pops the value from the stack | 414 | // this pops the value from the stack |
454 | lua_setiuservalue(L2, clone_i, nuv); // u | 415 | lua_setiuservalue(L2, clone_i, nuv); // L2: u |
455 | -- nuv; | 416 | --nuv; |
456 | } | 417 | } |
457 | } | 418 | } |
458 | 419 | ||
459 | STACK_CHECK(L2, 1); | 420 | STACK_CHECK(L2, 1); |
460 | STACK_CHECK(L1, 0); | 421 | STACK_CHECK(L1, 0); |
461 | 422 | ||
462 | if (errmsg != nullptr) | 423 | if (errmsg != nullptr) { |
463 | { | ||
464 | // raise the error in the proper state (not the keeper) | 424 | // raise the error in the proper state (not the keeper) |
465 | lua_State* const errL{ (mode == LookupMode::FromKeeper) ? L2 : L1 }; | 425 | lua_State* const errL{ (mode == LookupMode::FromKeeper) ? L2 : L1 }; |
466 | raise_luaL_error(errL, errmsg); | 426 | raise_luaL_error(errL, errmsg); |
diff --git a/src/keeper.cpp b/src/keeper.cpp index cbdd0c9..5350d26 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp | |||
@@ -56,7 +56,6 @@ | |||
56 | class keeper_fifo | 56 | class keeper_fifo |
57 | { | 57 | { |
58 | public: | 58 | public: |
59 | |||
60 | int first{ 1 }; | 59 | int first{ 1 }; |
61 | int count{ 0 }; | 60 | int count{ 0 }; |
62 | int limit{ -1 }; | 61 | int limit{ -1 }; |
@@ -81,8 +80,7 @@ static constexpr int kContentsTableIndex{ 1 }; | |||
81 | [[nodiscard]] static keeper_fifo* prepare_fifo_access(lua_State* L_, int idx_) | 80 | [[nodiscard]] static keeper_fifo* prepare_fifo_access(lua_State* L_, int idx_) |
82 | { | 81 | { |
83 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, idx_) }; | 82 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, idx_) }; |
84 | if (fifo != nullptr) | 83 | if (fifo != nullptr) { |
85 | { | ||
86 | idx_ = lua_absindex(L_, idx_); | 84 | idx_ = lua_absindex(L_, idx_); |
87 | STACK_GROW(L_, 1); | 85 | STACK_GROW(L_, 1); |
88 | // we can replace the fifo userdata in the stack without fear of it being GCed, there are other references around | 86 | // we can replace the fifo userdata in the stack without fear of it being GCed, there are other references around |
@@ -117,8 +115,7 @@ static void fifo_push(lua_State* L_, keeper_fifo* fifo_, int count_) | |||
117 | int const idx{ lua_gettop(L_) - count_ }; | 115 | int const idx{ lua_gettop(L_) - count_ }; |
118 | int const start{ fifo_->first + fifo_->count - 1 }; | 116 | int const start{ fifo_->first + fifo_->count - 1 }; |
119 | // pop all additional arguments, storing them in the fifo | 117 | // pop all additional arguments, storing them in the fifo |
120 | for (int i = count_; i >= 1; --i) | 118 | for (int i = count_; i >= 1; --i) { |
121 | { | ||
122 | // store in the fifo the value at the top of the stack at the specified index, popping it from the stack | 119 | // store in the fifo the value at the top of the stack at the specified index, popping it from the stack |
123 | lua_rawseti(L_, idx, start + i); | 120 | lua_rawseti(L_, idx, start + i); |
124 | } | 121 | } |
@@ -135,8 +132,7 @@ static void fifo_push(lua_State* L_, keeper_fifo* fifo_, int count_) | |||
135 | static void fifo_peek(lua_State* L_, keeper_fifo* fifo_, int count_) | 132 | static void fifo_peek(lua_State* L_, keeper_fifo* fifo_, int count_) |
136 | { | 133 | { |
137 | STACK_GROW(L_, count_); | 134 | STACK_GROW(L_, count_); |
138 | for (int i = 0; i < count_; ++i) | 135 | for (int i = 0; i < count_; ++i) { |
139 | { | ||
140 | lua_rawgeti(L_, 1, (fifo_->first + i)); | 136 | lua_rawgeti(L_, 1, (fifo_->first + i)); |
141 | } | 137 | } |
142 | } | 138 | } |
@@ -145,29 +141,28 @@ static void fifo_peek(lua_State* L_, keeper_fifo* fifo_, int count_) | |||
145 | 141 | ||
146 | // in: fifo | 142 | // in: fifo |
147 | // out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number) | 143 | // out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number) |
148 | static void fifo_pop( lua_State* L_, keeper_fifo* fifo_, int count_) | 144 | static void fifo_pop(lua_State* L_, keeper_fifo* fifo_, int count_) |
149 | { | 145 | { |
150 | LUA_ASSERT(L_, lua_istable(L_, -1)); | 146 | LUA_ASSERT(L_, lua_istable(L_, -1)); |
151 | int const fifo_idx{ lua_gettop(L_) }; // ... fifotbl | 147 | int const fifo_idx{ lua_gettop(L_) }; // L_: ... fifotbl |
152 | // each iteration pushes a value on the stack! | 148 | // each iteration pushes a value on the stack! |
153 | STACK_GROW(L_, count_ + 2); | 149 | STACK_GROW(L_, count_ + 2); |
154 | // skip first item, we will push it last | 150 | // skip first item, we will push it last |
155 | for (int i = 1; i < count_; ++i) | 151 | for (int i = 1; i < count_; ++i) { |
156 | { | ||
157 | int const at{ fifo_->first + i }; | 152 | int const at{ fifo_->first + i }; |
158 | // push item on the stack | 153 | // push item on the stack |
159 | lua_rawgeti(L_, fifo_idx, at); // ... fifotbl val | 154 | lua_rawgeti(L_, fifo_idx, at); // L_: ... fifotbl val |
160 | // remove item from the fifo | 155 | // remove item from the fifo |
161 | lua_pushnil(L_); // ... fifotbl val nil | 156 | lua_pushnil(L_); // L_: ... fifotbl val nil |
162 | lua_rawseti(L_, fifo_idx, at); // ... fifotbl val | 157 | lua_rawseti(L_, fifo_idx, at); // L_: ... fifotbl val |
163 | } | 158 | } |
164 | // now process first item | 159 | // now process first item |
165 | { | 160 | { |
166 | int const at{ fifo_->first }; | 161 | int const at{ fifo_->first }; |
167 | lua_rawgeti(L_, fifo_idx, at); // ... fifotbl vals val | 162 | lua_rawgeti(L_, fifo_idx, at); // L_: ... fifotbl vals val |
168 | lua_pushnil(L_); // ... fifotbl vals val nil | 163 | lua_pushnil(L_); // L_: ... fifotbl vals val nil |
169 | lua_rawseti(L_, fifo_idx, at); // ... fifotbl vals val | 164 | lua_rawseti(L_, fifo_idx, at); // L_: ... fifotbl vals val |
170 | lua_replace(L_, fifo_idx); // ... vals | 165 | lua_replace(L_, fifo_idx); // L_: ... vals |
171 | } | 166 | } |
172 | 167 | ||
173 | // avoid ever-growing indexes by resetting each time we detect the fifo is empty | 168 | // avoid ever-growing indexes by resetting each time we detect the fifo is empty |
@@ -189,20 +184,19 @@ static void push_table(lua_State* L_, int idx_) | |||
189 | STACK_GROW(L_, 5); | 184 | STACK_GROW(L_, 5); |
190 | STACK_CHECK_START_REL(L_, 0); | 185 | STACK_CHECK_START_REL(L_, 0); |
191 | idx_ = lua_absindex(L_, idx_); | 186 | idx_ = lua_absindex(L_, idx_); |
192 | kFifosRegKey.pushValue(L_); // ud fifos | 187 | kFifosRegKey.pushValue(L_); // L_: ud fifos |
193 | lua_pushvalue(L_, idx_); // ud fifos ud | 188 | lua_pushvalue(L_, idx_); // L_: ud fifos ud |
194 | lua_rawget(L_, -2); // ud fifos fifos[ud] | 189 | lua_rawget(L_, -2); // L_: ud fifos fifos[ud] |
195 | STACK_CHECK(L_, 2); | 190 | STACK_CHECK(L_, 2); |
196 | if (lua_isnil(L_, -1)) | 191 | if (lua_isnil(L_, -1)) { |
197 | { | 192 | lua_pop(L_, 1); // L_: ud fifos |
198 | lua_pop(L_, 1); // ud fifos | ||
199 | // add a new fifos table for this linda | 193 | // add a new fifos table for this linda |
200 | lua_newtable(L_); // ud fifos fifos[ud] | 194 | lua_newtable(L_); // L_: ud fifos fifos[ud] |
201 | lua_pushvalue(L_, idx_); // ud fifos fifos[ud] ud | 195 | lua_pushvalue(L_, idx_); // L_: ud fifos fifos[ud] ud |
202 | lua_pushvalue(L_, -2); // ud fifos fifos[ud] ud fifos[ud] | 196 | lua_pushvalue(L_, -2); // L_: ud fifos fifos[ud] ud fifos[ud] |
203 | lua_rawset(L_, -4); // ud fifos fifos[ud] | 197 | lua_rawset(L_, -4); // L_: ud fifos fifos[ud] |
204 | } | 198 | } |
205 | lua_remove(L_, -2); // ud fifos[ud] | 199 | lua_remove(L_, -2); // L_: ud fifos[ud] |
206 | STACK_CHECK(L_, 1); | 200 | STACK_CHECK(L_, 1); |
207 | } | 201 | } |
208 | 202 | ||
@@ -215,47 +209,45 @@ int keeper_push_linda_storage(Linda& linda_, DestState L_) | |||
215 | SourceState const KL{ K ? K->L : nullptr }; | 209 | SourceState const KL{ K ? K->L : nullptr }; |
216 | if (KL == nullptr) | 210 | if (KL == nullptr) |
217 | return 0; | 211 | return 0; |
218 | STACK_GROW(KL, 4); // KEEPER MAIN | 212 | STACK_GROW(KL, 4); |
219 | STACK_CHECK_START_REL(KL, 0); | 213 | STACK_CHECK_START_REL(KL, 0); |
220 | kFifosRegKey.pushValue(KL); // fifos | 214 | kFifosRegKey.pushValue(KL); // KL: fifos L_: |
221 | lua_pushlightuserdata(KL, &linda_); // fifos ud | 215 | lua_pushlightuserdata(KL, &linda_); // KL: fifos ud L_: |
222 | lua_rawget(KL, -2); // fifos storage | 216 | lua_rawget(KL, -2); // KL: fifos storage L_: |
223 | lua_remove(KL, -2); // storage | 217 | lua_remove(KL, -2); // KL: storage L_: |
224 | if (!lua_istable(KL, -1)) | 218 | if (!lua_istable(KL, -1)) { |
225 | { | 219 | lua_pop(KL, 1); // KL: L_: |
226 | lua_pop(KL, 1); // | ||
227 | STACK_CHECK(KL, 0); | 220 | STACK_CHECK(KL, 0); |
228 | return 0; | 221 | return 0; |
229 | } | 222 | } |
230 | // move data from keeper to destination state | 223 | // move data from keeper to destination state |
231 | STACK_GROW(L_, 5); | 224 | STACK_GROW(L_, 5); |
232 | STACK_CHECK_START_REL(L_, 0); | 225 | STACK_CHECK_START_REL(L_, 0); |
233 | lua_newtable(L_); // out | 226 | lua_newtable(L_); // KL: storage L_: out |
234 | InterCopyContext c{ linda_.U, L_, KL, {}, {}, {}, LookupMode::FromKeeper, {} }; | 227 | InterCopyContext c{ linda_.U, L_, KL, {}, {}, {}, LookupMode::FromKeeper, {} }; |
235 | lua_pushnil(KL); // storage nil | 228 | lua_pushnil(KL); // KL: storage nil L_: out |
236 | while (lua_next(KL, -2)) // storage key fifo | 229 | while (lua_next(KL, -2)) { // KL: storage key fifo L_: out |
237 | { | 230 | keeper_fifo* fifo = prepare_fifo_access(KL, -1); // KL: storage key fifotbl L_: out |
238 | keeper_fifo* fifo = prepare_fifo_access(KL, -1); // storage key fifotbl | 231 | lua_pushvalue(KL, -2); // KL: storage key fifotbl key L_: out |
239 | lua_pushvalue(KL, -2); // storage key fifotbl key | 232 | std::ignore = c.inter_move(1); // KL: storage key fifotbl L_: out key |
240 | std::ignore = c.inter_move(1); // storage key fifotbl // out key | ||
241 | STACK_CHECK(L_, 2); | 233 | STACK_CHECK(L_, 2); |
242 | lua_newtable(L_); // out key keyout | 234 | lua_newtable(L_); // KL: storage key L_: out key keyout |
243 | std::ignore = c.inter_move(1); // storage key // out key keyout fifotbl | 235 | std::ignore = c.inter_move(1); // KL: storage key L_: out key keyout fifotbl |
244 | lua_pushinteger(L_, fifo->first); // out key keyout fifotbl first | 236 | lua_pushinteger(L_, fifo->first); // KL: storage key L_: out key keyout fifotbl first |
245 | STACK_CHECK(L_, 5); | 237 | STACK_CHECK(L_, 5); |
246 | lua_setfield(L_, -3, "first"); // out key keyout fifotbl | 238 | lua_setfield(L_, -3, "first"); // KL: storage key L_: out key keyout fifotbl |
247 | lua_pushinteger(L_, fifo->count); // out key keyout fifobtl count | 239 | lua_pushinteger(L_, fifo->count); // KL: storage key L_: out key keyout fifobtl count |
248 | STACK_CHECK(L_, 5); | 240 | STACK_CHECK(L_, 5); |
249 | lua_setfield(L_, -3, "count"); // out key keyout fifotbl | 241 | lua_setfield(L_, -3, "count"); // KL: storage key L_: out key keyout fifotbl |
250 | lua_pushinteger(L_, fifo->limit); // out key keyout fifotbl limit | 242 | lua_pushinteger(L_, fifo->limit); // KL: storage key L_: out key keyout fifotbl limit |
251 | STACK_CHECK(L_, 5); | 243 | STACK_CHECK(L_, 5); |
252 | lua_setfield(L_, -3, "limit"); // out key keyout fifotbl | 244 | lua_setfield(L_, -3, "limit"); // KL: storage key L_: out key keyout fifotbl |
253 | lua_setfield(L_, -2, "fifo"); // out key keyout | 245 | lua_setfield(L_, -2, "fifo"); // KL: storage key L_: out key keyout |
254 | lua_rawset(L_, -3); // out | 246 | lua_rawset(L_, -3); // KL: storage key L_: out |
255 | STACK_CHECK(L_, 1); | 247 | STACK_CHECK(L_, 1); |
256 | } | 248 | } // KL_: storage L_: out |
257 | STACK_CHECK(L_, 1); | 249 | STACK_CHECK(L_, 1); |
258 | lua_pop(KL, 1); // | 250 | lua_pop(KL, 1); // KL: L_: out |
259 | STACK_CHECK(KL, 0); | 251 | STACK_CHECK(KL, 0); |
260 | return 1; | 252 | return 1; |
261 | } | 253 | } |
@@ -267,11 +259,11 @@ int keepercall_clear(lua_State* L_) | |||
267 | { | 259 | { |
268 | STACK_GROW(L_, 3); | 260 | STACK_GROW(L_, 3); |
269 | STACK_CHECK_START_REL(L_, 0); | 261 | STACK_CHECK_START_REL(L_, 0); |
270 | kFifosRegKey.pushValue(L_); // ud fifos | 262 | kFifosRegKey.pushValue(L_); // L_: ud fifos |
271 | lua_pushvalue(L_, 1); // ud fifos ud | 263 | lua_pushvalue(L_, 1); // L_: ud fifos ud |
272 | lua_pushnil(L_); // ud fifos ud nil | 264 | lua_pushnil(L_); // L_: ud fifos ud nil |
273 | lua_rawset(L_, -3); // ud fifos | 265 | lua_rawset(L_, -3); // L_: ud fifos |
274 | lua_pop(L_, 1); // ud | 266 | lua_pop(L_, 1); // L_: ud |
275 | STACK_CHECK(L_, 0); | 267 | STACK_CHECK(L_, 0); |
276 | return 0; | 268 | return 0; |
277 | } | 269 | } |
@@ -283,32 +275,28 @@ int keepercall_clear(lua_State* L_) | |||
283 | int keepercall_send(lua_State* L_) | 275 | int keepercall_send(lua_State* L_) |
284 | { | 276 | { |
285 | int const n{ lua_gettop(L_) - 2 }; | 277 | int const n{ lua_gettop(L_) - 2 }; |
286 | push_table(L_, 1); // ud key ... fifos | 278 | push_table(L_, 1); // L_: ud key ... fifos |
287 | // get the fifo associated to this key in this linda, create it if it doesn't exist | 279 | // get the fifo associated to this key in this linda, create it if it doesn't exist |
288 | lua_pushvalue(L_, 2); // ud key ... fifos key | 280 | lua_pushvalue(L_, 2); // L_: ud key ... fifos key |
289 | lua_rawget(L_, -2); // ud key ... fifos fifo | 281 | lua_rawget(L_, -2); // L_: ud key ... fifos fifo |
290 | if (lua_isnil(L_, -1)) | 282 | if (lua_isnil(L_, -1)) { |
291 | { | 283 | lua_pop(L_, 1); // L_: ud key ... fifos |
292 | lua_pop(L_, 1); // ud key ... fifos | 284 | std::ignore = fifo_new(KeeperState{ L_ }); // L_: ud key ... fifos fifo |
293 | std::ignore = fifo_new(KeeperState{ L_ }); // ud key ... fifos fifo | 285 | lua_pushvalue(L_, 2); // L_: ud key ... fifos fifo key |
294 | lua_pushvalue(L_, 2); // ud key ... fifos fifo key | 286 | lua_pushvalue(L_, -2); // L_: ud key ... fifos fifo key fifo |
295 | lua_pushvalue(L_, -2); // ud key ... fifos fifo key fifo | 287 | lua_rawset(L_, -4); // L_: ud key ... fifos fifo |
296 | lua_rawset(L_, -4); // ud key ... fifos fifo | 288 | } |
297 | } | 289 | lua_remove(L_, -2); // L_: ud key ... fifo |
298 | lua_remove(L_, -2); // ud key ... fifo | ||
299 | keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) }; | 290 | keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) }; |
300 | if (fifo->limit >= 0 && fifo->count + n > fifo->limit) | 291 | if (fifo->limit >= 0 && fifo->count + n > fifo->limit) { |
301 | { | 292 | lua_settop(L_, 0); // L_: |
302 | lua_settop(L_, 0); // | 293 | lua_pushboolean(L_, 0); // L_:false |
303 | lua_pushboolean(L_, 0); // false | 294 | } else { |
304 | } | 295 | fifo = prepare_fifo_access(L_, -1); // L_: ud fifotbl |
305 | else | 296 | lua_replace(L_, 2); // L_: ud fifotbl ... |
306 | { | 297 | fifo_push(L_, fifo, n); // L_: ud fifotbl |
307 | fifo = prepare_fifo_access(L_, -1); // ud fifotbl | 298 | lua_settop(L_, 0); // L_: |
308 | lua_replace(L_, 2); // ud fifotbl ... | 299 | lua_pushboolean(L_, 1); // L_: true |
309 | fifo_push(L_, fifo, n); // ud fifotbl | ||
310 | lua_settop(L_, 0); // | ||
311 | lua_pushboolean(L_, 1); // true | ||
312 | } | 300 | } |
313 | return 1; | 301 | return 1; |
314 | } | 302 | } |
@@ -320,30 +308,26 @@ int keepercall_send(lua_State* L_) | |||
320 | int keepercall_receive(lua_State* L_) | 308 | int keepercall_receive(lua_State* L_) |
321 | { | 309 | { |
322 | int const top{ lua_gettop(L_) }; | 310 | int const top{ lua_gettop(L_) }; |
323 | push_table(L_, 1); // ud keys fifos | 311 | push_table(L_, 1); // L_: ud keys fifos |
324 | lua_replace(L_, 1); // fifos keys | 312 | lua_replace(L_, 1); // L_: fifos keys |
325 | for (int i = 2; i <= top; ++i) | 313 | for (int i = 2; i <= top; ++i) { |
326 | { | 314 | lua_pushvalue(L_, i); // L_: fifos keys key[i] |
327 | lua_pushvalue(L_, i); // fifos keys key[i] | 315 | lua_rawget(L_, 1); // L_: fifos keys fifo |
328 | lua_rawget(L_, 1); // fifos keys fifo | 316 | keeper_fifo* const fifo{ prepare_fifo_access(L_, -1) }; // L_: fifos keys fifotbl |
329 | keeper_fifo* const fifo{ prepare_fifo_access(L_, -1) }; // fifos keys fifotbl | 317 | if (fifo != nullptr && fifo->count > 0) { |
330 | if (fifo != nullptr && fifo->count > 0) | 318 | fifo_pop(L_, fifo, 1); // L_: fifos keys val |
331 | { | 319 | if (!lua_isnil(L_, -1)) { |
332 | fifo_pop(L_, fifo, 1); // fifos keys val | 320 | lua_replace(L_, 1); // L_: val keys |
333 | if (!lua_isnil(L_, -1)) | 321 | lua_settop(L_, i); // L_: val keys key[i] |
334 | { | 322 | if (i != 2) { |
335 | lua_replace(L_, 1); // val keys | 323 | lua_replace(L_, 2); // L_: val key keys |
336 | lua_settop(L_, i); // val keys key[i] | 324 | lua_settop(L_, 2); // L_: val key |
337 | if (i != 2) | ||
338 | { | ||
339 | lua_replace(L_, 2); // val key keys | ||
340 | lua_settop(L_, 2); // val key | ||
341 | } | 325 | } |
342 | lua_insert(L_, 1); // key, val | 326 | lua_insert(L_, 1); // L_: key, val |
343 | return 2; | 327 | return 2; |
344 | } | 328 | } |
345 | } | 329 | } |
346 | lua_settop(L_, top); // data keys | 330 | lua_settop(L_, top); // L_: data keys |
347 | } | 331 | } |
348 | // nothing to receive | 332 | // nothing to receive |
349 | return 0; | 333 | return 0; |
@@ -355,29 +339,23 @@ int keepercall_receive(lua_State* L_) | |||
355 | int keepercall_receive_batched(lua_State* L_) | 339 | int keepercall_receive_batched(lua_State* L_) |
356 | { | 340 | { |
357 | int const min_count{ static_cast<int>(lua_tointeger(L_, 3)) }; | 341 | int const min_count{ static_cast<int>(lua_tointeger(L_, 3)) }; |
358 | if (min_count > 0) | 342 | if (min_count > 0) { |
359 | { | ||
360 | int const max_count{ static_cast<int>(luaL_optinteger(L_, 4, min_count)) }; | 343 | int const max_count{ static_cast<int>(luaL_optinteger(L_, 4, min_count)) }; |
361 | lua_settop(L_, 2); // ud key | 344 | lua_settop(L_, 2); // L_: ud key |
362 | lua_insert(L_, 1); // key ud | 345 | lua_insert(L_, 1); // L_: key ud |
363 | push_table(L_, 2); // key ud fifos | 346 | push_table(L_, 2); // L_: key ud fifos |
364 | lua_remove(L_, 2); // key fifos | 347 | lua_remove(L_, 2); // L_: key fifos |
365 | lua_pushvalue(L_, 1); // key fifos key | 348 | lua_pushvalue(L_, 1); // L_: key fifos key |
366 | lua_rawget(L_, 2); // key fifos fifo | 349 | lua_rawget(L_, 2); // L_: key fifos fifo |
367 | lua_remove(L_, 2); // key fifo | 350 | lua_remove(L_, 2); // L_: key fifo |
368 | keeper_fifo* const fifo{ prepare_fifo_access(L_, 2) }; // key fifotbl | 351 | keeper_fifo* const fifo{ prepare_fifo_access(L_, 2) }; // L_: key fifotbl |
369 | if (fifo != nullptr && fifo->count >= min_count) | 352 | if (fifo != nullptr && fifo->count >= min_count) { |
370 | { | 353 | fifo_pop(L_, fifo, std::min(max_count, fifo->count)); // L_: key ... |
371 | fifo_pop(L_, fifo, std::min( max_count, fifo->count)); // key ... | 354 | } else { |
372 | } | 355 | lua_settop(L_, 0); // L_: |
373 | else | ||
374 | { | ||
375 | lua_settop(L_, 0); // | ||
376 | } | 356 | } |
377 | return lua_gettop(L_); | 357 | return lua_gettop(L_); |
378 | } | 358 | } else { |
379 | else | ||
380 | { | ||
381 | return 0; | 359 | return 0; |
382 | } | 360 | } |
383 | } | 361 | } |
@@ -389,28 +367,26 @@ int keepercall_receive_batched(lua_State* L_) | |||
389 | int keepercall_limit(lua_State* L_) | 367 | int keepercall_limit(lua_State* L_) |
390 | { | 368 | { |
391 | int const limit{ static_cast<int>(lua_tointeger(L_, 3)) }; | 369 | int const limit{ static_cast<int>(lua_tointeger(L_, 3)) }; |
392 | push_table(L_, 1); // ud key n fifos | 370 | push_table(L_, 1); // L_: ud key n fifos |
393 | lua_replace(L_, 1); // fifos key n | 371 | lua_replace(L_, 1); // L_: fifos key n |
394 | lua_pop(L_, 1); // fifos key | 372 | lua_pop(L_, 1); // L_: fifos key |
395 | lua_pushvalue(L_, -1); // fifos key key | 373 | lua_pushvalue(L_, -1); // L_: fifos key key |
396 | lua_rawget(L_, -3); // fifos key fifo|nil | 374 | lua_rawget(L_, -3); // L_: fifos key fifo|nil |
397 | keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) }; | 375 | keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) }; |
398 | if (fifo == nullptr) | 376 | if (fifo == nullptr) { // L_: fifos key nil |
399 | { // fifos key nil | 377 | lua_pop(L_, 1); // L_: fifos key |
400 | lua_pop(L_, 1); // fifos key | 378 | fifo = fifo_new(KeeperState{ L_ }); // L_: fifos key fifo |
401 | fifo = fifo_new(KeeperState{ L_ }); // fifos key fifo | 379 | lua_rawset(L_, -3); // L_: fifos |
402 | lua_rawset(L_, -3); // fifos | ||
403 | } | 380 | } |
404 | // remove any clutter on the stack | 381 | // remove any clutter on the stack |
405 | lua_settop(L_, 0); | 382 | lua_settop(L_, 0); // L_: |
406 | // return true if we decide that blocked threads waiting to write on that key should be awakened | 383 | // return true if we decide that blocked threads waiting to write on that key should be awakened |
407 | // this is the case if we detect the key was full but it is no longer the case | 384 | // this is the case if we detect the key was full but it is no longer the case |
408 | if ( | 385 | if ( |
409 | ((fifo->limit >= 0) && (fifo->count >= fifo->limit)) // the key was full if limited and count exceeded the previous limit | 386 | ((fifo->limit >= 0) && (fifo->count >= fifo->limit)) // the key was full if limited and count exceeded the previous limit |
410 | && ((limit < 0) || (fifo->count < limit)) // the key is not full if unlimited or count is lower than the new limit | 387 | && ((limit < 0) || (fifo->count < limit)) // the key is not full if unlimited or count is lower than the new limit |
411 | ) | 388 | ) { |
412 | { | 389 | lua_pushboolean(L_, 1); // L_: true |
413 | lua_pushboolean(L_, 1); // true | ||
414 | } | 390 | } |
415 | // set the new limit | 391 | // set the new limit |
416 | fifo->limit = limit; | 392 | fifo->limit = limit; |
@@ -421,72 +397,63 @@ int keepercall_limit(lua_State* L_) | |||
421 | // ################################################################################################# | 397 | // ################################################################################################# |
422 | 398 | ||
423 | // in: linda_ud key [[val] ...] | 399 | // in: linda_ud key [[val] ...] |
424 | //out: true if the linda was full but it's no longer the case, else nothing | 400 | // out: true if the linda was full but it's no longer the case, else nothing |
425 | int keepercall_set(lua_State* L_) | 401 | int keepercall_set(lua_State* L_) |
426 | { | 402 | { |
427 | bool should_wake_writers{ false }; | 403 | bool should_wake_writers{ false }; |
428 | STACK_GROW(L_, 6); | 404 | STACK_GROW(L_, 6); |
429 | 405 | ||
430 | // retrieve fifos associated with the linda | 406 | // retrieve fifos associated with the linda |
431 | push_table(L_, 1); // ud key [val [, ...]] fifos | 407 | push_table(L_, 1); // L_: ud key [val [, ...]] fifos |
432 | lua_replace(L_, 1); // fifos key [val [, ...]] | 408 | lua_replace(L_, 1); // L_: fifos key [val [, ...]] |
433 | 409 | ||
434 | // make sure we have a value on the stack | 410 | // make sure we have a value on the stack |
435 | if (lua_gettop(L_) == 2) // fifos key | 411 | if (lua_gettop(L_) == 2) { // L_: fifos key |
436 | { | 412 | lua_pushvalue(L_, -1); // L_: fifos key key |
437 | lua_pushvalue(L_, -1); // fifos key key | 413 | lua_rawget(L_, 1); // L_: fifos key fifo|nil |
438 | lua_rawget(L_, 1); // fifos key fifo|nil | ||
439 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | 414 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! |
440 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; | 415 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; |
441 | if (fifo != nullptr) // might be nullptr if we set a nonexistent key to nil | 416 | if (fifo != nullptr) { // might be nullptr if we set a nonexistent key to nil // L_: fifos key fifo |
442 | { // fifos key fifo | 417 | if (fifo->limit < 0) { // fifo limit value is the default (unlimited): we can totally remove it |
443 | if (fifo->limit < 0) // fifo limit value is the default (unlimited): we can totally remove it | 418 | lua_pop(L_, 1); // L_: fifos key |
444 | { | 419 | lua_pushnil(L_); // L_: fifos key nil |
445 | lua_pop(L_, 1); // fifos key | 420 | lua_rawset(L_, -3); // L_: fifos |
446 | lua_pushnil(L_); // fifos key nil | 421 | } else { |
447 | lua_rawset(L_, -3); // fifos | ||
448 | } | ||
449 | else | ||
450 | { | ||
451 | // we create room if the fifo was full but it is no longer the case | 422 | // we create room if the fifo was full but it is no longer the case |
452 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit); | 423 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit); |
453 | lua_remove(L_, -2); // fifos fifo | 424 | lua_remove(L_, -2); // L_: fifos fifo |
454 | lua_newtable(L_); // fifos fifo {} | 425 | lua_newtable(L_); // L_: fifos fifo {} |
455 | lua_setiuservalue(L_, -2, kContentsTableIndex); // fifos fifo | 426 | lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos fifo |
456 | fifo->first = 1; | 427 | fifo->first = 1; |
457 | fifo->count = 0; | 428 | fifo->count = 0; |
458 | } | 429 | } |
459 | } | 430 | } |
460 | } | 431 | } else { // set/replace contents stored at the specified key? |
461 | else // set/replace contents stored at the specified key? | 432 | int const count{ lua_gettop(L_) - 2 }; // number of items we want to store |
462 | { | 433 | lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] key |
463 | int const count{ lua_gettop(L_) - 2 }; // number of items we want to store | 434 | lua_rawget(L_, 1); // L_: fifos key [val [, ...]] fifo|nil |
464 | lua_pushvalue(L_, 2); // fifos key [val [, ...]] key | ||
465 | lua_rawget(L_, 1); // fifos key [val [, ...]] fifo|nil | ||
466 | keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) }; | 435 | keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) }; |
467 | if (fifo == nullptr) // can be nullptr if we store a value at a new key | 436 | if (fifo == nullptr) { // can be nullptr if we store a value at a new key // fifos key [val [, ...]] nil |
468 | { // fifos key [val [, ...]] nil | ||
469 | // no need to wake writers in that case, because a writer can't wait on an inexistent key | 437 | // no need to wake writers in that case, because a writer can't wait on an inexistent key |
470 | lua_pop(L_, 1); // fifos key [val [, ...]] | 438 | lua_pop(L_, 1); // L_: fifos key [val [, ...]] |
471 | std::ignore = fifo_new(KeeperState{ L_ }); // fifos key [val [, ...]] fifo | 439 | std::ignore = fifo_new(KeeperState{ L_ }); // L_: fifos key [val [, ...]] fifo |
472 | lua_pushvalue(L_, 2); // fifos key [val [, ...]] fifo key | 440 | lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] fifo key |
473 | lua_pushvalue(L_, -2); // fifos key [val [, ...]] fifo key fifo | 441 | lua_pushvalue(L_, -2); // L_: fifos key [val [, ...]] fifo key fifo |
474 | lua_rawset(L_, 1); // fifos key [val [, ...]] fifo | 442 | lua_rawset(L_, 1); // L_: fifos key [val [, ...]] fifo |
475 | } | 443 | } else { // L_: fifos key [val [, ...]] fifo |
476 | else // the fifo exists, we just want to update its contents | 444 | // the fifo exists, we just want to update its contents |
477 | { // fifos key [val [, ...]] fifo | ||
478 | // we create room if the fifo was full but it is no longer the case | 445 | // we create room if the fifo was full but it is no longer the case |
479 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit) && (count < fifo->limit); | 446 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit) && (count < fifo->limit); |
480 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | 447 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! |
481 | lua_newtable(L_); // fifos key [val [, ...]] fifo {} | 448 | lua_newtable(L_); // L_: fifos key [val [, ...]] fifo {} |
482 | lua_setiuservalue(L_, -2, kContentsTableIndex); // fifos key [val [, ...]] fifo | 449 | lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos key [val [, ...]] fifo |
483 | fifo->first = 1; | 450 | fifo->first = 1; |
484 | fifo->count = 0; | 451 | fifo->count = 0; |
485 | } | 452 | } |
486 | fifo = prepare_fifo_access(L_, -1); // fifos key [val [, ...]] fifotbl | 453 | fifo = prepare_fifo_access(L_, -1); // L_: fifos key [val [, ...]] fifotbl |
487 | // move the fifo below the values we want to store | 454 | // move the fifo below the values we want to store |
488 | lua_insert(L_, 3); // fifos key fifotbl [val [, ...]] | 455 | lua_insert(L_, 3); // L_: fifos key fifotbl [val [, ...]] |
489 | fifo_push(L_, fifo, count); // fifos key fifotbl | 456 | fifo_push(L_, fifo, count); // L_: fifos key fifotbl |
490 | } | 457 | } |
491 | return should_wake_writers ? (lua_pushboolean(L_, 1), 1) : 0; | 458 | return should_wake_writers ? (lua_pushboolean(L_, 1), 1) : 0; |
492 | } | 459 | } |
@@ -498,21 +465,19 @@ int keepercall_set(lua_State* L_) | |||
498 | int keepercall_get(lua_State* L_) | 465 | int keepercall_get(lua_State* L_) |
499 | { | 466 | { |
500 | int count{ 1 }; | 467 | int count{ 1 }; |
501 | if (lua_gettop(L_) == 3) // ud key count | 468 | if (lua_gettop(L_) == 3) { // L_: ud key count |
502 | { | ||
503 | count = static_cast<int>(lua_tointeger(L_, 3)); | 469 | count = static_cast<int>(lua_tointeger(L_, 3)); |
504 | lua_pop(L_, 1); // ud key | 470 | lua_pop(L_, 1); // L_: ud key |
505 | } | 471 | } |
506 | push_table(L_, 1); // ud key fifos | 472 | push_table(L_, 1); // L_: ud key fifos |
507 | lua_replace(L_, 1); // fifos key | 473 | lua_replace(L_, 1); // L_: fifos key |
508 | lua_rawget(L_, 1); // fifos fifo | 474 | lua_rawget(L_, 1); // L_: fifos fifo |
509 | keeper_fifo* const fifo{ prepare_fifo_access(L_, -1) }; // fifos fifotbl | 475 | keeper_fifo* const fifo{ prepare_fifo_access(L_, -1) }; // L_: fifos fifotbl |
510 | if (fifo != nullptr && fifo->count > 0) | 476 | if (fifo != nullptr && fifo->count > 0) { |
511 | { | 477 | lua_remove(L_, 1); // L_: fifotbl |
512 | lua_remove(L_, 1); // fifotbl | ||
513 | count = std::min(count, fifo->count); | 478 | count = std::min(count, fifo->count); |
514 | // read <count> value off the fifo | 479 | // read <count> value off the fifo |
515 | fifo_peek(L_, fifo, count); // fifotbl ... | 480 | fifo_peek(L_, fifo, count); // L_: fifotbl ... |
516 | return count; | 481 | return count; |
517 | } | 482 | } |
518 | // no fifo was ever registered for this key, or it is empty | 483 | // no fifo was ever registered for this key, or it is empty |
@@ -524,65 +489,56 @@ int keepercall_get(lua_State* L_) | |||
524 | // in: linda_ud [, key [, ...]] | 489 | // in: linda_ud [, key [, ...]] |
525 | int keepercall_count(lua_State* L_) | 490 | int keepercall_count(lua_State* L_) |
526 | { | 491 | { |
527 | push_table(L_, 1); // ud keys fifos | 492 | push_table(L_, 1); // L_: ud keys fifos |
528 | switch (lua_gettop(L_)) | 493 | switch (lua_gettop(L_)) { |
529 | { | 494 | // no key is specified: return a table giving the count of all known keys |
530 | // no key is specified: return a table giving the count of all known keys | 495 | case 2: // L_: ud fifos |
531 | case 2: // ud fifos | 496 | lua_newtable(L_); // L_: ud fifos out |
532 | lua_newtable(L_); // ud fifos out | 497 | lua_replace(L_, 1); // L_: out fifos |
533 | lua_replace(L_, 1); // out fifos | 498 | lua_pushnil(L_); // L_: out fifos nil |
534 | lua_pushnil(L_); // out fifos nil | 499 | while (lua_next(L_, 2)) { // L_: out fifos key fifo |
535 | while (lua_next(L_, 2)) // out fifos key fifo | ||
536 | { | ||
537 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; | 500 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; |
538 | lua_pop(L_, 1); // out fifos key | 501 | lua_pop(L_, 1); // L_: out fifos key |
539 | lua_pushvalue(L_, -1); // out fifos key key | 502 | lua_pushvalue(L_, -1); // L_: out fifos key key |
540 | lua_pushinteger(L_, fifo->count); // out fifos key key count | 503 | lua_pushinteger(L_, fifo->count); // L_: out fifos key key count |
541 | lua_rawset(L_, -5); // out fifos key | 504 | lua_rawset(L_, -5); // L_: out fifos key |
542 | } | 505 | } |
543 | lua_pop(L_, 1); // out | 506 | lua_pop(L_, 1); // L_: out |
544 | break; | 507 | break; |
545 | 508 | ||
546 | // 1 key is specified: return its count | 509 | // 1 key is specified: return its count |
547 | case 3: // ud key fifos | 510 | case 3: // L_: ud key fifos |
548 | lua_replace(L_, 1); // fifos key | 511 | lua_replace(L_, 1); // L_: fifos key |
549 | lua_rawget(L_, -2); // fifos fifo|nil | 512 | lua_rawget(L_, -2); // L_: fifos fifo|nil |
550 | if (lua_isnil(L_, -1)) // the key is unknown | 513 | if (lua_isnil(L_, -1)) { // L_: the key is unknown // L_: fifos nil |
551 | { // fifos nil | 514 | lua_remove(L_, -2); // L_: nil |
552 | lua_remove(L_, -2); // nil | 515 | } else { // the key is known // L_: fifos fifo |
553 | } | ||
554 | else // the key is known | ||
555 | { // fifos fifo | ||
556 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; | 516 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; |
557 | lua_pushinteger(L_, fifo->count); // fifos fifo count | 517 | lua_pushinteger(L_, fifo->count); // L_: fifos fifo count |
558 | lua_replace(L_, -3); // count fifo | 518 | lua_replace(L_, -3); // L_: count fifo |
559 | lua_pop(L_, 1); // count | 519 | lua_pop(L_, 1); // L_: count |
560 | } | 520 | } |
561 | break; | 521 | break; |
562 | 522 | ||
563 | // a variable number of keys is specified: return a table of their counts | 523 | // a variable number of keys is specified: return a table of their counts |
564 | default: // ud keys fifos | 524 | default: // ud keys fifos |
565 | lua_newtable(L_); // ud keys... fifos out | 525 | lua_newtable(L_); // L_: ud keys... fifos out |
566 | lua_replace(L_, 1); // out keys... fifos | 526 | lua_replace(L_, 1); // L_: out keys... fifos |
567 | // shifts all keys up in the stack. potentially slow if there are a lot of them, but then it should be bearable | 527 | // shifts all keys up in the stack. potentially slow if there are a lot of them, but then it should be bearable |
568 | lua_insert(L_, 2); // out fifos keys... | 528 | lua_insert(L_, 2); // L_: out fifos keys... |
569 | while (lua_gettop(L_) > 2) | 529 | while (lua_gettop(L_) > 2) { |
570 | { | 530 | lua_pushvalue(L_, -1); // L_: out fifos keys... key |
571 | lua_pushvalue(L_, -1); // out fifos keys... key | 531 | lua_rawget(L_, 2); // L_: out fifos keys... fifo|nil |
572 | lua_rawget(L_, 2); // out fifos keys... fifo|nil | ||
573 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; | 532 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; |
574 | lua_pop(L_, 1); // out fifos keys... | 533 | lua_pop(L_, 1); // L_: out fifos keys... |
575 | if (fifo != nullptr) // the key is known | 534 | if (fifo != nullptr) { // L_: the key is known |
576 | { | 535 | lua_pushinteger(L_, fifo->count); // L_: out fifos keys... count |
577 | lua_pushinteger(L_, fifo->count); // out fifos keys... count | 536 | lua_rawset(L_, 1); // L_: out fifos keys... |
578 | lua_rawset(L_, 1); // out fifos keys... | 537 | } else { // the key is unknown |
538 | lua_pop(L_, 1); // L_: out fifos keys... | ||
579 | } | 539 | } |
580 | else // the key is unknown | 540 | } // all keys are exhausted // L_: out fifos |
581 | { | 541 | lua_pop(L_, 1); // L_: out |
582 | lua_pop(L_, 1); // out fifos keys... | ||
583 | } | ||
584 | } // all keys are exhausted // out fifos | ||
585 | lua_pop(L_, 1); // out | ||
586 | } | 542 | } |
587 | LUA_ASSERT(L_, lua_gettop(L_) == 1); | 543 | LUA_ASSERT(L_, lua_gettop(L_) == 1); |
588 | return 1; | 544 | return 1; |
@@ -592,49 +548,40 @@ int keepercall_count(lua_State* L_) | |||
592 | // Keeper API, accessed from linda methods | 548 | // Keeper API, accessed from linda methods |
593 | // ################################################################################################# | 549 | // ################################################################################################# |
594 | 550 | ||
595 | /*---=== Keeper states ===--- | ||
596 | */ | ||
597 | |||
598 | /* | 551 | /* |
599 | * Pool of keeper states | 552 | * Pool of keeper states |
600 | * | 553 | * |
601 | * Access to keeper states is locked (only one OS thread at a time) so the | 554 | * Access to keeper states is locked (only one OS thread at a time) so the |
602 | * bigger the pool, the less chances of unnecessary waits. Lindas map to the | 555 | * bigger the pool, the less chances of unnecessary waits. Lindas map to the |
603 | * keepers randomly, by a hash. | 556 | * keepers randomly, by a hash. |
604 | */ | 557 | */ |
605 | 558 | ||
606 | // called as __gc for the keepers array userdata | 559 | // called as __gc for the keepers array userdata |
607 | void close_keepers(Universe* U) | 560 | void close_keepers(Universe* U_) |
608 | { | 561 | { |
609 | if (U->keepers != nullptr) | 562 | if (U_->keepers != nullptr) { |
610 | { | 563 | int nbKeepers{ U_->keepers->nb_keepers }; |
611 | int nbKeepers = U->keepers->nb_keepers; | ||
612 | // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it | 564 | // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it |
613 | // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists | 565 | // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists |
614 | // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success | 566 | // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success |
615 | // which is early-outed with a U->keepers->nbKeepers null-check | 567 | // which is early-outed with a U->keepers->nbKeepers null-check |
616 | U->keepers->nb_keepers = 0; | 568 | U_->keepers->nb_keepers = 0; |
617 | for (int i = 0; i < nbKeepers; ++i) | 569 | for (int i = 0; i < nbKeepers; ++i) { |
618 | { | 570 | lua_State* const K{ U_->keepers->keeper_array[i].L }; |
619 | lua_State* K = U->keepers->keeper_array[i].L; | 571 | U_->keepers->keeper_array[i].L = KeeperState{ nullptr }; |
620 | U->keepers->keeper_array[i].L = KeeperState{ nullptr }; | 572 | if (K != nullptr) { |
621 | if (K != nullptr) | ||
622 | { | ||
623 | lua_close(K); | 573 | lua_close(K); |
624 | } | 574 | } else { |
625 | else | ||
626 | { | ||
627 | // detected partial init: destroy only the mutexes that got initialized properly | 575 | // detected partial init: destroy only the mutexes that got initialized properly |
628 | nbKeepers = i; | 576 | nbKeepers = i; |
629 | } | 577 | } |
630 | } | 578 | } |
631 | for (int i = 0; i < nbKeepers; ++i) | 579 | for (int i = 0; i < nbKeepers; ++i) { |
632 | { | 580 | U_->keepers->keeper_array[i].~Keeper(); |
633 | U->keepers->keeper_array[i].~Keeper(); | ||
634 | } | 581 | } |
635 | // free the keeper bookkeeping structure | 582 | // free the keeper bookkeeping structure |
636 | U->internal_allocator.free(U->keepers, sizeof(Keepers) + (nbKeepers - 1) * sizeof(Keeper)); | 583 | U_->internal_allocator.free(U_->keepers, sizeof(Keepers) + (nbKeepers - 1) * sizeof(Keeper)); |
637 | U->keepers = nullptr; | 584 | U_->keepers = nullptr; |
638 | } | 585 | } |
639 | } | 586 | } |
640 | 587 | ||
@@ -651,98 +598,90 @@ void close_keepers(Universe* U) | |||
651 | * function never fails. | 598 | * function never fails. |
652 | * settings table is expected at position 1 on the stack | 599 | * settings table is expected at position 1 on the stack |
653 | */ | 600 | */ |
654 | void init_keepers(Universe* U, lua_State* L_) | 601 | void init_keepers(Universe* U_, lua_State* L_) |
655 | { | 602 | { |
656 | LUA_ASSERT(L_, lua_gettop(L_) == 1 && lua_istable(L_, 1)); | 603 | LUA_ASSERT(L_, lua_gettop(L_) == 1 && lua_istable(L_, 1)); |
657 | STACK_CHECK_START_REL(L_, 0); // L_ K | 604 | STACK_CHECK_START_REL(L_, 0); // L_: settings |
658 | lua_getfield(L_, 1, "nb_keepers"); // settings nb_keepers | 605 | lua_getfield(L_, 1, "nb_keepers"); // L_: settings nb_keepers |
659 | int const nb_keepers{ static_cast<int>(lua_tointeger(L_, -1)) }; | 606 | int const nb_keepers{ static_cast<int>(lua_tointeger(L_, -1)) }; |
660 | lua_pop(L_, 1); // settings | 607 | lua_pop(L_, 1); // L_: settings |
661 | if (nb_keepers < 1) | 608 | if (nb_keepers < 1) { |
662 | { | ||
663 | raise_luaL_error(L_, "Bad number of keepers (%d)", nb_keepers); | 609 | raise_luaL_error(L_, "Bad number of keepers (%d)", nb_keepers); |
664 | } | 610 | } |
665 | STACK_CHECK(L_, 0); | 611 | STACK_CHECK(L_, 0); |
666 | 612 | ||
667 | lua_getfield(L_, 1, "keepers_gc_threshold"); // settings keepers_gc_threshold | 613 | lua_getfield(L_, 1, "keepers_gc_threshold"); // L_: settings keepers_gc_threshold |
668 | int const keepers_gc_threshold{ static_cast<int>(lua_tointeger(L_, -1)) }; | 614 | int const keepers_gc_threshold{ static_cast<int>(lua_tointeger(L_, -1)) }; |
669 | lua_pop(L_, 1); // settings | 615 | lua_pop(L_, 1); // L_: settings |
670 | STACK_CHECK(L_, 0); | 616 | STACK_CHECK(L_, 0); |
671 | 617 | ||
672 | // Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states | 618 | // Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states |
673 | { | 619 | { |
674 | size_t const bytes = sizeof(Keepers) + (nb_keepers - 1) * sizeof(Keeper); | 620 | size_t const bytes = sizeof(Keepers) + (nb_keepers - 1) * sizeof(Keeper); |
675 | U->keepers = static_cast<Keepers*>(U->internal_allocator.alloc(bytes)); | 621 | U_->keepers = static_cast<Keepers*>(U_->internal_allocator.alloc(bytes)); |
676 | if (U->keepers == nullptr) | 622 | if (U_->keepers == nullptr) { |
677 | { | ||
678 | raise_luaL_error(L_, "init_keepers() failed while creating keeper array; out of memory"); | 623 | raise_luaL_error(L_, "init_keepers() failed while creating keeper array; out of memory"); |
679 | } | 624 | } |
680 | U->keepers->Keepers::Keepers(); | 625 | U_->keepers->Keepers::Keepers(); |
681 | U->keepers->gc_threshold = keepers_gc_threshold; | 626 | U_->keepers->gc_threshold = keepers_gc_threshold; |
682 | U->keepers->nb_keepers = nb_keepers; | 627 | U_->keepers->nb_keepers = nb_keepers; |
683 | 628 | ||
684 | for (int i = 0; i < nb_keepers; ++i) | 629 | for (int i = 0; i < nb_keepers; ++i) { |
685 | { | 630 | U_->keepers->keeper_array[i].Keeper::Keeper(); |
686 | U->keepers->keeper_array[i].Keeper::Keeper(); | ||
687 | } | 631 | } |
688 | } | 632 | } |
689 | for (int i = 0; i < nb_keepers; ++i) // settings | 633 | for (int i = 0; i < nb_keepers; ++i) { |
690 | { | ||
691 | // note that we will leak K if we raise an error later | 634 | // note that we will leak K if we raise an error later |
692 | KeeperState const K{ create_state(U, L_) }; | 635 | KeeperState const K{ create_state(U_, L_) }; // L_: settings K: |
693 | if (K == nullptr) | 636 | if (K == nullptr) { |
694 | { | ||
695 | raise_luaL_error(L_, "init_keepers() failed while creating keeper states; out of memory"); | 637 | raise_luaL_error(L_, "init_keepers() failed while creating keeper states; out of memory"); |
696 | } | 638 | } |
697 | 639 | ||
698 | U->keepers->keeper_array[i].L = K; | 640 | U_->keepers->keeper_array[i].L = K; |
699 | 641 | ||
700 | if (U->keepers->gc_threshold >= 0) | 642 | if (U_->keepers->gc_threshold >= 0) { |
701 | { | ||
702 | lua_gc(K, LUA_GCSTOP, 0); | 643 | lua_gc(K, LUA_GCSTOP, 0); |
703 | } | 644 | } |
704 | 645 | ||
705 | STACK_CHECK_START_ABS(K, 0); | 646 | STACK_CHECK_START_ABS(K, 0); |
706 | 647 | ||
707 | // copy the universe pointer in the keeper itself | 648 | // copy the universe pointer in the keeper itself |
708 | universe_store(K, U); | 649 | universe_store(K, U_); |
709 | STACK_CHECK(K, 0); | 650 | STACK_CHECK(K, 0); |
710 | 651 | ||
711 | // make sure 'package' is initialized in keeper states, so that we have require() | 652 | // make sure 'package' is initialized in keeper states, so that we have require() |
712 | // this because this is needed when transferring deep userdata object | 653 | // this because this is needed when transferring deep userdata object |
713 | luaL_requiref(K, "package", luaopen_package, 1); // settings package | 654 | luaL_requiref(K, "package", luaopen_package, 1); // L_: settings K: package |
714 | lua_pop(K, 1); // settings | 655 | lua_pop(K, 1); // L_: settings K: |
715 | STACK_CHECK(K, 0); | 656 | STACK_CHECK(K, 0); |
716 | serialize_require(DEBUGSPEW_PARAM_COMMA(U) K); | 657 | serialize_require(DEBUGSPEW_PARAM_COMMA(U_) K); |
717 | STACK_CHECK(K, 0); | 658 | STACK_CHECK(K, 0); |
718 | 659 | ||
719 | // copy package.path and package.cpath from the source state (TODO: use _R._LOADED.package instead of _G.package) | 660 | // copy package.path and package.cpath from the source state (TODO: use _R._LOADED.package instead of _G.package) |
720 | lua_getglobal(L_, "package"); // settings package | 661 | lua_getglobal(L_, "package"); // L_: settings package K: |
721 | if (!lua_isnil(L_, -1)) | 662 | if (!lua_isnil(L_, -1)) { |
722 | { | ||
723 | // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately | 663 | // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately |
724 | InterCopyContext c{ U, DestState{ K }, SourceState{ L_ }, {}, SourceIndex{ lua_absindex(L_, -1) }, {}, LookupMode::ToKeeper, {} }; | 664 | InterCopyContext c{ U_, DestState{ K }, SourceState{ L_ }, {}, SourceIndex{ lua_absindex(L_, -1) }, {}, LookupMode::ToKeeper, {} }; |
725 | if (c.inter_copy_package() != InterCopyResult::Success) | 665 | if (c.inter_copy_package() != InterCopyResult::Success) { // L_: settings ... error_msg K: |
726 | { | ||
727 | // if something went wrong, the error message is at the top of the stack | 666 | // if something went wrong, the error message is at the top of the stack |
728 | lua_remove(L_, -2); // settings error_msg | 667 | lua_remove(L_, -2); // L_: settings error_msg |
729 | raise_lua_error(L_); | 668 | raise_lua_error(L_); |
730 | } | 669 | } |
731 | } | 670 | } |
732 | lua_pop(L_, 1); // settings | 671 | lua_pop(L_, 1); // L_: settings K: |
733 | STACK_CHECK(L_, 0); | 672 | STACK_CHECK(L_, 0); |
734 | STACK_CHECK(K, 0); | 673 | STACK_CHECK(K, 0); |
735 | 674 | ||
736 | // attempt to call on_state_create(), if we have one and it is a C function | 675 | // attempt to call on_state_create(), if we have one and it is a C function |
737 | // (only support a C function because we can't transfer executable Lua code in keepers) | 676 | // (only support a C function because we can't transfer executable Lua code in keepers) |
738 | // will raise an error in L_ in case of problem | 677 | // will raise an error in L_ in case of problem |
739 | call_on_state_create(U, K, L_, LookupMode::ToKeeper); | 678 | call_on_state_create(U_, K, L_, LookupMode::ToKeeper); |
740 | 679 | ||
741 | // to see VM name in Decoda debugger | 680 | // to see VM name in Decoda debugger |
742 | lua_pushfstring(K, "Keeper #%d", i + 1); // "Keeper #n" | 681 | lua_pushfstring(K, "Keeper #%d", i + 1); // L_: settings K: "Keeper #n" |
743 | lua_setglobal(K, "decoda_name"); // | 682 | lua_setglobal(K, "decoda_name"); // L_: settings K: |
744 | // create the fifos table in the keeper state | 683 | // create the fifos table in the keeper state |
745 | kFifosRegKey.setValue(K, [](lua_State* L_) { lua_newtable(L_); }); | 684 | kFifosRegKey.setValue(K, [](lua_State* L_) { lua_newtable(L_); }); // L_: settings K: |
746 | STACK_CHECK(K, 0); | 685 | STACK_CHECK(K, 0); |
747 | } | 686 | } |
748 | STACK_CHECK(L_, 0); | 687 | STACK_CHECK(L_, 0); |
@@ -754,8 +693,7 @@ Keeper* Linda::acquireKeeper() const | |||
754 | { | 693 | { |
755 | int const nbKeepers{ U->keepers->nb_keepers }; | 694 | int const nbKeepers{ U->keepers->nb_keepers }; |
756 | // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) | 695 | // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) |
757 | if (nbKeepers) | 696 | if (nbKeepers) { |
758 | { | ||
759 | Keeper* const K{ &U->keepers->keeper_array[m_keeper_index] }; | 697 | Keeper* const K{ &U->keepers->keeper_array[m_keeper_index] }; |
760 | K->m_mutex.lock(); | 698 | K->m_mutex.lock(); |
761 | return K; | 699 | return K; |
@@ -767,8 +705,7 @@ Keeper* Linda::acquireKeeper() const | |||
767 | 705 | ||
768 | void Linda::releaseKeeper(Keeper* K_) const | 706 | void Linda::releaseKeeper(Keeper* K_) const |
769 | { | 707 | { |
770 | if (K_) // can be nullptr if we tried to acquire during shutdown | 708 | if (K_) { // can be nullptr if we tried to acquire during shutdown |
771 | { | ||
772 | assert(K_ == &U->keepers->keeper_array[m_keeper_index]); | 709 | assert(K_ == &U->keepers->keeper_array[m_keeper_index]); |
773 | K_->m_mutex.unlock(); | 710 | K_->m_mutex.unlock(); |
774 | } | 711 | } |
@@ -779,20 +716,14 @@ void Linda::releaseKeeper(Keeper* K_) const | |||
779 | void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mode_) | 716 | void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mode_) |
780 | { | 717 | { |
781 | int const n{ lua_gettop(L_) }; | 718 | int const n{ lua_gettop(L_) }; |
782 | for (int i = start_; i <= n; ++i) | 719 | for (int i = start_; i <= n; ++i) { |
783 | { | 720 | if (mode_ == LookupMode::ToKeeper) { |
784 | if (mode_ == LookupMode::ToKeeper) | 721 | if (lua_isnil(L_, i)) { |
785 | { | ||
786 | if (lua_isnil(L_, i)) | ||
787 | { | ||
788 | kNilSentinel.pushKey(L_); | 722 | kNilSentinel.pushKey(L_); |
789 | lua_replace(L_, i); | 723 | lua_replace(L_, i); |
790 | } | 724 | } |
791 | } | 725 | } else { |
792 | else | 726 | if (kNilSentinel.equals(L_, i)) { |
793 | { | ||
794 | if (kNilSentinel.equals(L_, i)) | ||
795 | { | ||
796 | lua_pushnil(L_); | 727 | lua_pushnil(L_); |
797 | lua_replace(L_, i); | 728 | lua_replace(L_, i); |
798 | } | 729 | } |
@@ -803,65 +734,57 @@ void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mod | |||
803 | // ################################################################################################# | 734 | // ################################################################################################# |
804 | 735 | ||
805 | /* | 736 | /* |
806 | * Call a function ('func_name') in the keeper state, and pass on the returned | 737 | * Call a function ('func_name') in the keeper state, and pass on the returned |
807 | * values to 'L'. | 738 | * values to 'L'. |
808 | * | 739 | * |
809 | * 'linda': deep Linda pointer (used only as a unique table key, first parameter) | 740 | * 'linda': deep Linda pointer (used only as a unique table key, first parameter) |
810 | * 'starting_index': first of the rest of parameters (none if 0) | 741 | * 'starting_index': first of the rest of parameters (none if 0) |
811 | * | 742 | * |
812 | * Returns: number of return values (pushed to 'L'), unset in case of error | 743 | * Returns: number of return values (pushed to 'L'), unset in case of error |
813 | */ | 744 | */ |
814 | KeeperCallResult keeper_call(Universe* U, KeeperState K, keeper_api_t func_, lua_State* L_, void* linda_, int starting_index_) | 745 | KeeperCallResult keeper_call(Universe* U_, KeeperState K_, keeper_api_t func_, lua_State* L_, void* linda_, int starting_index_) |
815 | { | 746 | { |
816 | KeeperCallResult result; | 747 | KeeperCallResult result; |
817 | int const args{ starting_index_ ? (lua_gettop(L_) - starting_index_ + 1) : 0 }; | 748 | int const args{ starting_index_ ? (lua_gettop(L_) - starting_index_ + 1) : 0 }; // L: ... args... K_: |
818 | int const top_K{ lua_gettop(K) }; | 749 | int const top_K{ lua_gettop(K_) }; |
819 | // if we didn't do anything wrong, the keeper stack should be clean | 750 | // if we didn't do anything wrong, the keeper stack should be clean |
820 | LUA_ASSERT(L_, lua_gettop(K) == 0); | 751 | LUA_ASSERT(L_, top_K == 0); |
821 | 752 | ||
822 | STACK_GROW(K, 2); | 753 | STACK_GROW(K_, 2); |
823 | PUSH_KEEPER_FUNC(K, func_); // func_ | 754 | PUSH_KEEPER_FUNC(K_, func_); // L: ... args... K_: func_ |
824 | lua_pushlightuserdata(K, linda_); // func_ linda | 755 | lua_pushlightuserdata(K_, linda_); // L: ... args... K_: func_ linda |
825 | if ( | 756 | if ( |
826 | (args == 0) || | 757 | (args == 0) || |
827 | (InterCopyContext{ U, DestState{ K }, SourceState{ L_ }, {}, {}, {}, LookupMode::ToKeeper, {} }.inter_copy(args) == InterCopyResult::Success) | 758 | (InterCopyContext{ U_, DestState{ K_ }, SourceState{ L_ }, {}, {}, {}, LookupMode::ToKeeper, {} }.inter_copy(args) == InterCopyResult::Success) |
828 | ) | 759 | ) { // L: ... args... K_: func_ linda args... |
829 | { // func_ linda args... | 760 | lua_call(K_, 1 + args, LUA_MULTRET); // L: ... args... K_: result... |
830 | lua_call(K, 1 + args, LUA_MULTRET); // result... | 761 | int const retvals{ lua_gettop(K_) - top_K }; |
831 | int const retvals{ lua_gettop(K) - top_K }; | ||
832 | // note that this can raise a lua error while the keeper state (and its mutex) is acquired | 762 | // note that this can raise a lua error while the keeper state (and its mutex) is acquired |
833 | // this may interrupt a lane, causing the destruction of the underlying OS thread | 763 | // this may interrupt a lane, causing the destruction of the underlying OS thread |
834 | // after this, another lane making use of this keeper can get an error code from the mutex-locking function | 764 | // after this, another lane making use of this keeper can get an error code from the mutex-locking function |
835 | // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) | 765 | // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) |
836 | if ( | 766 | if ( |
837 | (retvals == 0) || | 767 | (retvals == 0) || |
838 | (InterCopyContext{ U, DestState{ L_ }, SourceState{ K }, {}, {}, {}, LookupMode::FromKeeper, {} }.inter_move(retvals) == InterCopyResult::Success) | 768 | (InterCopyContext{ U_, DestState{ L_ }, SourceState{ K_ }, {}, {}, {}, LookupMode::FromKeeper, {} }.inter_move(retvals) == InterCopyResult::Success) |
839 | ) // K->L_ | 769 | ) { // L: ... args... result... K_: result... |
840 | { | ||
841 | result.emplace(retvals); | 770 | result.emplace(retvals); |
842 | } | 771 | } |
843 | } | 772 | } |
844 | // whatever happens, restore the stack to where it was at the origin | 773 | // whatever happens, restore the stack to where it was at the origin |
845 | lua_settop(K, top_K); | 774 | lua_settop(K_, top_K); // L: ... args... result... K_: |
846 | 775 | ||
847 | // don't do this for this particular function, as it is only called during Linda destruction, and we don't want to raise an error, ever | 776 | // don't do this for this particular function, as it is only called during Linda destruction, and we don't want to raise an error, ever |
848 | if (func_ != KEEPER_API(clear)) [[unlikely]] | 777 | if (func_ != KEEPER_API(clear)) [[unlikely]] { |
849 | { | ||
850 | // since keeper state GC is stopped, let's run a step once in a while if required | 778 | // since keeper state GC is stopped, let's run a step once in a while if required |
851 | int const gc_threshold{ U->keepers->gc_threshold }; | 779 | int const gc_threshold{ U_->keepers->gc_threshold }; |
852 | if (gc_threshold == 0) [[unlikely]] | 780 | if (gc_threshold == 0) [[unlikely]] { |
853 | { | 781 | lua_gc(K_, LUA_GCSTEP, 0); |
854 | lua_gc(K, LUA_GCSTEP, 0); | 782 | } else if (gc_threshold > 0) [[likely]] { |
855 | } | 783 | int const gc_usage{ lua_gc(K_, LUA_GCCOUNT, 0) }; |
856 | else if (gc_threshold > 0) [[likely]] | 784 | if (gc_usage >= gc_threshold) { |
857 | { | 785 | lua_gc(K_, LUA_GCCOLLECT, 0); |
858 | int const gc_usage{ lua_gc(K, LUA_GCCOUNT, 0) }; | 786 | int const gc_usage_after{ lua_gc(K_, LUA_GCCOUNT, 0) }; |
859 | if (gc_usage >= gc_threshold) | 787 | if (gc_usage_after > gc_threshold) [[unlikely]] { |
860 | { | ||
861 | lua_gc(K, LUA_GCCOLLECT, 0); | ||
862 | int const gc_usage_after{ lua_gc(K, LUA_GCCOUNT, 0) }; | ||
863 | if (gc_usage_after > gc_threshold) [[unlikely]] | ||
864 | { | ||
865 | raise_luaL_error(L_, "Keeper GC threshold is too low, need at least %d", gc_usage_after); | 788 | raise_luaL_error(L_, "Keeper GC threshold is too low, need at least %d", gc_usage_after); |
866 | } | 789 | } |
867 | } | 790 | } |
diff --git a/src/keeper.h b/src/keeper.h index bf1ba17..275d134 100644 --- a/src/keeper.h +++ b/src/keeper.h | |||
@@ -39,8 +39,8 @@ struct Keepers | |||
39 | // xxh64 of string "kNilSentinel" generated at https://www.pelock.com/products/hash-calculator | 39 | // xxh64 of string "kNilSentinel" generated at https://www.pelock.com/products/hash-calculator |
40 | static constexpr UniqueKey kNilSentinel{ 0xC457D4EDDB05B5E4ull, "lanes.null" }; | 40 | static constexpr UniqueKey kNilSentinel{ 0xC457D4EDDB05B5E4ull, "lanes.null" }; |
41 | 41 | ||
42 | void init_keepers(Universe* U, lua_State* L_); | 42 | void init_keepers(Universe* U_, lua_State* L_); |
43 | void close_keepers(Universe* U); | 43 | void close_keepers(Universe* U_); |
44 | 44 | ||
45 | void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mode_); | 45 | void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mode_); |
46 | [[nodiscard]] int keeper_push_linda_storage(Linda& linda_, DestState L_); | 46 | [[nodiscard]] int keeper_push_linda_storage(Linda& linda_, DestState L_); |
@@ -59,4 +59,4 @@ using keeper_api_t = lua_CFunction; | |||
59 | [[nodiscard]] int keepercall_count(lua_State* L_); | 59 | [[nodiscard]] int keepercall_count(lua_State* L_); |
60 | 60 | ||
61 | using KeeperCallResult = Unique<std::optional<int>>; | 61 | using KeeperCallResult = Unique<std::optional<int>>; |
62 | [[nodiscard]] KeeperCallResult keeper_call(Universe* U, KeeperState K, keeper_api_t _func, lua_State* L_, void* linda, int starting_index); | 62 | [[nodiscard]] KeeperCallResult keeper_call(Universe* U_, KeeperState K_, keeper_api_t func_, lua_State* L_, void* linda_, int starting_index_); |
diff --git a/src/lanes.cpp b/src/lanes.cpp index 16c8e47..91a2f8b 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp | |||
@@ -3,7 +3,7 @@ | |||
3 | * Copyright (C) 2009-24, Benoit Germain | 3 | * Copyright (C) 2009-24, Benoit Germain |
4 | * | 4 | * |
5 | * Multithreading in Lua. | 5 | * Multithreading in Lua. |
6 | * | 6 | * |
7 | * History: | 7 | * History: |
8 | * See CHANGES | 8 | * See CHANGES |
9 | * | 9 | * |
@@ -90,13 +90,13 @@ THE SOFTWARE. | |||
90 | #include "universe.h" | 90 | #include "universe.h" |
91 | 91 | ||
92 | #if !(defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC)) | 92 | #if !(defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC)) |
93 | # include <sys/time.h> | 93 | #include <sys/time.h> |
94 | #endif | 94 | #endif |
95 | 95 | ||
96 | /* geteuid() */ | 96 | /* geteuid() */ |
97 | #ifdef PLATFORM_LINUX | 97 | #ifdef PLATFORM_LINUX |
98 | # include <unistd.h> | 98 | #include <unistd.h> |
99 | # include <sys/types.h> | 99 | #include <sys/types.h> |
100 | #endif | 100 | #endif |
101 | 101 | ||
102 | #include <atomic> | 102 | #include <atomic> |
@@ -107,7 +107,7 @@ THE SOFTWARE. | |||
107 | 107 | ||
108 | // The chain is ended by '(Lane*)(-1)', not nullptr: | 108 | // The chain is ended by '(Lane*)(-1)', not nullptr: |
109 | // 'tracking_first -> ... -> ... -> (-1)' | 109 | // 'tracking_first -> ... -> ... -> (-1)' |
110 | #define TRACKING_END ((Lane *)(-1)) | 110 | #define TRACKING_END ((Lane*) (-1)) |
111 | 111 | ||
112 | /* | 112 | /* |
113 | * Add the lane to tracking chain; the ones still running at the end of the | 113 | * Add the lane to tracking chain; the ones still running at the end of the |
@@ -135,14 +135,11 @@ static void tracking_add(Lane* lane_) | |||
135 | // still (at process exit they will remove us from chain and then | 135 | // still (at process exit they will remove us from chain and then |
136 | // cancel/kill). | 136 | // cancel/kill). |
137 | // | 137 | // |
138 | if (lane_->tracking_next != nullptr) | 138 | if (lane_->tracking_next != nullptr) { |
139 | { | ||
140 | Lane** ref = (Lane**) &lane_->U->tracking_first; | 139 | Lane** ref = (Lane**) &lane_->U->tracking_first; |
141 | 140 | ||
142 | while( *ref != TRACKING_END) | 141 | while (*ref != TRACKING_END) { |
143 | { | 142 | if (*ref == lane_) { |
144 | if (*ref == lane_) | ||
145 | { | ||
146 | *ref = lane_->tracking_next; | 143 | *ref = lane_->tracking_next; |
147 | lane_->tracking_next = nullptr; | 144 | lane_->tracking_next = nullptr; |
148 | found = true; | 145 | found = true; |
@@ -150,7 +147,7 @@ static void tracking_add(Lane* lane_) | |||
150 | } | 147 | } |
151 | ref = (Lane**) &((*ref)->tracking_next); | 148 | ref = (Lane**) &((*ref)->tracking_next); |
152 | } | 149 | } |
153 | assert( found); | 150 | assert(found); |
154 | } | 151 | } |
155 | return found; | 152 | return found; |
156 | } | 153 | } |
@@ -164,41 +161,44 @@ Lane::Lane(Universe* U_, lua_State* L_) | |||
164 | , L{ L_ } | 161 | , L{ L_ } |
165 | { | 162 | { |
166 | #if HAVE_LANE_TRACKING() | 163 | #if HAVE_LANE_TRACKING() |
167 | if (U->tracking_first) | 164 | if (U->tracking_first) { |
168 | { | ||
169 | tracking_add(this); | 165 | tracking_add(this); |
170 | } | 166 | } |
171 | #endif // HAVE_LANE_TRACKING() | 167 | #endif // HAVE_LANE_TRACKING() |
172 | } | 168 | } |
173 | 169 | ||
170 | // ################################################################################################# | ||
171 | |||
174 | bool Lane::waitForCompletion(lua_Duration duration_) | 172 | bool Lane::waitForCompletion(lua_Duration duration_) |
175 | { | 173 | { |
176 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | 174 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; |
177 | if (duration_.count() >= 0.0) | 175 | if (duration_.count() >= 0.0) { |
178 | { | ||
179 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration_); | 176 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration_); |
180 | } | 177 | } |
181 | 178 | ||
182 | std::unique_lock lock{ m_done_mutex }; | 179 | std::unique_lock lock{ m_done_mutex }; |
183 | //std::stop_token token{ m_thread.get_stop_token() }; | 180 | // std::stop_token token{ m_thread.get_stop_token() }; |
184 | //return m_done_signal.wait_until(lock, token, secs_, [this](){ return m_status >= Lane::Done; }); | 181 | // return m_done_signal.wait_until(lock, token, secs_, [this](){ return m_status >= Lane::Done; }); |
185 | return m_done_signal.wait_until(lock, until, [this](){ return m_status >= Lane::Done; }); | 182 | return m_done_signal.wait_until(lock, until, [this]() { return m_status >= Lane::Done; }); |
186 | } | 183 | } |
187 | 184 | ||
185 | // ################################################################################################# | ||
186 | |||
188 | static void lane_main(Lane* lane); | 187 | static void lane_main(Lane* lane); |
189 | void Lane::startThread(int priority_) | 188 | void Lane::startThread(int priority_) |
190 | { | 189 | { |
191 | m_thread = std::jthread([this]() { lane_main(this); }); | 190 | m_thread = std::jthread([this]() { lane_main(this); }); |
192 | if (priority_ != kThreadPrioDefault) | 191 | if (priority_ != kThreadPrioDefault) { |
193 | { | ||
194 | JTHREAD_SET_PRIORITY(m_thread, priority_, U->m_sudo); | 192 | JTHREAD_SET_PRIORITY(m_thread, priority_, U->m_sudo); |
195 | } | 193 | } |
196 | } | 194 | } |
197 | 195 | ||
196 | // ################################################################################################# | ||
197 | |||
198 | /* Do you want full call stacks, or just the line where the error happened? | 198 | /* Do you want full call stacks, or just the line where the error happened? |
199 | * | 199 | * |
200 | * TBD: The full stack feature does not seem to work (try 'make error'). | 200 | * TBD: The full stack feature does not seem to work (try 'make error'). |
201 | */ | 201 | */ |
202 | #define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it! | 202 | #define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it! |
203 | 203 | ||
204 | // intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed | 204 | // intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed |
@@ -216,6 +216,8 @@ static void securize_debug_threadname(lua_State* L_, Lane* lane_) | |||
216 | STACK_CHECK(L_, 0); | 216 | STACK_CHECK(L_, 0); |
217 | } | 217 | } |
218 | 218 | ||
219 | // ################################################################################################# | ||
220 | |||
219 | #if ERROR_FULL_STACK | 221 | #if ERROR_FULL_STACK |
220 | [[nodiscard]] static int lane_error(lua_State* L_); | 222 | [[nodiscard]] static int lane_error(lua_State* L_); |
221 | // xxh64 of string "kStackTraceRegKey" generated at https://www.pelock.com/products/hash-calculator | 223 | // xxh64 of string "kStackTraceRegKey" generated at https://www.pelock.com/products/hash-calculator |
@@ -223,13 +225,13 @@ static constexpr RegistryUniqueKey kStackTraceRegKey{ 0x3F327747CACAA904ull }; | |||
223 | #endif // ERROR_FULL_STACK | 225 | #endif // ERROR_FULL_STACK |
224 | 226 | ||
225 | /* | 227 | /* |
226 | * registry[FINALIZER_REG_KEY] is either nil (no finalizers) or a table | 228 | * registry[FINALIZER_REG_KEY] is either nil (no finalizers) or a table |
227 | * of functions that Lanes will call after the executing 'pcall' has ended. | 229 | * of functions that Lanes will call after the executing 'pcall' has ended. |
228 | * | 230 | * |
229 | * We're NOT using the GC system for finalizer mainly because providing the | 231 | * We're NOT using the GC system for finalizer mainly because providing the |
230 | * error (and maybe stack trace) parameters to the finalizer functions would | 232 | * error (and maybe stack trace) parameters to the finalizer functions would |
231 | * anyways complicate that approach. | 233 | * anyways complicate that approach. |
232 | */ | 234 | */ |
233 | // xxh64 of string "kFinalizerRegKey" generated at https://www.pelock.com/products/hash-calculator | 235 | // xxh64 of string "kFinalizerRegKey" generated at https://www.pelock.com/products/hash-calculator |
234 | static constexpr RegistryUniqueKey kFinalizerRegKey{ 0xFE936BFAA718FEEAull }; | 236 | static constexpr RegistryUniqueKey kFinalizerRegKey{ 0xFE936BFAA718FEEAull }; |
235 | 237 | ||
@@ -240,8 +242,7 @@ Lane::~Lane() | |||
240 | // Clean up after a (finished) thread | 242 | // Clean up after a (finished) thread |
241 | // | 243 | // |
242 | #if HAVE_LANE_TRACKING() | 244 | #if HAVE_LANE_TRACKING() |
243 | if (U->tracking_first != nullptr) | 245 | if (U->tracking_first != nullptr) { |
244 | { | ||
245 | // Lane was cleaned up, no need to handle at process termination | 246 | // Lane was cleaned up, no need to handle at process termination |
246 | std::ignore = tracking_remove(this); | 247 | std::ignore = tracking_remove(this); |
247 | } | 248 | } |
@@ -252,7 +253,6 @@ Lane::~Lane() | |||
252 | // ########################################## Finalizer ############################################ | 253 | // ########################################## Finalizer ############################################ |
253 | // ################################################################################################# | 254 | // ################################################################################################# |
254 | 255 | ||
255 | |||
256 | // Push the finalizers table on the stack. | 256 | // Push the finalizers table on the stack. |
257 | // If there is no existing table, create ti. | 257 | // If there is no existing table, create ti. |
258 | static void push_finalizers_table(lua_State* L_) | 258 | static void push_finalizers_table(lua_State* L_) |
@@ -260,20 +260,18 @@ static void push_finalizers_table(lua_State* L_) | |||
260 | STACK_GROW(L_, 3); | 260 | STACK_GROW(L_, 3); |
261 | STACK_CHECK_START_REL(L_, 0); | 261 | STACK_CHECK_START_REL(L_, 0); |
262 | 262 | ||
263 | kFinalizerRegKey.pushValue(L_); // ? | 263 | kFinalizerRegKey.pushValue(L_); // L_: ? |
264 | if (lua_isnil(L_, -1)) // nil? | 264 | if (lua_isnil(L_, -1)) { // L_: nil? |
265 | { | 265 | lua_pop(L_, 1); // L_: |
266 | lua_pop(L_, 1); // | ||
267 | // store a newly created table in the registry, but leave it on the stack too | 266 | // store a newly created table in the registry, but leave it on the stack too |
268 | lua_newtable(L_); // t | 267 | lua_newtable(L_); // L_: t |
269 | kFinalizerRegKey.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); // t | 268 | kFinalizerRegKey.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); // L_: t |
270 | } | 269 | } |
271 | STACK_CHECK(L_, 1); | 270 | STACK_CHECK(L_, 1); |
272 | } | 271 | } |
273 | 272 | ||
274 | // ################################################################################################# | 273 | // ################################################################################################# |
275 | 274 | ||
276 | //--- | ||
277 | // void= finalizer( finalizer_func ) | 275 | // void= finalizer( finalizer_func ) |
278 | // | 276 | // |
279 | // finalizer_func( [err, stack_tbl] ) | 277 | // finalizer_func( [err, stack_tbl] ) |
@@ -286,12 +284,12 @@ LUAG_FUNC(set_finalizer) | |||
286 | luaL_argcheck(L_, lua_isfunction(L_, 1), 1, "finalizer should be a function"); | 284 | luaL_argcheck(L_, lua_isfunction(L_, 1), 1, "finalizer should be a function"); |
287 | luaL_argcheck(L_, lua_gettop(L_) == 1, 1, "too many arguments"); | 285 | luaL_argcheck(L_, lua_gettop(L_) == 1, 1, "too many arguments"); |
288 | // Get the current finalizer table (if any), create one if it doesn't exist | 286 | // Get the current finalizer table (if any), create one if it doesn't exist |
289 | push_finalizers_table(L_); // finalizer {finalisers} | 287 | push_finalizers_table(L_); // L_: finalizer {finalisers} |
290 | STACK_GROW(L_, 2); | 288 | STACK_GROW(L_, 2); |
291 | lua_pushinteger(L_, lua_rawlen(L_, -1) + 1); // finalizer {finalisers} idx | 289 | lua_pushinteger(L_, lua_rawlen(L_, -1) + 1); // L_: finalizer {finalisers} idx |
292 | lua_pushvalue(L_, 1); // finalizer {finalisers} idx finalizer | 290 | lua_pushvalue(L_, 1); // L_: finalizer {finalisers} idx finalizer |
293 | lua_rawset(L_, -3); // finalizer {finalisers} | 291 | lua_rawset(L_, -3); // L_: finalizer {finalisers} |
294 | lua_pop(L_, 2); | 292 | lua_pop(L_, 2); // L_: |
295 | return 0; | 293 | return 0; |
296 | } | 294 | } |
297 | 295 | ||
@@ -300,19 +298,18 @@ LUAG_FUNC(set_finalizer) | |||
300 | static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) | 298 | static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) |
301 | { | 299 | { |
302 | // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry | 300 | // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry |
303 | switch(rc_) | 301 | switch (rc_) { |
304 | { | 302 | case LUA_OK: // no error, body return values are on the stack |
305 | case LUA_OK: // no error, body return values are on the stack | ||
306 | break; | 303 | break; |
307 | 304 | ||
308 | case LUA_ERRRUN: // cancellation or a runtime error | 305 | case LUA_ERRRUN: // cancellation or a runtime error |
309 | #if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler | 306 | #if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler |
310 | { | 307 | { |
311 | STACK_CHECK_START_REL(L_, 0); | 308 | STACK_CHECK_START_REL(L_, 0); |
312 | // fetch the call stack table from the registry where the handler stored it | 309 | // fetch the call stack table from the registry where the handler stored it |
313 | STACK_GROW(L_, 1); | 310 | STACK_GROW(L_, 1); |
314 | // yields nil if no stack was generated (in case of cancellation for example) | 311 | // yields nil if no stack was generated (in case of cancellation for example) |
315 | kStackTraceRegKey.pushValue(L_); // err trace|nil | 312 | kStackTraceRegKey.pushValue(L_); // L_: err trace|nil |
316 | STACK_CHECK(L_, 1); | 313 | STACK_CHECK(L_, 1); |
317 | 314 | ||
318 | // For cancellation the error message is kCancelError, and a stack trace isn't placed | 315 | // For cancellation the error message is kCancelError, and a stack trace isn't placed |
@@ -321,11 +318,13 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) | |||
321 | // Just leaving the stack trace table on the stack is enough to get it through to the master. | 318 | // Just leaving the stack trace table on the stack is enough to get it through to the master. |
322 | break; | 319 | break; |
323 | } | 320 | } |
324 | #endif // fall through if not ERROR_FULL_STACK | 321 | #else // !ERROR_FULL_STACK |
322 | [[fallthrough]]; // fall through if not ERROR_FULL_STACK | ||
323 | #endif // !ERROR_FULL_STACK | ||
325 | 324 | ||
326 | case LUA_ERRMEM: // memory allocation error (handler not called) | 325 | case LUA_ERRMEM: // memory allocation error (handler not called) |
327 | case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition) | 326 | case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition) |
328 | default: | 327 | default: |
329 | // we should have a single value which is either a string (the error message) or kCancelError | 328 | // we should have a single value which is either a string (the error message) or kCancelError |
330 | LUA_ASSERT(L_, (lua_gettop(L_) == stk_base_) && ((lua_type(L_, stk_base_) == LUA_TSTRING) || kCancelError.equals(L_, stk_base_))); | 329 | LUA_ASSERT(L_, (lua_gettop(L_) == stk_base_) && ((lua_type(L_, stk_base_) == LUA_TSTRING) || kCancelError.equals(L_, stk_base_))); |
331 | break; | 330 | break; |
@@ -349,11 +348,10 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) | |||
349 | 348 | ||
350 | [[nodiscard]] static int run_finalizers(lua_State* L_, int lua_rc_) | 349 | [[nodiscard]] static int run_finalizers(lua_State* L_, int lua_rc_) |
351 | { | 350 | { |
352 | kFinalizerRegKey.pushValue(L_); // ... finalizers? | 351 | kFinalizerRegKey.pushValue(L_); // L_: ... finalizers? |
353 | if (lua_isnil(L_, -1)) | 352 | if (lua_isnil(L_, -1)) { |
354 | { | ||
355 | lua_pop(L_, 1); | 353 | lua_pop(L_, 1); |
356 | return 0; // no finalizers | 354 | return 0; // no finalizers |
357 | } | 355 | } |
358 | 356 | ||
359 | STACK_GROW(L_, 5); | 357 | STACK_GROW(L_, 5); |
@@ -362,68 +360,58 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) | |||
362 | int const err_handler_index{ ERROR_FULL_STACK ? (lua_pushcfunction(L_, lane_error), lua_gettop(L_)) : 0 }; | 360 | int const err_handler_index{ ERROR_FULL_STACK ? (lua_pushcfunction(L_, lane_error), lua_gettop(L_)) : 0 }; |
363 | 361 | ||
364 | int rc{ LUA_OK }; | 362 | int rc{ LUA_OK }; |
365 | for (int n = static_cast<int>(lua_rawlen(L_, finalizers_index)); n > 0; --n) | 363 | for (int n = static_cast<int>(lua_rawlen(L_, finalizers_index)); n > 0; --n) { |
366 | { | ||
367 | int args = 0; | 364 | int args = 0; |
368 | lua_pushinteger(L_, n); // ... finalizers lane_error n | 365 | lua_pushinteger(L_, n); // L_: ... finalizers lane_error n |
369 | lua_rawget(L_, finalizers_index); // ... finalizers lane_error finalizer | 366 | lua_rawget(L_, finalizers_index); // L_: ... finalizers lane_error finalizer |
370 | LUA_ASSERT(L_, lua_isfunction(L_, -1)); | 367 | LUA_ASSERT(L_, lua_isfunction(L_, -1)); |
371 | if (lua_rc_ != LUA_OK) // we have an error message and an optional stack trace at the bottom of the stack | 368 | if (lua_rc_ != LUA_OK) { // we have an error message and an optional stack trace at the bottom of the stack |
372 | { | 369 | LUA_ASSERT(L_, finalizers_index == 2 || finalizers_index == 3); |
373 | LUA_ASSERT(L_, finalizers_index == 2 || finalizers_index == 3); | 370 | // char const* err_msg = lua_tostring(L_, 1); |
374 | //char const* err_msg = lua_tostring(L_, 1); | 371 | lua_pushvalue(L_, 1); // L_: ... finalizers lane_error finalizer err_msg |
375 | lua_pushvalue(L_, 1); // ... finalizers lane_error finalizer err_msg | ||
376 | // note we don't always have a stack trace for example when kCancelError, or when we got an error that doesn't call our handler, such as LUA_ERRMEM | 372 | // note we don't always have a stack trace for example when kCancelError, or when we got an error that doesn't call our handler, such as LUA_ERRMEM |
377 | if (finalizers_index == 3) | 373 | if (finalizers_index == 3) { |
378 | { | 374 | lua_pushvalue(L_, 2); // L_: ... finalizers lane_error finalizer err_msg stack_trace |
379 | lua_pushvalue(L_, 2); // ... finalizers lane_error finalizer err_msg stack_trace | ||
380 | } | 375 | } |
381 | args = finalizers_index - 1; | 376 | args = finalizers_index - 1; |
382 | } | 377 | } |
383 | 378 | ||
384 | // if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace | 379 | // if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace |
385 | rc = lua_pcall(L_, args, 0, err_handler_index); // ... finalizers lane_error err_msg2? | 380 | rc = lua_pcall(L_, args, 0, err_handler_index); // L_: ... finalizers lane_error err_msg2? |
386 | if (rc != LUA_OK) | 381 | if (rc != LUA_OK) { |
387 | { | 382 | push_stack_trace(L_, rc, lua_gettop(L_)); // L_: ... finalizers lane_error err_msg2? trace |
388 | push_stack_trace(L_, rc, lua_gettop(L_)); | ||
389 | // If one finalizer fails, don't run the others. Return this | 383 | // If one finalizer fails, don't run the others. Return this |
390 | // as the 'real' error, replacing what we could have had (or not) | 384 | // as the 'real' error, replacing what we could have had (or not) |
391 | // from the actual code. | 385 | // from the actual code. |
392 | break; | 386 | break; |
393 | } | 387 | } |
394 | // no error, proceed to next finalizer // ... finalizers lane_error | 388 | // no error, proceed to next finalizer // L_: ... finalizers lane_error |
395 | } | 389 | } |
396 | 390 | ||
397 | if (rc != LUA_OK) | 391 | if (rc != LUA_OK) { |
398 | { | ||
399 | // ERROR_FULL_STACK accounts for the presence of lane_error on the stack | 392 | // ERROR_FULL_STACK accounts for the presence of lane_error on the stack |
400 | int const nb_err_slots{ lua_gettop(L_) - finalizers_index - ERROR_FULL_STACK }; | 393 | int const nb_err_slots{ lua_gettop(L_) - finalizers_index - ERROR_FULL_STACK }; |
401 | // a finalizer generated an error, this is what we leave of the stack | 394 | // a finalizer generated an error, this is what we leave of the stack |
402 | for (int n = nb_err_slots; n > 0; --n) | 395 | for (int n = nb_err_slots; n > 0; --n) { |
403 | { | ||
404 | lua_replace(L_, n); | 396 | lua_replace(L_, n); |
405 | } | 397 | } |
406 | // leave on the stack only the error and optional stack trace produced by the error in the finalizer | 398 | // leave on the stack only the error and optional stack trace produced by the error in the finalizer |
407 | lua_settop(L_, nb_err_slots); | 399 | lua_settop(L_, nb_err_slots); // L_: ... lane_error trace |
408 | } | 400 | } else { // no error from the finalizers, make sure only the original return values from the lane body remain on the stack |
409 | else // no error from the finalizers, make sure only the original return values from the lane body remain on the stack | ||
410 | { | ||
411 | lua_settop(L_, finalizers_index - 1); | 401 | lua_settop(L_, finalizers_index - 1); |
412 | } | 402 | } |
413 | 403 | ||
414 | return rc; | 404 | return rc; |
415 | } | 405 | } |
416 | 406 | ||
417 | /* | 407 | // ################################################################################################# |
418 | * ################################################################################################ | 408 | // ########################################### Threads ############################################# |
419 | * ########################################### Threads ############################################ | 409 | // ################################################################################################# |
420 | * ################################################################################################ | ||
421 | */ | ||
422 | 410 | ||
423 | // | 411 | // |
424 | // Protects modifying the selfdestruct chain | 412 | // Protects modifying the selfdestruct chain |
425 | 413 | ||
426 | #define SELFDESTRUCT_END ((Lane*)(-1)) | 414 | #define SELFDESTRUCT_END ((Lane*) (-1)) |
427 | // | 415 | // |
428 | // The chain is ended by '(Lane*)(-1)', not nullptr: | 416 | // The chain is ended by '(Lane*)(-1)', not nullptr: |
429 | // 'selfdestruct_first -> ... -> ... -> (-1)' | 417 | // 'selfdestruct_first -> ... -> ... -> (-1)' |
@@ -443,9 +431,7 @@ static void selfdestruct_add(Lane* lane_) | |||
443 | 431 | ||
444 | // ################################################################################################# | 432 | // ################################################################################################# |
445 | 433 | ||
446 | /* | 434 | // A free-running lane has ended; remove it from selfdestruct chain |
447 | * A free-running lane has ended; remove it from selfdestruct chain | ||
448 | */ | ||
449 | [[nodiscard]] static bool selfdestruct_remove(Lane* lane_) | 435 | [[nodiscard]] static bool selfdestruct_remove(Lane* lane_) |
450 | { | 436 | { |
451 | bool found{ false }; | 437 | bool found{ false }; |
@@ -454,14 +440,11 @@ static void selfdestruct_add(Lane* lane_) | |||
454 | // still (at process exit they will remove us from chain and then | 440 | // still (at process exit they will remove us from chain and then |
455 | // cancel/kill). | 441 | // cancel/kill). |
456 | // | 442 | // |
457 | if (lane_->selfdestruct_next != nullptr) | 443 | if (lane_->selfdestruct_next != nullptr) { |
458 | { | ||
459 | Lane** ref = (Lane**) &lane_->U->selfdestruct_first; | 444 | Lane** ref = (Lane**) &lane_->U->selfdestruct_first; |
460 | 445 | ||
461 | while (*ref != SELFDESTRUCT_END) | 446 | while (*ref != SELFDESTRUCT_END) { |
462 | { | 447 | if (*ref == lane_) { |
463 | if (*ref == lane_) | ||
464 | { | ||
465 | *ref = lane_->selfdestruct_next; | 448 | *ref = lane_->selfdestruct_next; |
466 | lane_->selfdestruct_next = nullptr; | 449 | lane_->selfdestruct_next = nullptr; |
467 | // the terminal shutdown should wait until the lane is done with its lua_close() | 450 | // the terminal shutdown should wait until the lane is done with its lua_close() |
@@ -478,9 +461,7 @@ static void selfdestruct_add(Lane* lane_) | |||
478 | 461 | ||
479 | // ################################################################################################# | 462 | // ################################################################################################# |
480 | 463 | ||
481 | /* | 464 | // process end: cancel any still free-running threads |
482 | * Process end; cancel any still free-running threads | ||
483 | */ | ||
484 | [[nodiscard]] static int universe_gc(lua_State* L_) | 465 | [[nodiscard]] static int universe_gc(lua_State* L_) |
485 | { | 466 | { |
486 | Universe* const U{ lua_tofulluserdata<Universe>(L_, 1) }; | 467 | Universe* const U{ lua_tofulluserdata<Universe>(L_, 1) }; |
@@ -488,35 +469,28 @@ static void selfdestruct_add(Lane* lane_) | |||
488 | [[maybe_unused]] char const* const op_string{ lua_tostring(L_, lua_upvalueindex(2)) }; | 469 | [[maybe_unused]] char const* const op_string{ lua_tostring(L_, lua_upvalueindex(2)) }; |
489 | CancelOp const op{ which_cancel_op(op_string) }; | 470 | CancelOp const op{ which_cancel_op(op_string) }; |
490 | 471 | ||
491 | if (U->selfdestruct_first != SELFDESTRUCT_END) | 472 | if (U->selfdestruct_first != SELFDESTRUCT_END) { |
492 | { | ||
493 | |||
494 | // Signal _all_ still running threads to exit (including the timer thread) | 473 | // Signal _all_ still running threads to exit (including the timer thread) |
495 | // | ||
496 | { | 474 | { |
497 | std::lock_guard<std::mutex> guard{ U->selfdestruct_cs }; | 475 | std::lock_guard<std::mutex> guard{ U->selfdestruct_cs }; |
498 | Lane* lane{ U->selfdestruct_first }; | 476 | Lane* lane{ U->selfdestruct_first }; |
499 | lua_Duration timeout{ 1us }; | 477 | lua_Duration timeout{ 1us }; |
500 | while (lane != SELFDESTRUCT_END) | 478 | while (lane != SELFDESTRUCT_END) { |
501 | { | ||
502 | // attempt the requested cancel with a small timeout. | 479 | // attempt the requested cancel with a small timeout. |
503 | // if waiting on a linda, they will raise a cancel_error. | 480 | // if waiting on a linda, they will raise a cancel_error. |
504 | // if a cancellation hook is desired, it will be installed to try to raise an error | 481 | // if a cancellation hook is desired, it will be installed to try to raise an error |
505 | if (lane->m_thread.joinable()) | 482 | if (lane->m_thread.joinable()) { |
506 | { | ||
507 | std::ignore = thread_cancel(lane, op, 1, timeout, true); | 483 | std::ignore = thread_cancel(lane, op, 1, timeout, true); |
508 | } | 484 | } |
509 | lane = lane->selfdestruct_next; | 485 | lane = lane->selfdestruct_next; |
510 | } | 486 | } |
511 | } | 487 | } |
512 | 488 | ||
513 | // When noticing their cancel, the lanes will remove themselves from | 489 | // When noticing their cancel, the lanes will remove themselves from the selfdestruct chain. |
514 | // the selfdestruct chain. | ||
515 | { | 490 | { |
516 | std::chrono::time_point<std::chrono::steady_clock> t_until{ std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(shutdown_timeout) }; | 491 | std::chrono::time_point<std::chrono::steady_clock> t_until{ std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(shutdown_timeout) }; |
517 | 492 | ||
518 | while (U->selfdestruct_first != SELFDESTRUCT_END) | 493 | while (U->selfdestruct_first != SELFDESTRUCT_END) { |
519 | { | ||
520 | // give threads time to act on their cancel | 494 | // give threads time to act on their cancel |
521 | std::this_thread::yield(); | 495 | std::this_thread::yield(); |
522 | // count the number of cancelled thread that didn't have the time to act yet | 496 | // count the number of cancelled thread that didn't have the time to act yet |
@@ -524,8 +498,7 @@ static void selfdestruct_add(Lane* lane_) | |||
524 | { | 498 | { |
525 | std::lock_guard<std::mutex> guard{ U->selfdestruct_cs }; | 499 | std::lock_guard<std::mutex> guard{ U->selfdestruct_cs }; |
526 | Lane* lane{ U->selfdestruct_first }; | 500 | Lane* lane{ U->selfdestruct_first }; |
527 | while (lane != SELFDESTRUCT_END) | 501 | while (lane != SELFDESTRUCT_END) { |
528 | { | ||
529 | if (lane->cancel_request != CancelRequest::None) | 502 | if (lane->cancel_request != CancelRequest::None) |
530 | ++n; | 503 | ++n; |
531 | lane = lane->selfdestruct_next; | 504 | lane = lane->selfdestruct_next; |
@@ -533,8 +506,7 @@ static void selfdestruct_add(Lane* lane_) | |||
533 | } | 506 | } |
534 | // if timeout elapsed, or we know all threads have acted, stop waiting | 507 | // if timeout elapsed, or we know all threads have acted, stop waiting |
535 | std::chrono::time_point<std::chrono::steady_clock> t_now = std::chrono::steady_clock::now(); | 508 | std::chrono::time_point<std::chrono::steady_clock> t_now = std::chrono::steady_clock::now(); |
536 | if (n == 0 || (t_now >= t_until)) | 509 | if (n == 0 || (t_now >= t_until)) { |
537 | { | ||
538 | DEBUGSPEW_CODE(fprintf(stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout.count())); | 510 | DEBUGSPEW_CODE(fprintf(stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout.count())); |
539 | break; | 511 | break; |
540 | } | 512 | } |
@@ -543,8 +515,7 @@ static void selfdestruct_add(Lane* lane_) | |||
543 | 515 | ||
544 | // If some lanes are currently cleaning after themselves, wait until they are done. | 516 | // If some lanes are currently cleaning after themselves, wait until they are done. |
545 | // They are no longer listed in the selfdestruct chain, but they still have to lua_close(). | 517 | // They are no longer listed in the selfdestruct chain, but they still have to lua_close(). |
546 | while (U->selfdestructing_count.load(std::memory_order_acquire) > 0) | 518 | while (U->selfdestructing_count.load(std::memory_order_acquire) > 0) { |
547 | { | ||
548 | std::this_thread::yield(); | 519 | std::this_thread::yield(); |
549 | } | 520 | } |
550 | } | 521 | } |
@@ -553,16 +524,14 @@ static void selfdestruct_add(Lane* lane_) | |||
553 | { | 524 | { |
554 | std::lock_guard<std::mutex> guard{ U->selfdestruct_cs }; | 525 | std::lock_guard<std::mutex> guard{ U->selfdestruct_cs }; |
555 | Lane* lane{ U->selfdestruct_first }; | 526 | Lane* lane{ U->selfdestruct_first }; |
556 | if (lane != SELFDESTRUCT_END) | 527 | if (lane != SELFDESTRUCT_END) { |
557 | { | ||
558 | // this causes a leak because we don't call U's destructor (which could be bad if the still running lanes are accessing it) | 528 | // this causes a leak because we don't call U's destructor (which could be bad if the still running lanes are accessing it) |
559 | raise_luaL_error(L_, "Zombie thread %s refuses to die!", lane->debug_name); | 529 | raise_luaL_error(L_, "Zombie thread %s refuses to die!", lane->debug_name); |
560 | } | 530 | } |
561 | } | 531 | } |
562 | 532 | ||
563 | // no need to mutex-protect this as all threads in the universe are gone at that point | 533 | // no need to mutex-protect this as all threads in the universe are gone at that point |
564 | if (U->timer_deep != nullptr) // test ins case some early internal error prevented Lanes from creating the deep timer | 534 | if (U->timer_deep != nullptr) { // test ins case some early internal error prevented Lanes from creating the deep timer |
565 | { | ||
566 | [[maybe_unused]] int const prev_ref_count{ U->timer_deep->m_refcount.fetch_sub(1, std::memory_order_relaxed) }; | 535 | [[maybe_unused]] int const prev_ref_count{ U->timer_deep->m_refcount.fetch_sub(1, std::memory_order_relaxed) }; |
567 | LUA_ASSERT(L_, prev_ref_count == 1); // this should be the last reference | 536 | LUA_ASSERT(L_, prev_ref_count == 1); // this should be the last reference |
568 | DeepFactory::DeleteDeepObject(L_, U->timer_deep); | 537 | DeepFactory::DeleteDeepObject(L_, U->timer_deep); |
@@ -591,18 +560,17 @@ static void selfdestruct_add(Lane* lane_) | |||
591 | // Limits the process to use only 'cores' CPU cores. To be used for performance | 560 | // Limits the process to use only 'cores' CPU cores. To be used for performance |
592 | // testing on multicore devices. DEBUGGING ONLY! | 561 | // testing on multicore devices. DEBUGGING ONLY! |
593 | // | 562 | // |
594 | LUAG_FUNC( set_singlethreaded) | 563 | LUAG_FUNC(set_singlethreaded) |
595 | { | 564 | { |
596 | [[maybe_unused]] lua_Integer const cores{ luaL_optinteger(L_, 1, 1) }; | 565 | [[maybe_unused]] lua_Integer const cores{ luaL_optinteger(L_, 1, 1) }; |
597 | 566 | ||
598 | #ifdef PLATFORM_OSX | 567 | #ifdef PLATFORM_OSX |
599 | #ifdef _UTILBINDTHREADTOCPU | 568 | #ifdef _UTILBINDTHREADTOCPU |
600 | if (cores > 1) | 569 | if (cores > 1) { |
601 | { | ||
602 | raise_luaL_error(L_, "Limiting to N>1 cores not possible"); | 570 | raise_luaL_error(L_, "Limiting to N>1 cores not possible"); |
603 | } | 571 | } |
604 | // requires 'chudInitialize()' | 572 | // requires 'chudInitialize()' |
605 | utilBindThreadToCPU(0); // # of CPU to run on (we cannot limit to 2..N CPUs?) | 573 | utilBindThreadToCPU(0); // # of CPU to run on (we cannot limit to 2..N CPUs?) |
606 | return 0; | 574 | return 0; |
607 | #else | 575 | #else |
608 | raise_luaL_error(L_, "Not available: compile with _UTILBINDTHREADTOCPU"); | 576 | raise_luaL_error(L_, "Not available: compile with _UTILBINDTHREADTOCPU"); |
@@ -615,35 +583,34 @@ LUAG_FUNC( set_singlethreaded) | |||
615 | // ################################################################################################# | 583 | // ################################################################################################# |
616 | 584 | ||
617 | /* | 585 | /* |
618 | * str= lane_error( error_val|str ) | 586 | * str= lane_error( error_val|str ) |
619 | * | 587 | * |
620 | * Called if there's an error in some lane; add call stack to error message | 588 | * Called if there's an error in some lane; add call stack to error message |
621 | * just like 'lua.c' normally does. | 589 | * just like 'lua.c' normally does. |
622 | * | 590 | * |
623 | * ".. will be called with the error message and its return value will be the | 591 | * ".. will be called with the error message and its return value will be the |
624 | * message returned on the stack by lua_pcall." | 592 | * message returned on the stack by lua_pcall." |
625 | * | 593 | * |
626 | * Note: Rather than modifying the error message itself, it would be better | 594 | * Note: Rather than modifying the error message itself, it would be better |
627 | * to provide the call stack (as string) completely separated. This would | 595 | * to provide the call stack (as string) completely separated. This would |
628 | * work great with non-string error values as well (current system does not). | 596 | * work great with non-string error values as well (current system does not). |
629 | * (This is NOT possible with the Lua 5.1 'lua_pcall()'; we could of course | 597 | * (This is NOT possible with the Lua 5.1 'lua_pcall()'; we could of course |
630 | * implement a Lanes-specific 'pcall' of our own that does this). TBD!!! :) | 598 | * implement a Lanes-specific 'pcall' of our own that does this). TBD!!! :) |
631 | * --AKa 22-Jan-2009 | 599 | * --AKa 22-Jan-2009 |
632 | */ | 600 | */ |
633 | #if ERROR_FULL_STACK | 601 | #if ERROR_FULL_STACK |
634 | 602 | ||
635 | // xxh64 of string "kExtendedStackTraceRegKey" generated at https://www.pelock.com/products/hash-calculator | 603 | // xxh64 of string "kExtendedStackTraceRegKey" generated at https://www.pelock.com/products/hash-calculator |
636 | static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2ull }; // used as registry key | 604 | static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2ull }; // used as registry key |
637 | 605 | ||
638 | LUAG_FUNC( set_error_reporting) | 606 | LUAG_FUNC(set_error_reporting) |
639 | { | 607 | { |
640 | luaL_checktype(L_, 1, LUA_TSTRING); | 608 | luaL_checktype(L_, 1, LUA_TSTRING); |
641 | char const* mode{ lua_tostring(L_, 1) }; | 609 | char const* mode{ lua_tostring(L_, 1) }; |
642 | lua_pushliteral(L_, "extended"); | 610 | lua_pushliteral(L_, "extended"); |
643 | bool const extended{ strcmp(mode, "extended") == 0 }; | 611 | bool const extended{ strcmp(mode, "extended") == 0 }; |
644 | bool const basic{ strcmp(mode, "basic") == 0 }; | 612 | bool const basic{ strcmp(mode, "basic") == 0 }; |
645 | if (!extended && !basic) | 613 | if (!extended && !basic) { |
646 | { | ||
647 | raise_luaL_error(L_, "unsupported error reporting model %s", mode); | 614 | raise_luaL_error(L_, "unsupported error reporting model %s", mode); |
648 | } | 615 | } |
649 | 616 | ||
@@ -651,16 +618,17 @@ LUAG_FUNC( set_error_reporting) | |||
651 | return 0; | 618 | return 0; |
652 | } | 619 | } |
653 | 620 | ||
621 | // ################################################################################################# | ||
622 | |||
654 | [[nodiscard]] static int lane_error(lua_State* L_) | 623 | [[nodiscard]] static int lane_error(lua_State* L_) |
655 | { | 624 | { |
656 | // error message (any type) | 625 | // error message (any type) |
657 | STACK_CHECK_START_ABS(L_, 1); // some_error | 626 | STACK_CHECK_START_ABS(L_, 1); // L_: some_error |
658 | 627 | ||
659 | // Don't do stack survey for cancelled lanes. | 628 | // Don't do stack survey for cancelled lanes. |
660 | // | 629 | // |
661 | if (kCancelError.equals(L_, 1)) | 630 | if (kCancelError.equals(L_, 1)) { |
662 | { | 631 | return 1; // just pass on |
663 | return 1; // just pass on | ||
664 | } | 632 | } |
665 | 633 | ||
666 | STACK_GROW(L_, 3); | 634 | STACK_GROW(L_, 3); |
@@ -677,48 +645,42 @@ LUAG_FUNC( set_error_reporting) | |||
677 | // | 645 | // |
678 | // table of { "sourcefile.lua:<line>", ... } | 646 | // table of { "sourcefile.lua:<line>", ... } |
679 | // | 647 | // |
680 | lua_newtable(L_); // some_error {} | 648 | lua_newtable(L_); // L_: some_error {} |
681 | 649 | ||
682 | // Best to start from level 1, but in some cases it might be a C function | 650 | // Best to start from level 1, but in some cases it might be a C function |
683 | // and we don't get '.currentline' for that. It's okay - just keep level | 651 | // and we don't get '.currentline' for that. It's okay - just keep level |
684 | // and table index growing separate. --AKa 22-Jan-2009 | 652 | // and table index growing separate. --AKa 22-Jan-2009 |
685 | // | 653 | // |
686 | lua_Debug ar; | 654 | lua_Debug ar; |
687 | for (int n = 1; lua_getstack(L_, n, &ar); ++n) | 655 | for (int n = 1; lua_getstack(L_, n, &ar); ++n) { |
688 | { | ||
689 | lua_getinfo(L_, extended ? "Sln" : "Sl", &ar); | 656 | lua_getinfo(L_, extended ? "Sln" : "Sl", &ar); |
690 | if (extended) | 657 | if (extended) { |
691 | { | 658 | lua_newtable(L_); // L_: some_error {} {} |
692 | lua_newtable(L_); // some_error {} {} | ||
693 | 659 | ||
694 | lua_pushstring(L_, ar.source); // some_error {} {} source | 660 | lua_pushstring(L_, ar.source); // L_: some_error {} {} source |
695 | lua_setfield(L_, -2, "source"); // some_error {} {} | 661 | lua_setfield(L_, -2, "source"); // L_: some_error {} {} |
696 | 662 | ||
697 | lua_pushinteger(L_, ar.currentline); // some_error {} {} currentline | 663 | lua_pushinteger(L_, ar.currentline); // L_: some_error {} {} currentline |
698 | lua_setfield(L_, -2, "currentline"); // some_error {} {} | 664 | lua_setfield(L_, -2, "currentline"); // L_: some_error {} {} |
699 | 665 | ||
700 | lua_pushstring(L_, ar.name); // some_error {} {} name | 666 | lua_pushstring(L_, ar.name); // L_: some_error {} {} name |
701 | lua_setfield(L_, -2, "name"); // some_error {} {} | 667 | lua_setfield(L_, -2, "name"); // L_: some_error {} {} |
702 | 668 | ||
703 | lua_pushstring(L_, ar.namewhat); // some_error {} {} namewhat | 669 | lua_pushstring(L_, ar.namewhat); // L_: some_error {} {} namewhat |
704 | lua_setfield(L_, -2, "namewhat"); // some_error {} {} | 670 | lua_setfield(L_, -2, "namewhat"); // L_: some_error {} {} |
705 | 671 | ||
706 | lua_pushstring(L_, ar.what); // some_error {} {} what | 672 | lua_pushstring(L_, ar.what); // L_: some_error {} {} what |
707 | lua_setfield(L_, -2, "what"); // some_error {} {} | 673 | lua_setfield(L_, -2, "what"); // L_: some_error {} {} |
708 | } | 674 | } else if (ar.currentline > 0) { |
709 | else if (ar.currentline > 0) | 675 | lua_pushfstring(L_, "%s:%d", ar.short_src, ar.currentline); // L_: some_error {} "blah:blah" |
710 | { | 676 | } else { |
711 | lua_pushfstring(L_, "%s:%d", ar.short_src, ar.currentline); // some_error {} "blah:blah" | 677 | lua_pushfstring(L_, "%s:?", ar.short_src); // L_: some_error {} "blah" |
712 | } | 678 | } |
713 | else | 679 | lua_rawseti(L_, -2, (lua_Integer) n); // L_: some_error {} |
714 | { | ||
715 | lua_pushfstring(L_, "%s:?", ar.short_src); // some_error {} "blah" | ||
716 | } | ||
717 | lua_rawseti(L_, -2, (lua_Integer) n); // some_error {} | ||
718 | } | 680 | } |
719 | 681 | ||
720 | // store the stack trace table in the registry | 682 | // store the stack trace table in the registry |
721 | kStackTraceRegKey.setValue(L_, [](lua_State* L_) { lua_insert(L_, -2); }); // some_error | 683 | kStackTraceRegKey.setValue(L_, [](lua_State* L_) { lua_insert(L_, -2); }); // L_: some_error |
722 | 684 | ||
723 | STACK_CHECK(L_, 1); | 685 | STACK_CHECK(L_, 1); |
724 | return 1; // the untouched error value | 686 | return 1; // the untouched error value |
@@ -766,8 +728,7 @@ LUAG_FUNC(set_thread_priority) | |||
766 | // public Lanes API accepts a generic range -3/+3 | 728 | // public Lanes API accepts a generic range -3/+3 |
767 | // that will be remapped into the platform-specific scheduler priority scheme | 729 | // that will be remapped into the platform-specific scheduler priority scheme |
768 | // On some platforms, -3 is equivalent to -2 and +3 to +2 | 730 | // On some platforms, -3 is equivalent to -2 and +3 to +2 |
769 | if (prio < kThreadPrioMin || prio > kThreadPrioMax) | 731 | if (prio < kThreadPrioMin || prio > kThreadPrioMax) { |
770 | { | ||
771 | raise_luaL_error(L_, "priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, prio); | 732 | raise_luaL_error(L_, "priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, prio); |
772 | } | 733 | } |
773 | THREAD_SET_PRIORITY(static_cast<int>(prio), universe_get(L_)->m_sudo); | 734 | THREAD_SET_PRIORITY(static_cast<int>(prio), universe_get(L_)->m_sudo); |
@@ -779,14 +740,15 @@ LUAG_FUNC(set_thread_priority) | |||
779 | LUAG_FUNC(set_thread_affinity) | 740 | LUAG_FUNC(set_thread_affinity) |
780 | { | 741 | { |
781 | lua_Integer const affinity{ luaL_checkinteger(L_, 1) }; | 742 | lua_Integer const affinity{ luaL_checkinteger(L_, 1) }; |
782 | if (affinity <= 0) | 743 | if (affinity <= 0) { |
783 | { | ||
784 | raise_luaL_error(L_, "invalid affinity (%d)", affinity); | 744 | raise_luaL_error(L_, "invalid affinity (%d)", affinity); |
785 | } | 745 | } |
786 | THREAD_SET_AFFINITY( static_cast<unsigned int>(affinity)); | 746 | THREAD_SET_AFFINITY(static_cast<unsigned int>(affinity)); |
787 | return 0; | 747 | return 0; |
788 | } | 748 | } |
789 | 749 | ||
750 | // ################################################################################################# | ||
751 | |||
790 | #if USE_DEBUG_SPEW() | 752 | #if USE_DEBUG_SPEW() |
791 | // can't use direct LUA_x errcode indexing because the sequence is not the same between Lua 5.1 and 5.2 :-( | 753 | // can't use direct LUA_x errcode indexing because the sequence is not the same between Lua 5.1 and 5.2 :-( |
792 | // LUA_ERRERR doesn't have the same value | 754 | // LUA_ERRERR doesn't have the same value |
@@ -796,22 +758,19 @@ struct errcode_name | |||
796 | char const* name; | 758 | char const* name; |
797 | }; | 759 | }; |
798 | 760 | ||
799 | static struct errcode_name s_errcodes[] = | 761 | static struct errcode_name s_errcodes[] = { |
800 | { | 762 | { LUA_OK, "LUA_OK" }, |
801 | { LUA_OK, "LUA_OK"}, | 763 | { LUA_YIELD, "LUA_YIELD" }, |
802 | { LUA_YIELD, "LUA_YIELD"}, | 764 | { LUA_ERRRUN, "LUA_ERRRUN" }, |
803 | { LUA_ERRRUN, "LUA_ERRRUN"}, | 765 | { LUA_ERRSYNTAX, "LUA_ERRSYNTAX" }, |
804 | { LUA_ERRSYNTAX, "LUA_ERRSYNTAX"}, | 766 | { LUA_ERRMEM, "LUA_ERRMEM" }, |
805 | { LUA_ERRMEM, "LUA_ERRMEM"}, | 767 | { LUA_ERRGCMM, "LUA_ERRGCMM" }, |
806 | { LUA_ERRGCMM, "LUA_ERRGCMM"}, | 768 | { LUA_ERRERR, "LUA_ERRERR" }, |
807 | { LUA_ERRERR, "LUA_ERRERR"}, | ||
808 | }; | 769 | }; |
809 | static char const* get_errcode_name( int _code) | 770 | static char const* get_errcode_name(int _code) |
810 | { | 771 | { |
811 | for (int i{ 0 }; i < 7; ++i) | 772 | for (int i{ 0 }; i < 7; ++i) { |
812 | { | 773 | if (s_errcodes[i].code == _code) { |
813 | if (s_errcodes[i].code == _code) | ||
814 | { | ||
815 | return s_errcodes[i].name; | 774 | return s_errcodes[i].name; |
816 | } | 775 | } |
817 | } | 776 | } |
@@ -819,14 +778,15 @@ static char const* get_errcode_name( int _code) | |||
819 | } | 778 | } |
820 | #endif // USE_DEBUG_SPEW() | 779 | #endif // USE_DEBUG_SPEW() |
821 | 780 | ||
781 | // ################################################################################################# | ||
782 | |||
822 | static void lane_main(Lane* lane_) | 783 | static void lane_main(Lane* lane_) |
823 | { | 784 | { |
824 | lua_State* const L{ lane_->L }; | 785 | lua_State* const L{ lane_->L }; |
825 | // wait until the launching thread has finished preparing L | 786 | // wait until the launching thread has finished preparing L |
826 | lane_->m_ready.wait(); | 787 | lane_->m_ready.wait(); |
827 | int rc{ LUA_ERRRUN }; | 788 | int rc{ LUA_ERRRUN }; |
828 | if (lane_->m_status == Lane::Pending) // nothing wrong happened during preparation, we can work | 789 | if (lane_->m_status == Lane::Pending) { // nothing wrong happened during preparation, we can work |
829 | { | ||
830 | // At this point, the lane function and arguments are on the stack | 790 | // At this point, the lane function and arguments are on the stack |
831 | int const nargs{ lua_gettop(L) - 1 }; | 791 | int const nargs{ lua_gettop(L) - 1 }; |
832 | DEBUGSPEW_CODE(Universe* U = universe_get(L)); | 792 | DEBUGSPEW_CODE(Universe* U = universe_get(L)); |
@@ -856,32 +816,30 @@ static void lane_main(Lane* lane_) | |||
856 | lua_setglobal(L, "set_error_reporting"); | 816 | lua_setglobal(L, "set_error_reporting"); |
857 | 817 | ||
858 | STACK_GROW(L, 1); | 818 | STACK_GROW(L, 1); |
859 | lua_pushcfunction(L, lane_error); // func args handler | 819 | lua_pushcfunction(L, lane_error); // L: func args handler |
860 | lua_insert(L, 1); // handler func args | 820 | lua_insert(L, 1); // L: handler func args |
861 | #endif // ERROR_FULL_STACK | 821 | #endif // L: ERROR_FULL_STACK |
862 | 822 | ||
863 | rc = lua_pcall(L, nargs, LUA_MULTRET, ERROR_FULL_STACK); // retvals|err | 823 | rc = lua_pcall(L, nargs, LUA_MULTRET, ERROR_FULL_STACK); // L: retvals|err |
864 | 824 | ||
865 | #if ERROR_FULL_STACK | 825 | #if ERROR_FULL_STACK |
866 | lua_remove(L, 1); // retvals|error | 826 | lua_remove(L, 1); // L: retvals|error |
867 | #endif // ERROR_FULL_STACK | 827 | #endif // ERROR_FULL_STACK |
868 | 828 | ||
869 | // in case of error and if it exists, fetch stack trace from registry and push it | 829 | // in case of error and if it exists, fetch stack trace from registry and push it |
870 | push_stack_trace(L, rc, 1); // retvals|error [trace] | 830 | push_stack_trace(L, rc, 1); // L: retvals|error [trace] |
871 | 831 | ||
872 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name(rc), kCancelError.equals(L, 1) ? "cancelled" : lua_typename(L, lua_type(L, 1)))); | 832 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END(U), L, get_errcode_name(rc), kCancelError.equals(L, 1) ? "cancelled" : lua_typename(L, lua_type(L, 1)))); |
873 | // Call finalizers, if the script has set them up. | 833 | // Call finalizers, if the script has set them up. |
874 | // | 834 | // |
875 | int rc2{ run_finalizers(L, rc) }; | 835 | int rc2{ run_finalizers(L, rc) }; |
876 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name(rc2))); | 836 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END(U), L, get_errcode_name(rc2))); |
877 | if (rc2 != LUA_OK) // Error within a finalizer! | 837 | if (rc2 != LUA_OK) { // Error within a finalizer! |
878 | { | ||
879 | // the finalizer generated an error, and left its own error message [and stack trace] on the stack | 838 | // the finalizer generated an error, and left its own error message [and stack trace] on the stack |
880 | rc = rc2; // we're overruling the earlier script error or normal return | 839 | rc = rc2; // we're overruling the earlier script error or normal return |
881 | } | 840 | } |
882 | lane_->m_waiting_on = nullptr; // just in case | 841 | lane_->m_waiting_on = nullptr; // just in case |
883 | if (selfdestruct_remove(lane_)) // check and remove (under lock!) | 842 | if (selfdestruct_remove(lane_)) { // check and remove (under lock!) |
884 | { | ||
885 | // We're a free-running thread and no-one's there to clean us up. | 843 | // We're a free-running thread and no-one's there to clean us up. |
886 | lua_close(lane_->L); | 844 | lua_close(lane_->L); |
887 | lane_->L = nullptr; // just in case | 845 | lane_->L = nullptr; // just in case |
@@ -896,17 +854,16 @@ static void lane_main(Lane* lane_) | |||
896 | lane_ = nullptr; | 854 | lane_ = nullptr; |
897 | } | 855 | } |
898 | } | 856 | } |
899 | if (lane_) | 857 | if (lane_) { |
900 | { | ||
901 | // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them | 858 | // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them |
902 | 859 | ||
903 | Lane::Status st = (rc == LUA_OK) ? Lane::Done : kCancelError.equals(L, 1) ? Lane::Cancelled : Lane::Error; | 860 | Lane::Status const st = (rc == LUA_OK) ? Lane::Done : kCancelError.equals(L, 1) ? Lane::Cancelled : Lane::Error; |
904 | 861 | ||
905 | { | 862 | { |
906 | // 'm_done_mutex' protects the -> Done|Error|Cancelled state change | 863 | // 'm_done_mutex' protects the -> Done|Error|Cancelled state change |
907 | std::lock_guard lock{ lane_->m_done_mutex }; | 864 | std::lock_guard lock{ lane_->m_done_mutex }; |
908 | lane_->m_status = st; | 865 | lane_->m_status = st; |
909 | lane_->m_done_signal.notify_one();// wake up master (while 'lane_->m_done_mutex' is on) | 866 | lane_->m_done_signal.notify_one(); // wake up master (while 'lane_->m_done_mutex' is on) |
910 | } | 867 | } |
911 | } | 868 | } |
912 | } | 869 | } |
@@ -919,17 +876,17 @@ static void lane_main(Lane* lane_) | |||
919 | // upvalue[1]: _G.require | 876 | // upvalue[1]: _G.require |
920 | LUAG_FUNC(require) | 877 | LUAG_FUNC(require) |
921 | { | 878 | { |
922 | char const* name = lua_tostring(L_, 1); | 879 | char const* name = lua_tostring(L_, 1); // L_: "name" ... |
923 | int const nargs = lua_gettop(L_); | 880 | int const nargs{ lua_gettop(L_) }; |
924 | DEBUGSPEW_CODE(Universe* U = universe_get(L_)); | 881 | DEBUGSPEW_CODE(Universe* U = universe_get(L_)); |
925 | STACK_CHECK_START_REL(L_, 0); | 882 | STACK_CHECK_START_REL(L_, 0); |
926 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END, name)); | 883 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END(U), name)); |
927 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 884 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
928 | lua_pushvalue(L_, lua_upvalueindex(1)); // "name" require | 885 | lua_pushvalue(L_, lua_upvalueindex(1)); // L_: "name" ... require |
929 | lua_insert(L_, 1); // require "name" | 886 | lua_insert(L_, 1); // L_: require "name" ... |
930 | lua_call(L_, nargs, 1); // module | 887 | lua_call(L_, nargs, 1); // L_: module |
931 | populate_func_lookup_table(L_, -1, name); | 888 | populate_func_lookup_table(L_, -1, name); |
932 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END, name)); | 889 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END(U), name)); |
933 | STACK_CHECK(L_, 0); | 890 | STACK_CHECK(L_, 0); |
934 | return 1; | 891 | return 1; |
935 | } | 892 | } |
@@ -948,10 +905,10 @@ LUAG_FUNC(register) | |||
948 | luaL_argcheck(L_, (mod_type == LuaType::TABLE) || (mod_type == LuaType::FUNCTION), 2, "unexpected module type"); | 905 | luaL_argcheck(L_, (mod_type == LuaType::TABLE) || (mod_type == LuaType::FUNCTION), 2, "unexpected module type"); |
949 | DEBUGSPEW_CODE(Universe* U = universe_get(L_)); | 906 | DEBUGSPEW_CODE(Universe* U = universe_get(L_)); |
950 | STACK_CHECK_START_REL(L_, 0); // "name" mod_table | 907 | STACK_CHECK_START_REL(L_, 0); // "name" mod_table |
951 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END, name)); | 908 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END(U), name)); |
952 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 909 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
953 | populate_func_lookup_table(L_, -1, name); | 910 | populate_func_lookup_table(L_, -1, name); |
954 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END, name)); | 911 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END(U), name)); |
955 | STACK_CHECK(L_, 0); | 912 | STACK_CHECK(L_, 0); |
956 | return 0; | 913 | return 0; |
957 | } | 914 | } |
@@ -975,6 +932,7 @@ static constexpr UniqueKey kLaneGC{ 0x5D6122141727F960ull }; | |||
975 | // | 932 | // |
976 | LUAG_FUNC(lane_new) | 933 | LUAG_FUNC(lane_new) |
977 | { | 934 | { |
935 | // first 7 args: func libs priority globals package required gc_cb | ||
978 | char const* const libs_str{ lua_tostring(L_, 2) }; | 936 | char const* const libs_str{ lua_tostring(L_, 2) }; |
979 | bool const have_priority{ !lua_isnoneornil(L_, 3) }; | 937 | bool const have_priority{ !lua_isnoneornil(L_, 3) }; |
980 | int const priority{ have_priority ? static_cast<int>(lua_tointeger(L_, 3)) : kThreadPrioDefault }; | 938 | int const priority{ have_priority ? static_cast<int>(lua_tointeger(L_, 3)) : kThreadPrioDefault }; |
@@ -991,28 +949,26 @@ LUAG_FUNC(lane_new) | |||
991 | // public Lanes API accepts a generic range -3/+3 | 949 | // public Lanes API accepts a generic range -3/+3 |
992 | // that will be remapped into the platform-specific scheduler priority scheme | 950 | // that will be remapped into the platform-specific scheduler priority scheme |
993 | // On some platforms, -3 is equivalent to -2 and +3 to +2 | 951 | // On some platforms, -3 is equivalent to -2 and +3 to +2 |
994 | if (have_priority && (priority < kThreadPrioMin || priority > kThreadPrioMax)) | 952 | if (have_priority && (priority < kThreadPrioMin || priority > kThreadPrioMax)) { |
995 | { | ||
996 | raise_luaL_error(L_, "Priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, priority); | 953 | raise_luaL_error(L_, "Priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, priority); |
997 | } | 954 | } |
998 | 955 | ||
999 | /* --- Create and prepare the sub state --- */ | 956 | /* --- Create and prepare the sub state --- */ |
1000 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END)); | 957 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END(U))); |
1001 | 958 | ||
1002 | // populate with selected libraries at the same time | 959 | // populate with selected libraries at the same time. |
1003 | lua_State* const L2{ luaG_newstate(U, SourceState{ L_ }, libs_str) }; // L // L2 | 960 | lua_State* const L2{ luaG_newstate(U, SourceState{ L_ }, libs_str) }; // L_: [7 args] ... L2: |
961 | STACK_CHECK_START_REL(L2, 0); | ||
1004 | 962 | ||
1005 | // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) | 963 | // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) |
1006 | Lane* const lane{ new (U) Lane{ U, L2 } }; | 964 | Lane* const lane{ new (U) Lane{ U, L2 } }; |
1007 | if (lane == nullptr) | 965 | if (lane == nullptr) { |
1008 | { | ||
1009 | raise_luaL_error(L_, "could not create lane: out of memory"); | 966 | raise_luaL_error(L_, "could not create lane: out of memory"); |
1010 | } | 967 | } |
1011 | 968 | ||
1012 | class OnExit | 969 | class OnExit |
1013 | { | 970 | { |
1014 | private: | 971 | private: |
1015 | |||
1016 | lua_State* const m_L; | 972 | lua_State* const m_L; |
1017 | Lane* m_lane{ nullptr }; | 973 | Lane* m_lane{ nullptr }; |
1018 | int const m_gc_cb_idx; | 974 | int const m_gc_cb_idx; |
@@ -1020,7 +976,6 @@ LUAG_FUNC(lane_new) | |||
1020 | DEBUGSPEW_CODE(DebugSpewIndentScope m_scope); | 976 | DEBUGSPEW_CODE(DebugSpewIndentScope m_scope); |
1021 | 977 | ||
1022 | public: | 978 | public: |
1023 | |||
1024 | OnExit(lua_State* L_, Lane* lane_, int gc_cb_idx_ DEBUGSPEW_COMMA_PARAM(Universe* U_)) | 979 | OnExit(lua_State* L_, Lane* lane_, int gc_cb_idx_ DEBUGSPEW_COMMA_PARAM(Universe* U_)) |
1025 | : m_L{ L_ } | 980 | : m_L{ L_ } |
1026 | , m_lane{ lane_ } | 981 | , m_lane{ lane_ } |
@@ -1032,8 +987,7 @@ LUAG_FUNC(lane_new) | |||
1032 | 987 | ||
1033 | ~OnExit() | 988 | ~OnExit() |
1034 | { | 989 | { |
1035 | if (m_lane) | 990 | if (m_lane) { |
1036 | { | ||
1037 | // we still need a full userdata so that garbage collection can do its thing | 991 | // we still need a full userdata so that garbage collection can do its thing |
1038 | prepareUserData(); | 992 | prepareUserData(); |
1039 | // leave a single cancel_error on the stack for the caller | 993 | // leave a single cancel_error on the stack for the caller |
@@ -1050,39 +1004,36 @@ LUAG_FUNC(lane_new) | |||
1050 | } | 1004 | } |
1051 | 1005 | ||
1052 | private: | 1006 | private: |
1053 | |||
1054 | void prepareUserData() | 1007 | void prepareUserData() |
1055 | { | 1008 | { |
1056 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: preparing lane userdata\n" INDENT_END)); | 1009 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: preparing lane userdata\n" INDENT_END(U))); |
1057 | STACK_CHECK_START_REL(m_L, 0); | 1010 | STACK_CHECK_START_REL(m_L, 0); |
1058 | // a Lane full userdata needs a single uservalue | 1011 | // a Lane full userdata needs a single uservalue |
1059 | Lane** const ud{ lua_newuserdatauv<Lane*>(m_L, 1) }; // ... lane | 1012 | Lane** const ud{ lua_newuserdatauv<Lane*>(m_L, 1) }; // m_L: ... lane |
1060 | *ud = m_lane; // don't forget to store the pointer in the userdata! | 1013 | *ud = m_lane; // don't forget to store the pointer in the userdata! |
1061 | 1014 | ||
1062 | // Set metatable for the userdata | 1015 | // Set metatable for the userdata |
1063 | // | 1016 | // |
1064 | lua_pushvalue(m_L, lua_upvalueindex(1)); // ... lane mt | 1017 | lua_pushvalue(m_L, lua_upvalueindex(1)); // m_L: ... lane mt |
1065 | lua_setmetatable(m_L, -2); // ... lane | 1018 | lua_setmetatable(m_L, -2); // m_L: ... lane |
1066 | STACK_CHECK(m_L, 1); | 1019 | STACK_CHECK(m_L, 1); |
1067 | 1020 | ||
1068 | // Create uservalue for the userdata | 1021 | // Create uservalue for the userdata |
1069 | // (this is where lane body return values will be stored when the handle is indexed by a numeric key) | 1022 | // (this is where lane body return values will be stored when the handle is indexed by a numeric key) |
1070 | lua_newtable(m_L); // ... lane uv | 1023 | lua_newtable(m_L); // m_L: ... lane uv |
1071 | 1024 | ||
1072 | // Store the gc_cb callback in the uservalue | 1025 | // Store the gc_cb callback in the uservalue |
1073 | if (m_gc_cb_idx > 0) | 1026 | if (m_gc_cb_idx > 0) { |
1074 | { | 1027 | kLaneGC.pushKey(m_L); // m_L: ... lane uv k |
1075 | kLaneGC.pushKey(m_L); // ... lane uv k | 1028 | lua_pushvalue(m_L, m_gc_cb_idx); // m_L: ... lane uv k gc_cb |
1076 | lua_pushvalue(m_L, m_gc_cb_idx); // ... lane uv k gc_cb | 1029 | lua_rawset(m_L, -3); // m_L: ... lane uv |
1077 | lua_rawset(m_L, -3); // ... lane uv | ||
1078 | } | 1030 | } |
1079 | 1031 | ||
1080 | lua_setiuservalue(m_L, -2, 1); // ... lane | 1032 | lua_setiuservalue(m_L, -2, 1); // m_L: ... lane |
1081 | STACK_CHECK(m_L, 1); | 1033 | STACK_CHECK(m_L, 1); |
1082 | } | 1034 | } |
1083 | 1035 | ||
1084 | public: | 1036 | public: |
1085 | |||
1086 | void success() | 1037 | void success() |
1087 | { | 1038 | { |
1088 | prepareUserData(); | 1039 | prepareUserData(); |
@@ -1091,24 +1042,21 @@ LUAG_FUNC(lane_new) | |||
1091 | } | 1042 | } |
1092 | } onExit{ L_, lane, gc_cb_idx DEBUGSPEW_COMMA_PARAM(U) }; | 1043 | } onExit{ L_, lane, gc_cb_idx DEBUGSPEW_COMMA_PARAM(U) }; |
1093 | // launch the thread early, it will sync with a std::latch to parallelize OS thread warmup and L2 preparation | 1044 | // launch the thread early, it will sync with a std::latch to parallelize OS thread warmup and L2 preparation |
1094 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END)); | 1045 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END(U))); |
1095 | lane->startThread(priority); | 1046 | lane->startThread(priority); |
1096 | 1047 | ||
1097 | STACK_GROW( L2, nargs + 3); // | 1048 | STACK_GROW(L2, nargs + 3); |
1098 | STACK_CHECK_START_REL(L2, 0); | 1049 | STACK_GROW(L_, 3); |
1099 | |||
1100 | STACK_GROW(L_, 3); // func libs priority globals package required gc_cb [... args ...] | ||
1101 | STACK_CHECK_START_REL(L_, 0); | 1050 | STACK_CHECK_START_REL(L_, 0); |
1102 | 1051 | ||
1103 | // give a default "Lua" name to the thread to see VM name in Decoda debugger | 1052 | // give a default "Lua" name to the thread to see VM name in Decoda debugger |
1104 | lua_pushfstring( L2, "Lane #%p", L2); // "..." | 1053 | lua_pushfstring(L2, "Lane #%p", L2); // L_: [7 args] args... L2: "<name>" |
1105 | lua_setglobal( L2, "decoda_name"); // | 1054 | lua_setglobal(L2, "decoda_name"); // L_: [7 args] args... L2: |
1106 | LUA_ASSERT(L_, lua_gettop( L2) == 0); | 1055 | LUA_ASSERT(L_, lua_gettop(L2) == 0); |
1107 | 1056 | ||
1108 | // package | 1057 | // package |
1109 | if (package_idx != 0) | 1058 | if (package_idx != 0) { |
1110 | { | 1059 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END(U))); |
1111 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END)); | ||
1112 | // when copying with mode LookupMode::LaneBody, should raise an error in case of problem, not leave it one the stack | 1060 | // when copying with mode LookupMode::LaneBody, should raise an error in case of problem, not leave it one the stack |
1113 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, SourceIndex{ package_idx }, {}, {}, {} }; | 1061 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, SourceIndex{ package_idx }, {}, {}, {} }; |
1114 | [[maybe_unused]] InterCopyResult const ret{ c.inter_copy_package() }; | 1062 | [[maybe_unused]] InterCopyResult const ret{ c.inter_copy_package() }; |
@@ -1116,113 +1064,94 @@ LUAG_FUNC(lane_new) | |||
1116 | } | 1064 | } |
1117 | 1065 | ||
1118 | // modules to require in the target lane *before* the function is transfered! | 1066 | // modules to require in the target lane *before* the function is transfered! |
1119 | if (required_idx != 0) | 1067 | if (required_idx != 0) { |
1120 | { | ||
1121 | int nbRequired = 1; | 1068 | int nbRequired = 1; |
1122 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END)); | 1069 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END(U))); |
1123 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1070 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1124 | // should not happen, was checked in lanes.lua before calling lane_new() | 1071 | // should not happen, was checked in lanes.lua before calling lane_new() |
1125 | if (lua_type(L_, required_idx) != LUA_TTABLE) | 1072 | if (lua_type(L_, required_idx) != LUA_TTABLE) { |
1126 | { | ||
1127 | raise_luaL_error(L_, "expected required module list as a table, got %s", luaL_typename(L_, required_idx)); | 1073 | raise_luaL_error(L_, "expected required module list as a table, got %s", luaL_typename(L_, required_idx)); |
1128 | } | 1074 | } |
1129 | 1075 | ||
1130 | lua_pushnil(L_); // func libs priority globals package required gc_cb [... args ...] nil | 1076 | lua_pushnil(L_); // L_: [7 args] args... nil L2: |
1131 | while (lua_next(L_, required_idx) != 0) // func libs priority globals package required gc_cb [... args ...] n "modname" | 1077 | while (lua_next(L_, required_idx) != 0) { // L_: [7 args] args... n "modname" L2: |
1132 | { | 1078 | if (lua_type(L_, -1) != LUA_TSTRING || lua_type(L_, -2) != LUA_TNUMBER || lua_tonumber(L_, -2) != nbRequired) { |
1133 | if (lua_type(L_, -1) != LUA_TSTRING || lua_type(L_, -2) != LUA_TNUMBER || lua_tonumber(L_, -2) != nbRequired) | ||
1134 | { | ||
1135 | raise_luaL_error(L_, "required module list should be a list of strings"); | 1079 | raise_luaL_error(L_, "required module list should be a list of strings"); |
1136 | } | 1080 | } else { |
1137 | else | ||
1138 | { | ||
1139 | // require the module in the target state, and populate the lookup table there too | 1081 | // require the module in the target state, and populate the lookup table there too |
1140 | size_t len; | 1082 | size_t len; |
1141 | char const* name = lua_tolstring(L_, -1, &len); | 1083 | char const* name = lua_tolstring(L_, -1, &len); |
1142 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END, name)); | 1084 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END(U), name)); |
1143 | 1085 | ||
1144 | // require the module in the target lane | 1086 | // require the module in the target lane |
1145 | lua_getglobal( L2, "require"); // require()? | 1087 | lua_getglobal(L2, "require"); // L_: [7 args] args... n "modname" L2: require()? |
1146 | if (lua_isnil( L2, -1)) | 1088 | if (lua_isnil(L2, -1)) { |
1147 | { | 1089 | lua_pop(L2, 1); // L_: [7 args] args... n "modname" L2: |
1148 | lua_pop( L2, 1); // | ||
1149 | raise_luaL_error(L_, "cannot pre-require modules without loading 'package' library first"); | 1090 | raise_luaL_error(L_, "cannot pre-require modules without loading 'package' library first"); |
1150 | } | 1091 | } else { |
1151 | else | 1092 | lua_pushlstring(L2, name, len); // L_: [7 args] args... n "modname" L2: require() name |
1152 | { | 1093 | if (lua_pcall(L2, 1, 1, 0) != LUA_OK) { // L_: [7 args] args... n "modname" L2: ret/errcode |
1153 | lua_pushlstring( L2, name, len); // require() name | ||
1154 | if (lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode | ||
1155 | { | ||
1156 | // propagate error to main state if any | 1094 | // propagate error to main state if any |
1157 | InterCopyContext c{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }; | 1095 | InterCopyContext c{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }; |
1158 | std::ignore = c.inter_move(1); // func libs priority globals package required gc_cb [... args ...] n "modname" error | 1096 | std::ignore = c.inter_move(1); // L_: [7 args] args... n "modname" error L2: |
1159 | raise_lua_error(L_); | 1097 | raise_lua_error(L_); |
1160 | } | 1098 | } |
1099 | // here the module was successfully required // L_: [7 args] args... n "modname" L2: ret | ||
1161 | // after requiring the module, register the functions it exported in our name<->function database | 1100 | // after requiring the module, register the functions it exported in our name<->function database |
1162 | populate_func_lookup_table( L2, -1, name); | 1101 | populate_func_lookup_table(L2, -1, name); |
1163 | lua_pop( L2, 1); // | 1102 | lua_pop(L2, 1); // L_: [7 args] args... n "modname" L2: |
1164 | } | 1103 | } |
1165 | } | 1104 | } |
1166 | lua_pop(L_, 1); // func libs priority globals package required gc_cb [... args ...] n | 1105 | lua_pop(L_, 1); // L_: func libs priority globals package required gc_cb [... args ...] n |
1167 | ++ nbRequired; | 1106 | ++nbRequired; |
1168 | } // func libs priority globals package required gc_cb [... args ...] | 1107 | } // L_: [7 args] args... |
1169 | } | 1108 | } |
1170 | STACK_CHECK(L_, 0); | 1109 | STACK_CHECK(L_, 0); |
1171 | STACK_CHECK(L2, 0); // | 1110 | STACK_CHECK(L2, 0); // L_: [7 args] args... L2: |
1172 | 1111 | ||
1173 | // Appending the specified globals to the global environment | 1112 | // Appending the specified globals to the global environment |
1174 | // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... | 1113 | // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... |
1175 | // | 1114 | // |
1176 | if (globals_idx != 0) | 1115 | if (globals_idx != 0) { |
1177 | { | 1116 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END(U))); |
1178 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END)); | 1117 | if (!lua_istable(L_, globals_idx)) { |
1179 | if (!lua_istable(L_, globals_idx)) | ||
1180 | { | ||
1181 | raise_luaL_error(L_, "Expected table, got %s", luaL_typename(L_, globals_idx)); | 1118 | raise_luaL_error(L_, "Expected table, got %s", luaL_typename(L_, globals_idx)); |
1182 | } | 1119 | } |
1183 | 1120 | ||
1184 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1121 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1185 | lua_pushnil(L_); // func libs priority globals package required gc_cb [... args ...] nil | 1122 | lua_pushnil(L_); // L_: [7 args] args... nil L2: |
1186 | // Lua 5.2 wants us to push the globals table on the stack | 1123 | // Lua 5.2 wants us to push the globals table on the stack |
1187 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; | 1124 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; |
1188 | lua_pushglobaltable(L2); // _G | 1125 | lua_pushglobaltable(L2); // L_: [7 args] args... nil L2: _G |
1189 | while( lua_next(L_, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v | 1126 | while (lua_next(L_, globals_idx)) { // L_: [7 args] args... k v L2: _G |
1190 | { | 1127 | std::ignore = c.inter_copy(2); // L_: [7 args] args... k v L2: _G k v |
1191 | std::ignore = c.inter_copy(2); // _G k v | ||
1192 | // assign it in L2's globals table | 1128 | // assign it in L2's globals table |
1193 | lua_rawset(L2, -3); // _G | 1129 | lua_rawset(L2, -3); // L_: [7 args] args... k v L2: _G |
1194 | lua_pop(L_, 1); // func libs priority globals package required gc_cb [... args ...] k | 1130 | lua_pop(L_, 1); // L_: [7 args] args... k |
1195 | } // func libs priority globals package required gc_cb [... args ...] | 1131 | } // L_: [7 args] args... |
1196 | lua_pop( L2, 1); // | 1132 | lua_pop(L2, 1); // L_: [7 args] args... L2: |
1197 | } | 1133 | } |
1198 | STACK_CHECK(L_, 0); | 1134 | STACK_CHECK(L_, 0); |
1199 | STACK_CHECK(L2, 0); | 1135 | STACK_CHECK(L2, 0); |
1200 | 1136 | ||
1201 | // Lane main function | 1137 | // Lane main function |
1202 | LuaType const func_type{ lua_type_as_enum(L_, 1) }; | 1138 | LuaType const func_type{ lua_type_as_enum(L_, 1) }; |
1203 | if (func_type == LuaType::FUNCTION) | 1139 | if (func_type == LuaType::FUNCTION) { |
1204 | { | 1140 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END(U))); |
1205 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END)); | ||
1206 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1141 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1207 | lua_pushvalue(L_, 1); // func libs priority globals package required gc_cb [... args ...] func | 1142 | lua_pushvalue(L_, 1); // L_: [7 args] args... func L2: |
1208 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; | 1143 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; |
1209 | InterCopyResult const res{ c.inter_move(1) }; // func libs priority globals package required gc_cb [... args ...] // func | 1144 | InterCopyResult const res{ c.inter_move(1) }; // L_: [7 args] args... L2: func |
1210 | if (res != InterCopyResult::Success) | 1145 | if (res != InterCopyResult::Success) { |
1211 | { | ||
1212 | raise_luaL_error(L_, "tried to copy unsupported types"); | 1146 | raise_luaL_error(L_, "tried to copy unsupported types"); |
1213 | } | 1147 | } |
1214 | } | 1148 | } else if (func_type == LuaType::STRING) { |
1215 | else if (func_type == LuaType::STRING) | 1149 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: compile lane body\n" INDENT_END(U))); |
1216 | { | ||
1217 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: compile lane body\n" INDENT_END)); | ||
1218 | // compile the string | 1150 | // compile the string |
1219 | if (luaL_loadstring(L2, lua_tostring(L_, 1)) != 0) // func | 1151 | if (luaL_loadstring(L2, lua_tostring(L_, 1)) != 0) { // L_: [7 args] args... L2: func |
1220 | { | ||
1221 | raise_luaL_error(L_, "error when parsing lane function code"); | 1152 | raise_luaL_error(L_, "error when parsing lane function code"); |
1222 | } | 1153 | } |
1223 | } | 1154 | } else { |
1224 | else | ||
1225 | { | ||
1226 | raise_luaL_error(L_, "Expected function, got %s", lua_typename(L_, func_type)); | 1155 | raise_luaL_error(L_, "Expected function, got %s", lua_typename(L_, func_type)); |
1227 | } | 1156 | } |
1228 | STACK_CHECK(L_, 0); | 1157 | STACK_CHECK(L_, 0); |
@@ -1230,14 +1159,12 @@ LUAG_FUNC(lane_new) | |||
1230 | LUA_ASSERT(L_, lua_isfunction(L2, 1)); | 1159 | LUA_ASSERT(L_, lua_isfunction(L2, 1)); |
1231 | 1160 | ||
1232 | // revive arguments | 1161 | // revive arguments |
1233 | if (nargs > 0) | 1162 | if (nargs > 0) { |
1234 | { | 1163 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END(U))); |
1235 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END)); | ||
1236 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1164 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1237 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; | 1165 | InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; |
1238 | InterCopyResult const res{ c.inter_move(nargs) }; // func libs priority globals package required gc_cb // func [... args ...] | 1166 | InterCopyResult const res{ c.inter_move(nargs) }; // L_: [7 args] L2: func args... |
1239 | if (res != InterCopyResult::Success) | 1167 | if (res != InterCopyResult::Success) { |
1240 | { | ||
1241 | raise_luaL_error(L_, "tried to copy unsupported types"); | 1168 | raise_luaL_error(L_, "tried to copy unsupported types"); |
1242 | } | 1169 | } |
1243 | } | 1170 | } |
@@ -1245,12 +1172,12 @@ LUAG_FUNC(lane_new) | |||
1245 | LUA_ASSERT(L_, lua_gettop(L_) == kFixedArgsIdx); | 1172 | LUA_ASSERT(L_, lua_gettop(L_) == kFixedArgsIdx); |
1246 | 1173 | ||
1247 | // Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). | 1174 | // Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). |
1248 | kLanePointerRegKey.setValue(L2, [lane](lua_State* L_) { lua_pushlightuserdata(L_, lane); }); // func [... args ...] | 1175 | kLanePointerRegKey.setValue(L2, [lane](lua_State* L_) { lua_pushlightuserdata(L_, lane); }); // L_: [7 args] L2: func args... |
1249 | STACK_CHECK(L2, 1 + nargs); | 1176 | STACK_CHECK(L2, 1 + nargs); |
1250 | 1177 | ||
1251 | STACK_CHECK_RESET_REL(L_, 0); | 1178 | STACK_CHECK_RESET_REL(L_, 0); |
1252 | // all went well, the lane's thread can start working | 1179 | // all went well, the lane's thread can start working |
1253 | onExit.success(); | 1180 | onExit.success(); // L_: [7 args] lane L2: <living its own life> |
1254 | // we should have the lane userdata on top of the stack | 1181 | // we should have the lane userdata on top of the stack |
1255 | STACK_CHECK(L_, 1); | 1182 | STACK_CHECK(L_, 1); |
1256 | return 1; | 1183 | return 1; |
@@ -1258,7 +1185,6 @@ LUAG_FUNC(lane_new) | |||
1258 | 1185 | ||
1259 | // ################################################################################################# | 1186 | // ################################################################################################# |
1260 | 1187 | ||
1261 | //--- | ||
1262 | // = thread_gc( lane_ud ) | 1188 | // = thread_gc( lane_ud ) |
1263 | // | 1189 | // |
1264 | // Cleanup for a thread userdata. If the thread is still executing, leave it | 1190 | // Cleanup for a thread userdata. If the thread is still executing, leave it |
@@ -1273,38 +1199,31 @@ LUAG_FUNC(lane_new) | |||
1273 | [[nodiscard]] static int lane_gc(lua_State* L_) | 1199 | [[nodiscard]] static int lane_gc(lua_State* L_) |
1274 | { | 1200 | { |
1275 | bool have_gc_cb{ false }; | 1201 | bool have_gc_cb{ false }; |
1276 | Lane* const lane{ ToLane(L_, 1) }; // ud | 1202 | Lane* const lane{ ToLane(L_, 1) }; // L_: ud |
1277 | 1203 | ||
1278 | // if there a gc callback? | 1204 | // if there a gc callback? |
1279 | lua_getiuservalue(L_, 1, 1); // ud uservalue | 1205 | lua_getiuservalue(L_, 1, 1); // L_: ud uservalue |
1280 | kLaneGC.pushKey(L_); // ud uservalue __gc | 1206 | kLaneGC.pushKey(L_); // L_: ud uservalue __gc |
1281 | lua_rawget(L_, -2); // ud uservalue gc_cb|nil | 1207 | lua_rawget(L_, -2); // L_: ud uservalue gc_cb|nil |
1282 | if (!lua_isnil(L_, -1)) | 1208 | if (!lua_isnil(L_, -1)) { |
1283 | { | 1209 | lua_remove(L_, -2); // L_: ud gc_cb|nil |
1284 | lua_remove(L_, -2); // ud gc_cb|nil | 1210 | lua_pushstring(L_, lane->debug_name); // L_: ud gc_cb name |
1285 | lua_pushstring(L_, lane->debug_name); // ud gc_cb name | ||
1286 | have_gc_cb = true; | 1211 | have_gc_cb = true; |
1287 | } | 1212 | } else { |
1288 | else | 1213 | lua_pop(L_, 2); // L_: ud |
1289 | { | ||
1290 | lua_pop(L_, 2); // ud | ||
1291 | } | 1214 | } |
1292 | 1215 | ||
1293 | // We can read 'lane->status' without locks, but not wait for it | 1216 | // We can read 'lane->status' without locks, but not wait for it |
1294 | if (lane->m_status < Lane::Done) | 1217 | if (lane->m_status < Lane::Done) { |
1295 | { | ||
1296 | // still running: will have to be cleaned up later | 1218 | // still running: will have to be cleaned up later |
1297 | selfdestruct_add(lane); | 1219 | selfdestruct_add(lane); |
1298 | assert(lane->selfdestruct_next); | 1220 | assert(lane->selfdestruct_next); |
1299 | if (have_gc_cb) | 1221 | if (have_gc_cb) { |
1300 | { | 1222 | lua_pushliteral(L_, "selfdestruct"); // L_: ud gc_cb name status |
1301 | lua_pushliteral(L_, "selfdestruct"); // ud gc_cb name status | 1223 | lua_call(L_, 2, 0); // L_: ud |
1302 | lua_call(L_, 2, 0); // ud | ||
1303 | } | 1224 | } |
1304 | return 0; | 1225 | return 0; |
1305 | } | 1226 | } else if (lane->L) { |
1306 | else if (lane->L) | ||
1307 | { | ||
1308 | // no longer accessing the Lua VM: we can close right now | 1227 | // no longer accessing the Lua VM: we can close right now |
1309 | lua_close(lane->L); | 1228 | lua_close(lane->L); |
1310 | lane->L = nullptr; | 1229 | lane->L = nullptr; |
@@ -1316,10 +1235,9 @@ LUAG_FUNC(lane_new) | |||
1316 | delete lane; | 1235 | delete lane; |
1317 | 1236 | ||
1318 | // do this after lane cleanup in case the callback triggers an error | 1237 | // do this after lane cleanup in case the callback triggers an error |
1319 | if (have_gc_cb) | 1238 | if (have_gc_cb) { |
1320 | { | 1239 | lua_pushliteral(L_, "closed"); // L_: ud gc_cb name status |
1321 | lua_pushliteral(L_, "closed"); // ud gc_cb name status | 1240 | lua_call(L_, 2, 0); // L_: ud |
1322 | lua_call(L_, 2, 0); // ud | ||
1323 | } | 1241 | } |
1324 | return 0; | 1242 | return 0; |
1325 | } | 1243 | } |
@@ -1338,8 +1256,7 @@ LUAG_FUNC(lane_new) | |||
1338 | // | 1256 | // |
1339 | [[nodiscard]] static char const* thread_status_string(Lane::Status status_) | 1257 | [[nodiscard]] static char const* thread_status_string(Lane::Status status_) |
1340 | { | 1258 | { |
1341 | char const* const str | 1259 | char const* const str{ |
1342 | { | ||
1343 | (status_ == Lane::Pending) ? "pending" : | 1260 | (status_ == Lane::Pending) ? "pending" : |
1344 | (status_ == Lane::Running) ? "running" : // like in 'co.status()' | 1261 | (status_ == Lane::Running) ? "running" : // like in 'co.status()' |
1345 | (status_ == Lane::Waiting) ? "waiting" : | 1262 | (status_ == Lane::Waiting) ? "waiting" : |
@@ -1378,11 +1295,10 @@ LUAG_FUNC(thread_join) | |||
1378 | lua_State* const L2{ lane->L }; | 1295 | lua_State* const L2{ lane->L }; |
1379 | 1296 | ||
1380 | bool const done{ !lane->m_thread.joinable() || lane->waitForCompletion(duration) }; | 1297 | bool const done{ !lane->m_thread.joinable() || lane->waitForCompletion(duration) }; |
1381 | if (!done || !L2) | 1298 | if (!done || !L2) { |
1382 | { | ||
1383 | STACK_GROW(L_, 2); | 1299 | STACK_GROW(L_, 2); |
1384 | lua_pushnil(L_); | 1300 | lua_pushnil(L_); // L_: lane timeout? nil |
1385 | lua_pushliteral(L_, "timeout"); | 1301 | lua_pushliteral(L_, "timeout"); // L_: lane timeout? nil "timeout" |
1386 | return 2; | 1302 | return 2; |
1387 | } | 1303 | } |
1388 | 1304 | ||
@@ -1394,42 +1310,39 @@ LUAG_FUNC(thread_join) | |||
1394 | // debug_name is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed | 1310 | // debug_name is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed |
1395 | // so store it in the userdata uservalue at a key that can't possibly collide | 1311 | // so store it in the userdata uservalue at a key that can't possibly collide |
1396 | securize_debug_threadname(L_, lane); | 1312 | securize_debug_threadname(L_, lane); |
1397 | switch (lane->m_status) | 1313 | switch (lane->m_status) { |
1398 | { | 1314 | case Lane::Done: |
1399 | case Lane::Done: | ||
1400 | { | 1315 | { |
1401 | int const n{ lua_gettop(L2) }; // whole L2 stack | 1316 | int const n{ lua_gettop(L2) }; // whole L2 stack |
1402 | if ( | 1317 | if ( |
1403 | (n > 0) && | 1318 | (n > 0) && |
1404 | (InterCopyContext{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }.inter_move(n) != InterCopyResult::Success) | 1319 | (InterCopyContext{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }.inter_move(n) != InterCopyResult::Success) |
1405 | ) | 1320 | ) { // L_: lane timeout? results L2: |
1406 | { | ||
1407 | raise_luaL_error(L_, "tried to copy unsupported types"); | 1321 | raise_luaL_error(L_, "tried to copy unsupported types"); |
1408 | } | 1322 | } |
1409 | ret = n; | 1323 | ret = n; |
1410 | } | 1324 | } |
1411 | break; | 1325 | break; |
1412 | 1326 | ||
1413 | case Lane::Error: | 1327 | case Lane::Error: |
1414 | { | 1328 | { |
1415 | int const n{ lua_gettop(L2) }; | 1329 | int const n{ lua_gettop(L2) }; // L_: lane timeout? L2: "err" [trace] |
1416 | STACK_GROW(L_, 3); | 1330 | STACK_GROW(L_, 3); |
1417 | lua_pushnil(L_); | 1331 | lua_pushnil(L_); // L_: lane timeout? nil |
1418 | // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ... | 1332 | // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ... |
1419 | InterCopyContext c{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }; | 1333 | InterCopyContext c{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }; |
1420 | if (c.inter_move(n) != InterCopyResult::Success) // nil "err" [trace] | 1334 | if (c.inter_move(n) != InterCopyResult::Success) { // L_: lane timeout? nil "err" [trace] L2: |
1421 | { | ||
1422 | raise_luaL_error(L_, "tried to copy unsupported types: %s", lua_tostring(L_, -n)); | 1335 | raise_luaL_error(L_, "tried to copy unsupported types: %s", lua_tostring(L_, -n)); |
1423 | } | 1336 | } |
1424 | ret = 1 + n; | 1337 | ret = 1 + n; |
1425 | } | 1338 | } |
1426 | break; | 1339 | break; |
1427 | 1340 | ||
1428 | case Lane::Cancelled: | 1341 | case Lane::Cancelled: |
1429 | ret = 0; | 1342 | ret = 0; |
1430 | break; | 1343 | break; |
1431 | 1344 | ||
1432 | default: | 1345 | default: |
1433 | DEBUGSPEW_CODE(fprintf(stderr, "Status: %d\n", lane->m_status)); | 1346 | DEBUGSPEW_CODE(fprintf(stderr, "Status: %d\n", lane->m_status)); |
1434 | LUA_ASSERT(L_, false); | 1347 | LUA_ASSERT(L_, false); |
1435 | ret = 0; | 1348 | ret = 0; |
@@ -1459,8 +1372,7 @@ LUAG_FUNC(thread_index) | |||
1459 | STACK_GROW(L_, 8); // up to 8 positions are needed in case of error propagation | 1372 | STACK_GROW(L_, 8); // up to 8 positions are needed in case of error propagation |
1460 | 1373 | ||
1461 | // If key is numeric, wait until the thread returns and populate the environment with the return values | 1374 | // If key is numeric, wait until the thread returns and populate the environment with the return values |
1462 | if (lua_type(L_, kKey) == LUA_TNUMBER) | 1375 | if (lua_type(L_, kKey) == LUA_TNUMBER) { |
1463 | { | ||
1464 | static constexpr int kUsr{ 3 }; | 1376 | static constexpr int kUsr{ 3 }; |
1465 | // first, check that we don't already have an environment that holds the requested value | 1377 | // first, check that we don't already have an environment that holds the requested value |
1466 | { | 1378 | { |
@@ -1468,8 +1380,7 @@ LUAG_FUNC(thread_index) | |||
1468 | lua_getiuservalue(L_, kSelf, 1); | 1380 | lua_getiuservalue(L_, kSelf, 1); |
1469 | lua_pushvalue(L_, kKey); | 1381 | lua_pushvalue(L_, kKey); |
1470 | lua_rawget(L_, kUsr); | 1382 | lua_rawget(L_, kUsr); |
1471 | if (!lua_isnil(L_, -1)) | 1383 | if (!lua_isnil(L_, -1)) { |
1472 | { | ||
1473 | return 1; | 1384 | return 1; |
1474 | } | 1385 | } |
1475 | lua_pop(L_, 1); | 1386 | lua_pop(L_, 1); |
@@ -1480,8 +1391,7 @@ LUAG_FUNC(thread_index) | |||
1480 | lua_rawget(L_, kUsr); | 1391 | lua_rawget(L_, kUsr); |
1481 | bool const fetched{ !lua_isnil(L_, -1) }; | 1392 | bool const fetched{ !lua_isnil(L_, -1) }; |
1482 | lua_pop(L_, 1); // back to our 2 args + uservalue on the stack | 1393 | lua_pop(L_, 1); // back to our 2 args + uservalue on the stack |
1483 | if (!fetched) | 1394 | if (!fetched) { |
1484 | { | ||
1485 | lua_pushinteger(L_, 0); | 1395 | lua_pushinteger(L_, 0); |
1486 | lua_pushboolean(L_, 1); | 1396 | lua_pushboolean(L_, 1); |
1487 | lua_rawset(L_, kUsr); | 1397 | lua_rawset(L_, kUsr); |
@@ -1489,9 +1399,8 @@ LUAG_FUNC(thread_index) | |||
1489 | lua_pushcfunction(L_, LG_thread_join); | 1399 | lua_pushcfunction(L_, LG_thread_join); |
1490 | lua_pushvalue(L_, kSelf); | 1400 | lua_pushvalue(L_, kSelf); |
1491 | lua_call(L_, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+ | 1401 | lua_call(L_, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+ |
1492 | switch (lane->m_status) | 1402 | switch (lane->m_status) { |
1493 | { | 1403 | default: |
1494 | default: | ||
1495 | // this is an internal error, we probably never get here | 1404 | // this is an internal error, we probably never get here |
1496 | lua_settop(L_, 0); | 1405 | lua_settop(L_, 0); |
1497 | lua_pushliteral(L_, "Unexpected status: "); | 1406 | lua_pushliteral(L_, "Unexpected status: "); |
@@ -1500,18 +1409,17 @@ LUAG_FUNC(thread_index) | |||
1500 | raise_lua_error(L_); | 1409 | raise_lua_error(L_); |
1501 | [[fallthrough]]; // fall through if we are killed, as we got nil, "killed" on the stack | 1410 | [[fallthrough]]; // fall through if we are killed, as we got nil, "killed" on the stack |
1502 | 1411 | ||
1503 | case Lane::Done: // got regular return values | 1412 | case Lane::Done: // got regular return values |
1504 | { | 1413 | { |
1505 | int const nvalues{ lua_gettop(L_) - 3 }; | 1414 | int const nvalues{ lua_gettop(L_) - 3 }; |
1506 | for (int i = nvalues; i > 0; --i) | 1415 | for (int i = nvalues; i > 0; --i) { |
1507 | { | ||
1508 | // pop the last element of the stack, to store it in the uservalue at its proper index | 1416 | // pop the last element of the stack, to store it in the uservalue at its proper index |
1509 | lua_rawseti(L_, kUsr, i); | 1417 | lua_rawseti(L_, kUsr, i); |
1510 | } | 1418 | } |
1511 | } | 1419 | } |
1512 | break; | 1420 | break; |
1513 | 1421 | ||
1514 | case Lane::Error: // got 3 values: nil, errstring, callstack table | 1422 | case Lane::Error: // got 3 values: nil, errstring, callstack table |
1515 | // me[-2] could carry the stack table, but even | 1423 | // me[-2] could carry the stack table, but even |
1516 | // me[-1] is rather unnecessary (and undocumented); | 1424 | // me[-1] is rather unnecessary (and undocumented); |
1517 | // use ':join()' instead. --AKa 22-Jan-2009 | 1425 | // use ':join()' instead. --AKa 22-Jan-2009 |
@@ -1522,19 +1430,17 @@ LUAG_FUNC(thread_index) | |||
1522 | lua_rawset(L_, kUsr); | 1430 | lua_rawset(L_, kUsr); |
1523 | break; | 1431 | break; |
1524 | 1432 | ||
1525 | case Lane::Cancelled: | 1433 | case Lane::Cancelled: |
1526 | // do nothing | 1434 | // do nothing |
1527 | break; | 1435 | break; |
1528 | } | 1436 | } |
1529 | } | 1437 | } |
1530 | lua_settop(L_, 3); // self KEY ENV | 1438 | lua_settop(L_, 3); // L_: self KEY ENV |
1531 | int const key{ static_cast<int>(lua_tointeger(L_, kKey)) }; | 1439 | int const key{ static_cast<int>(lua_tointeger(L_, kKey)) }; |
1532 | if (key != -1) | 1440 | if (key != -1) { |
1533 | { | 1441 | lua_pushnumber(L_, -1); // L_: self KEY ENV -1 |
1534 | lua_pushnumber(L_, -1); // self KEY ENV -1 | 1442 | lua_rawget(L_, kUsr); // L_: self KEY ENV "error"|nil |
1535 | lua_rawget(L_, kUsr); // self KEY ENV "error"|nil | 1443 | if (!lua_isnil(L_, -1)) { // L_: an error was stored |
1536 | if (!lua_isnil(L_, -1)) // an error was stored | ||
1537 | { | ||
1538 | // Note: Lua 5.1 interpreter is not prepared to show | 1444 | // Note: Lua 5.1 interpreter is not prepared to show |
1539 | // non-string errors, so we use 'tostring()' here | 1445 | // non-string errors, so we use 'tostring()' here |
1540 | // to get meaningful output. --AKa 22-Jan-2009 | 1446 | // to get meaningful output. --AKa 22-Jan-2009 |
@@ -1546,53 +1452,50 @@ LUAG_FUNC(thread_index) | |||
1546 | // Level 3 should show the line where 'h[x]' was read | 1452 | // Level 3 should show the line where 'h[x]' was read |
1547 | // but this only seems to work for string messages | 1453 | // but this only seems to work for string messages |
1548 | // (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009 | 1454 | // (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009 |
1549 | lua_getmetatable(L_, kSelf); // self KEY ENV "error" mt | 1455 | lua_getmetatable(L_, kSelf); // L_: self KEY ENV "error" mt |
1550 | lua_getfield(L_, -1, "cached_error"); // self KEY ENV "error" mt error() | 1456 | lua_getfield(L_, -1, "cached_error"); // L_: self KEY ENV "error" mt error() |
1551 | lua_getfield(L_, -2, "cached_tostring"); // self KEY ENV "error" mt error() tostring() | 1457 | lua_getfield(L_, -2, "cached_tostring"); // L_: self KEY ENV "error" mt error() tostring() |
1552 | lua_pushvalue(L_, 4); // self KEY ENV "error" mt error() tostring() "error" | 1458 | lua_pushvalue(L_, 4); // L_: self KEY ENV "error" mt error() tostring() "error" |
1553 | lua_call(L_, 1, 1); // tostring( errstring) -- just in case // self KEY ENV "error" mt error() "error" | 1459 | lua_call(L_, 1, 1); // tostring(errstring) -- just in case // L_: self KEY ENV "error" mt error() "error" |
1554 | lua_pushinteger(L_, 3); // self KEY ENV "error" mt error() "error" 3 | 1460 | lua_pushinteger(L_, 3); // L_: self KEY ENV "error" mt error() "error" 3 |
1555 | lua_call(L_, 2, 0); // error( tostring( errstring), 3) // self KEY ENV "error" mt | 1461 | lua_call(L_, 2, 0); // error(tostring(errstring), 3) -> doesn't return // L_: self KEY ENV "error" mt |
1556 | } | 1462 | } else { |
1557 | else | 1463 | lua_pop(L_, 1); // L_: self KEY ENV |
1558 | { | ||
1559 | lua_pop(L_, 1); // self KEY ENV | ||
1560 | } | 1464 | } |
1561 | } | 1465 | } |
1562 | lua_rawgeti(L_, kUsr, key); | 1466 | lua_rawgeti(L_, kUsr, key); |
1563 | } | 1467 | } |
1564 | return 1; | 1468 | return 1; |
1565 | } | 1469 | } |
1566 | if (lua_type(L_, kKey) == LUA_TSTRING) | 1470 | if (lua_type(L_, kKey) == LUA_TSTRING) { |
1567 | { | ||
1568 | char const* const keystr{ lua_tostring(L_, kKey) }; | 1471 | char const* const keystr{ lua_tostring(L_, kKey) }; |
1569 | lua_settop(L_, 2); // keep only our original arguments on the stack | 1472 | lua_settop(L_, 2); // keep only our original arguments on the stack |
1570 | if (strcmp( keystr, "status") == 0) | 1473 | if (strcmp(keystr, "status") == 0) { |
1571 | { | ||
1572 | lane->pushThreadStatus(L_); // push the string representing the status | 1474 | lane->pushThreadStatus(L_); // push the string representing the status |
1573 | return 1; | 1475 | return 1; |
1574 | } | 1476 | } |
1575 | // return self.metatable[key] | 1477 | // return self.metatable[key] |
1576 | lua_getmetatable(L_, kSelf); // self KEY mt | 1478 | lua_getmetatable(L_, kSelf); // L_: self KEY mt |
1577 | lua_replace(L_, -3); // mt KEY | 1479 | lua_replace(L_, -3); // L_: mt KEY |
1578 | lua_rawget(L_, -2); // mt value | 1480 | lua_rawget(L_, -2); // L_: mt value |
1579 | // only "cancel" and "join" are registered as functions, any other string will raise an error | 1481 | // only "cancel" and "join" are registered as functions, any other string will raise an error |
1580 | if (!lua_iscfunction(L_, -1)) | 1482 | if (!lua_iscfunction(L_, -1)) { |
1581 | { | ||
1582 | raise_luaL_error(L_, "can't index a lane with '%s'", keystr); | 1483 | raise_luaL_error(L_, "can't index a lane with '%s'", keystr); |
1583 | } | 1484 | } |
1584 | return 1; | 1485 | return 1; |
1585 | } | 1486 | } |
1586 | // unknown key | 1487 | // unknown key |
1587 | lua_getmetatable(L_, kSelf); | 1488 | lua_getmetatable(L_, kSelf); // L_: mt |
1588 | lua_getfield(L_, -1, "cached_error"); | 1489 | lua_getfield(L_, -1, "cached_error"); // L_: mt error |
1589 | lua_pushliteral(L_, "Unknown key: "); | 1490 | lua_pushliteral(L_, "Unknown key: "); // L_: mt error "Unknown key: " |
1590 | lua_pushvalue(L_, kKey); | 1491 | lua_pushvalue(L_, kKey); // L_: mt error "Unknown key: " k |
1591 | lua_concat(L_, 2); | 1492 | lua_concat(L_, 2); // L_: mt error "Unknown key: <k>" |
1592 | lua_call(L_, 1, 0); // error( "Unknown key: " .. key) -> doesn't return | 1493 | lua_call(L_, 1, 0); // error( "Unknown key: " .. key) -> doesn't return // L_: mt |
1593 | return 0; | 1494 | return 0; |
1594 | } | 1495 | } |
1595 | 1496 | ||
1497 | // ################################################################################################ | ||
1498 | |||
1596 | #if HAVE_LANE_TRACKING() | 1499 | #if HAVE_LANE_TRACKING() |
1597 | //--- | 1500 | //--- |
1598 | // threads() -> {}|nil | 1501 | // threads() -> {}|nil |
@@ -1606,24 +1509,22 @@ LUAG_FUNC(threads) | |||
1606 | // List _all_ still running threads | 1509 | // List _all_ still running threads |
1607 | // | 1510 | // |
1608 | std::lock_guard<std::mutex> guard{ U->tracking_cs }; | 1511 | std::lock_guard<std::mutex> guard{ U->tracking_cs }; |
1609 | if (U->tracking_first && U->tracking_first != TRACKING_END) | 1512 | if (U->tracking_first && U->tracking_first != TRACKING_END) { |
1610 | { | ||
1611 | Lane* lane{ U->tracking_first }; | 1513 | Lane* lane{ U->tracking_first }; |
1612 | int index = 0; | 1514 | int index = 0; |
1613 | lua_newtable(L_); // {} | 1515 | lua_newtable(L_); // L_: {} |
1614 | while (lane != TRACKING_END) | 1516 | while (lane != TRACKING_END) { |
1615 | { | ||
1616 | // insert a { name, status } tuple, so that several lanes with the same name can't clobber each other | 1517 | // insert a { name, status } tuple, so that several lanes with the same name can't clobber each other |
1617 | lua_newtable(L_); // {} {} | 1518 | lua_newtable(L_); // L_: {} {} |
1618 | lua_pushstring(L_, lane->debug_name); // {} {} "name" | 1519 | lua_pushstring(L_, lane->debug_name); // L_: {} {} "name" |
1619 | lua_setfield(L_, -2, "name"); // {} {} | 1520 | lua_setfield(L_, -2, "name"); // L_: {} {} |
1620 | lane->pushThreadStatus(L_); // {} {} "status" | 1521 | lane->pushThreadStatus(L_); // L_: {} {} "status" |
1621 | lua_setfield(L_, -2, "status"); // {} {} | 1522 | lua_setfield(L_, -2, "status"); // L_: {} {} |
1622 | lua_rawseti(L_, -2, ++index); // {} | 1523 | lua_rawseti(L_, -2, ++index); // L_: {} |
1623 | lane = lane->tracking_next; | 1524 | lane = lane->tracking_next; |
1624 | } | 1525 | } |
1625 | } | 1526 | } |
1626 | return lua_gettop(L_) - top; // 0 or 1 | 1527 | return lua_gettop(L_) - top; // L_: 0 or 1 |
1627 | } | 1528 | } |
1628 | #endif // HAVE_LANE_TRACKING() | 1529 | #endif // HAVE_LANE_TRACKING() |
1629 | 1530 | ||
@@ -1632,15 +1533,15 @@ LUAG_FUNC(threads) | |||
1632 | // ################################################################################################# | 1533 | // ################################################################################################# |
1633 | 1534 | ||
1634 | /* | 1535 | /* |
1635 | * secs = now_secs() | 1536 | * secs = now_secs() |
1636 | * | 1537 | * |
1637 | * Returns the current time, as seconds. Resolution depends on std::system_clock implementation | 1538 | * Returns the current time, as seconds. Resolution depends on std::system_clock implementation |
1638 | * Can't use std::chrono::steady_clock because we need the same baseline as std::mktime | 1539 | * Can't use std::chrono::steady_clock because we need the same baseline as std::mktime |
1639 | */ | 1540 | */ |
1640 | LUAG_FUNC(now_secs) | 1541 | LUAG_FUNC(now_secs) |
1641 | { | 1542 | { |
1642 | auto const now{ std::chrono::system_clock::now() }; | 1543 | auto const now{ std::chrono::system_clock::now() }; |
1643 | lua_Duration duration { now.time_since_epoch() }; | 1544 | lua_Duration duration{ now.time_since_epoch() }; |
1644 | 1545 | ||
1645 | lua_pushnumber(L_, duration.count()); | 1546 | lua_pushnumber(L_, duration.count()); |
1646 | return 1; | 1547 | return 1; |
@@ -1648,9 +1549,7 @@ LUAG_FUNC(now_secs) | |||
1648 | 1549 | ||
1649 | // ################################################################################################# | 1550 | // ################################################################################################# |
1650 | 1551 | ||
1651 | /* | 1552 | // wakeup_at_secs= wakeup_conv(date_tbl) |
1652 | * wakeup_at_secs= wakeup_conv(date_tbl) | ||
1653 | */ | ||
1654 | LUAG_FUNC(wakeup_conv) | 1553 | LUAG_FUNC(wakeup_conv) |
1655 | { | 1554 | { |
1656 | // date_tbl | 1555 | // date_tbl |
@@ -1664,8 +1563,7 @@ LUAG_FUNC(wakeup_conv) | |||
1664 | // .isdst (daylight saving on/off) | 1563 | // .isdst (daylight saving on/off) |
1665 | 1564 | ||
1666 | STACK_CHECK_START_REL(L_, 0); | 1565 | STACK_CHECK_START_REL(L_, 0); |
1667 | auto readInteger = [L = L_](char const* name_) | 1566 | auto readInteger = [L = L_](char const* name_) { |
1668 | { | ||
1669 | lua_getfield(L, 1, name_); | 1567 | lua_getfield(L, 1, name_); |
1670 | lua_Integer const val{ lua_tointeger(L, -1) }; | 1568 | lua_Integer const val{ lua_tointeger(L, -1) }; |
1671 | lua_pop(L, 1); | 1569 | lua_pop(L, 1); |
@@ -1682,19 +1580,19 @@ LUAG_FUNC(wakeup_conv) | |||
1682 | // If Lua table has '.isdst' we trust that. If it does not, we'll let | 1580 | // If Lua table has '.isdst' we trust that. If it does not, we'll let |
1683 | // 'mktime' decide on whether the time is within DST or not (value -1). | 1581 | // 'mktime' decide on whether the time is within DST or not (value -1). |
1684 | // | 1582 | // |
1685 | lua_getfield(L_, 1, "isdst" ); | 1583 | lua_getfield(L_, 1, "isdst"); |
1686 | int const isdst{ lua_isboolean(L_, -1) ? lua_toboolean(L_, -1) : -1 }; | 1584 | int const isdst{ lua_isboolean(L_, -1) ? lua_toboolean(L_, -1) : -1 }; |
1687 | lua_pop(L_,1); | 1585 | lua_pop(L_, 1); |
1688 | STACK_CHECK(L_, 0); | 1586 | STACK_CHECK(L_, 0); |
1689 | 1587 | ||
1690 | std::tm t{}; | 1588 | std::tm t{}; |
1691 | t.tm_year = year - 1900; | 1589 | t.tm_year = year - 1900; |
1692 | t.tm_mon= month-1; // 0..11 | 1590 | t.tm_mon = month - 1; // 0..11 |
1693 | t.tm_mday= day; // 1..31 | 1591 | t.tm_mday = day; // 1..31 |
1694 | t.tm_hour= hour; // 0..23 | 1592 | t.tm_hour = hour; // 0..23 |
1695 | t.tm_min= min; // 0..59 | 1593 | t.tm_min = min; // 0..59 |
1696 | t.tm_sec= sec; // 0..60 | 1594 | t.tm_sec = sec; // 0..60 |
1697 | t.tm_isdst= isdst; // 0/1/negative | 1595 | t.tm_isdst = isdst; // 0/1/negative |
1698 | 1596 | ||
1699 | lua_pushnumber(L_, static_cast<lua_Number>(std::mktime(&t))); // resolution: 1 second | 1597 | lua_pushnumber(L_, static_cast<lua_Number>(std::mktime(&t))); // resolution: 1 second |
1700 | return 1; | 1598 | return 1; |
@@ -1705,8 +1603,7 @@ LUAG_FUNC(wakeup_conv) | |||
1705 | // ################################################################################################# | 1603 | // ################################################################################################# |
1706 | 1604 | ||
1707 | extern int LG_linda(lua_State* L_); | 1605 | extern int LG_linda(lua_State* L_); |
1708 | static struct luaL_Reg const lanes_functions[] = | 1606 | static struct luaL_Reg const lanes_functions[] = { |
1709 | { | ||
1710 | { "linda", LG_linda }, | 1607 | { "linda", LG_linda }, |
1711 | { "now_secs", LG_now_secs }, | 1608 | { "now_secs", LG_now_secs }, |
1712 | { "wakeup_conv", LG_wakeup_conv }, | 1609 | { "wakeup_conv", LG_wakeup_conv }, |
@@ -1729,14 +1626,12 @@ LUAG_FUNC(configure) | |||
1729 | { | 1626 | { |
1730 | // C++ guarantees that the static variable initialization is threadsafe. | 1627 | // C++ guarantees that the static variable initialization is threadsafe. |
1731 | static auto _ = std::invoke( | 1628 | static auto _ = std::invoke( |
1732 | []() | 1629 | []() { |
1733 | { | ||
1734 | #if (defined PLATFORM_OSX) && (defined _UTILBINDTHREADTOCPU) | 1630 | #if (defined PLATFORM_OSX) && (defined _UTILBINDTHREADTOCPU) |
1735 | chudInitialize(); | 1631 | chudInitialize(); |
1736 | #endif | 1632 | #endif |
1737 | return false; | 1633 | return false; |
1738 | } | 1634 | }); |
1739 | ); | ||
1740 | } | 1635 | } |
1741 | 1636 | ||
1742 | Universe* U = universe_get(L_); | 1637 | Universe* U = universe_get(L_); |
@@ -1745,135 +1640,136 @@ LUAG_FUNC(configure) | |||
1745 | LUA_ASSERT(L_, lua_type(L_, 1) == LUA_TTABLE); | 1640 | LUA_ASSERT(L_, lua_type(L_, 1) == LUA_TTABLE); |
1746 | 1641 | ||
1747 | STACK_GROW(L_, 4); | 1642 | STACK_GROW(L_, 4); |
1748 | STACK_CHECK_START_ABS(L_, 1); // settings | 1643 | STACK_CHECK_START_ABS(L_, 1); // L_: settings |
1749 | 1644 | ||
1750 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L_)); | 1645 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END(U), L_)); |
1751 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1646 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1752 | 1647 | ||
1753 | if (U == nullptr) | 1648 | if (U == nullptr) { |
1754 | { | 1649 | U = universe_create(L_); // L_: settings universe |
1755 | U = universe_create(L_); // settings universe | ||
1756 | DEBUGSPEW_CODE(DebugSpewIndentScope scope2{ U }); | 1650 | DEBUGSPEW_CODE(DebugSpewIndentScope scope2{ U }); |
1757 | lua_newtable(L_); // settings universe mt | 1651 | lua_newtable(L_); // L_: settings universe mt |
1758 | lua_getfield(L_, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout | 1652 | lua_getfield(L_, 1, "shutdown_timeout"); // L_: settings universe mt shutdown_timeout |
1759 | lua_getfield(L_, 1, "shutdown_mode"); // settings universe mt shutdown_timeout shutdown_mode | 1653 | lua_getfield(L_, 1, "shutdown_mode"); // L_: settings universe mt shutdown_timeout shutdown_mode |
1760 | lua_pushcclosure(L_, universe_gc, 2); // settings universe mt universe_gc | 1654 | lua_pushcclosure(L_, universe_gc, 2); // L_: settings universe mt universe_gc |
1761 | lua_setfield(L_, -2, "__gc"); // settings universe mt | 1655 | lua_setfield(L_, -2, "__gc"); // L_: settings universe mt |
1762 | lua_setmetatable(L_, -2); // settings universe | 1656 | lua_setmetatable(L_, -2); // L_: settings universe |
1763 | lua_pop(L_, 1); // settings | 1657 | lua_pop(L_, 1); // L_: settings |
1764 | lua_getfield(L_, 1, "verbose_errors"); // settings verbose_errors | 1658 | lua_getfield(L_, 1, "verbose_errors"); // L_: settings verbose_errors |
1765 | U->verboseErrors = lua_toboolean(L_, -1) ? true : false; | 1659 | U->verboseErrors = lua_toboolean(L_, -1) ? true : false; |
1766 | lua_pop(L_, 1); // settings | 1660 | lua_pop(L_, 1); // L_: settings |
1767 | lua_getfield(L_, 1, "demote_full_userdata"); // settings demote_full_userdata | 1661 | lua_getfield(L_, 1, "demote_full_userdata"); // L_: settings demote_full_userdata |
1768 | U->demoteFullUserdata = lua_toboolean(L_, -1) ? true : false; | 1662 | U->demoteFullUserdata = lua_toboolean(L_, -1) ? true : false; |
1769 | lua_pop(L_, 1); // settings | 1663 | lua_pop(L_, 1); // L_: settings |
1770 | #if HAVE_LANE_TRACKING() | 1664 | #if HAVE_LANE_TRACKING() |
1771 | lua_getfield(L_, 1, "track_lanes"); // settings track_lanes | 1665 | lua_getfield(L_, 1, "track_lanes"); // L_: settings track_lanes |
1772 | U->tracking_first = lua_toboolean(L_, -1) ? TRACKING_END : nullptr; | 1666 | U->tracking_first = lua_toboolean(L_, -1) ? TRACKING_END : nullptr; |
1773 | lua_pop(L_, 1); // settings | 1667 | lua_pop(L_, 1); // L_: settings |
1774 | #endif // HAVE_LANE_TRACKING() | 1668 | #endif // HAVE_LANE_TRACKING() |
1775 | // Linked chains handling | 1669 | // Linked chains handling |
1776 | U->selfdestruct_first = SELFDESTRUCT_END; | 1670 | U->selfdestruct_first = SELFDESTRUCT_END; |
1777 | initialize_allocator_function( U, L_); | 1671 | initialize_allocator_function(U, L_); |
1778 | initialize_on_state_create( U, L_); | 1672 | initialize_on_state_create(U, L_); |
1779 | init_keepers( U, L_); | 1673 | init_keepers(U, L_); |
1780 | STACK_CHECK(L_, 1); | 1674 | STACK_CHECK(L_, 1); |
1781 | 1675 | ||
1782 | // Initialize 'timer_deep'; a common Linda object shared by all states | 1676 | // Initialize 'timer_deep'; a common Linda object shared by all states |
1783 | lua_pushcfunction(L_, LG_linda); // settings lanes.linda | 1677 | lua_pushcfunction(L_, LG_linda); // L_: settings lanes.linda |
1784 | lua_pushliteral(L_, "lanes-timer"); // settings lanes.linda "lanes-timer" | 1678 | lua_pushliteral(L_, "lanes-timer"); // L_: settings lanes.linda "lanes-timer" |
1785 | lua_call(L_, 1, 1); // settings linda | 1679 | lua_call(L_, 1, 1); // L_: settings linda |
1786 | STACK_CHECK(L_, 2); | 1680 | STACK_CHECK(L_, 2); |
1787 | 1681 | ||
1788 | // Proxy userdata contents is only a 'DeepPrelude*' pointer | 1682 | // Proxy userdata contents is only a 'DeepPrelude*' pointer |
1789 | U->timer_deep = *lua_tofulluserdata<DeepPrelude*>(L_, -1); | 1683 | U->timer_deep = *lua_tofulluserdata<DeepPrelude*>(L_, -1); |
1790 | // increment refcount so that this linda remains alive as long as the universe exists. | 1684 | // increment refcount so that this linda remains alive as long as the universe exists. |
1791 | U->timer_deep->m_refcount.fetch_add(1, std::memory_order_relaxed); | 1685 | U->timer_deep->m_refcount.fetch_add(1, std::memory_order_relaxed); |
1792 | lua_pop(L_, 1); // settings | 1686 | lua_pop(L_, 1); // L_: settings |
1793 | } | 1687 | } |
1794 | STACK_CHECK(L_, 1); | 1688 | STACK_CHECK(L_, 1); |
1795 | 1689 | ||
1796 | // Serialize calls to 'require' from now on, also in the primary state | 1690 | // Serialize calls to 'require' from now on, also in the primary state |
1797 | serialize_require( DEBUGSPEW_PARAM_COMMA( U) L_); | 1691 | serialize_require(DEBUGSPEW_PARAM_COMMA(U) L_); |
1798 | 1692 | ||
1799 | // Retrieve main module interface table | 1693 | // Retrieve main module interface table |
1800 | lua_pushvalue(L_, lua_upvalueindex( 2)); // settings M | 1694 | lua_pushvalue(L_, lua_upvalueindex(2)); // L_: settings M |
1801 | // remove configure() (this function) from the module interface | 1695 | // remove configure() (this function) from the module interface |
1802 | lua_pushnil( L_); // settings M nil | 1696 | lua_pushnil(L_); // L_: settings M nil |
1803 | lua_setfield(L_, -2, "configure"); // settings M | 1697 | lua_setfield(L_, -2, "configure"); // L_: settings M |
1804 | // add functions to the module's table | 1698 | // add functions to the module's table |
1805 | luaG_registerlibfuncs(L_, lanes_functions); | 1699 | luaG_registerlibfuncs(L_, lanes_functions); |
1806 | #if HAVE_LANE_TRACKING() | 1700 | #if HAVE_LANE_TRACKING() |
1807 | // register core.threads() only if settings say it should be available | 1701 | // register core.threads() only if settings say it should be available |
1808 | if (U->tracking_first != nullptr) | 1702 | if (U->tracking_first != nullptr) { |
1809 | { | 1703 | lua_pushcfunction(L_, LG_threads); // L_: settings M LG_threads() |
1810 | lua_pushcfunction(L_, LG_threads); // settings M LG_threads() | 1704 | lua_setfield(L_, -2, "threads"); // L_: settings M |
1811 | lua_setfield(L_, -2, "threads"); // settings M | ||
1812 | } | 1705 | } |
1813 | #endif // HAVE_LANE_TRACKING() | 1706 | #endif // HAVE_LANE_TRACKING() |
1814 | STACK_CHECK(L_, 2); | 1707 | STACK_CHECK(L_, 2); |
1815 | 1708 | ||
1816 | { | 1709 | { |
1817 | char const* errmsg{ DeepFactory::PushDeepProxy(DestState{ L_ }, U->timer_deep, 0, LookupMode::LaneBody) }; // settings M timer_deep | 1710 | char const* errmsg{ |
1818 | if (errmsg != nullptr) | 1711 | DeepFactory::PushDeepProxy(DestState{ L_ }, U->timer_deep, 0, LookupMode::LaneBody) |
1819 | { | 1712 | }; // L_: settings M timer_deep |
1713 | if (errmsg != nullptr) { | ||
1820 | raise_luaL_error(L_, errmsg); | 1714 | raise_luaL_error(L_, errmsg); |
1821 | } | 1715 | } |
1822 | lua_setfield(L_, -2, "timer_gateway"); // settings M | 1716 | lua_setfield(L_, -2, "timer_gateway"); // L_: settings M |
1823 | } | 1717 | } |
1824 | STACK_CHECK(L_, 2); | 1718 | STACK_CHECK(L_, 2); |
1825 | 1719 | ||
1826 | // prepare the metatable for threads | 1720 | // prepare the metatable for threads |
1827 | // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname } | 1721 | // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname } |
1828 | // | 1722 | // |
1829 | if (luaL_newmetatable(L_, "Lane")) // settings M mt | 1723 | if (luaL_newmetatable(L_, "Lane")) { // L_: settings M mt |
1830 | { | 1724 | lua_pushcfunction(L_, lane_gc); // L_: settings M mt lane_gc |
1831 | lua_pushcfunction(L_, lane_gc); // settings M mt lane_gc | 1725 | lua_setfield(L_, -2, "__gc"); // L_: settings M mt |
1832 | lua_setfield(L_, -2, "__gc"); // settings M mt | 1726 | lua_pushcfunction(L_, LG_thread_index); // L_: settings M mt LG_thread_index |
1833 | lua_pushcfunction(L_, LG_thread_index); // settings M mt LG_thread_index | 1727 | lua_setfield(L_, -2, "__index"); // L_: settings M mt |
1834 | lua_setfield(L_, -2, "__index"); // settings M mt | 1728 | lua_getglobal(L_, "error"); // L_: settings M mt error |
1835 | lua_getglobal(L_, "error"); // settings M mt error | ||
1836 | LUA_ASSERT(L_, lua_isfunction(L_, -1)); | 1729 | LUA_ASSERT(L_, lua_isfunction(L_, -1)); |
1837 | lua_setfield(L_, -2, "cached_error"); // settings M mt | 1730 | lua_setfield(L_, -2, "cached_error"); // L_: settings M mt |
1838 | lua_getglobal(L_, "tostring"); // settings M mt tostring | 1731 | lua_getglobal(L_, "tostring"); // L_: settings M mt tostring |
1839 | LUA_ASSERT(L_, lua_isfunction(L_, -1)); | 1732 | LUA_ASSERT(L_, lua_isfunction(L_, -1)); |
1840 | lua_setfield(L_, -2, "cached_tostring"); // settings M mt | 1733 | lua_setfield(L_, -2, "cached_tostring"); // L_: settings M mt |
1841 | lua_pushcfunction(L_, LG_thread_join); // settings M mt LG_thread_join | 1734 | lua_pushcfunction(L_, LG_thread_join); // L_: settings M mt LG_thread_join |
1842 | lua_setfield(L_, -2, "join"); // settings M mt | 1735 | lua_setfield(L_, -2, "join"); // L_: settings M mt |
1843 | lua_pushcfunction(L_, LG_get_debug_threadname); // settings M mt LG_get_debug_threadname | 1736 | lua_pushcfunction(L_, LG_get_debug_threadname); // L_: settings M mt LG_get_debug_threadname |
1844 | lua_setfield(L_, -2, "get_debug_threadname"); // settings M mt | 1737 | lua_setfield(L_, -2, "get_debug_threadname"); // L_: settings M mt |
1845 | lua_pushcfunction(L_, LG_thread_cancel); // settings M mt LG_thread_cancel | 1738 | lua_pushcfunction(L_, LG_thread_cancel); // L_: settings M mt LG_thread_cancel |
1846 | lua_setfield(L_, -2, "cancel"); // settings M mt | 1739 | lua_setfield(L_, -2, "cancel"); // L_: settings M mt |
1847 | lua_pushliteral(L_, "Lane"); // settings M mt "Lane" | 1740 | lua_pushliteral(L_, "Lane"); // L_: settings M mt "Lane" |
1848 | lua_setfield(L_, -2, "__metatable"); // settings M mt | 1741 | lua_setfield(L_, -2, "__metatable"); // L_: settings M mt |
1849 | } | 1742 | } |
1850 | 1743 | ||
1851 | lua_pushcclosure(L_, LG_lane_new, 1); // settings M lane_new | 1744 | lua_pushcclosure(L_, LG_lane_new, 1); // L_: settings M lane_new |
1852 | lua_setfield(L_, -2, "lane_new"); // settings M | 1745 | lua_setfield(L_, -2, "lane_new"); // L_: settings M |
1853 | 1746 | ||
1854 | // we can't register 'lanes.require' normally because we want to create an upvalued closure | 1747 | // we can't register 'lanes.require' normally because we want to create an upvalued closure |
1855 | lua_getglobal(L_, "require"); // settings M require | 1748 | lua_getglobal(L_, "require"); // L_: settings M require |
1856 | lua_pushcclosure(L_, LG_require, 1); // settings M lanes.require | 1749 | lua_pushcclosure(L_, LG_require, 1); // L_: settings M lanes.require |
1857 | lua_setfield(L_, -2, "require"); // settings M | 1750 | lua_setfield(L_, -2, "require"); // L_: settings M |
1858 | 1751 | ||
1859 | lua_pushfstring( | 1752 | lua_pushfstring( |
1860 | L_, "%d.%d.%d" | 1753 | L_, |
1861 | , LANES_VERSION_MAJOR, LANES_VERSION_MINOR, LANES_VERSION_PATCH | 1754 | "%d.%d.%d", |
1862 | ); // settings M VERSION | 1755 | LANES_VERSION_MAJOR, |
1863 | lua_setfield(L_, -2, "version"); // settings M | 1756 | LANES_VERSION_MINOR, |
1757 | LANES_VERSION_PATCH | ||
1758 | ); // L_: settings M VERSION | ||
1759 | lua_setfield(L_, -2, "version"); // L_: settings M | ||
1864 | 1760 | ||
1865 | lua_pushinteger(L_, kThreadPrioMax); // settings M kThreadPrioMax | 1761 | lua_pushinteger(L_, kThreadPrioMax); // L_: settings M kThreadPrioMax |
1866 | lua_setfield(L_, -2, "max_prio"); // settings M | 1762 | lua_setfield(L_, -2, "max_prio"); // L_: settings M |
1867 | 1763 | ||
1868 | kCancelError.pushKey(L_); // settings M kCancelError | 1764 | kCancelError.pushKey(L_); // L_: settings M kCancelError |
1869 | lua_setfield(L_, -2, "cancel_error"); // settings M | 1765 | lua_setfield(L_, -2, "cancel_error"); // L_: settings M |
1870 | 1766 | ||
1871 | kNilSentinel.pushKey(L_); // settings M kNilSentinel | 1767 | kNilSentinel.pushKey(L_); // L_: settings M kNilSentinel |
1872 | lua_setfield(L_, -2, "null"); // settings M | 1768 | lua_setfield(L_, -2, "null"); // L_: settings M |
1873 | 1769 | ||
1874 | STACK_CHECK(L_, 2); // reference stack contains only the function argument 'settings' | 1770 | STACK_CHECK(L_, 2); // reference stack contains only the function argument 'settings' |
1875 | // we'll need this every time we transfer some C function from/to this state | 1771 | // we'll need this every time we transfer some C function from/to this state |
1876 | kLookupRegKey.setValue(L_, [](lua_State* L_) { lua_newtable(L_); }); // settings M | 1772 | kLookupRegKey.setValue(L_, [](lua_State* L_) { lua_newtable(L_); }); // L_: settings M |
1877 | STACK_CHECK(L_, 2); | 1773 | STACK_CHECK(L_, 2); |
1878 | 1774 | ||
1879 | // register all native functions found in that module in the transferable functions database | 1775 | // register all native functions found in that module in the transferable functions database |
@@ -1884,21 +1780,20 @@ LUAG_FUNC(configure) | |||
1884 | 1780 | ||
1885 | // record all existing C/JIT-fast functions | 1781 | // record all existing C/JIT-fast functions |
1886 | // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack | 1782 | // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack |
1887 | if (from_master_state) | 1783 | if (from_master_state) { |
1888 | { | ||
1889 | // don't do this when called during the initialization of a new lane, | 1784 | // don't do this when called during the initialization of a new lane, |
1890 | // because we will do it after on_state_create() is called, | 1785 | // because we will do it after on_state_create() is called, |
1891 | // and we don't want to skip _G because of caching in case globals are created then | 1786 | // and we don't want to skip _G because of caching in case globals are created then |
1892 | lua_pushglobaltable(L_); // settings M _G | 1787 | lua_pushglobaltable(L_); // L_: settings M _G |
1893 | populate_func_lookup_table(L_, -1, nullptr); | 1788 | populate_func_lookup_table(L_, -1, nullptr); |
1894 | lua_pop(L_, 1); // settings M | 1789 | lua_pop(L_, 1); // L_: settings M |
1895 | } | 1790 | } |
1896 | lua_pop(L_, 1); // settings | 1791 | lua_pop(L_, 1); // L_: settings |
1897 | 1792 | ||
1898 | // set _R[kConfigRegKey] = settings | 1793 | // set _R[kConfigRegKey] = settings |
1899 | kConfigRegKey.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); | 1794 | kConfigRegKey.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); |
1900 | STACK_CHECK(L_, 1); | 1795 | STACK_CHECK(L_, 1); |
1901 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END, L_)); | 1796 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END(U), L_)); |
1902 | // Return the settings table | 1797 | // Return the settings table |
1903 | return 1; | 1798 | return 1; |
1904 | } | 1799 | } |
@@ -1911,35 +1806,32 @@ LUAG_FUNC(configure) | |||
1911 | 1806 | ||
1912 | void signal_handler(int signal) | 1807 | void signal_handler(int signal) |
1913 | { | 1808 | { |
1914 | if (signal == SIGABRT) | 1809 | if (signal == SIGABRT) { |
1915 | { | ||
1916 | _cprintf("caught abnormal termination!"); | 1810 | _cprintf("caught abnormal termination!"); |
1917 | abort(); | 1811 | abort(); |
1918 | } | 1812 | } |
1919 | } | 1813 | } |
1920 | 1814 | ||
1815 | // ################################################################################################# | ||
1816 | |||
1921 | // helper to have correct callstacks when crashing a Win32 running on 64 bits Windows | 1817 | // helper to have correct callstacks when crashing a Win32 running on 64 bits Windows |
1922 | // don't forget to toggle Debug/Exceptions/Win32 in visual Studio too! | 1818 | // don't forget to toggle Debug/Exceptions/Win32 in visual Studio too! |
1923 | static volatile long s_ecoc_initCount = 0; | 1819 | static volatile long s_ecoc_initCount = 0; |
1924 | static volatile int s_ecoc_go_ahead = 0; | 1820 | static volatile int s_ecoc_go_ahead = 0; |
1925 | static void EnableCrashingOnCrashes(void) | 1821 | static void EnableCrashingOnCrashes(void) |
1926 | { | 1822 | { |
1927 | if (InterlockedCompareExchange(&s_ecoc_initCount, 1, 0) == 0) | 1823 | if (InterlockedCompareExchange(&s_ecoc_initCount, 1, 0) == 0) { |
1928 | { | ||
1929 | typedef BOOL(WINAPI * tGetPolicy)(LPDWORD lpFlags); | 1824 | typedef BOOL(WINAPI * tGetPolicy)(LPDWORD lpFlags); |
1930 | typedef BOOL(WINAPI * tSetPolicy)(DWORD dwFlags); | 1825 | typedef BOOL(WINAPI * tSetPolicy)(DWORD dwFlags); |
1931 | const DWORD EXCEPTION_SWALLOWING = 0x1; | 1826 | const DWORD EXCEPTION_SWALLOWING = 0x1; |
1932 | 1827 | ||
1933 | HMODULE kernel32 = LoadLibraryA("kernel32.dll"); | 1828 | HMODULE kernel32 = LoadLibraryA("kernel32.dll"); |
1934 | if (kernel32) | 1829 | if (kernel32) { |
1935 | { | ||
1936 | tGetPolicy pGetPolicy = (tGetPolicy) GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy"); | 1830 | tGetPolicy pGetPolicy = (tGetPolicy) GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy"); |
1937 | tSetPolicy pSetPolicy = (tSetPolicy) GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy"); | 1831 | tSetPolicy pSetPolicy = (tSetPolicy) GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy"); |
1938 | if (pGetPolicy && pSetPolicy) | 1832 | if (pGetPolicy && pSetPolicy) { |
1939 | { | ||
1940 | DWORD dwFlags; | 1833 | DWORD dwFlags; |
1941 | if (pGetPolicy(&dwFlags)) | 1834 | if (pGetPolicy(&dwFlags)) { |
1942 | { | ||
1943 | // Turn off the filter | 1835 | // Turn off the filter |
1944 | pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING); | 1836 | pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING); |
1945 | } | 1837 | } |
@@ -1950,18 +1842,17 @@ static void EnableCrashingOnCrashes(void) | |||
1950 | /*SignalHandlerPointer previousHandler =*/signal(SIGABRT, signal_handler); | 1842 | /*SignalHandlerPointer previousHandler =*/signal(SIGABRT, signal_handler); |
1951 | 1843 | ||
1952 | s_ecoc_go_ahead = 1; // let others pass | 1844 | s_ecoc_go_ahead = 1; // let others pass |
1953 | } | 1845 | } else { |
1954 | else | 1846 | while (!s_ecoc_go_ahead) { |
1955 | { | ||
1956 | while (!s_ecoc_go_ahead) | ||
1957 | { | ||
1958 | Sleep(1); | 1847 | Sleep(1); |
1959 | } // changes threads | 1848 | } // changes threads |
1960 | } | 1849 | } |
1961 | } | 1850 | } |
1962 | #endif // PLATFORM_WIN32 && !defined NDEBUG | 1851 | #endif // PLATFORM_WIN32 && !defined NDEBUG |
1963 | 1852 | ||
1964 | LANES_API int luaopen_lanes_core( lua_State* L_) | 1853 | // ################################################################################################# |
1854 | |||
1855 | LANES_API int luaopen_lanes_core(lua_State* L_) | ||
1965 | { | 1856 | { |
1966 | #if defined PLATFORM_WIN32 && !defined NDEBUG | 1857 | #if defined PLATFORM_WIN32 && !defined NDEBUG |
1967 | EnableCrashingOnCrashes(); | 1858 | EnableCrashingOnCrashes(); |
@@ -1971,7 +1862,7 @@ LANES_API int luaopen_lanes_core( lua_State* L_) | |||
1971 | STACK_CHECK_START_REL(L_, 0); | 1862 | STACK_CHECK_START_REL(L_, 0); |
1972 | 1863 | ||
1973 | // Prevent PUC-Lua/LuaJIT mismatch. Hopefully this works for MoonJIT too | 1864 | // Prevent PUC-Lua/LuaJIT mismatch. Hopefully this works for MoonJIT too |
1974 | lua_getglobal(L_, "jit"); // {jit?} | 1865 | lua_getglobal(L_, "jit"); // L_: {jit?} |
1975 | #if LUAJIT_FLAVOR() == 0 | 1866 | #if LUAJIT_FLAVOR() == 0 |
1976 | if (!lua_isnil(L_, -1)) | 1867 | if (!lua_isnil(L_, -1)) |
1977 | raise_luaL_error(L_, "Lanes is built for PUC-Lua, don't run from LuaJIT"); | 1868 | raise_luaL_error(L_, "Lanes is built for PUC-Lua, don't run from LuaJIT"); |
@@ -1979,52 +1870,52 @@ LANES_API int luaopen_lanes_core( lua_State* L_) | |||
1979 | if (lua_isnil(L_, -1)) | 1870 | if (lua_isnil(L_, -1)) |
1980 | raise_luaL_error(L_, "Lanes is built for LuaJIT, don't run from PUC-Lua"); | 1871 | raise_luaL_error(L_, "Lanes is built for LuaJIT, don't run from PUC-Lua"); |
1981 | #endif | 1872 | #endif |
1982 | lua_pop(L_, 1); // | 1873 | lua_pop(L_, 1); // L_: |
1983 | STACK_CHECK(L_, 0); | 1874 | STACK_CHECK(L_, 0); |
1984 | 1875 | ||
1985 | // Create main module interface table | 1876 | // Create main module interface table |
1986 | // we only have 1 closure, which must be called to configure Lanes | 1877 | // we only have 1 closure, which must be called to configure Lanes |
1987 | lua_newtable(L_); // M | 1878 | lua_newtable(L_); // L_: M |
1988 | lua_pushvalue(L_, 1); // M "lanes.core" | 1879 | lua_pushvalue(L_, 1); // L_: M "lanes.core" |
1989 | lua_pushvalue(L_, -2); // M "lanes.core" M | 1880 | lua_pushvalue(L_, -2); // L_: M "lanes.core" M |
1990 | lua_pushcclosure(L_, LG_configure, 2); // M LG_configure() | 1881 | lua_pushcclosure(L_, LG_configure, 2); // L_: M LG_configure() |
1991 | kConfigRegKey.pushValue(L_); // M LG_configure() settings | 1882 | kConfigRegKey.pushValue(L_); // L_: M LG_configure() settings |
1992 | if (!lua_isnil(L_, -1)) // this is not the first require "lanes.core": call configure() immediately | 1883 | if (!lua_isnil(L_, -1)) { // this is not the first require "lanes.core": call configure() immediately |
1993 | { | 1884 | lua_pushvalue(L_, -1); // L_: M LG_configure() settings settings |
1994 | lua_pushvalue(L_, -1); // M LG_configure() settings settings | 1885 | lua_setfield(L_, -4, "settings"); // L_: M LG_configure() settings |
1995 | lua_setfield(L_, -4, "settings"); // M LG_configure() settings | 1886 | lua_call(L_, 1, 0); // L_: M |
1996 | lua_call(L_, 1, 0); // M | 1887 | } else { |
1997 | } | ||
1998 | else | ||
1999 | { | ||
2000 | // will do nothing on first invocation, as we haven't stored settings in the registry yet | 1888 | // will do nothing on first invocation, as we haven't stored settings in the registry yet |
2001 | lua_setfield(L_, -3, "settings"); // M LG_configure() | 1889 | lua_setfield(L_, -3, "settings"); // L_: M LG_configure() |
2002 | lua_setfield(L_, -2, "configure"); // M | 1890 | lua_setfield(L_, -2, "configure"); // L_: M |
2003 | } | 1891 | } |
2004 | 1892 | ||
2005 | STACK_CHECK(L_, 1); | 1893 | STACK_CHECK(L_, 1); |
2006 | return 1; | 1894 | return 1; |
2007 | } | 1895 | } |
2008 | 1896 | ||
1897 | // ################################################################################################# | ||
1898 | |||
2009 | [[nodiscard]] static int default_luaopen_lanes(lua_State* L_) | 1899 | [[nodiscard]] static int default_luaopen_lanes(lua_State* L_) |
2010 | { | 1900 | { |
2011 | int const rc{ luaL_loadfile(L_, "lanes.lua") || lua_pcall(L_, 0, 1, 0) }; | 1901 | int const rc{ luaL_loadfile(L_, "lanes.lua") || lua_pcall(L_, 0, 1, 0) }; |
2012 | if (rc != LUA_OK) | 1902 | if (rc != LUA_OK) { |
2013 | { | ||
2014 | raise_luaL_error(L_, "failed to initialize embedded Lanes"); | 1903 | raise_luaL_error(L_, "failed to initialize embedded Lanes"); |
2015 | } | 1904 | } |
2016 | return 1; | 1905 | return 1; |
2017 | } | 1906 | } |
2018 | 1907 | ||
1908 | // ################################################################################################# | ||
1909 | |||
2019 | // call this instead of luaopen_lanes_core() when embedding Lua and Lanes in a custom application | 1910 | // call this instead of luaopen_lanes_core() when embedding Lua and Lanes in a custom application |
2020 | LANES_API void luaopen_lanes_embedded( lua_State* L_, lua_CFunction _luaopen_lanes) | 1911 | LANES_API void luaopen_lanes_embedded(lua_State* L_, lua_CFunction _luaopen_lanes) |
2021 | { | 1912 | { |
2022 | STACK_CHECK_START_REL(L_, 0); | 1913 | STACK_CHECK_START_REL(L_, 0); |
2023 | // pre-require lanes.core so that when lanes.lua calls require "lanes.core" it finds it is already loaded | 1914 | // pre-require lanes.core so that when lanes.lua calls require "lanes.core" it finds it is already loaded |
2024 | luaL_requiref(L_, "lanes.core", luaopen_lanes_core, 0); // ... lanes.core | 1915 | luaL_requiref(L_, "lanes.core", luaopen_lanes_core, 0); // L_: ... lanes.core |
2025 | lua_pop(L_, 1); // ... | 1916 | lua_pop(L_, 1); // L_: ... |
2026 | STACK_CHECK(L_, 0); | 1917 | STACK_CHECK(L_, 0); |
2027 | // call user-provided function that runs the chunk "lanes.lua" from wherever they stored it | 1918 | // call user-provided function that runs the chunk "lanes.lua" from wherever they stored it |
2028 | luaL_requiref(L_, "lanes", _luaopen_lanes ? _luaopen_lanes : default_luaopen_lanes, 0); // ... lanes | 1919 | luaL_requiref(L_, "lanes", _luaopen_lanes ? _luaopen_lanes : default_luaopen_lanes, 0); // L_: ... lanes |
2029 | STACK_CHECK(L_, 1); | 1920 | STACK_CHECK(L_, 1); |
2030 | } | 1921 | } |
diff --git a/src/linda.cpp b/src/linda.cpp index f88158a..82f5f98 100644 --- a/src/linda.cpp +++ b/src/linda.cpp | |||
@@ -2,7 +2,7 @@ | |||
2 | * LINDA.CPP Copyright (c) 2018-2024, Benoit Germain | 2 | * LINDA.CPP Copyright (c) 2018-2024, Benoit Germain |
3 | * | 3 | * |
4 | * Linda deep userdata. | 4 | * Linda deep userdata. |
5 | */ | 5 | */ |
6 | 6 | ||
7 | /* | 7 | /* |
8 | =============================================================================== | 8 | =============================================================================== |
@@ -62,8 +62,7 @@ Linda::Linda(Universe* U_, LindaGroup group_, char const* name_, size_t len_) | |||
62 | 62 | ||
63 | Linda::~Linda() | 63 | Linda::~Linda() |
64 | { | 64 | { |
65 | if (std::holds_alternative<AllocatedName>(m_name)) | 65 | if (std::holds_alternative<AllocatedName>(m_name)) { |
66 | { | ||
67 | AllocatedName& name = std::get<AllocatedName>(m_name); | 66 | AllocatedName& name = std::get<AllocatedName>(m_name); |
68 | U->internal_allocator.free(name.name, name.len); | 67 | U->internal_allocator.free(name.name, name.len); |
69 | } | 68 | } |
@@ -74,19 +73,15 @@ Linda::~Linda() | |||
74 | void Linda::setName(char const* name_, size_t len_) | 73 | void Linda::setName(char const* name_, size_t len_) |
75 | { | 74 | { |
76 | // keep default | 75 | // keep default |
77 | if (!name_ || len_ == 0) | 76 | if (!name_ || len_ == 0) { |
78 | { | ||
79 | return; | 77 | return; |
80 | } | 78 | } |
81 | ++len_; // don't forget terminating 0 | 79 | ++len_; // don't forget terminating 0 |
82 | if (len_ < kEmbeddedNameLength) | 80 | if (len_ < kEmbeddedNameLength) { |
83 | { | ||
84 | m_name.emplace<EmbeddedName>(); | 81 | m_name.emplace<EmbeddedName>(); |
85 | char* const name{ std::get<EmbeddedName>(m_name).data() }; | 82 | char* const name{ std::get<EmbeddedName>(m_name).data() }; |
86 | memcpy(name, name_, len_); | 83 | memcpy(name, name_, len_); |
87 | } | 84 | } else { |
88 | else | ||
89 | { | ||
90 | AllocatedName& name = std::get<AllocatedName>(m_name); | 85 | AllocatedName& name = std::get<AllocatedName>(m_name); |
91 | name.name = static_cast<char*>(U->internal_allocator.alloc(len_)); | 86 | name.name = static_cast<char*>(U->internal_allocator.alloc(len_)); |
92 | name.len = len_; | 87 | name.len = len_; |
@@ -98,13 +93,11 @@ void Linda::setName(char const* name_, size_t len_) | |||
98 | 93 | ||
99 | char const* Linda::getName() const | 94 | char const* Linda::getName() const |
100 | { | 95 | { |
101 | if (std::holds_alternative<AllocatedName>(m_name)) | 96 | if (std::holds_alternative<AllocatedName>(m_name)) { |
102 | { | ||
103 | AllocatedName const& name = std::get<AllocatedName>(m_name); | 97 | AllocatedName const& name = std::get<AllocatedName>(m_name); |
104 | return name.name; | 98 | return name.name; |
105 | } | 99 | } |
106 | if (std::holds_alternative<EmbeddedName>(m_name)) | 100 | if (std::holds_alternative<EmbeddedName>(m_name)) { |
107 | { | ||
108 | char const* const name{ std::get<EmbeddedName>(m_name).data() }; | 101 | char const* const name{ std::get<EmbeddedName>(m_name).data() }; |
109 | return name; | 102 | return name; |
110 | } | 103 | } |
@@ -117,8 +110,7 @@ template <bool OPT> | |||
117 | [[nodiscard]] static inline Linda* ToLinda(lua_State* L_, int idx_) | 110 | [[nodiscard]] static inline Linda* ToLinda(lua_State* L_, int idx_) |
118 | { | 111 | { |
119 | Linda* const linda{ static_cast<Linda*>(LindaFactory::Instance.toDeep(L_, idx_)) }; | 112 | Linda* const linda{ static_cast<Linda*>(LindaFactory::Instance.toDeep(L_, idx_)) }; |
120 | if constexpr (!OPT) | 113 | if constexpr (!OPT) { |
121 | { | ||
122 | luaL_argcheck(L_, linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr | 114 | luaL_argcheck(L_, linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr |
123 | LUA_ASSERT(L_, linda->U == universe_get(L_)); | 115 | LUA_ASSERT(L_, linda->U == universe_get(L_)); |
124 | } | 116 | } |
@@ -129,26 +121,20 @@ template <bool OPT> | |||
129 | 121 | ||
130 | static void check_key_types(lua_State* L_, int start_, int end_) | 122 | static void check_key_types(lua_State* L_, int start_, int end_) |
131 | { | 123 | { |
132 | for (int i{ start_ }; i <= end_; ++i) | 124 | for (int i{ start_ }; i <= end_; ++i) { |
133 | { | ||
134 | LuaType const t{ lua_type_as_enum(L_, i) }; | 125 | LuaType const t{ lua_type_as_enum(L_, i) }; |
135 | switch (t) | 126 | switch (t) { |
136 | { | 127 | case LuaType::BOOLEAN: |
137 | case LuaType::BOOLEAN: | 128 | case LuaType::NUMBER: |
138 | case LuaType::NUMBER: | 129 | case LuaType::STRING: |
139 | case LuaType::STRING: | ||
140 | continue; | 130 | continue; |
141 | 131 | ||
142 | case LuaType::LIGHTUSERDATA: | 132 | case LuaType::LIGHTUSERDATA: |
143 | { | 133 | static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel }; |
144 | static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel }; | 134 | for (UniqueKey const& key : kKeysToCheck) { |
145 | for (UniqueKey const& key : kKeysToCheck) | 135 | if (key.equals(L_, i)) { |
146 | { | 136 | raise_luaL_error(L_, "argument #%d: can't use %s as a key", i, key.m_debugName); |
147 | if (key.equals(L_, i)) | 137 | break; |
148 | { | ||
149 | raise_luaL_error(L_, "argument #%d: can't use %s as a key", i, key.m_debugName); | ||
150 | break; | ||
151 | } | ||
152 | } | 138 | } |
153 | } | 139 | } |
154 | break; | 140 | break; |
@@ -184,8 +170,7 @@ int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_) | |||
184 | linda->releaseKeeper(K); | 170 | linda->releaseKeeper(K); |
185 | 171 | ||
186 | // if there was an error, forward it | 172 | // if there was an error, forward it |
187 | if (rc != LUA_OK) | 173 | if (rc != LUA_OK) { |
188 | { | ||
189 | raise_lua_error(L_); | 174 | raise_lua_error(L_); |
190 | } | 175 | } |
191 | // return whatever the actual operation provided | 176 | // return whatever the actual operation provided |
@@ -195,39 +180,33 @@ int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_) | |||
195 | // ################################################################################################# | 180 | // ################################################################################################# |
196 | 181 | ||
197 | /* | 182 | /* |
198 | * bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... ) | 183 | * bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... ) |
199 | * | 184 | * |
200 | * Send one or more values to a Linda. If there is a limit, all values must fit. | 185 | * Send one or more values to a Linda. If there is a limit, all values must fit. |
201 | * | 186 | * |
202 | * Returns: 'true' if the value was queued | 187 | * Returns: 'true' if the value was queued |
203 | * 'false' for timeout (only happens when the queue size is limited) | 188 | * 'false' for timeout (only happens when the queue size is limited) |
204 | * nil, kCancelError if cancelled | 189 | * nil, kCancelError if cancelled |
205 | */ | 190 | */ |
206 | LUAG_FUNC(linda_send) | 191 | LUAG_FUNC(linda_send) |
207 | { | 192 | { |
208 | auto send = [](lua_State* L_) | 193 | auto send = [](lua_State* L_) { |
209 | { | ||
210 | Linda* const linda{ ToLinda<false>(L_, 1) }; | 194 | Linda* const linda{ ToLinda<false>(L_, 1) }; |
211 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | 195 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; |
212 | int key_i{ 2 }; // index of first key, if timeout not there | 196 | int key_i{ 2 }; // index of first key, if timeout not there |
213 | 197 | ||
214 | if (lua_type(L_, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | 198 | if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion |
215 | { | ||
216 | lua_Duration const duration{ lua_tonumber(L_, 2) }; | 199 | lua_Duration const duration{ lua_tonumber(L_, 2) }; |
217 | if (duration.count() >= 0.0) | 200 | if (duration.count() >= 0.0) { |
218 | { | ||
219 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); | 201 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); |
220 | } | 202 | } |
221 | ++key_i; | 203 | ++key_i; |
222 | } | 204 | } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key |
223 | else if (lua_isnil(L_, 2)) // alternate explicit "infinite timeout" by passing nil before the key | ||
224 | { | ||
225 | ++key_i; | 205 | ++key_i; |
226 | } | 206 | } |
227 | 207 | ||
228 | bool const as_nil_sentinel{ kNilSentinel.equals(L_, key_i) }; // if not nullptr, send() will silently send a single nil if nothing is provided | 208 | bool const as_nil_sentinel{ kNilSentinel.equals(L_, key_i) }; // if not nullptr, send() will silently send a single nil if nothing is provided |
229 | if (as_nil_sentinel) | 209 | if (as_nil_sentinel) { |
230 | { | ||
231 | // the real key to send data to is after the kNilSentinel marker | 210 | // the real key to send data to is after the kNilSentinel marker |
232 | ++key_i; | 211 | ++key_i; |
233 | } | 212 | } |
@@ -238,15 +217,11 @@ LUAG_FUNC(linda_send) | |||
238 | STACK_GROW(L_, 1); | 217 | STACK_GROW(L_, 1); |
239 | 218 | ||
240 | // make sure there is something to send | 219 | // make sure there is something to send |
241 | if (lua_gettop(L_) == key_i) | 220 | if (lua_gettop(L_) == key_i) { |
242 | { | 221 | if (as_nil_sentinel) { |
243 | if (as_nil_sentinel) | ||
244 | { | ||
245 | // send a single nil if nothing is provided | 222 | // send a single nil if nothing is provided |
246 | kNilSentinel.pushKey(L_); | 223 | kNilSentinel.pushKey(L_); |
247 | } | 224 | } else { |
248 | else | ||
249 | { | ||
250 | raise_luaL_error(L_, "no data to send"); | 225 | raise_luaL_error(L_, "no data to send"); |
251 | } | 226 | } |
252 | } | 227 | } |
@@ -264,24 +239,20 @@ LUAG_FUNC(linda_send) | |||
264 | return 0; | 239 | return 0; |
265 | 240 | ||
266 | STACK_CHECK_START_REL(KL, 0); | 241 | STACK_CHECK_START_REL(KL, 0); |
267 | for (bool try_again{ true };;) | 242 | for (bool try_again{ true };;) { |
268 | { | 243 | if (lane != nullptr) { |
269 | if (lane != nullptr) | ||
270 | { | ||
271 | cancel = lane->cancel_request; | 244 | cancel = lane->cancel_request; |
272 | } | 245 | } |
273 | cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; | 246 | cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; |
274 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | 247 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything |
275 | if (!try_again || cancel != CancelRequest::None) | 248 | if (!try_again || cancel != CancelRequest::None) { |
276 | { | ||
277 | pushed.emplace(0); | 249 | pushed.emplace(0); |
278 | break; | 250 | break; |
279 | } | 251 | } |
280 | 252 | ||
281 | STACK_CHECK(KL, 0); | 253 | STACK_CHECK(KL, 0); |
282 | pushed = keeper_call(linda->U, KL, KEEPER_API(send), L_, linda, key_i); | 254 | pushed = keeper_call(linda->U, KL, KEEPER_API(send), L_, linda, key_i); |
283 | if (!pushed.has_value()) | 255 | if (!pushed.has_value()) { |
284 | { | ||
285 | break; | 256 | break; |
286 | } | 257 | } |
287 | LUA_ASSERT(L_, pushed.value() == 1); | 258 | LUA_ASSERT(L_, pushed.value() == 1); |
@@ -289,26 +260,23 @@ LUAG_FUNC(linda_send) | |||
289 | ret = lua_toboolean(L_, -1) ? true : false; | 260 | ret = lua_toboolean(L_, -1) ? true : false; |
290 | lua_pop(L_, 1); | 261 | lua_pop(L_, 1); |
291 | 262 | ||
292 | if (ret) | 263 | if (ret) { |
293 | { | ||
294 | // Wake up ALL waiting threads | 264 | // Wake up ALL waiting threads |
295 | linda->m_write_happened.notify_all(); | 265 | linda->m_write_happened.notify_all(); |
296 | break; | 266 | break; |
297 | } | 267 | } |
298 | 268 | ||
299 | // instant timout to bypass the wait syscall | 269 | // instant timout to bypass the wait syscall |
300 | if (std::chrono::steady_clock::now() >= until) | 270 | if (std::chrono::steady_clock::now() >= until) { |
301 | { | ||
302 | break; /* no wait; instant timeout */ | 271 | break; /* no wait; instant timeout */ |
303 | } | 272 | } |
304 | 273 | ||
305 | // storage limit hit, wait until timeout or signalled that we should try again | 274 | // storage limit hit, wait until timeout or signalled that we should try again |
306 | { | 275 | { |
307 | Lane::Status prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings | 276 | Lane::Status prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings |
308 | if (lane != nullptr) | 277 | if (lane != nullptr) { |
309 | { | ||
310 | // change status of lane to "waiting" | 278 | // change status of lane to "waiting" |
311 | prev_status = lane->m_status; // Running, most likely | 279 | prev_status = lane->m_status; // Running, most likely |
312 | LUA_ASSERT(L_, prev_status == Lane::Running); // but check, just in case | 280 | LUA_ASSERT(L_, prev_status == Lane::Running); // but check, just in case |
313 | lane->m_status = Lane::Waiting; | 281 | lane->m_status = Lane::Waiting; |
314 | LUA_ASSERT(L_, lane->m_waiting_on == nullptr); | 282 | LUA_ASSERT(L_, lane->m_waiting_on == nullptr); |
@@ -317,10 +285,9 @@ LUAG_FUNC(linda_send) | |||
317 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | 285 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached |
318 | std::unique_lock<std::mutex> keeper_lock{ K->m_mutex, std::adopt_lock }; | 286 | std::unique_lock<std::mutex> keeper_lock{ K->m_mutex, std::adopt_lock }; |
319 | std::cv_status const status{ linda->m_read_happened.wait_until(keeper_lock, until) }; | 287 | std::cv_status const status{ linda->m_read_happened.wait_until(keeper_lock, until) }; |
320 | keeper_lock.release(); // we don't want to release the lock! | 288 | keeper_lock.release(); // we don't want to release the lock! |
321 | try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups | 289 | try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups |
322 | if (lane != nullptr) | 290 | if (lane != nullptr) { |
323 | { | ||
324 | lane->m_waiting_on = nullptr; | 291 | lane->m_waiting_on = nullptr; |
325 | lane->m_status = prev_status; | 292 | lane->m_status = prev_status; |
326 | } | 293 | } |
@@ -329,23 +296,21 @@ LUAG_FUNC(linda_send) | |||
329 | STACK_CHECK(KL, 0); | 296 | STACK_CHECK(KL, 0); |
330 | } | 297 | } |
331 | 298 | ||
332 | if (!pushed.has_value()) | 299 | if (!pushed.has_value()) { |
333 | { | ||
334 | raise_luaL_error(L_, "tried to copy unsupported types"); | 300 | raise_luaL_error(L_, "tried to copy unsupported types"); |
335 | } | 301 | } |
336 | 302 | ||
337 | switch (cancel) | 303 | switch (cancel) { |
338 | { | 304 | case CancelRequest::Soft: |
339 | case CancelRequest::Soft: | ||
340 | // if user wants to soft-cancel, the call returns lanes.cancel_error | 305 | // if user wants to soft-cancel, the call returns lanes.cancel_error |
341 | kCancelError.pushKey(L_); | 306 | kCancelError.pushKey(L_); |
342 | return 1; | 307 | return 1; |
343 | 308 | ||
344 | case CancelRequest::Hard: | 309 | case CancelRequest::Hard: |
345 | // raise an error interrupting execution only in case of hard cancel | 310 | // raise an error interrupting execution only in case of hard cancel |
346 | raise_cancel_error(L_); // raises an error and doesn't return | 311 | raise_cancel_error(L_); // raises an error and doesn't return |
347 | 312 | ||
348 | default: | 313 | default: |
349 | lua_pushboolean(L_, ret); // true (success) or false (timeout) | 314 | lua_pushboolean(L_, ret); // true (success) or false (timeout) |
350 | return 1; | 315 | return 1; |
351 | } | 316 | } |
@@ -368,23 +333,18 @@ LUAG_FUNC(linda_send) | |||
368 | */ | 333 | */ |
369 | LUAG_FUNC(linda_receive) | 334 | LUAG_FUNC(linda_receive) |
370 | { | 335 | { |
371 | auto receive = [](lua_State* L_) | 336 | auto receive = [](lua_State* L_) { |
372 | { | ||
373 | Linda* const linda{ ToLinda<false>(L_, 1) }; | 337 | Linda* const linda{ ToLinda<false>(L_, 1) }; |
374 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | 338 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; |
375 | int key_i{ 2 }; // index of first key, if timeout not there | 339 | int key_i{ 2 }; // index of first key, if timeout not there |
376 | 340 | ||
377 | if (lua_type(L_, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | 341 | if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion |
378 | { | ||
379 | lua_Duration const duration{ lua_tonumber(L_, 2) }; | 342 | lua_Duration const duration{ lua_tonumber(L_, 2) }; |
380 | if (duration.count() >= 0.0) | 343 | if (duration.count() >= 0.0) { |
381 | { | ||
382 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); | 344 | until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); |
383 | } | 345 | } |
384 | ++key_i; | 346 | ++key_i; |
385 | } | 347 | } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key |
386 | else if (lua_isnil(L_, 2)) // alternate explicit "infinite timeout" by passing nil before the key | ||
387 | { | ||
388 | ++key_i; | 348 | ++key_i; |
389 | } | 349 | } |
390 | 350 | ||
@@ -394,8 +354,7 @@ LUAG_FUNC(linda_receive) | |||
394 | kLindaBatched.pushKey(L_); | 354 | kLindaBatched.pushKey(L_); |
395 | int const is_batched{ lua501_equal(L_, key_i, -1) }; | 355 | int const is_batched{ lua501_equal(L_, key_i, -1) }; |
396 | lua_pop(L_, 1); | 356 | lua_pop(L_, 1); |
397 | if (is_batched) | 357 | if (is_batched) { |
398 | { | ||
399 | // no need to pass linda.batched in the keeper state | 358 | // no need to pass linda.batched in the keeper state |
400 | ++key_i; | 359 | ++key_i; |
401 | // make sure the keys are of a valid type | 360 | // make sure the keys are of a valid type |
@@ -408,13 +367,10 @@ LUAG_FUNC(linda_receive) | |||
408 | // don't forget to count the key in addition to the values | 367 | // don't forget to count the key in addition to the values |
409 | ++expected_pushed_min; | 368 | ++expected_pushed_min; |
410 | ++expected_pushed_max; | 369 | ++expected_pushed_max; |
411 | if (expected_pushed_min > expected_pushed_max) | 370 | if (expected_pushed_min > expected_pushed_max) { |
412 | { | ||
413 | raise_luaL_error(L_, "batched min/max error"); | 371 | raise_luaL_error(L_, "batched min/max error"); |
414 | } | 372 | } |
415 | } | 373 | } else { |
416 | else | ||
417 | { | ||
418 | // make sure the keys are of a valid type | 374 | // make sure the keys are of a valid type |
419 | check_key_types(L_, key_i, lua_gettop(L_)); | 375 | check_key_types(L_, key_i, lua_gettop(L_)); |
420 | // receive a single value, checking multiple slots | 376 | // receive a single value, checking multiple slots |
@@ -432,28 +388,23 @@ LUAG_FUNC(linda_receive) | |||
432 | CancelRequest cancel{ CancelRequest::None }; | 388 | CancelRequest cancel{ CancelRequest::None }; |
433 | KeeperCallResult pushed; | 389 | KeeperCallResult pushed; |
434 | STACK_CHECK_START_REL(KL, 0); | 390 | STACK_CHECK_START_REL(KL, 0); |
435 | for (bool try_again{ true };;) | 391 | for (bool try_again{ true };;) { |
436 | { | 392 | if (lane != nullptr) { |
437 | if (lane != nullptr) | ||
438 | { | ||
439 | cancel = lane->cancel_request; | 393 | cancel = lane->cancel_request; |
440 | } | 394 | } |
441 | cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; | 395 | cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; |
442 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | 396 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything |
443 | if (!try_again || cancel != CancelRequest::None) | 397 | if (!try_again || cancel != CancelRequest::None) { |
444 | { | ||
445 | pushed.emplace(0); | 398 | pushed.emplace(0); |
446 | break; | 399 | break; |
447 | } | 400 | } |
448 | 401 | ||
449 | // all arguments of receive() but the first are passed to the keeper's receive function | 402 | // all arguments of receive() but the first are passed to the keeper's receive function |
450 | pushed = keeper_call(linda->U, KL, selected_keeper_receive, L_, linda, key_i); | 403 | pushed = keeper_call(linda->U, KL, selected_keeper_receive, L_, linda, key_i); |
451 | if (!pushed.has_value()) | 404 | if (!pushed.has_value()) { |
452 | { | ||
453 | break; | 405 | break; |
454 | } | 406 | } |
455 | if (pushed.value() > 0) | 407 | if (pushed.value() > 0) { |
456 | { | ||
457 | LUA_ASSERT(L_, pushed.value() >= expected_pushed_min && pushed.value() <= expected_pushed_max); | 408 | LUA_ASSERT(L_, pushed.value() >= expected_pushed_min && pushed.value() <= expected_pushed_max); |
458 | // replace sentinels with real nils | 409 | // replace sentinels with real nils |
459 | keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - pushed.value(), LookupMode::FromKeeper); | 410 | keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - pushed.value(), LookupMode::FromKeeper); |
@@ -463,18 +414,16 @@ LUAG_FUNC(linda_receive) | |||
463 | break; | 414 | break; |
464 | } | 415 | } |
465 | 416 | ||
466 | if (std::chrono::steady_clock::now() >= until) | 417 | if (std::chrono::steady_clock::now() >= until) { |
467 | { | ||
468 | break; /* instant timeout */ | 418 | break; /* instant timeout */ |
469 | } | 419 | } |
470 | 420 | ||
471 | // nothing received, wait until timeout or signalled that we should try again | 421 | // nothing received, wait until timeout or signalled that we should try again |
472 | { | 422 | { |
473 | Lane::Status prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings | 423 | Lane::Status prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings |
474 | if (lane != nullptr) | 424 | if (lane != nullptr) { |
475 | { | ||
476 | // change status of lane to "waiting" | 425 | // change status of lane to "waiting" |
477 | prev_status = lane->m_status; // Running, most likely | 426 | prev_status = lane->m_status; // Running, most likely |
478 | LUA_ASSERT(L_, prev_status == Lane::Running); // but check, just in case | 427 | LUA_ASSERT(L_, prev_status == Lane::Running); // but check, just in case |
479 | lane->m_status = Lane::Waiting; | 428 | lane->m_status = Lane::Waiting; |
480 | LUA_ASSERT(L_, lane->m_waiting_on == nullptr); | 429 | LUA_ASSERT(L_, lane->m_waiting_on == nullptr); |
@@ -483,10 +432,9 @@ LUAG_FUNC(linda_receive) | |||
483 | // not enough data to read: wakeup when data was sent, or when timeout is reached | 432 | // not enough data to read: wakeup when data was sent, or when timeout is reached |
484 | std::unique_lock<std::mutex> keeper_lock{ K->m_mutex, std::adopt_lock }; | 433 | std::unique_lock<std::mutex> keeper_lock{ K->m_mutex, std::adopt_lock }; |
485 | std::cv_status const status{ linda->m_write_happened.wait_until(keeper_lock, until) }; | 434 | std::cv_status const status{ linda->m_write_happened.wait_until(keeper_lock, until) }; |
486 | keeper_lock.release(); // we don't want to release the lock! | 435 | keeper_lock.release(); // we don't want to release the lock! |
487 | try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups | 436 | try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups |
488 | if (lane != nullptr) | 437 | if (lane != nullptr) { |
489 | { | ||
490 | lane->m_waiting_on = nullptr; | 438 | lane->m_waiting_on = nullptr; |
491 | lane->m_status = prev_status; | 439 | lane->m_status = prev_status; |
492 | } | 440 | } |
@@ -494,23 +442,21 @@ LUAG_FUNC(linda_receive) | |||
494 | } | 442 | } |
495 | STACK_CHECK(KL, 0); | 443 | STACK_CHECK(KL, 0); |
496 | 444 | ||
497 | if (!pushed.has_value()) | 445 | if (!pushed.has_value()) { |
498 | { | ||
499 | raise_luaL_error(L_, "tried to copy unsupported types"); | 446 | raise_luaL_error(L_, "tried to copy unsupported types"); |
500 | } | 447 | } |
501 | 448 | ||
502 | switch (cancel) | 449 | switch (cancel) { |
503 | { | 450 | case CancelRequest::Soft: |
504 | case CancelRequest::Soft: | ||
505 | // if user wants to soft-cancel, the call returns kCancelError | 451 | // if user wants to soft-cancel, the call returns kCancelError |
506 | kCancelError.pushKey(L_); | 452 | kCancelError.pushKey(L_); |
507 | return 1; | 453 | return 1; |
508 | 454 | ||
509 | case CancelRequest::Hard: | 455 | case CancelRequest::Hard: |
510 | // raise an error interrupting execution only in case of hard cancel | 456 | // raise an error interrupting execution only in case of hard cancel |
511 | raise_cancel_error(L_); // raises an error and doesn't return | 457 | raise_cancel_error(L_); // raises an error and doesn't return |
512 | 458 | ||
513 | default: | 459 | default: |
514 | return pushed.value(); | 460 | return pushed.value(); |
515 | } | 461 | } |
516 | }; | 462 | }; |
@@ -520,17 +466,16 @@ LUAG_FUNC(linda_receive) | |||
520 | // ################################################################################################# | 466 | // ################################################################################################# |
521 | 467 | ||
522 | /* | 468 | /* |
523 | * [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) | 469 | * [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) |
524 | * | 470 | * |
525 | * Set one or more value to Linda. | 471 | * Set one or more value to Linda. |
526 | * TODO: what do we do if we set to non-nil and limit is 0? | 472 | * TODO: what do we do if we set to non-nil and limit is 0? |
527 | * | 473 | * |
528 | * Existing slot value is replaced, and possible queued entries removed. | 474 | * Existing slot value is replaced, and possible queued entries removed. |
529 | */ | 475 | */ |
530 | LUAG_FUNC(linda_set) | 476 | LUAG_FUNC(linda_set) |
531 | { | 477 | { |
532 | auto set = [](lua_State* L_) | 478 | auto set = [](lua_State* L_) { |
533 | { | ||
534 | Linda* const linda{ ToLinda<false>(L_, 1) }; | 479 | Linda* const linda{ ToLinda<false>(L_, 1) }; |
535 | bool const has_value{ lua_gettop(L_) > 2 }; | 480 | bool const has_value{ lua_gettop(L_) > 2 }; |
536 | // make sure the key is of a valid type (throws an error if not the case) | 481 | // make sure the key is of a valid type (throws an error if not the case) |
@@ -538,33 +483,26 @@ LUAG_FUNC(linda_set) | |||
538 | 483 | ||
539 | Keeper* const K{ linda->whichKeeper() }; | 484 | Keeper* const K{ linda->whichKeeper() }; |
540 | KeeperCallResult pushed; | 485 | KeeperCallResult pushed; |
541 | if (linda->simulate_cancel == CancelRequest::None) | 486 | if (linda->simulate_cancel == CancelRequest::None) { |
542 | { | 487 | if (has_value) { |
543 | if (has_value) | ||
544 | { | ||
545 | // convert nils to some special non-nil sentinel in sent values | 488 | // convert nils to some special non-nil sentinel in sent values |
546 | keeper_toggle_nil_sentinels(L_, 3, LookupMode::ToKeeper); | 489 | keeper_toggle_nil_sentinels(L_, 3, LookupMode::ToKeeper); |
547 | } | 490 | } |
548 | pushed = keeper_call(linda->U, K->L, KEEPER_API(set), L_, linda, 2); | 491 | pushed = keeper_call(linda->U, K->L, KEEPER_API(set), L_, linda, 2); |
549 | if (pushed.has_value()) // no error? | 492 | if (pushed.has_value()) { // no error? |
550 | { | ||
551 | LUA_ASSERT(L_, pushed.value() == 0 || pushed.value() == 1); | 493 | LUA_ASSERT(L_, pushed.value() == 0 || pushed.value() == 1); |
552 | 494 | ||
553 | if (has_value) | 495 | if (has_value) { |
554 | { | ||
555 | // we put some data in the slot, tell readers that they should wake | 496 | // we put some data in the slot, tell readers that they should wake |
556 | linda->m_write_happened.notify_all(); // To be done from within the 'K' locking area | 497 | linda->m_write_happened.notify_all(); // To be done from within the 'K' locking area |
557 | } | 498 | } |
558 | if (pushed.value() == 1) | 499 | if (pushed.value() == 1) { |
559 | { | ||
560 | // the key was full, but it is no longer the case, tell writers they should wake | 500 | // the key was full, but it is no longer the case, tell writers they should wake |
561 | LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TBOOLEAN && lua_toboolean(L_, -1) == 1); | 501 | LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TBOOLEAN && lua_toboolean(L_, -1) == 1); |
562 | linda->m_read_happened.notify_all(); // To be done from within the 'K' locking area | 502 | linda->m_read_happened.notify_all(); // To be done from within the 'K' locking area |
563 | } | 503 | } |
564 | } | 504 | } |
565 | } | 505 | } else { // linda is cancelled |
566 | else // linda is cancelled | ||
567 | { | ||
568 | // do nothing and return lanes.cancel_error | 506 | // do nothing and return lanes.cancel_error |
569 | kCancelError.pushKey(L_); | 507 | kCancelError.pushKey(L_); |
570 | pushed.emplace(1); | 508 | pushed.emplace(1); |
@@ -585,8 +523,7 @@ LUAG_FUNC(linda_set) | |||
585 | */ | 523 | */ |
586 | LUAG_FUNC(linda_count) | 524 | LUAG_FUNC(linda_count) |
587 | { | 525 | { |
588 | auto count = [](lua_State* L_) | 526 | auto count = [](lua_State* L_) { |
589 | { | ||
590 | Linda* const linda{ ToLinda<false>(L_, 1) }; | 527 | Linda* const linda{ ToLinda<false>(L_, 1) }; |
591 | // make sure the keys are of a valid type | 528 | // make sure the keys are of a valid type |
592 | check_key_types(L_, 2, lua_gettop(L_)); | 529 | check_key_types(L_, 2, lua_gettop(L_)); |
@@ -601,14 +538,13 @@ LUAG_FUNC(linda_count) | |||
601 | // ################################################################################################# | 538 | // ################################################################################################# |
602 | 539 | ||
603 | /* | 540 | /* |
604 | * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1]) | 541 | * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1]) |
605 | * | 542 | * |
606 | * Get one or more values from Linda. | 543 | * Get one or more values from Linda. |
607 | */ | 544 | */ |
608 | LUAG_FUNC(linda_get) | 545 | LUAG_FUNC(linda_get) |
609 | { | 546 | { |
610 | auto get = [](lua_State* L_) | 547 | auto get = [](lua_State* L_) { |
611 | { | ||
612 | Linda* const linda{ ToLinda<false>(L_, 1) }; | 548 | Linda* const linda{ ToLinda<false>(L_, 1) }; |
613 | lua_Integer const count{ luaL_optinteger(L_, 3, 1) }; | 549 | lua_Integer const count{ luaL_optinteger(L_, 3, 1) }; |
614 | luaL_argcheck(L_, count >= 1, 3, "count should be >= 1"); | 550 | luaL_argcheck(L_, count >= 1, 3, "count should be >= 1"); |
@@ -617,17 +553,13 @@ LUAG_FUNC(linda_get) | |||
617 | check_key_types(L_, 2, 2); | 553 | check_key_types(L_, 2, 2); |
618 | 554 | ||
619 | KeeperCallResult pushed; | 555 | KeeperCallResult pushed; |
620 | if (linda->simulate_cancel == CancelRequest::None) | 556 | if (linda->simulate_cancel == CancelRequest::None) { |
621 | { | ||
622 | Keeper* const K{ linda->whichKeeper() }; | 557 | Keeper* const K{ linda->whichKeeper() }; |
623 | pushed = keeper_call(linda->U, K->L, KEEPER_API(get), L_, linda, 2); | 558 | pushed = keeper_call(linda->U, K->L, KEEPER_API(get), L_, linda, 2); |
624 | if (pushed.value_or(0) > 0) | 559 | if (pushed.value_or(0) > 0) { |
625 | { | ||
626 | keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - pushed.value(), LookupMode::FromKeeper); | 560 | keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - pushed.value(), LookupMode::FromKeeper); |
627 | } | 561 | } |
628 | } | 562 | } else { // linda is cancelled |
629 | else // linda is cancelled | ||
630 | { | ||
631 | // do nothing and return lanes.cancel_error | 563 | // do nothing and return lanes.cancel_error |
632 | kCancelError.pushKey(L_); | 564 | kCancelError.pushKey(L_); |
633 | pushed.emplace(1); | 565 | pushed.emplace(1); |
@@ -641,37 +573,32 @@ LUAG_FUNC(linda_get) | |||
641 | // ################################################################################################# | 573 | // ################################################################################################# |
642 | 574 | ||
643 | /* | 575 | /* |
644 | * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int) | 576 | * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int) |
645 | * | 577 | * |
646 | * Set limit to 1 Linda keys. | 578 | * Set limit to 1 Linda keys. |
647 | * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so | 579 | * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so |
648 | */ | 580 | */ |
649 | LUAG_FUNC(linda_limit) | 581 | LUAG_FUNC(linda_limit) |
650 | { | 582 | { |
651 | auto limit = [](lua_State* L_) | 583 | auto limit = [](lua_State* L_) { |
652 | { | ||
653 | Linda* const linda{ ToLinda<false>(L_, 1) }; | 584 | Linda* const linda{ ToLinda<false>(L_, 1) }; |
654 | // make sure we got 3 arguments: the linda, a key and a limit | 585 | // make sure we got 3 arguments: the linda, a key and a limit |
655 | luaL_argcheck( L_, lua_gettop( L_) == 3, 2, "wrong number of arguments"); | 586 | luaL_argcheck(L_, lua_gettop(L_) == 3, 2, "wrong number of arguments"); |
656 | // make sure we got a numeric limit | 587 | // make sure we got a numeric limit |
657 | luaL_checknumber( L_, 3); | 588 | luaL_checknumber(L_, 3); |
658 | // make sure the key is of a valid type | 589 | // make sure the key is of a valid type |
659 | check_key_types( L_, 2, 2); | 590 | check_key_types(L_, 2, 2); |
660 | 591 | ||
661 | KeeperCallResult pushed; | 592 | KeeperCallResult pushed; |
662 | if (linda->simulate_cancel == CancelRequest::None) | 593 | if (linda->simulate_cancel == CancelRequest::None) { |
663 | { | ||
664 | Keeper* const K{ linda->whichKeeper() }; | 594 | Keeper* const K{ linda->whichKeeper() }; |
665 | pushed = keeper_call(linda->U, K->L, KEEPER_API(limit), L_, linda, 2); | 595 | pushed = keeper_call(linda->U, K->L, KEEPER_API(limit), L_, linda, 2); |
666 | LUA_ASSERT(L_, pushed.has_value() && (pushed.value() == 0 || pushed.value() == 1)); // no error, optional boolean value saying if we should wake blocked writer threads | 596 | LUA_ASSERT(L_, pushed.has_value() && (pushed.value() == 0 || pushed.value() == 1)); // no error, optional boolean value saying if we should wake blocked writer threads |
667 | if (pushed.value() == 1) | 597 | if (pushed.value() == 1) { |
668 | { | 598 | LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TBOOLEAN && lua_toboolean(L_, -1) == 1); |
669 | LUA_ASSERT(L_, lua_type( L_, -1) == LUA_TBOOLEAN && lua_toboolean( L_, -1) == 1); | ||
670 | linda->m_read_happened.notify_all(); // To be done from within the 'K' locking area | 599 | linda->m_read_happened.notify_all(); // To be done from within the 'K' locking area |
671 | } | 600 | } |
672 | } | 601 | } else { // linda is cancelled |
673 | else // linda is cancelled | ||
674 | { | ||
675 | // do nothing and return lanes.cancel_error | 602 | // do nothing and return lanes.cancel_error |
676 | kCancelError.pushKey(L_); | 603 | kCancelError.pushKey(L_); |
677 | pushed.emplace(1); | 604 | pushed.emplace(1); |
@@ -685,10 +612,10 @@ LUAG_FUNC(linda_limit) | |||
685 | // ################################################################################################# | 612 | // ################################################################################################# |
686 | 613 | ||
687 | /* | 614 | /* |
688 | * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") | 615 | * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") |
689 | * | 616 | * |
690 | * Signal linda so that waiting threads wake up as if their own lane was cancelled | 617 | * Signal linda so that waiting threads wake up as if their own lane was cancelled |
691 | */ | 618 | */ |
692 | LUAG_FUNC(linda_cancel) | 619 | LUAG_FUNC(linda_cancel) |
693 | { | 620 | { |
694 | Linda* const linda{ ToLinda<false>(L_, 1) }; | 621 | Linda* const linda{ ToLinda<false>(L_, 1) }; |
@@ -697,25 +624,16 @@ LUAG_FUNC(linda_cancel) | |||
697 | luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments"); | 624 | luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments"); |
698 | 625 | ||
699 | linda->simulate_cancel = CancelRequest::Soft; | 626 | linda->simulate_cancel = CancelRequest::Soft; |
700 | if (strcmp(who, "both") == 0) // tell everyone writers to wake up | 627 | if (strcmp(who, "both") == 0) { // tell everyone writers to wake up |
701 | { | ||
702 | linda->m_write_happened.notify_all(); | 628 | linda->m_write_happened.notify_all(); |
703 | linda->m_read_happened.notify_all(); | 629 | linda->m_read_happened.notify_all(); |
704 | } | 630 | } else if (strcmp(who, "none") == 0) { // reset flag |
705 | else if (strcmp(who, "none") == 0) // reset flag | ||
706 | { | ||
707 | linda->simulate_cancel = CancelRequest::None; | 631 | linda->simulate_cancel = CancelRequest::None; |
708 | } | 632 | } else if (strcmp(who, "read") == 0) { // tell blocked readers to wake up |
709 | else if (strcmp(who, "read") == 0) // tell blocked readers to wake up | ||
710 | { | ||
711 | linda->m_write_happened.notify_all(); | 633 | linda->m_write_happened.notify_all(); |
712 | } | 634 | } else if (strcmp(who, "write") == 0) { // tell blocked writers to wake up |
713 | else if (strcmp(who, "write") == 0) // tell blocked writers to wake up | ||
714 | { | ||
715 | linda->m_read_happened.notify_all(); | 635 | linda->m_read_happened.notify_all(); |
716 | } | 636 | } else { |
717 | else | ||
718 | { | ||
719 | raise_luaL_error(L_, "unknown wake hint '%s'", who); | 637 | raise_luaL_error(L_, "unknown wake hint '%s'", who); |
720 | } | 638 | } |
721 | return 0; | 639 | return 0; |
@@ -724,15 +642,15 @@ LUAG_FUNC(linda_cancel) | |||
724 | // ################################################################################################# | 642 | // ################################################################################################# |
725 | 643 | ||
726 | /* | 644 | /* |
727 | * lightuserdata= linda_deep( linda_ud ) | 645 | * lightuserdata= linda_deep( linda_ud ) |
728 | * | 646 | * |
729 | * Return the 'deep' userdata pointer, identifying the Linda. | 647 | * Return the 'deep' userdata pointer, identifying the Linda. |
730 | * | 648 | * |
731 | * This is needed for using Lindas as key indices (timer system needs it); | 649 | * This is needed for using Lindas as key indices (timer system needs it); |
732 | * separately created proxies of the same underlying deep object will have | 650 | * separately created proxies of the same underlying deep object will have |
733 | * different userdata and won't be known to be essentially the same deep one | 651 | * different userdata and won't be known to be essentially the same deep one |
734 | * without this. | 652 | * without this. |
735 | */ | 653 | */ |
736 | LUAG_FUNC(linda_deep) | 654 | LUAG_FUNC(linda_deep) |
737 | { | 655 | { |
738 | Linda* const linda{ ToLinda<false>(L_, 1) }; | 656 | Linda* const linda{ ToLinda<false>(L_, 1) }; |
@@ -743,19 +661,18 @@ LUAG_FUNC(linda_deep) | |||
743 | // ################################################################################################# | 661 | // ################################################################################################# |
744 | 662 | ||
745 | /* | 663 | /* |
746 | * string = linda:__tostring( linda_ud) | 664 | * string = linda:__tostring( linda_ud) |
747 | * | 665 | * |
748 | * Return the stringification of a linda | 666 | * Return the stringification of a linda |
749 | * | 667 | * |
750 | * Useful for concatenation or debugging purposes | 668 | * Useful for concatenation or debugging purposes |
751 | */ | 669 | */ |
752 | 670 | ||
753 | template <bool OPT> | 671 | template <bool OPT> |
754 | [[nodiscard]] static int LindaToString(lua_State* L_, int idx_) | 672 | [[nodiscard]] static int LindaToString(lua_State* L_, int idx_) |
755 | { | 673 | { |
756 | Linda* const linda{ ToLinda<OPT>(L_, idx_) }; | 674 | Linda* const linda{ ToLinda<OPT>(L_, idx_) }; |
757 | if (linda != nullptr) | 675 | if (linda != nullptr) { |
758 | { | ||
759 | char text[128]; | 676 | char text[128]; |
760 | int len; | 677 | int len; |
761 | if (linda->getName()) | 678 | if (linda->getName()) |
@@ -768,6 +685,8 @@ template <bool OPT> | |||
768 | return 0; | 685 | return 0; |
769 | } | 686 | } |
770 | 687 | ||
688 | // ################################################################################################# | ||
689 | |||
771 | LUAG_FUNC(linda_tostring) | 690 | LUAG_FUNC(linda_tostring) |
772 | { | 691 | { |
773 | return LindaToString<false>(L_, 1); | 692 | return LindaToString<false>(L_, 1); |
@@ -776,28 +695,25 @@ LUAG_FUNC(linda_tostring) | |||
776 | // ################################################################################################# | 695 | // ################################################################################################# |
777 | 696 | ||
778 | /* | 697 | /* |
779 | * string = linda:__concat( a, b) | 698 | * string = linda:__concat( a, b) |
780 | * | 699 | * |
781 | * Return the concatenation of a pair of items, one of them being a linda | 700 | * Return the concatenation of a pair of items, one of them being a linda |
782 | * | 701 | * |
783 | * Useful for concatenation or debugging purposes | 702 | * Useful for concatenation or debugging purposes |
784 | */ | 703 | */ |
785 | LUAG_FUNC(linda_concat) | 704 | LUAG_FUNC(linda_concat) |
786 | { // linda1? linda2? | 705 | { // L_: linda1? linda2? |
787 | bool atLeastOneLinda{ false }; | 706 | bool atLeastOneLinda{ false }; |
788 | // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. | 707 | // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. |
789 | if (LindaToString<true>(L_, 1)) | 708 | if (LindaToString<true>(L_, 1)) { |
790 | { | ||
791 | atLeastOneLinda = true; | 709 | atLeastOneLinda = true; |
792 | lua_replace(L_, 1); | 710 | lua_replace(L_, 1); |
793 | } | 711 | } |
794 | if (LindaToString<true>(L_, 2)) | 712 | if (LindaToString<true>(L_, 2)) { |
795 | { | ||
796 | atLeastOneLinda = true; | 713 | atLeastOneLinda = true; |
797 | lua_replace(L_, 2); | 714 | lua_replace(L_, 2); |
798 | } | 715 | } |
799 | if (!atLeastOneLinda) // should not be possible | 716 | if (!atLeastOneLinda) { // should not be possible |
800 | { | ||
801 | raise_luaL_error(L_, "internal error: linda_concat called on non-Linda"); | 717 | raise_luaL_error(L_, "internal error: linda_concat called on non-Linda"); |
802 | } | 718 | } |
803 | lua_concat(L_, 2); | 719 | lua_concat(L_, 2); |
@@ -812,8 +728,7 @@ LUAG_FUNC(linda_concat) | |||
812 | */ | 728 | */ |
813 | LUAG_FUNC(linda_dump) | 729 | LUAG_FUNC(linda_dump) |
814 | { | 730 | { |
815 | auto dump = [](lua_State* L_) | 731 | auto dump = [](lua_State* L_) { |
816 | { | ||
817 | Linda* const linda{ ToLinda<false>(L_, 1) }; | 732 | Linda* const linda{ ToLinda<false>(L_, 1) }; |
818 | return keeper_push_linda_storage(*linda, DestState{ L_ }); | 733 | return keeper_push_linda_storage(*linda, DestState{ L_ }); |
819 | }; | 734 | }; |
@@ -830,8 +745,7 @@ LUAG_FUNC(linda_towatch) | |||
830 | { | 745 | { |
831 | Linda* const linda{ ToLinda<false>(L_, 1) }; | 746 | Linda* const linda{ ToLinda<false>(L_, 1) }; |
832 | int pushed{ keeper_push_linda_storage(*linda, DestState{ L_ }) }; | 747 | int pushed{ keeper_push_linda_storage(*linda, DestState{ L_ }) }; |
833 | if (pushed == 0) | 748 | if (pushed == 0) { |
834 | { | ||
835 | // if the linda is empty, don't return nil | 749 | // if the linda is empty, don't return nil |
836 | pushed = LindaToString<false>(L_, 1); | 750 | pushed = LindaToString<false>(L_, 1); |
837 | } | 751 | } |
@@ -872,13 +786,10 @@ LUAG_FUNC(linda) | |||
872 | { | 786 | { |
873 | int const top{ lua_gettop(L_) }; | 787 | int const top{ lua_gettop(L_) }; |
874 | luaL_argcheck(L_, top <= 2, top, "too many arguments"); | 788 | luaL_argcheck(L_, top <= 2, top, "too many arguments"); |
875 | if (top == 1) | 789 | if (top == 1) { |
876 | { | ||
877 | LuaType const t{ lua_type_as_enum(L_, 1) }; | 790 | LuaType const t{ lua_type_as_enum(L_, 1) }; |
878 | luaL_argcheck(L_, t == LuaType::STRING || t == LuaType::NUMBER, 1, "wrong parameter (should be a string or a number)"); | 791 | luaL_argcheck(L_, t == LuaType::STRING || t == LuaType::NUMBER, 1, "wrong parameter (should be a string or a number)"); |
879 | } | 792 | } else if (top == 2) { |
880 | else if (top == 2) | ||
881 | { | ||
882 | luaL_checktype(L_, 1, LUA_TSTRING); | 793 | luaL_checktype(L_, 1, LUA_TSTRING); |
883 | luaL_checktype(L_, 2, LUA_TNUMBER); | 794 | luaL_checktype(L_, 2, LUA_TNUMBER); |
884 | } | 795 | } |
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp index 3ee0ba4..1a8782e 100644 --- a/src/lindafactory.cpp +++ b/src/lindafactory.cpp | |||
@@ -2,7 +2,7 @@ | |||
2 | * LINDAFACTORY.CPP Copyright (c) 2024-, Benoit Germain | 2 | * LINDAFACTORY.CPP Copyright (c) 2024-, Benoit Germain |
3 | * | 3 | * |
4 | * Linda deep userdata factory | 4 | * Linda deep userdata factory |
5 | */ | 5 | */ |
6 | 6 | ||
7 | /* | 7 | /* |
8 | =============================================================================== | 8 | =============================================================================== |
@@ -69,8 +69,7 @@ void LindaFactory::deleteDeepObjectInternal(lua_State* L_, DeepPrelude* o_) cons | |||
69 | LUA_ASSERT(L_, linda); | 69 | LUA_ASSERT(L_, linda); |
70 | Keeper* const myK{ linda->whichKeeper() }; | 70 | Keeper* const myK{ linda->whichKeeper() }; |
71 | // if collected after the universe, keepers are already destroyed, and there is nothing to clear | 71 | // if collected after the universe, keepers are already destroyed, and there is nothing to clear |
72 | if (myK) | 72 | if (myK) { |
73 | { | ||
74 | // if collected from my own keeper, we can't acquire/release it | 73 | // if collected from my own keeper, we can't acquire/release it |
75 | // because we are already inside a protected area, and trying to do so would deadlock! | 74 | // because we are already inside a protected area, and trying to do so would deadlock! |
76 | bool const need_acquire_release{ myK->L != L_ }; | 75 | bool const need_acquire_release{ myK->L != L_ }; |
@@ -79,8 +78,7 @@ void LindaFactory::deleteDeepObjectInternal(lua_State* L_, DeepPrelude* o_) cons | |||
79 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... | 78 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... |
80 | [[maybe_unused]] KeeperCallResult const result{ keeper_call(linda->U, K->L, KEEPER_API(clear), L_, linda, 0) }; | 79 | [[maybe_unused]] KeeperCallResult const result{ keeper_call(linda->U, K->L, KEEPER_API(clear), L_, linda, 0) }; |
81 | LUA_ASSERT(L_, result.has_value() && result.value() == 0); | 80 | LUA_ASSERT(L_, result.has_value() && result.value() == 0); |
82 | if (need_acquire_release) | 81 | if (need_acquire_release) { |
83 | { | ||
84 | linda->releaseKeeper(K); | 82 | linda->releaseKeeper(K); |
85 | } | 83 | } |
86 | } | 84 | } |
@@ -106,23 +104,19 @@ DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* L_) const | |||
106 | char const* linda_name{ nullptr }; | 104 | char const* linda_name{ nullptr }; |
107 | LindaGroup linda_group{ 0 }; | 105 | LindaGroup linda_group{ 0 }; |
108 | // should have a string and/or a number of the stack as parameters (name and group) | 106 | // should have a string and/or a number of the stack as parameters (name and group) |
109 | switch (lua_gettop(L_)) | 107 | switch (lua_gettop(L_)) { |
110 | { | 108 | default: // 0 |
111 | default: // 0 | ||
112 | break; | 109 | break; |
113 | 110 | ||
114 | case 1: // 1 parameter, either a name or a group | 111 | case 1: // 1 parameter, either a name or a group |
115 | if (lua_type(L_, -1) == LUA_TSTRING) | 112 | if (lua_type(L_, -1) == LUA_TSTRING) { |
116 | { | ||
117 | linda_name = lua_tolstring(L_, -1, &name_len); | 113 | linda_name = lua_tolstring(L_, -1, &name_len); |
118 | } | 114 | } else { |
119 | else | ||
120 | { | ||
121 | linda_group = LindaGroup{ static_cast<int>(lua_tointeger(L_, -1)) }; | 115 | linda_group = LindaGroup{ static_cast<int>(lua_tointeger(L_, -1)) }; |
122 | } | 116 | } |
123 | break; | 117 | break; |
124 | 118 | ||
125 | case 2: // 2 parameters, a name and group, in that order | 119 | case 2: // 2 parameters, a name and group, in that order |
126 | linda_name = lua_tolstring(L_, -2, &name_len); | 120 | linda_name = lua_tolstring(L_, -2, &name_len); |
127 | linda_group = LindaGroup{ static_cast<int>(lua_tointeger(L_, -1)) }; | 121 | linda_group = LindaGroup{ static_cast<int>(lua_tointeger(L_, -1)) }; |
128 | break; | 122 | break; |
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h index 5d1467a..58567ac 100644 --- a/src/macros_and_utils.h +++ b/src/macros_and_utils.h | |||
@@ -64,7 +64,7 @@ template <typename... ARGS> | |||
64 | #define USE_DEBUG_SPEW() 0 | 64 | #define USE_DEBUG_SPEW() 0 |
65 | #if USE_DEBUG_SPEW() | 65 | #if USE_DEBUG_SPEW() |
66 | #define INDENT_BEGIN "%.*s " | 66 | #define INDENT_BEGIN "%.*s " |
67 | #define INDENT_END , (U ? U->debugspew_indent_depth.load(std::memory_order_relaxed) : 0), DebugSpewIndentScope::debugspew_indent | 67 | #define INDENT_END(U_) , (U_ ? U_->debugspew_indent_depth.load(std::memory_order_relaxed) : 0), DebugSpewIndentScope::debugspew_indent |
68 | #define DEBUGSPEW_CODE(_code) _code | 68 | #define DEBUGSPEW_CODE(_code) _code |
69 | #define DEBUGSPEW_OR_NOT(a_, b_) a_ | 69 | #define DEBUGSPEW_OR_NOT(a_, b_) a_ |
70 | #define DEBUGSPEW_PARAM_COMMA(param_) param_, | 70 | #define DEBUGSPEW_PARAM_COMMA(param_) param_, |
diff --git a/src/state.cpp b/src/state.cpp index e71865d..ebb24dd 100644 --- a/src/state.cpp +++ b/src/state.cpp | |||
@@ -40,7 +40,7 @@ THE SOFTWARE. | |||
40 | // ################################################################################################# | 40 | // ################################################################################################# |
41 | 41 | ||
42 | /*---=== Serialize require ===--- | 42 | /*---=== Serialize require ===--- |
43 | */ | 43 | */ |
44 | 44 | ||
45 | //--- | 45 | //--- |
46 | // [val,...]= new_require( ... ) | 46 | // [val,...]= new_require( ... ) |
@@ -52,55 +52,51 @@ THE SOFTWARE. | |||
52 | [[nodiscard]] static int luaG_new_require(lua_State* L_) | 52 | [[nodiscard]] static int luaG_new_require(lua_State* L_) |
53 | { | 53 | { |
54 | int rc; | 54 | int rc; |
55 | int const args = lua_gettop(L_); // args | 55 | int const args = lua_gettop(L_); // L_: args |
56 | Universe* U = universe_get(L_); | 56 | Universe* U = universe_get(L_); |
57 | //char const* modname = luaL_checkstring(L_, 1); | 57 | // char const* modname = luaL_checkstring(L_, 1); |
58 | 58 | ||
59 | STACK_GROW(L_, 1); | 59 | STACK_GROW(L_, 1); |
60 | 60 | ||
61 | lua_pushvalue(L_, lua_upvalueindex( 1)); // args require | 61 | lua_pushvalue(L_, lua_upvalueindex(1)); // L_: args require |
62 | lua_insert(L_, 1); // require args | 62 | lua_insert(L_, 1); // L_: require args |
63 | 63 | ||
64 | // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would | 64 | // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would |
65 | // leave us locked, blocking any future 'require' calls from other lanes. | 65 | // leave us locked, blocking any future 'require' calls from other lanes. |
66 | 66 | ||
67 | U->require_cs.lock(); | 67 | U->require_cs.lock(); |
68 | // starting with Lua 5.4, require may return a second optional value, so we need LUA_MULTRET | 68 | // starting with Lua 5.4, require may return a second optional value, so we need LUA_MULTRET |
69 | rc = lua_pcall(L_, args, LUA_MULTRET, 0 /*errfunc*/ ); // err|result(s) | 69 | rc = lua_pcall(L_, args, LUA_MULTRET, 0 /*errfunc*/); // L_: err|result(s) |
70 | U->require_cs.unlock(); | 70 | U->require_cs.unlock(); |
71 | 71 | ||
72 | // the required module (or an error message) is left on the stack as returned value by original require function | 72 | // the required module (or an error message) is left on the stack as returned value by original require function |
73 | 73 | ||
74 | if (rc != LUA_OK) // LUA_ERRRUN / LUA_ERRMEM ? | 74 | if (rc != LUA_OK) { // LUA_ERRRUN / LUA_ERRMEM ? |
75 | { | ||
76 | raise_lua_error(L_); | 75 | raise_lua_error(L_); |
77 | } | 76 | } |
78 | // should be 1 for Lua <= 5.3, 1 or 2 starting with Lua 5.4 | 77 | // should be 1 for Lua <= 5.3, 1 or 2 starting with Lua 5.4 |
79 | return lua_gettop(L_); // result(s) | 78 | return lua_gettop(L_); // L_: result(s) |
80 | } | 79 | } |
81 | 80 | ||
82 | // ################################################################################################# | 81 | // ################################################################################################# |
83 | 82 | ||
84 | /* | 83 | /* |
85 | * Serialize calls to 'require', if it exists | 84 | * Serialize calls to 'require', if it exists |
86 | */ | 85 | */ |
87 | void serialize_require(DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L_) | 86 | void serialize_require(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L_) |
88 | { | 87 | { |
89 | STACK_GROW(L_, 1); | 88 | STACK_GROW(L_, 1); |
90 | STACK_CHECK_START_REL(L_, 0); | 89 | STACK_CHECK_START_REL(L_, 0); |
91 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "serializing require()\n" INDENT_END)); | 90 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "serializing require()\n" INDENT_END(U_))); |
92 | 91 | ||
93 | // Check 'require' is there and not already wrapped; if not, do nothing | 92 | // Check 'require' is there and not already wrapped; if not, do nothing |
94 | // | 93 | // |
95 | lua_getglobal(L_, "require"); | 94 | lua_getglobal(L_, "require"); |
96 | if (lua_isfunction(L_, -1) && lua_tocfunction(L_, -1) != luaG_new_require) | 95 | if (lua_isfunction(L_, -1) && lua_tocfunction(L_, -1) != luaG_new_require) { |
97 | { | ||
98 | // [-1]: original 'require' function | 96 | // [-1]: original 'require' function |
99 | lua_pushcclosure(L_, luaG_new_require, 1 /*upvalues*/); | 97 | lua_pushcclosure(L_, luaG_new_require, 1 /*upvalues*/); |
100 | lua_setglobal(L_, "require"); | 98 | lua_setglobal(L_, "require"); |
101 | } | 99 | } else { |
102 | else | ||
103 | { | ||
104 | // [-1]: nil | 100 | // [-1]: nil |
105 | lua_pop(L_, 1); | 101 | lua_pop(L_, 1); |
106 | } | 102 | } |
@@ -115,72 +111,67 @@ void serialize_require(DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L_) | |||
115 | [[nodiscard]] static int require_lanes_core(lua_State* L_) | 111 | [[nodiscard]] static int require_lanes_core(lua_State* L_) |
116 | { | 112 | { |
117 | // leaves a copy of 'lanes.core' module table on the stack | 113 | // leaves a copy of 'lanes.core' module table on the stack |
118 | luaL_requiref( L_, "lanes.core", luaopen_lanes_core, 0); | 114 | luaL_requiref(L_, "lanes.core", luaopen_lanes_core, 0); |
119 | return 1; | 115 | return 1; |
120 | } | 116 | } |
121 | 117 | ||
122 | // ################################################################################################# | 118 | // ################################################################################################# |
123 | 119 | ||
124 | static luaL_Reg const libs[] = | 120 | static luaL_Reg const libs[] = { |
125 | { | 121 | { LUA_LOADLIBNAME, luaopen_package }, |
126 | { LUA_LOADLIBNAME, luaopen_package}, | 122 | { LUA_TABLIBNAME, luaopen_table }, |
127 | { LUA_TABLIBNAME, luaopen_table}, | 123 | { LUA_STRLIBNAME, luaopen_string }, |
128 | { LUA_STRLIBNAME, luaopen_string}, | 124 | { LUA_MATHLIBNAME, luaopen_math }, |
129 | { LUA_MATHLIBNAME, luaopen_math}, | ||
130 | #ifndef PLATFORM_XBOX // no os/io libs on xbox | 125 | #ifndef PLATFORM_XBOX // no os/io libs on xbox |
131 | { LUA_OSLIBNAME, luaopen_os}, | 126 | { LUA_OSLIBNAME, luaopen_os }, |
132 | { LUA_IOLIBNAME, luaopen_io}, | 127 | { LUA_IOLIBNAME, luaopen_io }, |
133 | #endif // PLATFORM_XBOX | 128 | #endif // PLATFORM_XBOX |
134 | #if LUA_VERSION_NUM >= 503 | 129 | #if LUA_VERSION_NUM >= 503 |
135 | { LUA_UTF8LIBNAME, luaopen_utf8}, | 130 | { LUA_UTF8LIBNAME, luaopen_utf8 }, |
136 | #endif | 131 | #endif |
137 | #if LUA_VERSION_NUM >= 502 | 132 | #if LUA_VERSION_NUM >= 502 |
138 | #ifdef luaopen_bit32 | 133 | #ifdef luaopen_bit32 |
139 | { LUA_BITLIBNAME, luaopen_bit32}, | 134 | { LUA_BITLIBNAME, luaopen_bit32 }, |
140 | #endif | 135 | #endif |
141 | { LUA_COLIBNAME, luaopen_coroutine}, // Lua 5.2: coroutine is no longer a part of base! | 136 | { LUA_COLIBNAME, luaopen_coroutine }, // Lua 5.2: coroutine is no longer a part of base! |
142 | #else // LUA_VERSION_NUM | 137 | #else // LUA_VERSION_NUM |
143 | { LUA_COLIBNAME, nullptr }, // Lua 5.1: part of base package | 138 | { LUA_COLIBNAME, nullptr }, // Lua 5.1: part of base package |
144 | #endif // LUA_VERSION_NUM | 139 | #endif // LUA_VERSION_NUM |
145 | { LUA_DBLIBNAME, luaopen_debug}, | 140 | { LUA_DBLIBNAME, luaopen_debug }, |
146 | #if LUAJIT_FLAVOR() != 0 // building against LuaJIT headers, add some LuaJIT-specific libs | 141 | #if LUAJIT_FLAVOR() != 0 // building against LuaJIT headers, add some LuaJIT-specific libs |
147 | //#pragma message( "supporting JIT base libs") | 142 | // #pragma message( "supporting JIT base libs") |
148 | { LUA_BITLIBNAME, luaopen_bit}, | 143 | { LUA_BITLIBNAME, luaopen_bit }, |
149 | { LUA_JITLIBNAME, luaopen_jit}, | 144 | { LUA_JITLIBNAME, luaopen_jit }, |
150 | { LUA_FFILIBNAME, luaopen_ffi}, | 145 | { LUA_FFILIBNAME, luaopen_ffi }, |
151 | #endif // LUAJIT_FLAVOR() | 146 | #endif // LUAJIT_FLAVOR() |
152 | 147 | ||
153 | { LUA_DBLIBNAME, luaopen_debug}, | 148 | { LUA_DBLIBNAME, luaopen_debug }, |
154 | { "lanes.core", require_lanes_core}, // So that we can open it like any base library (possible since we have access to the init function) | 149 | { "lanes.core", require_lanes_core }, // So that we can open it like any base library (possible since we have access to the init function) |
155 | // | 150 | // |
156 | { "base", nullptr }, // ignore "base" (already acquired it) | 151 | { "base", nullptr }, // ignore "base" (already acquired it) |
157 | { nullptr, nullptr } | 152 | { nullptr, nullptr } |
158 | }; | 153 | }; |
159 | 154 | ||
160 | // ################################################################################################# | 155 | // ################################################################################################# |
161 | 156 | ||
162 | static void open1lib(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_, char const* name_, size_t len_) | 157 | static void open1lib(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L_, char const* name_, size_t len_) |
163 | { | 158 | { |
164 | for (int i{ 0 }; libs[i].name; ++i) | 159 | for (int i{ 0 }; libs[i].name; ++i) { |
165 | { | 160 | if (strncmp(name_, libs[i].name, len_) == 0) { |
166 | if (strncmp( name_, libs[i].name, len_) == 0) | ||
167 | { | ||
168 | lua_CFunction libfunc = libs[i].func; | 161 | lua_CFunction libfunc = libs[i].func; |
169 | name_ = libs[i].name; // note that the provided name_ doesn't necessarily ends with '\0', hence len_ | 162 | name_ = libs[i].name; // note that the provided name_ doesn't necessarily ends with '\0', hence len_ |
170 | if (libfunc != nullptr) | 163 | if (libfunc != nullptr) { |
171 | { | ||
172 | bool const isLanesCore{ libfunc == require_lanes_core }; // don't want to create a global for "lanes.core" | 164 | bool const isLanesCore{ libfunc == require_lanes_core }; // don't want to create a global for "lanes.core" |
173 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening %.*s library\n" INDENT_END, (int) len_, name_)); | 165 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "opening %.*s library\n" INDENT_END(U_), (int) len_, name_)); |
174 | STACK_CHECK_START_REL(L_, 0); | 166 | STACK_CHECK_START_REL(L_, 0); |
175 | // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack) | 167 | // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack) |
176 | luaL_requiref( L_, name_, libfunc, !isLanesCore); | 168 | luaL_requiref(L_, name_, libfunc, !isLanesCore); |
177 | // lanes.core doesn't declare a global, so scan it here and now | 169 | // lanes.core doesn't declare a global, so scan it here and now |
178 | if (isLanesCore == true) | 170 | if (isLanesCore == true) { |
179 | { | 171 | populate_func_lookup_table(L_, -1, name_); |
180 | populate_func_lookup_table( L_, -1, name_); | ||
181 | } | 172 | } |
182 | lua_pop( L_, 1); | 173 | lua_pop(L_, 1); |
183 | STACK_CHECK( L_, 0); | 174 | STACK_CHECK(L_, 0); |
184 | } | 175 | } |
185 | break; | 176 | break; |
186 | } | 177 | } |
@@ -190,90 +181,80 @@ static void open1lib(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_, char cons | |||
190 | // ################################################################################################# | 181 | // ################################################################################################# |
191 | 182 | ||
192 | // just like lua_xmove, args are (from, to) | 183 | // just like lua_xmove, args are (from, to) |
193 | static void copy_one_time_settings(Universe* U, SourceState L1, DestState L2) | 184 | static void copy_one_time_settings(Universe* U_, SourceState L1_, DestState L2_) |
194 | { | 185 | { |
195 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 186 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U_ }); |
196 | 187 | ||
197 | STACK_GROW(L1, 2); | 188 | STACK_GROW(L1_, 2); |
198 | STACK_CHECK_START_REL(L1, 0); | 189 | STACK_CHECK_START_REL(L1_, 0); |
199 | STACK_CHECK_START_REL(L2, 0); | 190 | STACK_CHECK_START_REL(L2_, 0); |
200 | 191 | ||
201 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "copy_one_time_settings()\n" INDENT_END)); | 192 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "copy_one_time_settings()\n" INDENT_END(U_))); |
202 | 193 | ||
203 | kConfigRegKey.pushValue(L1); // config | 194 | kConfigRegKey.pushValue(L1_); // L1_: config |
204 | // copy settings from from source to destination registry | 195 | // copy settings from from source to destination registry |
205 | InterCopyContext c{ U, L2, L1, {}, {}, {}, {}, {} }; | 196 | InterCopyContext c{ U_, L2_, L1_, {}, {}, {}, {}, {} }; |
206 | if (c.inter_move(1) != InterCopyResult::Success) // // config | 197 | if (c.inter_move(1) != InterCopyResult::Success) { // L1_: L2_: config |
207 | { | 198 | raise_luaL_error(L1_, "failed to copy settings when loading lanes.core"); |
208 | raise_luaL_error(L1, "failed to copy settings when loading lanes.core"); | ||
209 | } | 199 | } |
210 | // set L2:_R[kConfigRegKey] = settings | 200 | // set L2:_R[kConfigRegKey] = settings |
211 | kConfigRegKey.setValue(L2, [](lua_State* L_) { lua_insert(L_, -2); }); // config | 201 | kConfigRegKey.setValue(L2_, [](lua_State* L_) { lua_insert(L_, -2); }); // L1_: L2_: config |
212 | STACK_CHECK(L2, 0); | 202 | STACK_CHECK(L2_, 0); |
213 | STACK_CHECK(L1, 0); | 203 | STACK_CHECK(L1_, 0); |
214 | } | 204 | } |
215 | 205 | ||
216 | // ################################################################################################# | 206 | // ################################################################################################# |
217 | 207 | ||
218 | void initialize_on_state_create( Universe* U, lua_State* L_) | 208 | void initialize_on_state_create(Universe* U_, lua_State* L_) |
219 | { | 209 | { |
220 | STACK_CHECK_START_REL(L_, 1); // settings | 210 | STACK_CHECK_START_REL(L_, 1); // L_: settings |
221 | lua_getfield(L_, -1, "on_state_create"); // settings on_state_create|nil | 211 | lua_getfield(L_, -1, "on_state_create"); // L_: settings on_state_create|nil |
222 | if (!lua_isnil(L_, -1)) | 212 | if (!lua_isnil(L_, -1)) { |
223 | { | ||
224 | // store C function pointer in an internal variable | 213 | // store C function pointer in an internal variable |
225 | U->on_state_create_func = lua_tocfunction(L_, -1); // settings on_state_create | 214 | U_->on_state_create_func = lua_tocfunction(L_, -1); // L_: settings on_state_create |
226 | if (U->on_state_create_func != nullptr) | 215 | if (U_->on_state_create_func != nullptr) { |
227 | { | ||
228 | // make sure the function doesn't have upvalues | 216 | // make sure the function doesn't have upvalues |
229 | char const* upname = lua_getupvalue(L_, -1, 1); // settings on_state_create upval? | 217 | char const* upname = lua_getupvalue(L_, -1, 1); // L_: settings on_state_create upval? |
230 | if (upname != nullptr) // should be "" for C functions with upvalues if any | 218 | if (upname != nullptr) { // should be "" for C functions with upvalues if any |
231 | { | ||
232 | raise_luaL_error(L_, "on_state_create shouldn't have upvalues"); | 219 | raise_luaL_error(L_, "on_state_create shouldn't have upvalues"); |
233 | } | 220 | } |
234 | // remove this C function from the config table so that it doesn't cause problems | 221 | // remove this C function from the config table so that it doesn't cause problems |
235 | // when we transfer the config table in newly created Lua states | 222 | // when we transfer the config table in newly created Lua states |
236 | lua_pushnil(L_); // settings on_state_create nil | 223 | lua_pushnil(L_); // L_: settings on_state_create nil |
237 | lua_setfield(L_, -3, "on_state_create"); // settings on_state_create | 224 | lua_setfield(L_, -3, "on_state_create"); // L_: settings on_state_create |
238 | } | 225 | } else { |
239 | else | ||
240 | { | ||
241 | // optim: store marker saying we have such a function in the config table | 226 | // optim: store marker saying we have such a function in the config table |
242 | U->on_state_create_func = (lua_CFunction) initialize_on_state_create; | 227 | U_->on_state_create_func = (lua_CFunction) initialize_on_state_create; |
243 | } | 228 | } |
244 | } | 229 | } |
245 | lua_pop(L_, 1); // settings | 230 | lua_pop(L_, 1); // L_: settings |
246 | STACK_CHECK(L_, 1); | 231 | STACK_CHECK(L_, 1); |
247 | } | 232 | } |
248 | 233 | ||
249 | // ################################################################################################# | 234 | // ################################################################################################# |
250 | 235 | ||
251 | lua_State* create_state(Universe* U, lua_State* from_) | 236 | lua_State* create_state(Universe* U_, lua_State* from_) |
252 | { | 237 | { |
253 | lua_State* L; | 238 | lua_State* L; |
254 | #if LUAJIT_FLAVOR() == 64 | 239 | #if LUAJIT_FLAVOR() == 64 |
255 | // for some reason, LuaJIT 64 bits does not support creating a state with lua_newstate... | 240 | // for some reason, LuaJIT 64 bits does not support creating a state with lua_newstate... |
256 | L = luaL_newstate(); | 241 | L = luaL_newstate(); |
257 | #else // LUAJIT_FLAVOR() == 64 | 242 | #else // LUAJIT_FLAVOR() == 64 |
258 | if (U->provide_allocator != nullptr) // we have a function we can call to obtain an allocator | 243 | if (U_->provide_allocator != nullptr) { // we have a function we can call to obtain an allocator |
259 | { | 244 | lua_pushcclosure(from_, U_->provide_allocator, 0); |
260 | lua_pushcclosure( from_, U->provide_allocator, 0); | 245 | lua_call(from_, 0, 1); |
261 | lua_call( from_, 0, 1); | ||
262 | { | 246 | { |
263 | AllocatorDefinition* const def{ lua_tofulluserdata<AllocatorDefinition>(from_, -1) }; | 247 | AllocatorDefinition* const def{ lua_tofulluserdata<AllocatorDefinition>(from_, -1) }; |
264 | L = lua_newstate( def->m_allocF, def->m_allocUD); | 248 | L = lua_newstate(def->m_allocF, def->m_allocUD); |
265 | } | 249 | } |
266 | lua_pop( from_, 1); | 250 | lua_pop(from_, 1); |
267 | } | 251 | } else { |
268 | else | ||
269 | { | ||
270 | // reuse the allocator provided when the master state was created | 252 | // reuse the allocator provided when the master state was created |
271 | L = lua_newstate(U->protected_allocator.m_allocF, U->protected_allocator.m_allocUD); | 253 | L = lua_newstate(U_->protected_allocator.m_allocF, U_->protected_allocator.m_allocUD); |
272 | } | 254 | } |
273 | #endif // LUAJIT_FLAVOR() == 64 | 255 | #endif // LUAJIT_FLAVOR() == 64 |
274 | 256 | ||
275 | if (L == nullptr) | 257 | if (L == nullptr) { |
276 | { | ||
277 | raise_luaL_error(from_, "luaG_newstate() failed while creating state; out of memory"); | 258 | raise_luaL_error(from_, "luaG_newstate() failed while creating state; out of memory"); |
278 | } | 259 | } |
279 | return L; | 260 | return L; |
@@ -281,35 +262,29 @@ lua_State* create_state(Universe* U, lua_State* from_) | |||
281 | 262 | ||
282 | // ################################################################################################# | 263 | // ################################################################################################# |
283 | 264 | ||
284 | void call_on_state_create(Universe* U, lua_State* L_, lua_State* from_, LookupMode mode_) | 265 | void call_on_state_create(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_) |
285 | { | 266 | { |
286 | if (U->on_state_create_func != nullptr) | 267 | if (U_->on_state_create_func != nullptr) { |
287 | { | ||
288 | STACK_CHECK_START_REL(L_, 0); | 268 | STACK_CHECK_START_REL(L_, 0); |
289 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END)); | 269 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END(U_))); |
290 | if (U->on_state_create_func != (lua_CFunction) initialize_on_state_create) | 270 | if (U_->on_state_create_func != (lua_CFunction) initialize_on_state_create) { |
291 | { | ||
292 | // C function: recreate a closure in the new state, bypassing the lookup scheme | 271 | // C function: recreate a closure in the new state, bypassing the lookup scheme |
293 | lua_pushcfunction(L_, U->on_state_create_func); // on_state_create() | 272 | lua_pushcfunction(L_, U_->on_state_create_func); // on_state_create() |
294 | } | 273 | } else { // Lua function located in the config table, copied when we opened "lanes.core" |
295 | else // Lua function located in the config table, copied when we opened "lanes.core" | 274 | if (mode_ != LookupMode::LaneBody) { |
296 | { | ||
297 | if (mode_ != LookupMode::LaneBody) | ||
298 | { | ||
299 | // if attempting to call in a keeper state, do nothing because the function doesn't exist there | 275 | // if attempting to call in a keeper state, do nothing because the function doesn't exist there |
300 | // this doesn't count as an error though | 276 | // this doesn't count as an error though |
301 | STACK_CHECK(L_, 0); | 277 | STACK_CHECK(L_, 0); |
302 | return; | 278 | return; |
303 | } | 279 | } |
304 | kConfigRegKey.pushValue(L_); // {} | 280 | kConfigRegKey.pushValue(L_); // L_: {} |
305 | STACK_CHECK(L_, 1); | 281 | STACK_CHECK(L_, 1); |
306 | lua_getfield(L_, -1, "on_state_create"); // {} on_state_create() | 282 | lua_getfield(L_, -1, "on_state_create"); // L_: {} on_state_create() |
307 | lua_remove(L_, -2); // on_state_create() | 283 | lua_remove(L_, -2); // L_: on_state_create() |
308 | } | 284 | } |
309 | STACK_CHECK(L_, 1); | 285 | STACK_CHECK(L_, 1); |
310 | // capture error and raise it in caller state | 286 | // capture error and raise it in caller state |
311 | if (lua_pcall(L_, 0, 0, 0) != LUA_OK) | 287 | if (lua_pcall(L_, 0, 0, 0) != LUA_OK) { |
312 | { | ||
313 | raise_luaL_error(from_, "on_state_create failed: \"%s\"", lua_isstring(L_, -1) ? lua_tostring(L_, -1) : lua_typename(L_, lua_type(L_, -1))); | 288 | raise_luaL_error(from_, "on_state_create failed: \"%s\"", lua_isstring(L_, -1) ? lua_tostring(L_, -1) : lua_typename(L_, lua_type(L_, -1))); |
314 | } | 289 | } |
315 | STACK_CHECK(L_, 0); | 290 | STACK_CHECK(L_, 0); |
@@ -319,28 +294,28 @@ void call_on_state_create(Universe* U, lua_State* L_, lua_State* from_, LookupMo | |||
319 | // ################################################################################################# | 294 | // ################################################################################################# |
320 | 295 | ||
321 | /* | 296 | /* |
322 | * Like 'luaL_openlibs()' but allows the set of libraries be selected | 297 | * Like 'luaL_openlibs()' but allows the set of libraries be selected |
323 | * | 298 | * |
324 | * nullptr no libraries, not even base | 299 | * nullptr no libraries, not even base |
325 | * "" base library only | 300 | * "" base library only |
326 | * "io,string" named libraries | 301 | * "io,string" named libraries |
327 | * "*" all libraries | 302 | * "*" all libraries |
328 | * | 303 | * |
329 | * Base ("unpack", "print" etc.) is always added, unless 'libs' is nullptr. | 304 | * Base ("unpack", "print" etc.) is always added, unless 'libs' is nullptr. |
330 | * | 305 | * |
331 | * *NOT* called for keeper states! | 306 | * *NOT* called for keeper states! |
332 | * | 307 | * |
333 | */ | 308 | */ |
334 | lua_State* luaG_newstate(Universe* U, SourceState from_, char const* libs_) | 309 | lua_State* luaG_newstate(Universe* U_, SourceState from_, char const* libs_) |
335 | { | 310 | { |
336 | DestState const L{ create_state(U, from_) }; | 311 | DestState const L{ create_state(U_, from_) }; |
337 | 312 | ||
338 | STACK_GROW(L, 2); | 313 | STACK_GROW(L, 2); |
339 | STACK_CHECK_START_ABS(L, 0); | 314 | STACK_CHECK_START_ABS(L, 0); |
340 | 315 | ||
341 | // copy the universe as a light userdata (only the master state holds the full userdata) | 316 | // copy the universe as a light userdata (only the master state holds the full userdata) |
342 | // that way, if Lanes is required in this new state, we'll know we are part of this universe | 317 | // that way, if Lanes is required in this new state, we'll know we are part of this universe |
343 | universe_store( L, U); | 318 | universe_store(L, U_); |
344 | STACK_CHECK(L, 0); | 319 | STACK_CHECK(L, 0); |
345 | 320 | ||
346 | // we'll need this every time we transfer some C function from/to this state | 321 | // we'll need this every time we transfer some C function from/to this state |
@@ -348,76 +323,68 @@ lua_State* luaG_newstate(Universe* U, SourceState from_, char const* libs_) | |||
348 | STACK_CHECK(L, 0); | 323 | STACK_CHECK(L, 0); |
349 | 324 | ||
350 | // neither libs (not even 'base') nor special init func: we are done | 325 | // neither libs (not even 'base') nor special init func: we are done |
351 | if (libs_ == nullptr && U->on_state_create_func == nullptr) | 326 | if (libs_ == nullptr && U_->on_state_create_func == nullptr) { |
352 | { | 327 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "luaG_newstate(nullptr)\n" INDENT_END(U_))); |
353 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_newstate(nullptr)\n" INDENT_END)); | ||
354 | return L; | 328 | return L; |
355 | } | 329 | } |
356 | 330 | ||
357 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "luaG_newstate()\n" INDENT_END)); | 331 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "luaG_newstate()\n" INDENT_END(U_))); |
358 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 332 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U_ }); |
359 | 333 | ||
360 | // copy settings (for example because it may contain a Lua on_state_create function) | 334 | // copy settings (for example because it may contain a Lua on_state_create function) |
361 | copy_one_time_settings( U, from_, L); | 335 | copy_one_time_settings(U_, from_, L); |
362 | 336 | ||
363 | // 'lua.c' stops GC during initialization so perhaps its a good idea. :) | 337 | // 'lua.c' stops GC during initialization so perhaps its a good idea. :) |
364 | lua_gc(L, LUA_GCSTOP, 0); | 338 | lua_gc(L, LUA_GCSTOP, 0); |
365 | 339 | ||
366 | |||
367 | // Anything causes 'base' to be taken in | 340 | // Anything causes 'base' to be taken in |
368 | // | 341 | // |
369 | if (libs_ != nullptr) | 342 | if (libs_ != nullptr) { |
370 | { | ||
371 | // special "*" case (mainly to help with LuaJIT compatibility) | 343 | // special "*" case (mainly to help with LuaJIT compatibility) |
372 | // as we are called from luaopen_lanes_core() already, and that would deadlock | 344 | // as we are called from luaopen_lanes_core() already, and that would deadlock |
373 | if (libs_[0] == '*' && libs_[1] == 0) | 345 | if (libs_[0] == '*' && libs_[1] == 0) { |
374 | { | 346 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "opening ALL standard libraries\n" INDENT_END(U_))); |
375 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening ALL standard libraries\n" INDENT_END)); | 347 | luaL_openlibs(L); |
376 | luaL_openlibs( L); | ||
377 | // don't forget lanes.core for regular lane states | 348 | // don't forget lanes.core for regular lane states |
378 | open1lib( DEBUGSPEW_PARAM_COMMA( U) L, "lanes.core", 10); | 349 | open1lib(DEBUGSPEW_PARAM_COMMA(U_) L, "lanes.core", 10); |
379 | libs_ = nullptr; // done with libs | 350 | libs_ = nullptr; // done with libs |
380 | } | 351 | } else { |
381 | else | 352 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "opening base library\n" INDENT_END(U_))); |
382 | { | ||
383 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening base library\n" INDENT_END)); | ||
384 | #if LUA_VERSION_NUM >= 502 | 353 | #if LUA_VERSION_NUM >= 502 |
385 | // open base library the same way as in luaL_openlibs() | 354 | // open base library the same way as in luaL_openlibs() |
386 | luaL_requiref( L, "_G", luaopen_base, 1); | 355 | luaL_requiref(L, "_G", luaopen_base, 1); |
387 | lua_pop( L, 1); | 356 | lua_pop(L, 1); |
388 | #else // LUA_VERSION_NUM | 357 | #else // LUA_VERSION_NUM |
389 | lua_pushcfunction( L, luaopen_base); | 358 | lua_pushcfunction(L, luaopen_base); |
390 | lua_pushstring( L, ""); | 359 | lua_pushstring(L, ""); |
391 | lua_call( L, 1, 0); | 360 | lua_call(L, 1, 0); |
392 | #endif // LUA_VERSION_NUM | 361 | #endif // LUA_VERSION_NUM |
393 | } | 362 | } |
394 | } | 363 | } |
395 | STACK_CHECK(L, 0); | 364 | STACK_CHECK(L, 0); |
396 | 365 | ||
397 | // scan all libraries, open them one by one | 366 | // scan all libraries, open them one by one |
398 | if (libs_) | 367 | if (libs_) { |
399 | { | ||
400 | unsigned int len{ 0 }; | 368 | unsigned int len{ 0 }; |
401 | for (char const* p{ libs_ }; *p; p += len) | 369 | for (char const* p{ libs_ }; *p; p += len) { |
402 | { | ||
403 | // skip delimiters ('.' can be part of name for "lanes.core") | 370 | // skip delimiters ('.' can be part of name for "lanes.core") |
404 | while( *p && !isalnum(*p) && *p != '.') | 371 | while (*p && !isalnum(*p) && *p != '.') |
405 | ++ p; | 372 | ++p; |
406 | // skip name | 373 | // skip name |
407 | len = 0; | 374 | len = 0; |
408 | while( isalnum(p[len]) || p[len] == '.') | 375 | while (isalnum(p[len]) || p[len] == '.') |
409 | ++ len; | 376 | ++len; |
410 | // open library | 377 | // open library |
411 | open1lib(DEBUGSPEW_PARAM_COMMA( U) L, p, len); | 378 | open1lib(DEBUGSPEW_PARAM_COMMA(U_) L, p, len); |
412 | } | 379 | } |
413 | } | 380 | } |
414 | lua_gc(L, LUA_GCRESTART, 0); | 381 | lua_gc(L, LUA_GCRESTART, 0); |
415 | 382 | ||
416 | serialize_require(DEBUGSPEW_PARAM_COMMA( U) L); | 383 | serialize_require(DEBUGSPEW_PARAM_COMMA(U_) L); |
417 | 384 | ||
418 | // call this after the base libraries are loaded and GC is restarted | 385 | // call this after the base libraries are loaded and GC is restarted |
419 | // will raise an error in from_ in case of problem | 386 | // will raise an error in from_ in case of problem |
420 | call_on_state_create(U, L, from_, LookupMode::LaneBody); | 387 | call_on_state_create(U_, L, from_, LookupMode::LaneBody); |
421 | 388 | ||
422 | STACK_CHECK(L, 0); | 389 | STACK_CHECK(L, 0); |
423 | // after all this, register everything we find in our name<->function database | 390 | // after all this, register everything we find in our name<->function database |
@@ -427,18 +394,18 @@ lua_State* luaG_newstate(Universe* U, SourceState from_, char const* libs_) | |||
427 | 394 | ||
428 | #if 1 && USE_DEBUG_SPEW() | 395 | #if 1 && USE_DEBUG_SPEW() |
429 | // dump the lookup database contents | 396 | // dump the lookup database contents |
430 | kLookupRegKey.pushValue(L); // {} | 397 | kLookupRegKey.pushValue(L); // L: {} |
431 | lua_pushnil(L); // {} nil | 398 | lua_pushnil(L); // L: {} nil |
432 | while (lua_next(L, -2)) // {} k v | 399 | while (lua_next(L, -2)) { // L: {} k v |
433 | { | 400 | lua_getglobal(L, "print"); // L: {} k v print |
434 | lua_getglobal(L, "print"); // {} k v print | 401 | int const indent{ U_->debugspew_indent_depth.load(std::memory_order_relaxed) }; |
435 | lua_pushlstring(L, DebugSpewIndentScope::debugspew_indent, U->debugspew_indent_depth.load(std::memory_order_relaxed)); // {} k v print " " | 402 | lua_pushlstring(L, DebugSpewIndentScope::debugspew_indent, indent); // L: {} k v print " " |
436 | lua_pushvalue(L, -4); // {} k v print " " k | 403 | lua_pushvalue(L, -4); // L: {} k v print " " k |
437 | lua_pushvalue(L, -4); // {} k v print " " k v | 404 | lua_pushvalue(L, -4); // L: {} k v print " " k v |
438 | lua_call(L, 3, 0); // {} k v | 405 | lua_call(L, 3, 0); // L: {} k v |
439 | lua_pop(L, 1); // {} k | 406 | lua_pop(L, 1); // L: {} k |
440 | } | 407 | } |
441 | lua_pop(L, 1); // {} | 408 | lua_pop(L, 1); // L: {} |
442 | #endif // USE_DEBUG_SPEW() | 409 | #endif // USE_DEBUG_SPEW() |
443 | 410 | ||
444 | lua_pop(L, 1); | 411 | lua_pop(L, 1); |
diff --git a/src/state.h b/src/state.h index 9e43b41..197e052 100644 --- a/src/state.h +++ b/src/state.h | |||
@@ -6,14 +6,14 @@ | |||
6 | enum class LookupMode; | 6 | enum class LookupMode; |
7 | class Universe; | 7 | class Universe; |
8 | 8 | ||
9 | void serialize_require(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_); | 9 | void serialize_require(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L_); |
10 | 10 | ||
11 | // ################################################################################################# | 11 | // ################################################################################################# |
12 | 12 | ||
13 | [[nodiscard]] lua_State* create_state(Universe* U, lua_State* from_); | 13 | [[nodiscard]] lua_State* create_state(Universe* U_, lua_State* from_); |
14 | [[nodiscard]] lua_State* luaG_newstate(Universe* U, SourceState _from, char const* libs); | 14 | [[nodiscard]] lua_State* luaG_newstate(Universe* U_, SourceState _from, char const* libs); |
15 | 15 | ||
16 | // ################################################################################################# | 16 | // ################################################################################################# |
17 | 17 | ||
18 | void initialize_on_state_create(Universe* U, lua_State* L_); | 18 | void initialize_on_state_create(Universe* U_, lua_State* L_); |
19 | void call_on_state_create(Universe* U, lua_State* L_, lua_State* from_, LookupMode mode_); | 19 | void call_on_state_create(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_); |
diff --git a/src/threading.cpp b/src/threading.cpp index acc79d8..254b2e3 100644 --- a/src/threading.cpp +++ b/src/threading.cpp | |||
@@ -6,7 +6,7 @@ | |||
6 | * | 6 | * |
7 | * References: | 7 | * References: |
8 | * <http://www.cse.wustl.edu/~schmidt/win32-cv-1.html> | 8 | * <http://www.cse.wustl.edu/~schmidt/win32-cv-1.html> |
9 | */ | 9 | */ |
10 | 10 | ||
11 | /* | 11 | /* |
12 | =============================================================================== | 12 | =============================================================================== |
@@ -36,35 +36,34 @@ THE SOFTWARE. | |||
36 | */ | 36 | */ |
37 | #if defined(__linux__) | 37 | #if defined(__linux__) |
38 | 38 | ||
39 | # ifndef _GNU_SOURCE // definition by the makefile can cause a redefinition error | 39 | #ifndef _GNU_SOURCE // definition by the makefile can cause a redefinition error |
40 | # define _GNU_SOURCE // must be defined before any include | 40 | #define _GNU_SOURCE // must be defined before any include |
41 | # endif // _GNU_SOURCE | 41 | #endif // _GNU_SOURCE |
42 | 42 | ||
43 | # ifdef __ANDROID__ | 43 | #ifdef __ANDROID__ |
44 | # include <android/log.h> | 44 | #include <android/log.h> |
45 | # define LOG_TAG "LuaLanes" | 45 | #define LOG_TAG "LuaLanes" |
46 | # endif // __ANDROID__ | 46 | #endif // __ANDROID__ |
47 | 47 | ||
48 | #endif // __linux__ | 48 | #endif // __linux__ |
49 | 49 | ||
50 | #include "threading.h" | 50 | #include "threading.h" |
51 | 51 | ||
52 | #if !defined( PLATFORM_XBOX) && !defined( PLATFORM_WIN32) && !defined( PLATFORM_POCKETPC) | 52 | #if !defined(PLATFORM_XBOX) && !defined(PLATFORM_WIN32) && !defined(PLATFORM_POCKETPC) |
53 | # include <sys/time.h> | 53 | #include <sys/time.h> |
54 | #endif // non-WIN32 timing | 54 | #endif // non-WIN32 timing |
55 | 55 | ||
56 | |||
57 | #if defined(PLATFORM_LINUX) || defined(PLATFORM_CYGWIN) | 56 | #if defined(PLATFORM_LINUX) || defined(PLATFORM_CYGWIN) |
58 | # include <sys/types.h> | 57 | #include <sys/types.h> |
59 | # include <unistd.h> | 58 | #include <unistd.h> |
60 | #endif | 59 | #endif |
61 | 60 | ||
62 | #ifdef PLATFORM_OSX | 61 | #ifdef PLATFORM_OSX |
63 | # include "threading_osx.h" | 62 | #include "threading_osx.h" |
64 | #endif | 63 | #endif |
65 | 64 | ||
66 | /* Linux with older glibc (such as Debian) don't have pthread_setname_np, but have prctl | 65 | /* Linux with older glibc (such as Debian) don't have pthread_setname_np, but have prctl |
67 | */ | 66 | */ |
68 | #if defined PLATFORM_LINUX | 67 | #if defined PLATFORM_LINUX |
69 | #if defined __GNU_LIBRARY__ && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 12 | 68 | #if defined __GNU_LIBRARY__ && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 12 |
70 | #define LINUX_USE_PTHREAD_SETNAME_NP 1 | 69 | #define LINUX_USE_PTHREAD_SETNAME_NP 1 |
@@ -76,41 +75,39 @@ THE SOFTWARE. | |||
76 | 75 | ||
77 | #ifdef _MSC_VER | 76 | #ifdef _MSC_VER |
78 | // ".. selected for automatic inline expansion" (/O2 option) | 77 | // ".. selected for automatic inline expansion" (/O2 option) |
79 | # pragma warning( disable : 4711 ) | 78 | #pragma warning(disable : 4711) |
80 | // ".. type cast from function pointer ... to data pointer" | 79 | // ".. type cast from function pointer ... to data pointer" |
81 | # pragma warning( disable : 4054 ) | 80 | #pragma warning(disable : 4054) |
82 | #endif | 81 | #endif |
83 | 82 | ||
84 | /* | 83 | /* |
85 | * FAIL is for unexpected API return values - essentially programming | 84 | * FAIL is for unexpected API return values - essentially programming |
86 | * error in _this_ code. | 85 | * error in _this_ code. |
87 | */ | 86 | */ |
88 | #if defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC) | 87 | #if defined(PLATFORM_XBOX) || defined(PLATFORM_WIN32) || defined(PLATFORM_POCKETPC) |
89 | static void FAIL(char const* funcname, int rc) | 88 | static void FAIL(char const* funcname, int rc) |
90 | { | 89 | { |
91 | #if defined(PLATFORM_XBOX) | 90 | #if defined(PLATFORM_XBOX) |
92 | fprintf(stderr, "%s() failed! (%d)\n", funcname, rc); | 91 | fprintf(stderr, "%s() failed! (%d)\n", funcname, rc); |
93 | #else // PLATFORM_XBOX | 92 | #else // PLATFORM_XBOX |
94 | char buf[256]; | 93 | char buf[256]; |
95 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, nullptr); | 94 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, nullptr); |
96 | fprintf(stderr, "%s() failed! [GetLastError() -> %d] '%s'", funcname, rc, buf); | 95 | fprintf(stderr, "%s() failed! [GetLastError() -> %d] '%s'", funcname, rc, buf); |
97 | #endif // PLATFORM_XBOX | 96 | #endif // PLATFORM_XBOX |
98 | #ifdef _MSC_VER | 97 | #ifdef _MSC_VER |
99 | __debugbreak(); // give a chance to the debugger! | 98 | __debugbreak(); // give a chance to the debugger! |
100 | #endif // _MSC_VER | 99 | #endif // _MSC_VER |
101 | abort(); | 100 | abort(); |
102 | } | 101 | } |
103 | #endif // win32 build | 102 | #endif // win32 build |
104 | 103 | ||
105 | |||
106 | /*---=== Threading ===---*/ | 104 | /*---=== Threading ===---*/ |
107 | 105 | ||
108 | // ################################################################################################# | 106 | // ################################################################################################# |
109 | // ################################################################################################# | 107 | // ################################################################################################# |
110 | #if THREADAPI == THREADAPI_WINDOWS | 108 | #if THREADAPI == THREADAPI_WINDOWS |
111 | 109 | ||
112 | static int const gs_prio_remap[] = | 110 | static int const gs_prio_remap[] = { |
113 | { | ||
114 | THREAD_PRIORITY_IDLE, | 111 | THREAD_PRIORITY_IDLE, |
115 | THREAD_PRIORITY_LOWEST, | 112 | THREAD_PRIORITY_LOWEST, |
116 | THREAD_PRIORITY_BELOW_NORMAL, | 113 | THREAD_PRIORITY_BELOW_NORMAL, |
@@ -125,8 +122,7 @@ static int const gs_prio_remap[] = | |||
125 | void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) | 122 | void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) |
126 | { | 123 | { |
127 | // prio range [-3,+3] was checked by the caller | 124 | // prio range [-3,+3] was checked by the caller |
128 | if (!SetThreadPriority(GetCurrentThread(), gs_prio_remap[prio_ + 3])) | 125 | if (!SetThreadPriority(GetCurrentThread(), gs_prio_remap[prio_ + 3])) { |
129 | { | ||
130 | FAIL("THREAD_SET_PRIORITY", GetLastError()); | 126 | FAIL("THREAD_SET_PRIORITY", GetLastError()); |
131 | } | 127 | } |
132 | } | 128 | } |
@@ -136,8 +132,7 @@ void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) | |||
136 | void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, [[maybe_unused]] bool sudo_) | 132 | void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, [[maybe_unused]] bool sudo_) |
137 | { | 133 | { |
138 | // prio range [-3,+3] was checked by the caller | 134 | // prio range [-3,+3] was checked by the caller |
139 | if (!SetThreadPriority(thread_.native_handle(), gs_prio_remap[prio_ + 3])) | 135 | if (!SetThreadPriority(thread_.native_handle(), gs_prio_remap[prio_ + 3])) { |
140 | { | ||
141 | FAIL("JTHREAD_SET_PRIORITY", GetLastError()); | 136 | FAIL("JTHREAD_SET_PRIORITY", GetLastError()); |
142 | } | 137 | } |
143 | } | 138 | } |
@@ -146,8 +141,7 @@ void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, [[maybe_unused]] boo | |||
146 | 141 | ||
147 | void THREAD_SET_AFFINITY(unsigned int aff) | 142 | void THREAD_SET_AFFINITY(unsigned int aff) |
148 | { | 143 | { |
149 | if (!SetThreadAffinityMask(GetCurrentThread(), aff)) | 144 | if (!SetThreadAffinityMask(GetCurrentThread(), aff)) { |
150 | { | ||
151 | FAIL("THREAD_SET_AFFINITY", GetLastError()); | 145 | FAIL("THREAD_SET_AFFINITY", GetLastError()); |
152 | } | 146 | } |
153 | } | 147 | } |
@@ -155,15 +149,15 @@ void THREAD_SET_AFFINITY(unsigned int aff) | |||
155 | // ################################################################################################# | 149 | // ################################################################################################# |
156 | 150 | ||
157 | #if !defined __GNUC__ | 151 | #if !defined __GNUC__ |
158 | //see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx | 152 | // see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx |
159 | #define MS_VC_EXCEPTION 0x406D1388 | 153 | #define MS_VC_EXCEPTION 0x406D1388 |
160 | #pragma pack(push,8) | 154 | #pragma pack(push, 8) |
161 | typedef struct tagTHREADNAME_INFO | 155 | typedef struct tagTHREADNAME_INFO |
162 | { | 156 | { |
163 | DWORD dwType; // Must be 0x1000. | 157 | DWORD dwType; // Must be 0x1000. |
164 | LPCSTR szName; // Pointer to name (in user addr space). | 158 | LPCSTR szName; // Pointer to name (in user addr space). |
165 | DWORD dwThreadID; // Thread ID (-1=caller thread). | 159 | DWORD dwThreadID; // Thread ID (-1=caller thread). |
166 | DWORD dwFlags; // Reserved for future use, must be zero. | 160 | DWORD dwFlags; // Reserved for future use, must be zero. |
167 | } THREADNAME_INFO; | 161 | } THREADNAME_INFO; |
168 | #pragma pack(pop) | 162 | #pragma pack(pop) |
169 | #endif // !__GNUC__ | 163 | #endif // !__GNUC__ |
@@ -177,12 +171,9 @@ void THREAD_SETNAME(char const* _name) | |||
177 | info.dwThreadID = GetCurrentThreadId(); | 171 | info.dwThreadID = GetCurrentThreadId(); |
178 | info.dwFlags = 0; | 172 | info.dwFlags = 0; |
179 | 173 | ||
180 | __try | 174 | __try { |
181 | { | 175 | RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*) &info); |
182 | RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); | 176 | } __except (EXCEPTION_EXECUTE_HANDLER) { |
183 | } | ||
184 | __except(EXCEPTION_EXECUTE_HANDLER) | ||
185 | { | ||
186 | } | 177 | } |
187 | #endif // !__GNUC__ | 178 | #endif // !__GNUC__ |
188 | } | 179 | } |
@@ -211,8 +202,7 @@ void THREAD_SETNAME(char const* _name) | |||
211 | #undef pthread_attr_setschedpolicy | 202 | #undef pthread_attr_setschedpolicy |
212 | [[nodiscard]] static int pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy) | 203 | [[nodiscard]] static int pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy) |
213 | { | 204 | { |
214 | if (policy != SCHED_OTHER) | 205 | if (policy != SCHED_OTHER) { |
215 | { | ||
216 | return ENOTSUP; | 206 | return ENOTSUP; |
217 | } | 207 | } |
218 | return 0; | 208 | return 0; |
@@ -220,131 +210,140 @@ void THREAD_SETNAME(char const* _name) | |||
220 | #endif // pthread_attr_setschedpolicy() | 210 | #endif // pthread_attr_setschedpolicy() |
221 | #endif // defined(__MINGW32__) || defined(__MINGW64__) | 211 | #endif // defined(__MINGW32__) || defined(__MINGW64__) |
222 | 212 | ||
223 | static void _PT_FAIL( int rc, const char *name, const char *file, int line ) | 213 | static void _PT_FAIL(int rc, const char* name, const char* file, int line) |
224 | { | 214 | { |
225 | const char *why= (rc==EINVAL) ? "EINVAL" : | 215 | const char* why = (rc == EINVAL) ? "EINVAL" |
226 | (rc==EBUSY) ? "EBUSY" : | 216 | : (rc == EBUSY) ? "EBUSY" |
227 | (rc==EPERM) ? "EPERM" : | 217 | : (rc == EPERM) ? "EPERM" |
228 | (rc==ENOMEM) ? "ENOMEM" : | 218 | : (rc == ENOMEM) ? "ENOMEM" |
229 | (rc==ESRCH) ? "ESRCH" : | 219 | : (rc == ESRCH) ? "ESRCH" |
230 | (rc==ENOTSUP) ? "ENOTSUP": | 220 | : (rc == ENOTSUP) ? "ENOTSUP" |
231 | //... | 221 | : "<UNKNOWN>"; |
232 | "<UNKNOWN>"; | 222 | fprintf(stderr, "%s %d: %s failed, %d %s\n", file, line, name, rc, why); |
233 | fprintf( stderr, "%s %d: %s failed, %d %s\n", file, line, name, rc, why ); | ||
234 | abort(); | 223 | abort(); |
235 | } | 224 | } |
236 | #define PT_CALL( call ) { int rc= call; if (rc!=0) _PT_FAIL( rc, #call, __FILE__, __LINE__ ); } | 225 | #define PT_CALL(call) \ |
226 | { \ | ||
227 | int rc = call; \ | ||
228 | if (rc != 0) \ | ||
229 | _PT_FAIL(rc, #call, __FILE__, __LINE__); \ | ||
230 | } | ||
237 | 231 | ||
238 | // array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range | 232 | // array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range |
239 | static int const gs_prio_remap[] = | 233 | static int const gs_prio_remap[] = { |
240 | { | 234 | // NB: PThreads priority handling is about as twisty as one can get it |
241 | // NB: PThreads priority handling is about as twisty as one can get it | 235 | // (and then some). DON*T TRUST ANYTHING YOU READ ON THE NET!!! |
242 | // (and then some). DON*T TRUST ANYTHING YOU READ ON THE NET!!! | 236 | |
243 | 237 | //--- | |
244 | //--- | 238 | // "Select the scheduling policy for the thread: one of SCHED_OTHER |
245 | // "Select the scheduling policy for the thread: one of SCHED_OTHER | 239 | // (regular, non-real-time scheduling), SCHED_RR (real-time, |
246 | // (regular, non-real-time scheduling), SCHED_RR (real-time, | 240 | // round-robin) or SCHED_FIFO (real-time, first-in first-out)." |
247 | // round-robin) or SCHED_FIFO (real-time, first-in first-out)." | 241 | // |
248 | // | 242 | // "Using the RR policy ensures that all threads having the same |
249 | // "Using the RR policy ensures that all threads having the same | 243 | // priority level will be scheduled equally, regardless of their activity." |
250 | // priority level will be scheduled equally, regardless of their activity." | 244 | // |
251 | // | 245 | // "For SCHED_FIFO and SCHED_RR, the only required member of the |
252 | // "For SCHED_FIFO and SCHED_RR, the only required member of the | 246 | // sched_param structure is the priority sched_priority. For SCHED_OTHER, |
253 | // sched_param structure is the priority sched_priority. For SCHED_OTHER, | 247 | // the affected scheduling parameters are implementation-defined." |
254 | // the affected scheduling parameters are implementation-defined." | 248 | // |
255 | // | 249 | // "The priority of a thread is specified as a delta which is added to |
256 | // "The priority of a thread is specified as a delta which is added to | 250 | // the priority of the process." |
257 | // the priority of the process." | 251 | // |
258 | // | 252 | // ".. priority is an integer value, in the range from 1 to 127. |
259 | // ".. priority is an integer value, in the range from 1 to 127. | 253 | // 1 is the least-favored priority, 127 is the most-favored." |
260 | // 1 is the least-favored priority, 127 is the most-favored." | 254 | // |
261 | // | 255 | // "Priority level 0 cannot be used: it is reserved for the system." |
262 | // "Priority level 0 cannot be used: it is reserved for the system." | 256 | // |
263 | // | 257 | // "When you use specify a priority of -99 in a call to |
264 | // "When you use specify a priority of -99 in a call to | 258 | // pthread_setschedparam(), the priority of the target thread is |
265 | // pthread_setschedparam(), the priority of the target thread is | 259 | // lowered to the lowest possible value." |
266 | // lowered to the lowest possible value." | 260 | // |
267 | // | 261 | // ... |
268 | // ... | ||
269 | |||
270 | // ** CONCLUSION ** | ||
271 | // | ||
272 | // PThread priorities are _hugely_ system specific, and we need at | ||
273 | // least OS specific settings. Hopefully, Linuxes and OS X versions | ||
274 | // are uniform enough, among each other... | ||
275 | // | ||
276 | # if defined PLATFORM_OSX | ||
277 | // AK 10-Apr-07 (OS X PowerPC 10.4.9): | ||
278 | // | ||
279 | // With SCHED_RR, 26 seems to be the "normal" priority, where setting | ||
280 | // it does not seem to affect the order of threads processed. | ||
281 | // | ||
282 | // With SCHED_OTHER, the range 25..32 is normal (maybe the same 26, | ||
283 | // but the difference is not so clear with OTHER). | ||
284 | // | ||
285 | // 'sched_get_priority_min()' and '..max()' give 15, 47 as the | ||
286 | // priority limits. This could imply, user mode applications won't | ||
287 | // be able to use values outside of that range. | ||
288 | // | ||
289 | # define _PRIO_MODE SCHED_OTHER | ||
290 | |||
291 | // OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope | ||
292 | //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS | ||
293 | |||
294 | # define _PRIO_HI 32 // seems to work (_carefully_ picked!) | ||
295 | # define _PRIO_0 26 // detected | ||
296 | # define _PRIO_LO 1 // seems to work (tested) | ||
297 | |||
298 | # elif defined PLATFORM_LINUX | ||
299 | // (based on Ubuntu Linux 2.6.15 kernel) | ||
300 | // | ||
301 | // SCHED_OTHER is the default policy, but does not allow for priorities. | ||
302 | // SCHED_RR allows priorities, all of which (1..99) are higher than | ||
303 | // a thread with SCHED_OTHER policy. | ||
304 | // | ||
305 | // <http://kerneltrap.org/node/6080> | ||
306 | // <http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library> | ||
307 | // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html> | ||
308 | // | ||
309 | // Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING, | ||
310 | // but even Ubuntu does not seem to define it. | ||
311 | // | ||
312 | # define _PRIO_MODE SCHED_RR | ||
313 | |||
314 | // NTLP 2.5: only system scope allowed (being the basic reason why | ||
315 | // root privileges are required..) | ||
316 | //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS | ||
317 | |||
318 | # define _PRIO_HI 99 | ||
319 | # define _PRIO_0 50 | ||
320 | # define _PRIO_LO 1 | ||
321 | |||
322 | # elif defined(PLATFORM_BSD) | ||
323 | // | ||
324 | // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html> | ||
325 | // | ||
326 | // "When control over the thread scheduling is desired, then FreeBSD | ||
327 | // with the libpthread implementation is by far the best choice .." | ||
328 | // | ||
329 | # define _PRIO_MODE SCHED_OTHER | ||
330 | # define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS | ||
331 | # define _PRIO_HI 31 | ||
332 | # define _PRIO_0 15 | ||
333 | # define _PRIO_LO 1 | ||
334 | |||
335 | # elif defined(PLATFORM_CYGWIN) | ||
336 | // | ||
337 | // TBD: Find right values for Cygwin | ||
338 | // | ||
339 | # else | ||
340 | # error "Unknown OS: not implemented!" | ||
341 | # endif | ||
342 | 262 | ||
343 | #if defined _PRIO_0 | 263 | // ** CONCLUSION ** |
344 | # define _PRIO_AN (_PRIO_0 + ((_PRIO_HI-_PRIO_0)/2)) | 264 | // |
345 | # define _PRIO_BN (_PRIO_LO + ((_PRIO_0-_PRIO_LO)/2)) | 265 | // PThread priorities are _hugely_ system specific, and we need at |
266 | // least OS specific settings. Hopefully, Linuxes and OS X versions | ||
267 | // are uniform enough, among each other... | ||
268 | // | ||
269 | #if defined PLATFORM_OSX | ||
270 | // AK 10-Apr-07 (OS X PowerPC 10.4.9): | ||
271 | // | ||
272 | // With SCHED_RR, 26 seems to be the "normal" priority, where setting | ||
273 | // it does not seem to affect the order of threads processed. | ||
274 | // | ||
275 | // With SCHED_OTHER, the range 25..32 is normal (maybe the same 26, | ||
276 | // but the difference is not so clear with OTHER). | ||
277 | // | ||
278 | // 'sched_get_priority_min()' and '..max()' give 15, 47 as the | ||
279 | // priority limits. This could imply, user mode applications won't | ||
280 | // be able to use values outside of that range. | ||
281 | // | ||
282 | #define _PRIO_MODE SCHED_OTHER | ||
283 | |||
284 | // OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope | ||
285 | // #define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS | ||
286 | |||
287 | #define _PRIO_HI 32 // seems to work (_carefully_ picked!) | ||
288 | #define _PRIO_0 26 // detected | ||
289 | #define _PRIO_LO 1 // seems to work (tested) | ||
290 | |||
291 | #elif defined PLATFORM_LINUX | ||
292 | // (based on Ubuntu Linux 2.6.15 kernel) | ||
293 | // | ||
294 | // SCHED_OTHER is the default policy, but does not allow for priorities. | ||
295 | // SCHED_RR allows priorities, all of which (1..99) are higher than | ||
296 | // a thread with SCHED_OTHER policy. | ||
297 | // | ||
298 | // <http://kerneltrap.org/node/6080> | ||
299 | // <http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library> | ||
300 | // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html> | ||
301 | // | ||
302 | // Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING, | ||
303 | // but even Ubuntu does not seem to define it. | ||
304 | // | ||
305 | #define _PRIO_MODE SCHED_RR | ||
346 | 306 | ||
347 | _PRIO_LO, _PRIO_LO, _PRIO_BN, _PRIO_0, _PRIO_AN, _PRIO_HI, _PRIO_HI | 307 | // NTLP 2.5: only system scope allowed (being the basic reason why |
308 | // root privileges are required..) | ||
309 | // #define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS | ||
310 | |||
311 | #define _PRIO_HI 99 | ||
312 | #define _PRIO_0 50 | ||
313 | #define _PRIO_LO 1 | ||
314 | |||
315 | #elif defined(PLATFORM_BSD) | ||
316 | // | ||
317 | // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html> | ||
318 | // | ||
319 | // "When control over the thread scheduling is desired, then FreeBSD | ||
320 | // with the libpthread implementation is by far the best choice .." | ||
321 | // | ||
322 | #define _PRIO_MODE SCHED_OTHER | ||
323 | #define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS | ||
324 | #define _PRIO_HI 31 | ||
325 | #define _PRIO_0 15 | ||
326 | #define _PRIO_LO 1 | ||
327 | |||
328 | #elif defined(PLATFORM_CYGWIN) | ||
329 | // | ||
330 | // TBD: Find right values for Cygwin | ||
331 | // | ||
332 | #else | ||
333 | #error "Unknown OS: not implemented!" | ||
334 | #endif | ||
335 | |||
336 | #if defined _PRIO_0 | ||
337 | #define _PRIO_AN (_PRIO_0 + ((_PRIO_HI - _PRIO_0) / 2)) | ||
338 | #define _PRIO_BN (_PRIO_LO + ((_PRIO_0 - _PRIO_LO) / 2)) | ||
339 | |||
340 | _PRIO_LO, | ||
341 | _PRIO_LO, | ||
342 | _PRIO_BN, | ||
343 | _PRIO_0, | ||
344 | _PRIO_AN, | ||
345 | _PRIO_HI, | ||
346 | _PRIO_HI | ||
348 | #endif // _PRIO_0 | 347 | #endif // _PRIO_0 |
349 | }; | 348 | }; |
350 | 349 | ||
@@ -398,10 +397,8 @@ void THREAD_SET_AFFINITY(unsigned int aff) | |||
398 | cpu_set_t cpuset; | 397 | cpu_set_t cpuset; |
399 | CPU_ZERO(&cpuset); | 398 | CPU_ZERO(&cpuset); |
400 | #endif | 399 | #endif |
401 | while (aff != 0) | 400 | while (aff != 0) { |
402 | { | 401 | if (aff & 1) { |
403 | if (aff & 1) | ||
404 | { | ||
405 | CPU_SET(bit, &cpuset); | 402 | CPU_SET(bit, &cpuset); |
406 | } | 403 | } |
407 | ++bit; | 404 | ++bit; |
@@ -430,7 +427,7 @@ void THREAD_SETNAME(char const* _name) | |||
430 | #elif defined PLATFORM_LINUX | 427 | #elif defined PLATFORM_LINUX |
431 | #if LINUX_USE_PTHREAD_SETNAME_NP | 428 | #if LINUX_USE_PTHREAD_SETNAME_NP |
432 | pthread_setname_np(pthread_self(), _name); | 429 | pthread_setname_np(pthread_self(), _name); |
433 | #else // LINUX_USE_PTHREAD_SETNAME_NP | 430 | #else // LINUX_USE_PTHREAD_SETNAME_NP |
434 | prctl(PR_SET_NAME, _name, 0, 0, 0); | 431 | prctl(PR_SET_NAME, _name, 0, 0, 0); |
435 | #endif // LINUX_USE_PTHREAD_SETNAME_NP | 432 | #endif // LINUX_USE_PTHREAD_SETNAME_NP |
436 | #elif defined PLATFORM_QNX || defined PLATFORM_CYGWIN | 433 | #elif defined PLATFORM_QNX || defined PLATFORM_CYGWIN |
diff --git a/src/tools.cpp b/src/tools.cpp index 7b2d153..63bb323 100644 --- a/src/tools.cpp +++ b/src/tools.cpp | |||
@@ -2,7 +2,7 @@ | |||
2 | * TOOLS.C Copyright (c) 2002-10, Asko Kauppi | 2 | * TOOLS.C Copyright (c) 2002-10, Asko Kauppi |
3 | * | 3 | * |
4 | * Lua tools to support Lanes. | 4 | * Lua tools to support Lanes. |
5 | */ | 5 | */ |
6 | 6 | ||
7 | /* | 7 | /* |
8 | =============================================================================== | 8 | =============================================================================== |
@@ -42,33 +42,29 @@ static constexpr RegistryUniqueKey kLookupCacheRegKey{ 0x9BF75F84E54B691Bull }; | |||
42 | 42 | ||
43 | // ################################################################################################# | 43 | // ################################################################################################# |
44 | 44 | ||
45 | /* | 45 | // does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it |
46 | * Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it | ||
47 | */ | ||
48 | void push_registry_subtable_mode(lua_State* L_, RegistryUniqueKey key_, const char* mode_) | 46 | void push_registry_subtable_mode(lua_State* L_, RegistryUniqueKey key_, const char* mode_) |
49 | { | 47 | { |
50 | STACK_GROW(L_, 3); | 48 | STACK_GROW(L_, 3); |
51 | STACK_CHECK_START_REL(L_, 0); | 49 | STACK_CHECK_START_REL(L_, 0); |
52 | 50 | ||
53 | key_.pushValue(L_); // {}|nil | 51 | key_.pushValue(L_); // L_: {}|nil |
54 | STACK_CHECK(L_, 1); | 52 | STACK_CHECK(L_, 1); |
55 | 53 | ||
56 | if (lua_isnil(L_, -1)) | 54 | if (lua_isnil(L_, -1)) { |
57 | { | 55 | lua_pop(L_, 1); // L_: |
58 | lua_pop(L_, 1); // | 56 | lua_newtable(L_); // L_: {} |
59 | lua_newtable(L_); // {} | ||
60 | // _R[key_] = {} | 57 | // _R[key_] = {} |
61 | key_.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); // {} | 58 | key_.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); // L_: {} |
62 | STACK_CHECK(L_, 1); | 59 | STACK_CHECK(L_, 1); |
63 | 60 | ||
64 | // Set its metatable if requested | 61 | // Set its metatable if requested |
65 | if (mode_) | 62 | if (mode_) { |
66 | { | 63 | lua_newtable(L_); // L_: {} mt |
67 | lua_newtable(L_); // {} mt | 64 | lua_pushliteral(L_, "__mode"); // L_: {} mt "__mode" |
68 | lua_pushliteral(L_, "__mode"); // {} mt "__mode" | 65 | lua_pushstring(L_, mode_); // L_: {} mt "__mode" mode |
69 | lua_pushstring(L_, mode_); // {} mt "__mode" mode | 66 | lua_rawset(L_, -3); // L_: {} mt |
70 | lua_rawset(L_, -3); // {} mt | 67 | lua_setmetatable(L_, -2); // L_: {} |
71 | lua_setmetatable(L_, -2); // {} | ||
72 | } | 68 | } |
73 | } | 69 | } |
74 | STACK_CHECK(L_, 1); | 70 | STACK_CHECK(L_, 1); |
@@ -83,7 +79,7 @@ void push_registry_subtable_mode(lua_State* L_, RegistryUniqueKey key_, const ch | |||
83 | */ | 79 | */ |
84 | void push_registry_subtable(lua_State* L_, RegistryUniqueKey key_) | 80 | void push_registry_subtable(lua_State* L_, RegistryUniqueKey key_) |
85 | { | 81 | { |
86 | push_registry_subtable_mode(L_, key_, nullptr); | 82 | push_registry_subtable_mode(L_, key_, nullptr); |
87 | } | 83 | } |
88 | 84 | ||
89 | // ################################################################################################# | 85 | // ################################################################################################# |
@@ -91,13 +87,10 @@ void push_registry_subtable(lua_State* L_, RegistryUniqueKey key_) | |||
91 | // same as PUC-Lua l_alloc | 87 | // same as PUC-Lua l_alloc |
92 | extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_) | 88 | extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_) |
93 | { | 89 | { |
94 | if (nsize_ == 0) | 90 | if (nsize_ == 0) { |
95 | { | ||
96 | free(ptr_); | 91 | free(ptr_); |
97 | return nullptr; | 92 | return nullptr; |
98 | } | 93 | } else { |
99 | else | ||
100 | { | ||
101 | return realloc(ptr_, nsize_); | 94 | return realloc(ptr_, nsize_); |
102 | } | 95 | } |
103 | } | 96 | } |
@@ -116,64 +109,52 @@ extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, | |||
116 | 109 | ||
117 | // called once at the creation of the universe (therefore L is the master Lua state everything originates from) | 110 | // called once at the creation of the universe (therefore L is the master Lua state everything originates from) |
118 | // Do I need to disable this when compiling for LuaJIT to prevent issues? | 111 | // Do I need to disable this when compiling for LuaJIT to prevent issues? |
119 | void initialize_allocator_function(Universe* U, lua_State* L_) | 112 | void initialize_allocator_function(Universe* U_, lua_State* L_) |
120 | { | 113 | { |
121 | STACK_CHECK_START_REL(L_, 1); // settings | 114 | STACK_CHECK_START_REL(L_, 1); // L_: settings |
122 | lua_getfield(L_, -1, "allocator"); // settings allocator|nil|"protected" | 115 | lua_getfield(L_, -1, "allocator"); // L_: settings allocator|nil|"protected" |
123 | if (!lua_isnil(L_, -1)) | 116 | if (!lua_isnil(L_, -1)) { |
124 | { | ||
125 | // store C function pointer in an internal variable | 117 | // store C function pointer in an internal variable |
126 | U->provide_allocator = lua_tocfunction(L_, -1); // settings allocator | 118 | U_->provide_allocator = lua_tocfunction(L_, -1); // L_: settings allocator |
127 | if (U->provide_allocator != nullptr) | 119 | if (U_->provide_allocator != nullptr) { |
128 | { | ||
129 | // make sure the function doesn't have upvalues | 120 | // make sure the function doesn't have upvalues |
130 | char const* upname = lua_getupvalue(L_, -1, 1); // settings allocator upval? | 121 | char const* upname = lua_getupvalue(L_, -1, 1); // L_: settings allocator upval? |
131 | if (upname != nullptr) // should be "" for C functions with upvalues if any | 122 | if (upname != nullptr) { // should be "" for C functions with upvalues if any |
132 | { | ||
133 | raise_luaL_error(L_, "config.allocator() shouldn't have upvalues"); | 123 | raise_luaL_error(L_, "config.allocator() shouldn't have upvalues"); |
134 | } | 124 | } |
135 | // remove this C function from the config table so that it doesn't cause problems | 125 | // remove this C function from the config table so that it doesn't cause problems |
136 | // when we transfer the config table in newly created Lua states | 126 | // when we transfer the config table in newly created Lua states |
137 | lua_pushnil(L_); // settings allocator nil | 127 | lua_pushnil(L_); // L_: settings allocator nil |
138 | lua_setfield(L_, -3, "allocator"); // settings allocator | 128 | lua_setfield(L_, -3, "allocator"); // L_: settings allocator |
139 | } | 129 | } else if (lua_type(L_, -1) == LUA_TSTRING) { // should be "protected" |
140 | else if (lua_type(L_, -1) == LUA_TSTRING) // should be "protected" | ||
141 | { | ||
142 | LUA_ASSERT(L_, strcmp(lua_tostring(L_, -1), "protected") == 0); | 130 | LUA_ASSERT(L_, strcmp(lua_tostring(L_, -1), "protected") == 0); |
143 | // set the original allocator to call from inside protection by the mutex | 131 | // set the original allocator to call from inside protection by the mutex |
144 | U->protected_allocator.initFrom(L_); | 132 | U_->protected_allocator.initFrom(L_); |
145 | U->protected_allocator.installIn(L_); | 133 | U_->protected_allocator.installIn(L_); |
146 | // before a state is created, this function will be called to obtain the allocator | 134 | // before a state is created, this function will be called to obtain the allocator |
147 | U->provide_allocator = luaG_provide_protected_allocator; | 135 | U_->provide_allocator = luaG_provide_protected_allocator; |
148 | } | 136 | } |
149 | } | 137 | } else { |
150 | else | ||
151 | { | ||
152 | // just grab whatever allocator was provided to lua_newstate | 138 | // just grab whatever allocator was provided to lua_newstate |
153 | U->protected_allocator.initFrom(L_); | 139 | U_->protected_allocator.initFrom(L_); |
154 | } | 140 | } |
155 | lua_pop(L_, 1); // settings | 141 | lua_pop(L_, 1); // L_: settings |
156 | STACK_CHECK(L_, 1); | 142 | STACK_CHECK(L_, 1); |
157 | 143 | ||
158 | lua_getfield(L_, -1, "internal_allocator"); // settings "libc"|"allocator" | 144 | lua_getfield(L_, -1, "internal_allocator"); // L_: settings "libc"|"allocator" |
159 | { | 145 | { |
160 | char const* allocator = lua_tostring(L_, -1); | 146 | char const* allocator = lua_tostring(L_, -1); |
161 | if (strcmp(allocator, "libc") == 0) | 147 | if (strcmp(allocator, "libc") == 0) { |
162 | { | 148 | U_->internal_allocator = AllocatorDefinition{ libc_lua_Alloc, nullptr }; |
163 | U->internal_allocator = AllocatorDefinition{ libc_lua_Alloc, nullptr }; | 149 | } else if (U_->provide_allocator == luaG_provide_protected_allocator) { |
164 | } | ||
165 | else if (U->provide_allocator == luaG_provide_protected_allocator) | ||
166 | { | ||
167 | // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case. | 150 | // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case. |
168 | U->internal_allocator = U->protected_allocator.makeDefinition(); | 151 | U_->internal_allocator = U_->protected_allocator.makeDefinition(); |
169 | } | 152 | } else { |
170 | else | ||
171 | { | ||
172 | // no protection required, just use whatever we have as-is. | 153 | // no protection required, just use whatever we have as-is. |
173 | U->internal_allocator = U->protected_allocator; | 154 | U_->internal_allocator = U_->protected_allocator; |
174 | } | 155 | } |
175 | } | 156 | } |
176 | lua_pop(L_, 1); // settings | 157 | lua_pop(L_, 1); // L_: settings |
177 | STACK_CHECK(L_, 1); | 158 | STACK_CHECK(L_, 1); |
178 | } | 159 | } |
179 | 160 | ||
@@ -204,18 +185,16 @@ enum class FuncSubType | |||
204 | Bytecode, | 185 | Bytecode, |
205 | Native, | 186 | Native, |
206 | FastJIT | 187 | FastJIT |
207 | } ; | 188 | }; |
208 | 189 | ||
209 | FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) | 190 | FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) |
210 | { | 191 | { |
211 | if (lua_tocfunction(L_, _i)) // nullptr for LuaJIT-fast && bytecode functions | 192 | if (lua_tocfunction(L_, _i)) { // nullptr for LuaJIT-fast && bytecode functions |
212 | { | ||
213 | return FuncSubType::Native; | 193 | return FuncSubType::Native; |
214 | } | 194 | } |
215 | { | 195 | { |
216 | int mustpush{ 0 }; | 196 | int mustpush{ 0 }; |
217 | if (lua_absindex(L_, _i) != lua_gettop(L_)) | 197 | if (lua_absindex(L_, _i) != lua_gettop(L_)) { |
218 | { | ||
219 | lua_pushvalue(L_, _i); | 198 | lua_pushvalue(L_, _i); |
220 | mustpush = 1; | 199 | mustpush = 1; |
221 | } | 200 | } |
@@ -224,8 +203,7 @@ FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) | |||
224 | // all other cases mean this is either a C or LuaJIT-fast function | 203 | // all other cases mean this is either a C or LuaJIT-fast function |
225 | int const dumpres{ lua504_dump(L_, dummy_writer, nullptr, 0) }; | 204 | int const dumpres{ lua504_dump(L_, dummy_writer, nullptr, 0) }; |
226 | lua_pop(L_, mustpush); | 205 | lua_pop(L_, mustpush); |
227 | if (dumpres == 666) | 206 | if (dumpres == 666) { |
228 | { | ||
229 | return FuncSubType::Bytecode; | 207 | return FuncSubType::Bytecode; |
230 | } | 208 | } |
231 | } | 209 | } |
@@ -235,28 +213,26 @@ FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) | |||
235 | // ################################################################################################# | 213 | // ################################################################################################# |
236 | 214 | ||
237 | // inspired from tconcat() in ltablib.c | 215 | // inspired from tconcat() in ltablib.c |
238 | [[nodiscard]] static char const* luaG_pushFQN(lua_State* L_, int t, int last, size_t* length) | 216 | [[nodiscard]] static char const* luaG_pushFQN(lua_State* L_, int t_, int last_, size_t* length_) |
239 | { | 217 | { |
240 | luaL_Buffer b; | 218 | luaL_Buffer b; |
241 | STACK_CHECK_START_REL(L_, 0); | 219 | STACK_CHECK_START_REL(L_, 0); |
242 | // Lua 5.4 pushes &b as light userdata on the stack. be aware of it... | 220 | // Lua 5.4 pushes &b as light userdata on the stack. be aware of it... |
243 | luaL_buffinit(L_, &b); // ... {} ... &b? | 221 | luaL_buffinit(L_, &b); // L_: ... {} ... &b? |
244 | int i = 1; | 222 | int i = 1; |
245 | for (; i < last; ++i) | 223 | for (; i < last_; ++i) { |
246 | { | 224 | lua_rawgeti(L_, t_, i); |
247 | lua_rawgeti( L_, t, i); | 225 | luaL_addvalue(&b); |
248 | luaL_addvalue( &b); | ||
249 | luaL_addlstring(&b, "/", 1); | 226 | luaL_addlstring(&b, "/", 1); |
250 | } | 227 | } |
251 | if (i == last) // add last value (if interval was not empty) | 228 | if (i == last_) { // add last value (if interval was not empty) |
252 | { | 229 | lua_rawgeti(L_, t_, i); |
253 | lua_rawgeti(L_, t, i); | ||
254 | luaL_addvalue(&b); | 230 | luaL_addvalue(&b); |
255 | } | 231 | } |
256 | // &b is popped at that point (-> replaced by the result) | 232 | // &b is popped at that point (-> replaced by the result) |
257 | luaL_pushresult(&b); // ... {} ... "<result>" | 233 | luaL_pushresult(&b); // L_: ... {} ... "<result>" |
258 | STACK_CHECK(L_, 1); | 234 | STACK_CHECK(L_, 1); |
259 | return lua_tolstring( L_, -1, length); | 235 | return lua_tolstring(L_, -1, length_); |
260 | } | 236 | } |
261 | 237 | ||
262 | // ################################################################################################# | 238 | // ################################################################################################# |
@@ -269,7 +245,7 @@ FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) | |||
269 | * if we already had an entry of type [o] = ..., replace the name if the new one is shorter | 245 | * if we already had an entry of type [o] = ..., replace the name if the new one is shorter |
270 | * pops the processed object from the stack | 246 | * pops the processed object from the stack |
271 | */ | 247 | */ |
272 | static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_, int _ctx_base, int _depth) | 248 | static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L_, int _ctx_base, int _depth) |
273 | { | 249 | { |
274 | // slot 1 in the stack contains the table that receives everything we found | 250 | // slot 1 in the stack contains the table that receives everything we found |
275 | int const dest{ _ctx_base }; | 251 | int const dest{ _ctx_base }; |
@@ -278,22 +254,22 @@ static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_ | |||
278 | 254 | ||
279 | size_t prevNameLength, newNameLength; | 255 | size_t prevNameLength, newNameLength; |
280 | char const* prevName; | 256 | char const* prevName; |
281 | DEBUGSPEW_CODE(char const *newName); | 257 | DEBUGSPEW_CODE(char const* newName); |
282 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "update_lookup_entry()\n" INDENT_END)); | 258 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "update_lookup_entry()\n" INDENT_END(U_))); |
283 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 259 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U_ }); |
284 | 260 | ||
285 | STACK_CHECK_START_REL(L_, 0); | 261 | STACK_CHECK_START_REL(L_, 0); |
286 | // first, raise an error if the function is already known | 262 | // first, raise an error if the function is already known |
287 | lua_pushvalue(L_, -1); // ... {bfc} k o o | 263 | lua_pushvalue(L_, -1); // L_: ... {bfc} k o o |
288 | lua_rawget(L_, dest); // ... {bfc} k o name? | 264 | lua_rawget(L_, dest); // L_: ... {bfc} k o name? |
289 | prevName = lua_tolstring( L_, -1, &prevNameLength); // nullptr if we got nil (first encounter of this object) | 265 | prevName = lua_tolstring(L_, -1, &prevNameLength); // nullptr if we got nil (first encounter of this object) |
290 | // push name in fqn stack (note that concatenation will crash if name is a not string or a number) | 266 | // push name in fqn stack (note that concatenation will crash if name is a not string or a number) |
291 | lua_pushvalue(L_, -3); // ... {bfc} k o name? k | 267 | lua_pushvalue(L_, -3); // L_: ... {bfc} k o name? k |
292 | LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TNUMBER || lua_type(L_, -1) == LUA_TSTRING); | 268 | LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TNUMBER || lua_type(L_, -1) == LUA_TSTRING); |
293 | ++_depth; | 269 | ++_depth; |
294 | lua_rawseti(L_, fqn, _depth); // ... {bfc} k o name? | 270 | lua_rawseti(L_, fqn, _depth); // L_: ... {bfc} k o name? |
295 | // generate name | 271 | // generate name |
296 | DEBUGSPEW_OR_NOT(newName, std::ignore) = luaG_pushFQN(L_, fqn, _depth, &newNameLength); // ... {bfc} k o name? "f.q.n" | 272 | DEBUGSPEW_OR_NOT(newName, std::ignore) = luaG_pushFQN(L_, fqn, _depth, &newNameLength); // L_: ... {bfc} k o name? "f.q.n" |
297 | // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order | 273 | // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order |
298 | // on different VMs even when the tables are populated the exact same way. | 274 | // on different VMs even when the tables are populated the exact same way. |
299 | // When Lua is built with compatibility options (such as LUA_COMPAT_ALL), | 275 | // When Lua is built with compatibility options (such as LUA_COMPAT_ALL), |
@@ -303,227 +279,200 @@ static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_ | |||
303 | // Also, nothing prevents any external module from exposing a given object under several names, so... | 279 | // Also, nothing prevents any external module from exposing a given object under several names, so... |
304 | // Therefore, when we encounter an object for which a name was previously registered, we need to select the names | 280 | // Therefore, when we encounter an object for which a name was previously registered, we need to select the names |
305 | // based on some sorting order so that we end up with the same name in all databases whatever order the table walk yielded | 281 | // based on some sorting order so that we end up with the same name in all databases whatever order the table walk yielded |
306 | if (prevName != nullptr && (prevNameLength < newNameLength || lua_lessthan(L_, -2, -1))) | 282 | if (prevName != nullptr && (prevNameLength < newNameLength || lua_lessthan(L_, -2, -1))) { |
307 | { | 283 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s '%s' remained named '%s'\n" INDENT_END(U_), lua_typename(L_, lua_type(L_, -3)), newName, prevName)); |
308 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s '%s' remained named '%s'\n" INDENT_END, lua_typename(L_, lua_type(L_, -3)), newName, prevName)); | ||
309 | // the previous name is 'smaller' than the one we just generated: keep it! | 284 | // the previous name is 'smaller' than the one we just generated: keep it! |
310 | lua_pop(L_, 3); // ... {bfc} k | 285 | lua_pop(L_, 3); // L_: ... {bfc} k |
311 | } | 286 | } else { |
312 | else | ||
313 | { | ||
314 | // the name we generated is either the first one, or a better fit for our purposes | 287 | // the name we generated is either the first one, or a better fit for our purposes |
315 | if (prevName) | 288 | if (prevName) { |
316 | { | ||
317 | // clear the previous name for the database to avoid clutter | 289 | // clear the previous name for the database to avoid clutter |
318 | lua_insert(L_, -2); // ... {bfc} k o "f.q.n" prevName | 290 | lua_insert(L_, -2); // L_: ... {bfc} k o "f.q.n" prevName |
319 | // t[prevName] = nil | 291 | // t[prevName] = nil |
320 | lua_pushnil(L_); // ... {bfc} k o "f.q.n" prevName nil | 292 | lua_pushnil(L_); // L_: ... {bfc} k o "f.q.n" prevName nil |
321 | lua_rawset(L_, dest); // ... {bfc} k o "f.q.n" | 293 | lua_rawset(L_, dest); // L_: ... {bfc} k o "f.q.n" |
322 | } | 294 | } else { |
323 | else | 295 | lua_remove(L_, -2); // L_: ... {bfc} k o "f.q.n" |
324 | { | ||
325 | lua_remove(L_, -2); // ... {bfc} k o "f.q.n" | ||
326 | } | 296 | } |
327 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s '%s'\n" INDENT_END, lua_typename(L_, lua_type(L_, -2)), newName)); | 297 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s '%s'\n" INDENT_END(U_), lua_typename(L_, lua_type(L_, -2)), newName)); |
328 | // prepare the stack for database feed | 298 | // prepare the stack for database feed |
329 | lua_pushvalue(L_, -1); // ... {bfc} k o "f.q.n" "f.q.n" | 299 | lua_pushvalue(L_, -1); // L_: ... {bfc} k o "f.q.n" "f.q.n" |
330 | lua_pushvalue(L_, -3); // ... {bfc} k o "f.q.n" "f.q.n" o | 300 | lua_pushvalue(L_, -3); // L_: ... {bfc} k o "f.q.n" "f.q.n" o |
331 | LUA_ASSERT(L_, lua_rawequal(L_, -1, -4)); | 301 | LUA_ASSERT(L_, lua_rawequal(L_, -1, -4)); |
332 | LUA_ASSERT(L_, lua_rawequal(L_, -2, -3)); | 302 | LUA_ASSERT(L_, lua_rawequal(L_, -2, -3)); |
333 | // t["f.q.n"] = o | 303 | // t["f.q.n"] = o |
334 | lua_rawset(L_, dest); // ... {bfc} k o "f.q.n" | 304 | lua_rawset(L_, dest); // L_: ... {bfc} k o "f.q.n" |
335 | // t[o] = "f.q.n" | 305 | // t[o] = "f.q.n" |
336 | lua_rawset(L_, dest); // ... {bfc} k | 306 | lua_rawset(L_, dest); // L_: ... {bfc} k |
337 | // remove table name from fqn stack | 307 | // remove table name from fqn stack |
338 | lua_pushnil(L_); // ... {bfc} k nil | 308 | lua_pushnil(L_); // L_: ... {bfc} k nil |
339 | lua_rawseti(L_, fqn, _depth); // ... {bfc} k | 309 | lua_rawseti(L_, fqn, _depth); // L_: ... {bfc} k |
340 | } | 310 | } |
341 | -- _depth; | 311 | --_depth; |
342 | STACK_CHECK(L_, -1); | 312 | STACK_CHECK(L_, -1); |
343 | } | 313 | } |
344 | 314 | ||
345 | // ################################################################################################# | 315 | // ################################################################################################# |
346 | 316 | ||
347 | static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_, int _ctx_base, int _i, int _depth) | 317 | static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L_, int ctxBase_, int i_, int depth_) |
348 | { | 318 | { |
349 | // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i | 319 | // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot i_ |
350 | int const fqn = _ctx_base + 1; | 320 | int const fqn = ctxBase_ + 1; |
351 | // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops | 321 | // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops |
352 | int const cache = _ctx_base + 2; | 322 | int const cache = ctxBase_ + 2; |
353 | // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search) | 323 | // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search) |
354 | int const breadth_first_cache = lua_gettop(L_) + 1; | 324 | int const breadth_first_cache = lua_gettop(L_) + 1; |
355 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "populate_func_lookup_table_recur()\n" INDENT_END)); | 325 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "populate_func_lookup_table_recur()\n" INDENT_END(U_))); |
356 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 326 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U_ }); |
357 | 327 | ||
358 | STACK_GROW(L_, 6); | 328 | STACK_GROW(L_, 6); |
359 | // slot _i contains a table where we search for functions (or a full userdata with a metatable) | 329 | // slot i_ contains a table where we search for functions (or a full userdata with a metatable) |
360 | STACK_CHECK_START_REL(L_, 0); // ... {_i} | 330 | STACK_CHECK_START_REL(L_, 0); // L_: ... {i_} |
361 | 331 | ||
362 | // if object is a userdata, replace it by its metatable | 332 | // if object is a userdata, replace it by its metatable |
363 | if (lua_type(L_, _i) == LUA_TUSERDATA) | 333 | if (lua_type(L_, i_) == LUA_TUSERDATA) { |
364 | { | 334 | lua_getmetatable(L_, i_); // L_: ... {i_} mt |
365 | lua_getmetatable(L_, _i); // ... {_i} mt | 335 | lua_replace(L_, i_); // L_: ... {i_} |
366 | lua_replace(L_, _i); // ... {_i} | ||
367 | } | 336 | } |
368 | 337 | ||
369 | // if table is already visited, we are done | 338 | // if table is already visited, we are done |
370 | lua_pushvalue(L_, _i); // ... {_i} {} | 339 | lua_pushvalue(L_, i_); // L_: ... {i_} {} |
371 | lua_rawget(L_, cache); // ... {_i} nil|n | 340 | lua_rawget(L_, cache); // L_: ... {i_} nil|n |
372 | lua_Integer visit_count{ lua_tointeger(L_, -1) }; // 0 if nil, else n | 341 | lua_Integer visit_count{ lua_tointeger(L_, -1) }; // 0 if nil, else n |
373 | lua_pop(L_, 1); // ... {_i} | 342 | lua_pop(L_, 1); // L_: ... {i_} |
374 | STACK_CHECK(L_, 0); | 343 | STACK_CHECK(L_, 0); |
375 | if (visit_count > 0) | 344 | if (visit_count > 0) { |
376 | { | 345 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "already visited\n" INDENT_END(U_))); |
377 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "already visited\n" INDENT_END)); | ||
378 | return; | 346 | return; |
379 | } | 347 | } |
380 | 348 | ||
381 | // remember we visited this table (1-visit count) | 349 | // remember we visited this table (1-visit count) |
382 | lua_pushvalue(L_, _i); // ... {_i} {} | 350 | lua_pushvalue(L_, i_); // L_: ... {i_} {} |
383 | lua_pushinteger(L_, visit_count + 1); // ... {_i} {} 1 | 351 | lua_pushinteger(L_, visit_count + 1); // L_: ... {i_} {} 1 |
384 | lua_rawset(L_, cache); // ... {_i} | 352 | lua_rawset(L_, cache); // L_: ... {i_} |
385 | STACK_CHECK(L_, 0); | 353 | STACK_CHECK(L_, 0); |
386 | 354 | ||
387 | // this table is at breadth_first_cache index | 355 | // this table is at breadth_first_cache index |
388 | lua_newtable(L_); // ... {_i} {bfc} | 356 | lua_newtable(L_); // L_: ... {i_} {bfc} |
389 | LUA_ASSERT(L_, lua_gettop(L_) == breadth_first_cache); | 357 | LUA_ASSERT(L_, lua_gettop(L_) == breadth_first_cache); |
390 | // iterate over all entries in the processed table | 358 | // iterate over all entries in the processed table |
391 | lua_pushnil(L_); // ... {_i} {bfc} nil | 359 | lua_pushnil(L_); // L_: ... {i_} {bfc} nil |
392 | while( lua_next(L_, _i) != 0) // ... {_i} {bfc} k v | 360 | while (lua_next(L_, i_) != 0) { // L_: ... {i_} {bfc} k v |
393 | { | ||
394 | // just for debug, not actually needed | 361 | // just for debug, not actually needed |
395 | //char const* key = (lua_type(L, -2) == LUA_TSTRING) ? lua_tostring(L, -2) : "not a string"; | 362 | // char const* key = (lua_type(L, -2) == LUA_TSTRING) ? lua_tostring(L, -2) : "not a string"; |
396 | // subtable: process it recursively | 363 | // subtable: process it recursively |
397 | if (lua_istable(L_, -1)) // ... {_i} {bfc} k {} | 364 | if (lua_istable(L_, -1)) { // L_: ... {i_} {bfc} k {} |
398 | { | ||
399 | // increment visit count to make sure we will actually scan it at this recursive level | 365 | // increment visit count to make sure we will actually scan it at this recursive level |
400 | lua_pushvalue(L_, -1); // ... {_i} {bfc} k {} {} | 366 | lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {} |
401 | lua_pushvalue(L_, -1); // ... {_i} {bfc} k {} {} {} | 367 | lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {} {} |
402 | lua_rawget(L_, cache); // ... {_i} {bfc} k {} {} n? | 368 | lua_rawget(L_, cache); // L_: ... {i_} {bfc} k {} {} n? |
403 | visit_count = lua_tointeger(L_, -1) + 1; // 1 if we got nil, else n+1 | 369 | visit_count = lua_tointeger(L_, -1) + 1; // 1 if we got nil, else n+1 |
404 | lua_pop(L_, 1); // ... {_i} {bfc} k {} {} | 370 | lua_pop(L_, 1); // L_: ... {i_} {bfc} k {} {} |
405 | lua_pushinteger(L_, visit_count); // ... {_i} {bfc} k {} {} n | 371 | lua_pushinteger(L_, visit_count); // L_: ... {i_} {bfc} k {} {} n |
406 | lua_rawset(L_, cache); // ... {_i} {bfc} k {} | 372 | lua_rawset(L_, cache); // L_: ... {i_} {bfc} k {} |
407 | // store the table in the breadth-first cache | 373 | // store the table in the breadth-first cache |
408 | lua_pushvalue(L_, -2); // ... {_i} {bfc} k {} k | 374 | lua_pushvalue(L_, -2); // L_: ... {i_} {bfc} k {} k |
409 | lua_pushvalue(L_, -2); // ... {_i} {bfc} k {} k {} | 375 | lua_pushvalue(L_, -2); // L_: ... {i_} {bfc} k {} k {} |
410 | lua_rawset(L_, breadth_first_cache); // ... {_i} {bfc} k {} | 376 | lua_rawset(L_, breadth_first_cache); // L_: ... {i_} {bfc} k {} |
411 | // generate a name, and if we already had one name, keep whichever is the shorter | 377 | // generate a name, and if we already had one name, keep whichever is the shorter |
412 | update_lookup_entry( DEBUGSPEW_PARAM_COMMA(U) L_, _ctx_base, _depth); // ... {_i} {bfc} k | 378 | update_lookup_entry(DEBUGSPEW_PARAM_COMMA(U_) L_, ctxBase_, depth_); // L_: ... {i_} {bfc} k |
413 | } | 379 | } else if (lua_isfunction(L_, -1) && (luaG_getfuncsubtype(L_, -1) != FuncSubType::Bytecode)) { |
414 | else if (lua_isfunction(L_, -1) && (luaG_getfuncsubtype(L_, -1) != FuncSubType::Bytecode)) | ||
415 | { | ||
416 | // generate a name, and if we already had one name, keep whichever is the shorter | 380 | // generate a name, and if we already had one name, keep whichever is the shorter |
417 | // this pops the function from the stack | 381 | // this pops the function from the stack |
418 | update_lookup_entry( DEBUGSPEW_PARAM_COMMA(U) L_, _ctx_base, _depth); // ... {_i} {bfc} k | 382 | update_lookup_entry(DEBUGSPEW_PARAM_COMMA(U_) L_, ctxBase_, depth_); // L_: ... {i_} {bfc} k |
419 | } | 383 | } else { |
420 | else | 384 | lua_pop(L_, 1); // L_: ... {i_} {bfc} k |
421 | { | ||
422 | lua_pop(L_, 1); // ... {_i} {bfc} k | ||
423 | } | 385 | } |
424 | STACK_CHECK(L_, 2); | 386 | STACK_CHECK(L_, 2); |
425 | } | 387 | } |
426 | // now process the tables we encountered at that depth | 388 | // now process the tables we encountered at that depth |
427 | ++ _depth; | 389 | ++depth_; |
428 | lua_pushnil(L_); // ... {_i} {bfc} nil | 390 | lua_pushnil(L_); // L_: ... {i_} {bfc} nil |
429 | while (lua_next(L_, breadth_first_cache) != 0) // ... {_i} {bfc} k {} | 391 | while (lua_next(L_, breadth_first_cache) != 0) { // L_: ... {i_} {bfc} k {} |
430 | { | ||
431 | DEBUGSPEW_CODE(char const* key = (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : "not a string"); | 392 | DEBUGSPEW_CODE(char const* key = (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : "not a string"); |
432 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "table '%s'\n" INDENT_END, key)); | 393 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "table '%s'\n" INDENT_END(U_), key)); |
433 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 394 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U_ }); |
434 | // un-visit this table in case we do need to process it | 395 | // un-visit this table in case we do need to process it |
435 | lua_pushvalue(L_, -1); // ... {_i} {bfc} k {} {} | 396 | lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {} |
436 | lua_rawget(L_, cache); // ... {_i} {bfc} k {} n | 397 | lua_rawget(L_, cache); // L_: ... {i_} {bfc} k {} n |
437 | LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TNUMBER); | 398 | LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TNUMBER); |
438 | visit_count = lua_tointeger(L_, -1) - 1; | 399 | visit_count = lua_tointeger(L_, -1) - 1; |
439 | lua_pop(L_, 1); // ... {_i} {bfc} k {} | 400 | lua_pop(L_, 1); // L_: ... {i_} {bfc} k {} |
440 | lua_pushvalue(L_, -1); // ... {_i} {bfc} k {} {} | 401 | lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {} |
441 | if (visit_count > 0) | 402 | if (visit_count > 0) { |
442 | { | 403 | lua_pushinteger(L_, visit_count); // L_: ... {i_} {bfc} k {} {} n |
443 | lua_pushinteger(L_, visit_count); // ... {_i} {bfc} k {} {} n | 404 | } else { |
444 | } | 405 | lua_pushnil(L_); // L_: ... {i_} {bfc} k {} {} nil |
445 | else | ||
446 | { | ||
447 | lua_pushnil(L_); // ... {_i} {bfc} k {} {} nil | ||
448 | } | 406 | } |
449 | lua_rawset(L_, cache); // ... {_i} {bfc} k {} | 407 | lua_rawset(L_, cache); // L_: ... {i_} {bfc} k {} |
450 | // push table name in fqn stack (note that concatenation will crash if name is a not string!) | 408 | // push table name in fqn stack (note that concatenation will crash if name is a not string!) |
451 | lua_pushvalue(L_, -2); // ... {_i} {bfc} k {} k | 409 | lua_pushvalue(L_, -2); // L_: ... {i_} {bfc} k {} k |
452 | lua_rawseti(L_, fqn, _depth); // ... {_i} {bfc} k {} | 410 | lua_rawseti(L_, fqn, depth_); // L_: ... {i_} {bfc} k {} |
453 | populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(U) L_, _ctx_base, lua_gettop(L_), _depth); | 411 | populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(U_) L_, ctxBase_, lua_gettop(L_), depth_); |
454 | lua_pop(L_, 1); // ... {_i} {bfc} k | 412 | lua_pop(L_, 1); // L_: ... {i_} {bfc} k |
455 | STACK_CHECK(L_, 2); | 413 | STACK_CHECK(L_, 2); |
456 | } | 414 | } |
457 | // remove table name from fqn stack | 415 | // remove table name from fqn stack |
458 | lua_pushnil(L_); // ... {_i} {bfc} nil | 416 | lua_pushnil(L_); // L_: ... {i_} {bfc} nil |
459 | lua_rawseti(L_, fqn, _depth); // ... {_i} {bfc} | 417 | lua_rawseti(L_, fqn, depth_); // L_: ... {i_} {bfc} |
460 | -- _depth; | 418 | --depth_; |
461 | // we are done with our cache | 419 | // we are done with our cache |
462 | lua_pop(L_, 1); // ... {_i} | 420 | lua_pop(L_, 1); // L_: ... {i_} |
463 | STACK_CHECK(L_, 0); | 421 | STACK_CHECK(L_, 0); |
464 | // we are done // ... {_i} {bfc} | 422 | // we are done // L_: ... {i_} {bfc} |
465 | } | 423 | } |
466 | 424 | ||
467 | // ################################################################################################# | 425 | // ################################################################################################# |
468 | 426 | ||
469 | /* | 427 | // create a "fully.qualified.name" <-> function equivalence database |
470 | * create a "fully.qualified.name" <-> function equivalence database | ||
471 | */ | ||
472 | void populate_func_lookup_table(lua_State* L_, int i_, char const* name_) | 428 | void populate_func_lookup_table(lua_State* L_, int i_, char const* name_) |
473 | { | 429 | { |
474 | int const ctx_base = lua_gettop(L_) + 1; | 430 | int const ctx_base = lua_gettop(L_) + 1; |
475 | int const in_base = lua_absindex(L_, i_); | 431 | int const in_base = lua_absindex(L_, i_); |
476 | int start_depth = 0; | 432 | int start_depth = 0; |
477 | DEBUGSPEW_CODE(Universe* U = universe_get(L_)); | 433 | DEBUGSPEW_CODE(Universe* U = universe_get(L_)); |
478 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END, L_, name_ ? name_ : "nullptr")); | 434 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END(U), L_, name_ ? name_ : "nullptr")); |
479 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 435 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
480 | STACK_GROW(L_, 3); | 436 | STACK_GROW(L_, 3); |
481 | STACK_CHECK_START_REL(L_, 0); | 437 | STACK_CHECK_START_REL(L_, 0); |
482 | kLookupRegKey.pushValue(L_); // {} | 438 | kLookupRegKey.pushValue(L_); // L_: {} |
483 | STACK_CHECK(L_, 1); | 439 | STACK_CHECK(L_, 1); |
484 | LUA_ASSERT(L_, lua_istable(L_, -1)); | 440 | LUA_ASSERT(L_, lua_istable(L_, -1)); |
485 | if (lua_type(L_, in_base) == LUA_TFUNCTION) // for example when a module is a simple function | 441 | if (lua_type(L_, in_base) == LUA_TFUNCTION) { // for example when a module is a simple function |
486 | { | ||
487 | name_ = name_ ? name_ : "nullptr"; | 442 | name_ = name_ ? name_ : "nullptr"; |
488 | lua_pushvalue(L_, in_base); // {} f | 443 | lua_pushvalue(L_, in_base); // L_: {} f |
489 | lua_pushstring(L_, name_); // {} f _name | 444 | lua_pushstring(L_, name_); // L_: {} f _name |
490 | lua_rawset(L_, -3); // {} | 445 | lua_rawset(L_, -3); // L_: {} |
491 | lua_pushstring(L_, name_); // {} _name | 446 | lua_pushstring(L_, name_); // L_: {} _name |
492 | lua_pushvalue(L_, in_base); // {} _name f | 447 | lua_pushvalue(L_, in_base); // L_: {} _name f |
493 | lua_rawset(L_, -3); // {} | 448 | lua_rawset(L_, -3); // L_: {} |
494 | lua_pop(L_, 1); // | 449 | lua_pop(L_, 1); // L_: |
495 | } | 450 | } else if (lua_type(L_, in_base) == LUA_TTABLE) { |
496 | else if (lua_type(L_, in_base) == LUA_TTABLE) | 451 | lua_newtable(L_); // L_: {} {fqn} |
497 | { | 452 | if (name_) { |
498 | lua_newtable(L_); // {} {fqn} | ||
499 | if (name_) | ||
500 | { | ||
501 | STACK_CHECK(L_, 2); | 453 | STACK_CHECK(L_, 2); |
502 | lua_pushstring(L_, name_); // {} {fqn} "name" | 454 | lua_pushstring(L_, name_); // L_: {} {fqn} "name" |
503 | // generate a name, and if we already had one name, keep whichever is the shorter | 455 | // generate a name, and if we already had one name, keep whichever is the shorter |
504 | lua_pushvalue(L_, in_base); // {} {fqn} "name" t | 456 | lua_pushvalue(L_, in_base); // L_: {} {fqn} "name" t |
505 | update_lookup_entry(DEBUGSPEW_PARAM_COMMA(U) L_, ctx_base, start_depth); // {} {fqn} "name" | 457 | update_lookup_entry(DEBUGSPEW_PARAM_COMMA(U) L_, ctx_base, start_depth); // L_: {} {fqn} "name" |
506 | // don't forget to store the name at the bottom of the fqn stack | 458 | // don't forget to store the name at the bottom of the fqn stack |
507 | ++ start_depth; | 459 | ++start_depth; |
508 | lua_rawseti(L_, -2, start_depth); // {} {fqn} | 460 | lua_rawseti(L_, -2, start_depth); // L_: {} {fqn} |
509 | STACK_CHECK(L_, 2); | 461 | STACK_CHECK(L_, 2); |
510 | } | 462 | } |
511 | // retrieve the cache, create it if we haven't done it yet | 463 | // retrieve the cache, create it if we haven't done it yet |
512 | kLookupCacheRegKey.pushValue(L_); // {} {fqn} {cache}? | 464 | kLookupCacheRegKey.pushValue(L_); // L_: {} {fqn} {cache}? |
513 | if (lua_isnil(L_, -1)) | 465 | if (lua_isnil(L_, -1)) { |
514 | { | 466 | lua_pop(L_, 1); // L_: {} {fqn} |
515 | lua_pop(L_, 1); // {} {fqn} | 467 | lua_newtable(L_); // L_: {} {fqn} {cache} |
516 | lua_newtable(L_); // {} {fqn} {cache} | ||
517 | kLookupCacheRegKey.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); | 468 | kLookupCacheRegKey.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); |
518 | STACK_CHECK(L_, 3); | 469 | STACK_CHECK(L_, 3); |
519 | } | 470 | } |
520 | // process everything we find in that table, filling in lookup data for all functions and tables we see there | 471 | // process everything we find in that table, filling in lookup data for all functions and tables we see there |
521 | populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(U) L_, ctx_base, in_base, start_depth); | 472 | populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(U) L_, ctx_base, in_base, start_depth); |
522 | lua_pop(L_, 3); | 473 | lua_pop(L_, 3); |
523 | } | 474 | } else { |
524 | else | 475 | lua_pop(L_, 1); // L_: |
525 | { | ||
526 | lua_pop(L_, 1); // | ||
527 | raise_luaL_error(L_, "unsupported module type %s", lua_typename(L_, lua_type(L_, in_base))); | 476 | raise_luaL_error(L_, "unsupported module type %s", lua_typename(L_, lua_type(L_, in_base))); |
528 | } | 477 | } |
529 | STACK_CHECK(L_, 0); | 478 | STACK_CHECK(L_, 0); |
@@ -536,38 +485,35 @@ void populate_func_lookup_table(lua_State* L_, int i_, char const* name_) | |||
536 | // xxh64 of string "kMtIdRegKey" generated at https://www.pelock.com/products/hash-calculator | 485 | // xxh64 of string "kMtIdRegKey" generated at https://www.pelock.com/products/hash-calculator |
537 | static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; | 486 | static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; |
538 | 487 | ||
539 | /* | 488 | // get a unique ID for metatable at [i]. |
540 | * Get a unique ID for metatable at [i]. | 489 | [[nodiscard]] static lua_Integer get_mt_id(Universe* U_, lua_State* L_, int i) |
541 | */ | ||
542 | [[nodiscard]] static lua_Integer get_mt_id(Universe* U, lua_State* L_, int i) | ||
543 | { | 490 | { |
544 | i = lua_absindex(L_, i); | 491 | i = lua_absindex(L_, i); |
545 | 492 | ||
546 | STACK_GROW(L_, 3); | 493 | STACK_GROW(L_, 3); |
547 | 494 | ||
548 | STACK_CHECK_START_REL(L_, 0); | 495 | STACK_CHECK_START_REL(L_, 0); |
549 | push_registry_subtable(L_, kMtIdRegKey); // ... _R[kMtIdRegKey] | 496 | push_registry_subtable(L_, kMtIdRegKey); // L_: ... _R[kMtIdRegKey] |
550 | lua_pushvalue(L_, i); // ... _R[kMtIdRegKey] {mt} | 497 | lua_pushvalue(L_, i); // L_: ... _R[kMtIdRegKey] {mt} |
551 | lua_rawget(L_, -2); // ... _R[kMtIdRegKey] mtk? | 498 | lua_rawget(L_, -2); // L_: ... _R[kMtIdRegKey] mtk? |
552 | 499 | ||
553 | lua_Integer id{ lua_tointeger(L_, -1) }; // 0 for nil | 500 | lua_Integer id{ lua_tointeger(L_, -1) }; // 0 for nil |
554 | lua_pop(L_, 1); // ... _R[kMtIdRegKey] | 501 | lua_pop(L_, 1); // L_: ... _R[kMtIdRegKey] |
555 | STACK_CHECK(L_, 1); | 502 | STACK_CHECK(L_, 1); |
556 | 503 | ||
557 | if (id == 0) | 504 | if (id == 0) { |
558 | { | 505 | id = U_->next_mt_id.fetch_add(1, std::memory_order_relaxed); |
559 | id = U->next_mt_id.fetch_add(1, std::memory_order_relaxed); | ||
560 | 506 | ||
561 | // Create two-way references: id_uint <-> table | 507 | // Create two-way references: id_uint <-> table |
562 | lua_pushvalue(L_, i); // ... _R[kMtIdRegKey] {mt} | 508 | lua_pushvalue(L_, i); // L_: ... _R[kMtIdRegKey] {mt} |
563 | lua_pushinteger(L_, id); // ... _R[kMtIdRegKey] {mt} id | 509 | lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] {mt} id |
564 | lua_rawset(L_, -3); // ... _R[kMtIdRegKey] | 510 | lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey] |
565 | 511 | ||
566 | lua_pushinteger(L_, id); // ... _R[kMtIdRegKey] id | 512 | lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] id |
567 | lua_pushvalue(L_, i); // ... _R[kMtIdRegKey] id {mt} | 513 | lua_pushvalue(L_, i); // L_: ... _R[kMtIdRegKey] id {mt} |
568 | lua_rawset(L_, -3); // ... _R[kMtIdRegKey] | 514 | lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey] |
569 | } | 515 | } |
570 | lua_pop(L_, 1); // ... | 516 | lua_pop(L_, 1); // L_: ... |
571 | STACK_CHECK(L_, 0); | 517 | STACK_CHECK(L_, 0); |
572 | 518 | ||
573 | return id; | 519 | return id; |
@@ -599,74 +545,62 @@ static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; | |||
599 | 545 | ||
600 | // ################################################################################################# | 546 | // ################################################################################################# |
601 | 547 | ||
602 | /* | 548 | // retrieve the name of a function/table in the lookup database |
603 | * retrieve the name of a function/table in the lookup database | 549 | [[nodiscard]] static char const* find_lookup_name(lua_State* L_, int i_, LookupMode mode_, char const* upName_, size_t* len_) |
604 | */ | ||
605 | [[nodiscard]] static char const* find_lookup_name(lua_State* L_, int i, LookupMode mode_, char const* upName_, size_t* len_) | ||
606 | { | 550 | { |
607 | DEBUGSPEW_CODE( Universe* const U = universe_get( L_)); | 551 | DEBUGSPEW_CODE(Universe* const U = universe_get(L_)); |
608 | char const* fqn; | 552 | char const* fqn; |
609 | LUA_ASSERT(L_, lua_isfunction( L_, i) || lua_istable( L_, i)); // ... v ... | 553 | LUA_ASSERT(L_, lua_isfunction(L_, i_) || lua_istable(L_, i_)); // L_: ... v ... |
610 | STACK_CHECK_START_REL(L_, 0); | 554 | STACK_CHECK_START_REL(L_, 0); |
611 | STACK_GROW( L_, 3); // up to 3 slots are necessary on error | 555 | STACK_GROW(L_, 3); // up to 3 slots are necessary on error |
612 | if (mode_ == LookupMode::FromKeeper) | 556 | if (mode_ == LookupMode::FromKeeper) { |
613 | { | 557 | lua_CFunction f = lua_tocfunction(L_, i_); // should *always* be func_lookup_sentinel or table_lookup_sentinel! |
614 | lua_CFunction f = lua_tocfunction( L_, i); // should *always* be func_lookup_sentinel or table_lookup_sentinel! | 558 | if (f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel) { |
615 | if (f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel) | 559 | lua_getupvalue(L_, i_, 1); // L_: ... v ... "f.q.n" |
616 | { | 560 | } else { |
617 | lua_getupvalue( L_, i, 1); // ... v ... "f.q.n" | ||
618 | } | ||
619 | else | ||
620 | { | ||
621 | // if this is not a sentinel, this is some user-created table we wanted to lookup | 561 | // if this is not a sentinel, this is some user-created table we wanted to lookup |
622 | LUA_ASSERT(L_, nullptr == f && lua_istable(L_, i)); | 562 | LUA_ASSERT(L_, nullptr == f && lua_istable(L_, i_)); |
623 | // push anything that will convert to nullptr string | 563 | // push anything that will convert to nullptr string |
624 | lua_pushnil( L_); // ... v ... nil | 564 | lua_pushnil(L_); // L_: ... v ... nil |
625 | } | 565 | } |
626 | } | 566 | } else { |
627 | else | ||
628 | { | ||
629 | // fetch the name from the source state's lookup table | 567 | // fetch the name from the source state's lookup table |
630 | kLookupRegKey.pushValue(L_); // ... v ... {} | 568 | kLookupRegKey.pushValue(L_); // L_: ... v ... {} |
631 | STACK_CHECK( L_, 1); | 569 | STACK_CHECK(L_, 1); |
632 | LUA_ASSERT(L_, lua_istable( L_, -1)); | 570 | LUA_ASSERT(L_, lua_istable(L_, -1)); |
633 | lua_pushvalue( L_, i); // ... v ... {} v | 571 | lua_pushvalue(L_, i_); // L_: ... v ... {} v |
634 | lua_rawget( L_, -2); // ... v ... {} "f.q.n" | 572 | lua_rawget(L_, -2); // L_: ... v ... {} "f.q.n" |
635 | } | 573 | } |
636 | fqn = lua_tolstring( L_, -1, len_); | 574 | fqn = lua_tolstring(L_, -1, len_); |
637 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END, fqn)); | 575 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END(U), fqn)); |
638 | // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database | 576 | // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database |
639 | lua_pop( L_, (mode_ == LookupMode::FromKeeper) ? 1 : 2); // ... v ... | 577 | lua_pop(L_, (mode_ == LookupMode::FromKeeper) ? 1 : 2); // L_: ... v ... |
640 | STACK_CHECK( L_, 0); | 578 | STACK_CHECK(L_, 0); |
641 | if (nullptr == fqn && !lua_istable(L_, i)) // raise an error if we try to send an unknown function (but not for tables) | 579 | if (nullptr == fqn && !lua_istable(L_, i_)) { // raise an error if we try to send an unknown function (but not for tables) |
642 | { | ||
643 | char const *from, *typewhat, *what, *gotchaA, *gotchaB; | 580 | char const *from, *typewhat, *what, *gotchaA, *gotchaB; |
644 | // try to discover the name of the function we want to send | 581 | // try to discover the name of the function we want to send |
645 | lua_getglobal( L_, "decoda_name"); // ... v ... decoda_name | 582 | lua_getglobal(L_, "decoda_name"); // L_: ... v ... decoda_name |
646 | from = lua_tostring( L_, -1); | 583 | from = lua_tostring(L_, -1); |
647 | lua_pushcfunction( L_, luaG_nameof); // ... v ... decoda_name luaG_nameof | 584 | lua_pushcfunction(L_, luaG_nameof); // L_: ... v ... decoda_name luaG_nameof |
648 | lua_pushvalue( L_, i); // ... v ... decoda_name luaG_nameof t | 585 | lua_pushvalue(L_, i_); // L_: ... v ... decoda_name luaG_nameof t |
649 | lua_call( L_, 1, 2); // ... v ... decoda_name "type" "name"|nil | 586 | lua_call(L_, 1, 2); // L_: ... v ... decoda_name "type" "name"|nil |
650 | typewhat = (lua_type( L_, -2) == LUA_TSTRING) ? lua_tostring( L_, -2) : luaL_typename( L_, -2); | 587 | typewhat = (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : luaL_typename(L_, -2); |
651 | // second return value can be nil if the table was not found | 588 | // second return value can be nil if the table was not found |
652 | // probable reason: the function was removed from the source Lua state before Lanes was required. | 589 | // probable reason: the function was removed from the source Lua state before Lanes was required. |
653 | if (lua_isnil( L_, -1)) | 590 | if (lua_isnil(L_, -1)) { |
654 | { | ||
655 | gotchaA = " referenced by"; | 591 | gotchaA = " referenced by"; |
656 | gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)"; | 592 | gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)"; |
657 | what = upName_; | 593 | what = upName_; |
658 | } | 594 | } else { |
659 | else | ||
660 | { | ||
661 | gotchaA = ""; | 595 | gotchaA = ""; |
662 | gotchaB = ""; | 596 | gotchaB = ""; |
663 | what = (lua_type( L_, -1) == LUA_TSTRING) ? lua_tostring( L_, -1) : luaL_typename( L_, -1); | 597 | what = (lua_type(L_, -1) == LUA_TSTRING) ? lua_tostring(L_, -1) : luaL_typename(L_, -1); |
664 | } | 598 | } |
665 | raise_luaL_error(L_, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB); | 599 | raise_luaL_error(L_, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB); |
666 | *len_ = 0; | 600 | *len_ = 0; |
667 | return nullptr; | 601 | return nullptr; |
668 | } | 602 | } |
669 | STACK_CHECK( L_, 0); | 603 | STACK_CHECK(L_, 0); |
670 | return fqn; | 604 | return fqn; |
671 | } | 605 | } |
672 | 606 | ||
@@ -678,60 +612,54 @@ static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; | |||
678 | // get the name of the table we want to send | 612 | // get the name of the table we want to send |
679 | size_t len; | 613 | size_t len; |
680 | char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len); | 614 | char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len); |
681 | if (nullptr == fqn) // name not found, it is some user-created table | 615 | if (nullptr == fqn) { // name not found, it is some user-created table |
682 | { | ||
683 | return false; | 616 | return false; |
684 | } | 617 | } |
685 | // push the equivalent table in the destination's stack, retrieved from the lookup table | 618 | // push the equivalent table in the destination's stack, retrieved from the lookup table |
686 | STACK_CHECK_START_REL(L2, 0); // L // L2 | 619 | STACK_CHECK_START_REL(L2, 0); |
687 | STACK_GROW(L2, 3); // up to 3 slots are necessary on error | 620 | STACK_GROW(L2, 3); // up to 3 slots are necessary on error |
688 | switch (mode) | 621 | switch (mode) { |
689 | { | 622 | default: // shouldn't happen, in theory... |
690 | default: // shouldn't happen, in theory... | ||
691 | raise_luaL_error(L1, "internal error: unknown lookup mode"); | 623 | raise_luaL_error(L1, "internal error: unknown lookup mode"); |
692 | break; | 624 | break; |
693 | 625 | ||
694 | case LookupMode::ToKeeper: | 626 | case LookupMode::ToKeeper: |
695 | // push a sentinel closure that holds the lookup name as upvalue | 627 | // push a sentinel closure that holds the lookup name as upvalue |
696 | lua_pushlstring(L2, fqn, len); // "f.q.n" | 628 | lua_pushlstring(L2, fqn, len); // L1: ... t ... L2: "f.q.n" |
697 | lua_pushcclosure(L2, table_lookup_sentinel, 1); // f | 629 | lua_pushcclosure(L2, table_lookup_sentinel, 1); // L1: ... t ... L2: f |
698 | break; | 630 | break; |
699 | 631 | ||
700 | case LookupMode::LaneBody: | 632 | case LookupMode::LaneBody: |
701 | case LookupMode::FromKeeper: | 633 | case LookupMode::FromKeeper: |
702 | kLookupRegKey.pushValue(L2); // {} | 634 | kLookupRegKey.pushValue(L2); // L1: ... t ... L2: {} |
703 | STACK_CHECK(L2, 1); | 635 | STACK_CHECK(L2, 1); |
704 | LUA_ASSERT(L1, lua_istable(L2, -1)); | 636 | LUA_ASSERT(L1, lua_istable(L2, -1)); |
705 | lua_pushlstring(L2, fqn, len); // {} "f.q.n" | 637 | lua_pushlstring(L2, fqn, len); // L2: {} "f.q.n" |
706 | lua_rawget(L2, -2); // {} t | 638 | lua_rawget(L2, -2); // L2: {} t |
707 | // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead) | 639 | // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead) |
708 | // but not when we extract something out of a keeper, as there is nothing to clone! | 640 | // but not when we extract something out of a keeper, as there is nothing to clone! |
709 | if (lua_isnil(L2, -1) && mode == LookupMode::LaneBody) | 641 | if (lua_isnil(L2, -1) && mode == LookupMode::LaneBody) { |
710 | { | 642 | lua_pop(L2, 2); // L1: ... t ... L2: |
711 | lua_pop(L2, 2); // | ||
712 | STACK_CHECK(L2, 0); | 643 | STACK_CHECK(L2, 0); |
713 | return false; | 644 | return false; |
714 | } | 645 | } else if (!lua_istable(L2, -1)) { |
715 | else if (!lua_istable(L2, -1)) | 646 | char const *from, *to; |
716 | { | 647 | lua_getglobal(L1, "decoda_name"); // L1: ... t ... decoda_name |
717 | char const* from, *to; | ||
718 | lua_getglobal(L1, "decoda_name"); // ... t ... decoda_name | ||
719 | from = lua_tostring(L1, -1); | 648 | from = lua_tostring(L1, -1); |
720 | lua_pop(L1, 1); // ... t ... | 649 | lua_pop(L1, 1); // L1: ... t ... |
721 | lua_getglobal(L2, "decoda_name"); // {} t decoda_name | 650 | lua_getglobal(L2, "decoda_name"); // L1: ... t ... L2: {} t decoda_name |
722 | to = lua_tostring(L2, -1); | 651 | to = lua_tostring(L2, -1); |
723 | lua_pop(L2, 1); // {} t | 652 | lua_pop(L2, 1); // L1: ... t ... L2: {} t |
724 | // when mode_ == LookupMode::FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error | 653 | // when mode_ == LookupMode::FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error |
725 | raise_luaL_error( | 654 | raise_luaL_error( |
726 | (mode == LookupMode::FromKeeper) ? L2 : L1 | 655 | (mode == LookupMode::FromKeeper) ? L2 : L1, |
727 | , "INTERNAL ERROR IN %s: table '%s' not found in %s destination transfer database." | 656 | "INTERNAL ERROR IN %s: table '%s' not found in %s destination transfer database.", |
728 | , from ? from : "main" | 657 | from ? from : "main", |
729 | , fqn | 658 | fqn, |
730 | , to ? to : "main" | 659 | to ? to : "main"); |
731 | ); | ||
732 | return false; | 660 | return false; |
733 | } | 661 | } |
734 | lua_remove(L2, -2); // t | 662 | lua_remove(L2, -2); // L1: ... t ... L2: t |
735 | break; | 663 | break; |
736 | } | 664 | } |
737 | STACK_CHECK(L2, 1); | 665 | STACK_CHECK(L2, 1); |
@@ -740,12 +668,12 @@ static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; | |||
740 | 668 | ||
741 | // ################################################################################################# | 669 | // ################################################################################################# |
742 | 670 | ||
743 | /* | 671 | /* |
744 | * Check if we've already copied the same table from 'L', and | 672 | * Check if we've already copied the same table from 'L1_', and |
745 | * reuse the old copy. This allows table upvalues shared by multiple | 673 | * reuse the old copy. This allows table upvalues shared by multiple |
746 | * local functions to point to the same table, also in the target. | 674 | * local functions to point to the same table, also in the target. |
747 | * | 675 | * |
748 | * Always pushes a table to 'L2'. | 676 | * Always pushes a table to 'L2_'. |
749 | * | 677 | * |
750 | * Returns true if the table was cached (no need to fill it!); false if | 678 | * Returns true if the table was cached (no need to fill it!); false if |
751 | * it's a virgin. | 679 | * it's a virgin. |
@@ -755,271 +683,247 @@ static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; | |||
755 | void const* p{ lua_topointer(L1, i) }; | 683 | void const* p{ lua_topointer(L1, i) }; |
756 | 684 | ||
757 | LUA_ASSERT(L1, L2_cache_i != 0); | 685 | LUA_ASSERT(L1, L2_cache_i != 0); |
758 | STACK_GROW(L2, 3); // L2 | 686 | STACK_GROW(L2, 3); |
759 | STACK_CHECK_START_REL(L2, 0); | 687 | STACK_CHECK_START_REL(L2, 0); |
760 | 688 | ||
761 | // We don't need to use the from state ('L1') in ID since the life span | 689 | // We don't need to use the from state ('L1') in ID since the life span |
762 | // is only for the duration of a copy (both states are locked). | 690 | // is only for the duration of a copy (both states are locked). |
763 | // push a light userdata uniquely representing the table | 691 | // push a light userdata uniquely representing the table |
764 | lua_pushlightuserdata(L2, const_cast<void*>(p)); // ... p | 692 | lua_pushlightuserdata(L2, const_cast<void*>(p)); // L2: ... p |
765 | 693 | ||
766 | //fprintf(stderr, "<< ID: %s >>\n", lua_tostring(L2, -1)); | 694 | // fprintf(stderr, "<< ID: %s >>\n", lua_tostring(L2, -1)); |
767 | 695 | ||
768 | lua_rawget(L2, L2_cache_i); // ... {cached|nil} | 696 | lua_rawget(L2, L2_cache_i); // L2: ... {cached|nil} |
769 | bool const not_found_in_cache{ lua_isnil(L2, -1) }; | 697 | bool const not_found_in_cache{ lua_isnil(L2, -1) }; |
770 | if (not_found_in_cache) | 698 | if (not_found_in_cache) { |
771 | { | ||
772 | // create a new entry in the cache | 699 | // create a new entry in the cache |
773 | lua_pop(L2, 1); // ... | 700 | lua_pop(L2, 1); // L2: ... |
774 | lua_newtable(L2); // ... {} | 701 | lua_newtable(L2); // L2: ... {} |
775 | lua_pushlightuserdata(L2, const_cast<void*>(p)); // ... {} p | 702 | lua_pushlightuserdata(L2, const_cast<void*>(p)); // L2: ... {} p |
776 | lua_pushvalue(L2, -2); // ... {} p {} | 703 | lua_pushvalue(L2, -2); // L2: ... {} p {} |
777 | lua_rawset(L2, L2_cache_i); // ... {} | 704 | lua_rawset(L2, L2_cache_i); // L2: ... {} |
778 | } | 705 | } |
779 | STACK_CHECK(L2, 1); | 706 | STACK_CHECK(L2, 1); |
780 | LUA_ASSERT(L1, lua_istable( L2, -1)); | 707 | LUA_ASSERT(L1, lua_istable(L2, -1)); |
781 | return !not_found_in_cache; | 708 | return !not_found_in_cache; |
782 | } | 709 | } |
783 | 710 | ||
784 | // ################################################################################################# | 711 | // ################################################################################################# |
785 | 712 | ||
786 | /* | 713 | // Return some name helping to identify an object |
787 | * Return some name helping to identify an object | ||
788 | */ | ||
789 | [[nodiscard]] static int discover_object_name_recur(lua_State* L_, int shortest_, int depth_) | 714 | [[nodiscard]] static int discover_object_name_recur(lua_State* L_, int shortest_, int depth_) |
790 | { | 715 | { |
791 | int const what = 1; // o "r" {c} {fqn} ... {?} | 716 | int const what = 1; // L_: o "r" {c} {fqn} ... {?} |
792 | int const result = 2; | 717 | int const result = 2; |
793 | int const cache = 3; | 718 | int const cache = 3; |
794 | int const fqn = 4; | 719 | int const fqn = 4; |
795 | // no need to scan this table if the name we will discover is longer than one we already know | 720 | // no need to scan this table if the name we will discover is longer than one we already know |
796 | if (shortest_ <= depth_ + 1) | 721 | if (shortest_ <= depth_ + 1) { |
797 | { | ||
798 | return shortest_; | 722 | return shortest_; |
799 | } | 723 | } |
800 | STACK_GROW(L_, 3); | 724 | STACK_GROW(L_, 3); |
801 | STACK_CHECK_START_REL(L_, 0); | 725 | STACK_CHECK_START_REL(L_, 0); |
802 | // stack top contains the table to search in | 726 | // stack top contains the table to search in |
803 | lua_pushvalue(L_, -1); // o "r" {c} {fqn} ... {?} {?} | 727 | lua_pushvalue(L_, -1); // L_: o "r" {c} {fqn} ... {?} {?} |
804 | lua_rawget(L_, cache); // o "r" {c} {fqn} ... {?} nil/1 | 728 | lua_rawget(L_, cache); // L_: o "r" {c} {fqn} ... {?} nil/1 |
805 | // if table is already visited, we are done | 729 | // if table is already visited, we are done |
806 | if (!lua_isnil(L_, -1)) | 730 | if (!lua_isnil(L_, -1)) { |
807 | { | 731 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} |
808 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} | ||
809 | return shortest_; | 732 | return shortest_; |
810 | } | 733 | } |
811 | // examined table is not in the cache, add it now | 734 | // examined table is not in the cache, add it now |
812 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} | 735 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} |
813 | lua_pushvalue(L_, -1); // o "r" {c} {fqn} ... {?} {?} | 736 | lua_pushvalue(L_, -1); // L_: o "r" {c} {fqn} ... {?} {?} |
814 | lua_pushinteger(L_, 1); // o "r" {c} {fqn} ... {?} {?} 1 | 737 | lua_pushinteger(L_, 1); // L_: o "r" {c} {fqn} ... {?} {?} 1 |
815 | lua_rawset(L_, cache); // o "r" {c} {fqn} ... {?} | 738 | lua_rawset(L_, cache); // L_: o "r" {c} {fqn} ... {?} |
816 | // scan table contents | 739 | // scan table contents |
817 | lua_pushnil(L_); // o "r" {c} {fqn} ... {?} nil | 740 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} nil |
818 | while (lua_next(L_, -2)) // o "r" {c} {fqn} ... {?} k v | 741 | while (lua_next(L_, -2)) { // L_: o "r" {c} {fqn} ... {?} k v |
819 | { | 742 | // char const *const strKey = (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : nullptr; // only for debugging |
820 | //char const *const strKey = (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : nullptr; // only for debugging | 743 | // lua_Number const numKey = (lua_type(L_, -2) == LUA_TNUMBER) ? lua_tonumber(L_, -2) : -6666; // only for debugging |
821 | //lua_Number const numKey = (lua_type(L_, -2) == LUA_TNUMBER) ? lua_tonumber(L_, -2) : -6666; // only for debugging | ||
822 | STACK_CHECK(L_, 2); | 744 | STACK_CHECK(L_, 2); |
823 | // append key name to fqn stack | 745 | // append key name to fqn stack |
824 | ++ depth_; | 746 | ++depth_; |
825 | lua_pushvalue(L_, -2); // o "r" {c} {fqn} ... {?} k v k | 747 | lua_pushvalue(L_, -2); // L_: o "r" {c} {fqn} ... {?} k v k |
826 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k v | 748 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k v |
827 | if (lua_rawequal(L_, -1, what)) // is it what we are looking for? | 749 | if (lua_rawequal(L_, -1, what)) { // is it what we are looking for? |
828 | { | ||
829 | STACK_CHECK(L_, 2); | 750 | STACK_CHECK(L_, 2); |
830 | // update shortest name | 751 | // update shortest name |
831 | if (depth_ < shortest_) | 752 | if (depth_ < shortest_) { |
832 | { | ||
833 | shortest_ = depth_; | 753 | shortest_ = depth_; |
834 | std::ignore = luaG_pushFQN(L_, fqn, depth_, nullptr); // o "r" {c} {fqn} ... {?} k v "fqn" | 754 | std::ignore = luaG_pushFQN(L_, fqn, depth_, nullptr); // L_: o "r" {c} {fqn} ... {?} k v "fqn" |
835 | lua_replace(L_, result); // o "r" {c} {fqn} ... {?} k v | 755 | lua_replace(L_, result); // L_: o "r" {c} {fqn} ... {?} k v |
836 | } | 756 | } |
837 | // no need to search further at this level | 757 | // no need to search further at this level |
838 | lua_pop(L_, 2); // o "r" {c} {fqn} ... {?} | 758 | lua_pop(L_, 2); // L_: o "r" {c} {fqn} ... {?} |
839 | STACK_CHECK(L_, 0); | 759 | STACK_CHECK(L_, 0); |
840 | break; | 760 | break; |
841 | } | 761 | } |
842 | switch (lua_type(L_, -1)) // o "r" {c} {fqn} ... {?} k v | 762 | switch (lua_type(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k v |
843 | { | 763 | default: // nil, boolean, light userdata, number and string aren't identifiable |
844 | default: // nil, boolean, light userdata, number and string aren't identifiable | ||
845 | break; | 764 | break; |
846 | 765 | ||
847 | case LUA_TTABLE: // o "r" {c} {fqn} ... {?} k {} | 766 | case LUA_TTABLE: // L_: o "r" {c} {fqn} ... {?} k {} |
848 | STACK_CHECK(L_, 2); | 767 | STACK_CHECK(L_, 2); |
849 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); | 768 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); |
850 | // search in the table's metatable too | 769 | // search in the table's metatable too |
851 | if (lua_getmetatable(L_, -1)) // o "r" {c} {fqn} ... {?} k {} {mt} | 770 | if (lua_getmetatable(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k {} {mt} |
852 | { | 771 | if (lua_istable(L_, -1)) { |
853 | if (lua_istable(L_, -1)) | 772 | ++depth_; |
854 | { | 773 | lua_pushliteral(L_, "__metatable"); // L_: o "r" {c} {fqn} ... {?} k {} {mt} "__metatable" |
855 | ++ depth_; | 774 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k {} {mt} |
856 | lua_pushliteral(L_, "__metatable"); // o "r" {c} {fqn} ... {?} k {} {mt} "__metatable" | ||
857 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k {} {mt} | ||
858 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); | 775 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); |
859 | lua_pushnil(L_); // o "r" {c} {fqn} ... {?} k {} {mt} nil | 776 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k {} {mt} nil |
860 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k {} {mt} | 777 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k {} {mt} |
861 | -- depth_; | 778 | --depth_; |
862 | } | 779 | } |
863 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} k {} | 780 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k {} |
864 | } | 781 | } |
865 | STACK_CHECK(L_, 2); | 782 | STACK_CHECK(L_, 2); |
866 | break; | 783 | break; |
867 | 784 | ||
868 | case LUA_TTHREAD: // o "r" {c} {fqn} ... {?} k T | 785 | case LUA_TTHREAD: // L_: o "r" {c} {fqn} ... {?} k T |
869 | // TODO: explore the thread's stack frame looking for our culprit? | 786 | // TODO: explore the thread's stack frame looking for our culprit? |
870 | break; | 787 | break; |
871 | 788 | ||
872 | case LUA_TUSERDATA: // o "r" {c} {fqn} ... {?} k U | 789 | case LUA_TUSERDATA: // L_: o "r" {c} {fqn} ... {?} k U |
873 | STACK_CHECK(L_, 2); | 790 | STACK_CHECK(L_, 2); |
874 | // search in the object's metatable (some modules are built that way) | 791 | // search in the object's metatable (some modules are built that way) |
875 | if (lua_getmetatable(L_, -1)) // o "r" {c} {fqn} ... {?} k U {mt} | 792 | if (lua_getmetatable(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k U {mt} |
876 | { | 793 | if (lua_istable(L_, -1)) { |
877 | if (lua_istable(L_, -1)) | 794 | ++depth_; |
878 | { | 795 | lua_pushliteral(L_, "__metatable"); // L_: o "r" {c} {fqn} ... {?} k U {mt} "__metatable" |
879 | ++ depth_; | 796 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k U {mt} |
880 | lua_pushliteral(L_, "__metatable"); // o "r" {c} {fqn} ... {?} k U {mt} "__metatable" | ||
881 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k U {mt} | ||
882 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); | 797 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); |
883 | lua_pushnil(L_); // o "r" {c} {fqn} ... {?} k U {mt} nil | 798 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k U {mt} nil |
884 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k U {mt} | 799 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k U {mt} |
885 | -- depth_; | 800 | --depth_; |
886 | } | 801 | } |
887 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} k U | 802 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U |
888 | } | 803 | } |
889 | STACK_CHECK(L_, 2); | 804 | STACK_CHECK(L_, 2); |
890 | // search in the object's uservalues | 805 | // search in the object's uservalues |
891 | { | 806 | { |
892 | int uvi = 1; | 807 | int uvi = 1; |
893 | while (lua_getiuservalue(L_, -1, uvi) != LUA_TNONE) // o "r" {c} {fqn} ... {?} k U {u} | 808 | while (lua_getiuservalue(L_, -1, uvi) != LUA_TNONE) { // L_: o "r" {c} {fqn} ... {?} k U {u} |
894 | { | 809 | if (lua_istable(L_, -1)) { // if it is a table, look inside |
895 | if (lua_istable(L_, -1)) // if it is a table, look inside | 810 | ++depth_; |
896 | { | 811 | lua_pushliteral(L_, "uservalue"); // L_: o "r" {c} {fqn} ... {?} k v {u} "uservalue" |
897 | ++ depth_; | 812 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k v {u} |
898 | lua_pushliteral(L_, "uservalue"); // o "r" {c} {fqn} ... {?} k v {u} "uservalue" | ||
899 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k v {u} | ||
900 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); | 813 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); |
901 | lua_pushnil(L_); // o "r" {c} {fqn} ... {?} k v {u} nil | 814 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k v {u} nil |
902 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k v {u} | 815 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k v {u} |
903 | -- depth_; | 816 | --depth_; |
904 | } | 817 | } |
905 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} k U | 818 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U |
906 | ++ uvi; | 819 | ++uvi; |
907 | } | 820 | } |
908 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now | 821 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now |
909 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} k U | 822 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U |
910 | } | 823 | } |
911 | STACK_CHECK(L_, 2); | 824 | STACK_CHECK(L_, 2); |
912 | break; | 825 | break; |
913 | } | 826 | } |
914 | // make ready for next iteration | 827 | // make ready for next iteration |
915 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} k | 828 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k |
916 | // remove name from fqn stack | 829 | // remove name from fqn stack |
917 | lua_pushnil(L_); // o "r" {c} {fqn} ... {?} k nil | 830 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k nil |
918 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k | 831 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k |
919 | STACK_CHECK(L_, 1); | 832 | STACK_CHECK(L_, 1); |
920 | -- depth_; | 833 | --depth_; |
921 | } // o "r" {c} {fqn} ... {?} | 834 | } // L_: o "r" {c} {fqn} ... {?} |
922 | STACK_CHECK(L_, 0); | 835 | STACK_CHECK(L_, 0); |
923 | // remove the visited table from the cache, in case a shorter path to the searched object exists | 836 | // remove the visited table from the cache, in case a shorter path to the searched object exists |
924 | lua_pushvalue(L_, -1); // o "r" {c} {fqn} ... {?} {?} | 837 | lua_pushvalue(L_, -1); // L_: o "r" {c} {fqn} ... {?} {?} |
925 | lua_pushnil(L_); // o "r" {c} {fqn} ... {?} {?} nil | 838 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} {?} nil |
926 | lua_rawset(L_, cache); // o "r" {c} {fqn} ... {?} | 839 | lua_rawset(L_, cache); // L_: o "r" {c} {fqn} ... {?} |
927 | STACK_CHECK(L_, 0); | 840 | STACK_CHECK(L_, 0); |
928 | return shortest_; | 841 | return shortest_; |
929 | } | 842 | } |
930 | 843 | ||
931 | // ################################################################################################# | 844 | // ################################################################################################# |
932 | 845 | ||
933 | /* | 846 | // "type", "name" = lanes.nameof( o) |
934 | * "type", "name" = lanes.nameof( o) | 847 | int luaG_nameof(lua_State* L_) |
935 | */ | ||
936 | int luaG_nameof( lua_State* L_) | ||
937 | { | 848 | { |
938 | int const what{ lua_gettop(L_) }; | 849 | int const what{ lua_gettop(L_) }; |
939 | if (what > 1) | 850 | if (what > 1) { |
940 | { | 851 | raise_luaL_argerror(L_, what, "too many arguments."); |
941 | raise_luaL_argerror( L_, what, "too many arguments."); | ||
942 | } | 852 | } |
943 | 853 | ||
944 | // nil, boolean, light userdata, number and string aren't identifiable | 854 | // nil, boolean, light userdata, number and string aren't identifiable |
945 | if (lua_type( L_, 1) < LUA_TTABLE) | 855 | if (lua_type(L_, 1) < LUA_TTABLE) { |
946 | { | 856 | lua_pushstring(L_, luaL_typename(L_, 1)); // L_: o "type" |
947 | lua_pushstring( L_, luaL_typename( L_, 1)); // o "type" | 857 | lua_insert(L_, -2); // L_: "type" o |
948 | lua_insert( L_, -2); // "type" o | ||
949 | return 2; | 858 | return 2; |
950 | } | 859 | } |
951 | 860 | ||
952 | STACK_GROW( L_, 4); | 861 | STACK_GROW(L_, 4); |
953 | STACK_CHECK_START_REL(L_, 0); | 862 | STACK_CHECK_START_REL(L_, 0); |
954 | // this slot will contain the shortest name we found when we are done | 863 | // this slot will contain the shortest name we found when we are done |
955 | lua_pushnil( L_); // o nil | 864 | lua_pushnil(L_); // L_: o nil |
956 | // push a cache that will contain all already visited tables | 865 | // push a cache that will contain all already visited tables |
957 | lua_newtable( L_); // o nil {c} | 866 | lua_newtable(L_); // o nil {c} |
958 | // push a table whose contents are strings that, when concatenated, produce unique name | 867 | // push a table whose contents are strings that, when concatenated, produce unique name |
959 | lua_newtable( L_); // o nil {c} {fqn} | 868 | lua_newtable(L_); // L_: o nil {c} {fqn} |
960 | lua_pushliteral( L_, "_G"); // o nil {c} {fqn} "_G" | 869 | lua_pushliteral(L_, "_G"); // L_: o nil {c} {fqn} "_G" |
961 | lua_rawseti( L_, -2, 1); // o nil {c} {fqn} | 870 | lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} |
962 | // this is where we start the search | 871 | // this is where we start the search |
963 | lua_pushglobaltable( L_); // o nil {c} {fqn} _G | 872 | lua_pushglobaltable(L_); // L_: o nil {c} {fqn} _G |
964 | (void) discover_object_name_recur( L_, 6666, 1); | 873 | std::ignore = discover_object_name_recur(L_, 6666, 1); |
965 | if (lua_isnil( L_, 2)) // try again with registry, just in case... | 874 | if (lua_isnil(L_, 2)) { // try again with registry, just in case... |
966 | { | 875 | lua_pop(L_, 1); // L_: o nil {c} {fqn} |
967 | lua_pop( L_, 1); // o nil {c} {fqn} | 876 | lua_pushliteral(L_, "_R"); // L_: o nil {c} {fqn} "_R" |
968 | lua_pushliteral( L_, "_R"); // o nil {c} {fqn} "_R" | 877 | lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} |
969 | lua_rawseti( L_, -2, 1); // o nil {c} {fqn} | 878 | lua_pushvalue(L_, LUA_REGISTRYINDEX); // L_: o nil {c} {fqn} _R |
970 | lua_pushvalue( L_, LUA_REGISTRYINDEX); // o nil {c} {fqn} _R | 879 | (void) discover_object_name_recur(L_, 6666, 1); |
971 | (void) discover_object_name_recur( L_, 6666, 1); | 880 | } |
972 | } | 881 | lua_pop(L_, 3); // L_: o "result" |
973 | lua_pop( L_, 3); // o "result" | 882 | STACK_CHECK(L_, 1); |
974 | STACK_CHECK( L_, 1); | 883 | lua_pushstring(L_, luaL_typename(L_, 1)); // L_: o "result" "type" |
975 | lua_pushstring( L_, luaL_typename( L_, 1)); // o "result" "type" | 884 | lua_replace(L_, -3); // L_: "type" "result" |
976 | lua_replace( L_, -3); // "type" "result" | ||
977 | return 2; | 885 | return 2; |
978 | } | 886 | } |
979 | 887 | ||
980 | // ################################################################################################# | 888 | // ################################################################################################# |
981 | 889 | ||
982 | /* | 890 | // Push a looked-up native/LuaJIT function. |
983 | * Push a looked-up native/LuaJIT function. | ||
984 | */ | ||
985 | void InterCopyContext::lookup_native_func() const | 891 | void InterCopyContext::lookup_native_func() const |
986 | { | 892 | { |
987 | // get the name of the function we want to send | 893 | // get the name of the function we want to send |
988 | size_t len; | 894 | size_t len; |
989 | char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len); | 895 | char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len); |
990 | // push the equivalent function in the destination's stack, retrieved from the lookup table | 896 | // push the equivalent function in the destination's stack, retrieved from the lookup table |
991 | STACK_CHECK_START_REL(L2, 0); // L1 // L2 | 897 | STACK_CHECK_START_REL(L2, 0); |
992 | STACK_GROW(L2, 3); // up to 3 slots are necessary on error | 898 | STACK_GROW(L2, 3); // up to 3 slots are necessary on error |
993 | switch (mode) | 899 | switch (mode) { |
994 | { | 900 | default: // shouldn't happen, in theory... |
995 | default: // shouldn't happen, in theory... | ||
996 | raise_luaL_error(L1, "internal error: unknown lookup mode"); | 901 | raise_luaL_error(L1, "internal error: unknown lookup mode"); |
997 | break; | 902 | break; |
998 | 903 | ||
999 | case LookupMode::ToKeeper: | 904 | case LookupMode::ToKeeper: |
1000 | // push a sentinel closure that holds the lookup name as upvalue | 905 | // push a sentinel closure that holds the lookup name as upvalue |
1001 | lua_pushlstring(L2, fqn, len); // "f.q.n" | 906 | lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: "f.q.n" |
1002 | lua_pushcclosure(L2, func_lookup_sentinel, 1); // f | 907 | lua_pushcclosure(L2, func_lookup_sentinel, 1); // L1: ... f ... L2: f |
1003 | break; | 908 | break; |
1004 | 909 | ||
1005 | case LookupMode::LaneBody: | 910 | case LookupMode::LaneBody: |
1006 | case LookupMode::FromKeeper: | 911 | case LookupMode::FromKeeper: |
1007 | kLookupRegKey.pushValue(L2); // {} | 912 | kLookupRegKey.pushValue(L2); // L1: ... f ... L2: {} |
1008 | STACK_CHECK(L2, 1); | 913 | STACK_CHECK(L2, 1); |
1009 | LUA_ASSERT(L1, lua_istable(L2, -1)); | 914 | LUA_ASSERT(L1, lua_istable(L2, -1)); |
1010 | lua_pushlstring(L2, fqn, len); // {} "f.q.n" | 915 | lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: {} "f.q.n" |
1011 | lua_rawget(L2, -2); // {} f | 916 | lua_rawget(L2, -2); // L1: ... f ... L2: {} f |
1012 | // nil means we don't know how to transfer stuff: user should do something | 917 | // nil means we don't know how to transfer stuff: user should do something |
1013 | // anything other than function or table should not happen! | 918 | // anything other than function or table should not happen! |
1014 | if (!lua_isfunction( L2, -1) && !lua_istable( L2, -1)) | 919 | if (!lua_isfunction(L2, -1) && !lua_istable(L2, -1)) { |
1015 | { | 920 | char const *from, *to; |
1016 | char const* from, * to; | 921 | lua_getglobal(L1, "decoda_name"); // L1: ... f ... decoda_name |
1017 | lua_getglobal(L1, "decoda_name"); // ... f ... decoda_name | ||
1018 | from = lua_tostring(L1, -1); | 922 | from = lua_tostring(L1, -1); |
1019 | lua_pop(L1, 1); // ... f ... | 923 | lua_pop(L1, 1); // L1: ... f ... |
1020 | lua_getglobal(L2, "decoda_name"); // {} f decoda_name | 924 | lua_getglobal(L2, "decoda_name"); // L1: ... f ... L2: {} f decoda_name |
1021 | to = lua_tostring(L2, -1); | 925 | to = lua_tostring(L2, -1); |
1022 | lua_pop(L2, 1); // {} f | 926 | lua_pop(L2, 1); // L2: {} f |
1023 | // when mode_ == LookupMode::FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error | 927 | // when mode_ == LookupMode::FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error |
1024 | raise_luaL_error( | 928 | raise_luaL_error( |
1025 | (mode == LookupMode::FromKeeper) ? L2 : L1 | 929 | (mode == LookupMode::FromKeeper) ? L2 : L1 |
@@ -1031,38 +935,31 @@ void InterCopyContext::lookup_native_func() const | |||
1031 | ); | 935 | ); |
1032 | return; | 936 | return; |
1033 | } | 937 | } |
1034 | lua_remove(L2, -2); // f | 938 | lua_remove(L2, -2); // L2: f |
1035 | break; | 939 | break; |
1036 | 940 | ||
1037 | /* keep it in case I need it someday, who knows... | 941 | /* keep it in case I need it someday, who knows... |
1038 | case LookupMode::RawFunctions: | 942 | case LookupMode::RawFunctions: |
1039 | { | 943 | { |
1040 | int n; | 944 | int n; |
1041 | char const* upname; | 945 | char const* upname; |
1042 | lua_CFunction f = lua_tocfunction( L, i); | 946 | lua_CFunction f = lua_tocfunction( L, i); |
1043 | // copy upvalues | 947 | // copy upvalues |
1044 | for (n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != nullptr; ++ n) | 948 | for (n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != nullptr; ++ n) { |
1045 | { | 949 | luaG_inter_move( U, L, L2, 1, mode_); // L2: [up[,up ...]] |
1046 | luaG_inter_move( U, L, L2, 1, mode_); // [up[,up ...]] | ||
1047 | } | ||
1048 | lua_pushcclosure( L2, f, n); // | ||
1049 | } | 950 | } |
1050 | break; | 951 | lua_pushcclosure( L2, f, n); // L2: |
1051 | */ | ||
1052 | } | 952 | } |
1053 | STACK_CHECK( L2, 1); | 953 | break; |
954 | */ | ||
955 | } | ||
956 | STACK_CHECK(L2, 1); | ||
1054 | } | 957 | } |
1055 | 958 | ||
1056 | // ################################################################################################# | 959 | // ################################################################################################# |
1057 | 960 | ||
1058 | /* | ||
1059 | * Copy a function over, which has not been found in the cache. | ||
1060 | * L2 has the cache key for this function at the top of the stack | ||
1061 | */ | ||
1062 | |||
1063 | #if USE_DEBUG_SPEW() | 961 | #if USE_DEBUG_SPEW() |
1064 | static char const* lua_type_names[] = | 962 | static char const* lua_type_names[] = { |
1065 | { | ||
1066 | "LUA_TNIL" | 963 | "LUA_TNIL" |
1067 | , "LUA_TBOOLEAN" | 964 | , "LUA_TBOOLEAN" |
1068 | , "LUA_TLIGHTUSERDATA" | 965 | , "LUA_TLIGHTUSERDATA" |
@@ -1075,9 +972,8 @@ static char const* lua_type_names[] = | |||
1075 | , "<LUA_NUMTAGS>" // not really a type | 972 | , "<LUA_NUMTAGS>" // not really a type |
1076 | , "LUA_TJITCDATA" // LuaJIT specific | 973 | , "LUA_TJITCDATA" // LuaJIT specific |
1077 | }; | 974 | }; |
1078 | static char const* vt_names[] = | 975 | static char const* vt_names[] = { |
1079 | { | 976 | "VT::NORMAL" |
1080 | "VT::NORMAL" | ||
1081 | , "VT::KEY" | 977 | , "VT::KEY" |
1082 | , "VT::METATABLE" | 978 | , "VT::METATABLE" |
1083 | }; | 979 | }; |
@@ -1092,82 +988,78 @@ static char const* vt_names[] = | |||
1092 | [[nodiscard]] static int buf_writer(lua_State* L_, void const* b, size_t size, void* ud) | 988 | [[nodiscard]] static int buf_writer(lua_State* L_, void const* b, size_t size, void* ud) |
1093 | { | 989 | { |
1094 | luaL_Buffer* B = (luaL_Buffer*) ud; | 990 | luaL_Buffer* B = (luaL_Buffer*) ud; |
1095 | if (!B->L) | 991 | if (!B->L) { |
1096 | { | 992 | luaL_buffinit(L_, B); |
1097 | luaL_buffinit( L_, B); | ||
1098 | } | 993 | } |
1099 | luaL_addlstring( B, (char const*) b, size); | 994 | luaL_addlstring(B, (char const*) b, size); |
1100 | return 0; | 995 | return 0; |
1101 | } | 996 | } |
1102 | 997 | ||
1103 | // ################################################################################################# | 998 | // ################################################################################################# |
1104 | 999 | ||
1000 | // Copy a function over, which has not been found in the cache. | ||
1001 | // L2 has the cache key for this function at the top of the stack | ||
1105 | void InterCopyContext::copy_func() const | 1002 | void InterCopyContext::copy_func() const |
1106 | { | 1003 | { |
1107 | LUA_ASSERT(L1, L2_cache_i != 0); // ... {cache} ... p | 1004 | LUA_ASSERT(L1, L2_cache_i != 0); // L2: ... {cache} ... p |
1108 | STACK_GROW(L1, 2); | 1005 | STACK_GROW(L1, 2); |
1109 | STACK_CHECK_START_REL(L1, 0); | 1006 | STACK_CHECK_START_REL(L1, 0); |
1110 | 1007 | ||
1111 | |||
1112 | // 'lua_dump()' needs the function at top of stack | 1008 | // 'lua_dump()' needs the function at top of stack |
1113 | // if already on top of the stack, no need to push again | 1009 | // if already on top of the stack, no need to push again |
1114 | bool const needToPush{ L1_i != lua_gettop(L1) }; | 1010 | bool const needToPush{ L1_i != lua_gettop(L1) }; |
1115 | if (needToPush) | 1011 | if (needToPush) { |
1116 | { | 1012 | lua_pushvalue(L1, L1_i); // L1: ... f |
1117 | lua_pushvalue(L1, L1_i); // ... f | ||
1118 | } | 1013 | } |
1119 | 1014 | ||
1120 | // | 1015 | // |
1121 | // "value returned is the error code returned by the last call | 1016 | // "value returned is the error code returned by the last call |
1122 | // to the writer" (and we only return 0) | 1017 | // to the writer" (and we only return 0) |
1123 | // not sure this could ever fail but for memory shortage reasons | 1018 | // not sure this could ever fail but for memory shortage reasons |
1124 | // last parameter is Lua 5.4-specific (no stripping) | 1019 | // last parameter is Lua 5.4-specific (no stripping) |
1125 | luaL_Buffer B; | 1020 | luaL_Buffer B; |
1126 | B.L = nullptr; | 1021 | B.L = nullptr; |
1127 | if (lua504_dump(L1, buf_writer, &B, 0) != 0) | 1022 | if (lua504_dump(L1, buf_writer, &B, 0) != 0) { |
1128 | { | ||
1129 | raise_luaL_error(L1, "internal error: function dump failed."); | 1023 | raise_luaL_error(L1, "internal error: function dump failed."); |
1130 | } | 1024 | } |
1131 | 1025 | ||
1132 | // pushes dumped string on 'L1' | 1026 | // pushes dumped string on 'L1' |
1133 | luaL_pushresult(&B); // ... f b | 1027 | luaL_pushresult(&B); // L1: ... f b |
1134 | 1028 | ||
1135 | // if not pushed, no need to pop | 1029 | // if not pushed, no need to pop |
1136 | if (needToPush) | 1030 | if (needToPush) { |
1137 | { | 1031 | lua_remove(L1, -2); // L1: ... b |
1138 | lua_remove(L1, -2); // ... b | ||
1139 | } | 1032 | } |
1140 | 1033 | ||
1141 | // transfer the bytecode, then the upvalues, to create a similar closure | 1034 | // transfer the bytecode, then the upvalues, to create a similar closure |
1142 | { | 1035 | { |
1143 | char const* name = nullptr; | 1036 | char const* name = nullptr; |
1144 | 1037 | #define LOG_FUNC_INFO 0 | |
1145 | #if LOG_FUNC_INFO | 1038 | #if LOG_FUNC_INFO |
1146 | // "To get information about a function you push it onto the | 1039 | // "To get information about a function you push it onto the |
1147 | // stack and start the what string with the character '>'." | 1040 | // stack and start the what string with the character '>'." |
1148 | // | 1041 | // |
1149 | { | 1042 | { |
1150 | lua_Debug ar; | 1043 | lua_Debug ar; |
1151 | lua_pushvalue( L, i); // ... b f | 1044 | lua_pushvalue(L1, L1_i); // L1: ... b f |
1152 | // fills 'name' 'namewhat' and 'linedefined', pops function | 1045 | // fills 'name' 'namewhat' and 'linedefined', pops function |
1153 | lua_getinfo( L, ">nS", &ar); // ... b | 1046 | lua_getinfo(L1, ">nS", &ar); // L1: ... b |
1154 | name = ar.namewhat; | 1047 | name = ar.namewhat; |
1155 | fprintf( stderr, INDENT_BEGIN "FNAME: %s @ %d\n", i, s_indent, ar.short_src, ar.linedefined); // just gives nullptr | 1048 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "FNAME: %s @ %d" INDENT_END(U), ar.short_src, ar.linedefined)); // just gives nullptr |
1156 | } | 1049 | } |
1157 | #endif // LOG_FUNC_INFO | 1050 | #endif // LOG_FUNC_INFO |
1158 | { | 1051 | { |
1159 | size_t sz; | 1052 | size_t sz; |
1160 | char const* s = lua_tolstring(L1, -1, &sz); // ... b | 1053 | char const* s = lua_tolstring(L1, -1, &sz); // L1: ... b |
1161 | LUA_ASSERT(L1, s && sz); | 1054 | LUA_ASSERT(L1, s && sz); |
1162 | STACK_GROW(L2, 2); | 1055 | STACK_GROW(L2, 2); |
1163 | // Note: Line numbers seem to be taken precisely from the | 1056 | // Note: Line numbers seem to be taken precisely from the |
1164 | // original function. 'name' is not used since the chunk | 1057 | // original function. 'name' is not used since the chunk |
1165 | // is precompiled (it seems...). | 1058 | // is precompiled (it seems...). |
1166 | // | 1059 | // |
1167 | // TBD: Can we get the function's original name through, as well? | 1060 | // TBD: Can we get the function's original name through, as well? |
1168 | // | 1061 | // |
1169 | if (luaL_loadbuffer(L2, s, sz, name) != 0) // ... {cache} ... p function | 1062 | if (luaL_loadbuffer(L2, s, sz, name) != 0) { // L2: ... {cache} ... p function |
1170 | { | ||
1171 | // chunk is precompiled so only LUA_ERRMEM can happen | 1063 | // chunk is precompiled so only LUA_ERRMEM can happen |
1172 | // "Otherwise, it pushes an error message" | 1064 | // "Otherwise, it pushes an error message" |
1173 | // | 1065 | // |
@@ -1175,22 +1067,22 @@ void InterCopyContext::copy_func() const | |||
1175 | raise_luaL_error(L1, "%s: %s", name, lua_tostring(L2, -1)); | 1067 | raise_luaL_error(L1, "%s: %s", name, lua_tostring(L2, -1)); |
1176 | } | 1068 | } |
1177 | // remove the dumped string | 1069 | // remove the dumped string |
1178 | lua_pop(L1, 1); // ... | 1070 | lua_pop(L1, 1); // ... |
1179 | // now set the cache as soon as we can. | 1071 | // now set the cache as soon as we can. |
1180 | // this is necessary if one of the function's upvalues references it indirectly | 1072 | // this is necessary if one of the function's upvalues references it indirectly |
1181 | // we need to find it in the cache even if it isn't fully transfered yet | 1073 | // we need to find it in the cache even if it isn't fully transfered yet |
1182 | lua_insert(L2, -2); // ... {cache} ... function p | 1074 | lua_insert(L2, -2); // L2: ... {cache} ... function p |
1183 | lua_pushvalue(L2, -2); // ... {cache} ... function p function | 1075 | lua_pushvalue(L2, -2); // L2: ... {cache} ... function p function |
1184 | // cache[p] = function | 1076 | // cache[p] = function |
1185 | lua_rawset(L2, L2_cache_i); // ... {cache} ... function | 1077 | lua_rawset(L2, L2_cache_i); // L2: ... {cache} ... function |
1186 | } | 1078 | } |
1187 | STACK_CHECK(L1, 0); | 1079 | STACK_CHECK(L1, 0); |
1188 | 1080 | ||
1189 | /* push over any upvalues; references to this function will come from | 1081 | /* push over any upvalues; references to this function will come from |
1190 | * cache so we don't end up in eternal loop. | 1082 | * cache so we don't end up in eternal loop. |
1191 | * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy! | 1083 | * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy! |
1192 | * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state! | 1084 | * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state! |
1193 | */ | 1085 | */ |
1194 | int n{ 0 }; | 1086 | int n{ 0 }; |
1195 | { | 1087 | { |
1196 | InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} }; | 1088 | InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} }; |
@@ -1198,50 +1090,45 @@ void InterCopyContext::copy_func() const | |||
1198 | // Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default) | 1090 | // Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default) |
1199 | // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state... | 1091 | // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state... |
1200 | // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table | 1092 | // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table |
1201 | lua_pushglobaltable(L1); // ... _G | 1093 | lua_pushglobaltable(L1); // L1: ... _G |
1202 | #endif // LUA_VERSION_NUM | 1094 | #endif // LUA_VERSION_NUM |
1203 | for (n = 0; (c.name = lua_getupvalue(L1, L1_i, 1 + n)) != nullptr; ++n) | 1095 | for (n = 0; (c.name = lua_getupvalue(L1, L1_i, 1 + n)) != nullptr; ++n) { // L1: ... _G up[n] |
1204 | { // ... _G up[n] | 1096 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END(U), n, c.name)); |
1205 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END, n, c.name)); | ||
1206 | #if LUA_VERSION_NUM >= 502 | 1097 | #if LUA_VERSION_NUM >= 502 |
1207 | if (lua_rawequal(L1, -1, -2)) // is the upvalue equal to the global table? | 1098 | if (lua_rawequal(L1, -1, -2)) { // is the upvalue equal to the global table? |
1208 | { | ||
1209 | DEBUGSPEW_CODE(fprintf(stderr, "pushing destination global scope\n")); | 1099 | DEBUGSPEW_CODE(fprintf(stderr, "pushing destination global scope\n")); |
1210 | lua_pushglobaltable(L2); // ... {cache} ... function <upvalues> | 1100 | lua_pushglobaltable(L2); // L2: ... {cache} ... function <upvalues> |
1211 | } | 1101 | } else |
1212 | else | ||
1213 | #endif // LUA_VERSION_NUM | 1102 | #endif // LUA_VERSION_NUM |
1214 | { | 1103 | { |
1215 | DEBUGSPEW_CODE(fprintf(stderr, "copying value\n")); | 1104 | DEBUGSPEW_CODE(fprintf(stderr, "copying value\n")); |
1216 | c.L1_i = SourceIndex{ lua_gettop(L1) }; | 1105 | c.L1_i = SourceIndex{ lua_gettop(L1) }; |
1217 | if (!c.inter_copy_one()) // ... {cache} ... function <upvalues> | 1106 | if (!c.inter_copy_one()) { // L2: ... {cache} ... function <upvalues> |
1218 | { | ||
1219 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); | 1107 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); |
1220 | } | 1108 | } |
1221 | } | 1109 | } |
1222 | lua_pop(L1, 1); // ... _G | 1110 | lua_pop(L1, 1); // L1: ... _G |
1223 | } | 1111 | } |
1224 | #if LUA_VERSION_NUM >= 502 | 1112 | #if LUA_VERSION_NUM >= 502 |
1225 | lua_pop(L1, 1); // ... | 1113 | lua_pop(L1, 1); // L1: ... |
1226 | #endif // LUA_VERSION_NUM | 1114 | #endif // LUA_VERSION_NUM |
1227 | } | 1115 | } |
1228 | // L2: function + 'n' upvalues (>=0) | 1116 | // L2: ... {cache} ... function + 'n' upvalues (>=0) |
1229 | 1117 | ||
1230 | STACK_CHECK(L1, 0); | 1118 | STACK_CHECK(L1, 0); |
1231 | 1119 | ||
1232 | // Set upvalues (originally set to 'nil' by 'lua_load') | 1120 | // Set upvalues (originally set to 'nil' by 'lua_load') |
1233 | { | 1121 | { |
1234 | for (int const func_index{ lua_gettop(L2) - n }; n > 0; --n) | 1122 | for (int const func_index{ lua_gettop(L2) - n }; n > 0; --n) { |
1235 | { | 1123 | char const* rc{ lua_setupvalue(L2, func_index, n) }; // L2: ... {cache} ... function |
1236 | char const* rc{ lua_setupvalue(L2, func_index, n) }; // ... {cache} ... function | ||
1237 | // | 1124 | // |
1238 | // "assigns the value at the top of the stack to the upvalue and returns its name. | 1125 | // "assigns the value at the top of the stack to the upvalue and returns its name. |
1239 | // It also pops the value from the stack." | 1126 | // It also pops the value from the stack." |
1240 | 1127 | ||
1241 | LUA_ASSERT(L1, rc); // not having enough slots? | 1128 | LUA_ASSERT(L1, rc); // not having enough slots? |
1242 | } | 1129 | } |
1243 | // once all upvalues have been set we are left | 1130 | // once all upvalues have been set we are left |
1244 | // with the function at the top of the stack // ... {cache} ... function | 1131 | // with the function at the top of the stack // L2: ... {cache} ... function |
1245 | } | 1132 | } |
1246 | } | 1133 | } |
1247 | STACK_CHECK(L1, 0); | 1134 | STACK_CHECK(L1, 0); |
@@ -1249,16 +1136,12 @@ void InterCopyContext::copy_func() const | |||
1249 | 1136 | ||
1250 | // ################################################################################################# | 1137 | // ################################################################################################# |
1251 | 1138 | ||
1252 | /* | 1139 | // Check if we've already copied the same function from 'L1', and reuse the old copy. |
1253 | * Check if we've already copied the same function from 'L1', and reuse the old copy. | 1140 | // Always pushes a function to 'L2'. |
1254 | * | ||
1255 | * Always pushes a function to 'L2'. | ||
1256 | */ | ||
1257 | void InterCopyContext::copy_cached_func() const | 1141 | void InterCopyContext::copy_cached_func() const |
1258 | { | 1142 | { |
1259 | FuncSubType const funcSubType{ luaG_getfuncsubtype(L1, L1_i) }; | 1143 | FuncSubType const funcSubType{ luaG_getfuncsubtype(L1, L1_i) }; |
1260 | if (funcSubType == FuncSubType::Bytecode) | 1144 | if (funcSubType == FuncSubType::Bytecode) { |
1261 | { | ||
1262 | void* const aspointer = const_cast<void*>(lua_topointer(L1, L1_i)); | 1145 | void* const aspointer = const_cast<void*>(lua_topointer(L1, L1_i)); |
1263 | // TBD: Merge this and same code for tables | 1146 | // TBD: Merge this and same code for tables |
1264 | LUA_ASSERT(L1, L2_cache_i != 0); | 1147 | LUA_ASSERT(L1, L2_cache_i != 0); |
@@ -1274,33 +1157,28 @@ void InterCopyContext::copy_cached_func() const | |||
1274 | // | 1157 | // |
1275 | 1158 | ||
1276 | // push a light userdata uniquely representing the function | 1159 | // push a light userdata uniquely representing the function |
1277 | lua_pushlightuserdata(L2, aspointer); // ... {cache} ... p | 1160 | lua_pushlightuserdata(L2, aspointer); // L2: ... {cache} ... p |
1278 | 1161 | ||
1279 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); | 1162 | // fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); |
1280 | 1163 | ||
1281 | lua_pushvalue(L2, -1); // ... {cache} ... p p | 1164 | lua_pushvalue(L2, -1); // L2: ... {cache} ... p p |
1282 | lua_rawget(L2, L2_cache_i); // ... {cache} ... p function|nil|true | 1165 | lua_rawget(L2, L2_cache_i); // L2: ... {cache} ... p function|nil|true |
1283 | 1166 | ||
1284 | if (lua_isnil(L2, -1)) // function is unknown | 1167 | if (lua_isnil(L2, -1)) { // function is unknown |
1285 | { | 1168 | lua_pop(L2, 1); // L2: ... {cache} ... p |
1286 | lua_pop(L2, 1); // ... {cache} ... p | ||
1287 | 1169 | ||
1288 | // Set to 'true' for the duration of creation; need to find self-references | 1170 | // Set to 'true' for the duration of creation; need to find self-references |
1289 | // via upvalues | 1171 | // via upvalues |
1290 | // | 1172 | // |
1291 | // pushes a copy of the func, stores a reference in the cache | 1173 | // pushes a copy of the func, stores a reference in the cache |
1292 | copy_func(); // ... {cache} ... function | 1174 | copy_func(); // L2: ... {cache} ... function |
1293 | } | 1175 | } else { // found function in the cache |
1294 | else // found function in the cache | 1176 | lua_remove(L2, -2); // L2: ... {cache} ... function |
1295 | { | ||
1296 | lua_remove(L2, -2); // ... {cache} ... function | ||
1297 | } | 1177 | } |
1298 | STACK_CHECK(L2, 1); | 1178 | STACK_CHECK(L2, 1); |
1299 | LUA_ASSERT(L1, lua_isfunction(L2, -1)); | 1179 | LUA_ASSERT(L1, lua_isfunction(L2, -1)); |
1300 | } | 1180 | } else { // function is native/LuaJIT: no need to cache |
1301 | else // function is native/LuaJIT: no need to cache | 1181 | lookup_native_func(); // L2: ... {cache} ... function |
1302 | { | ||
1303 | lookup_native_func(); // ... {cache} ... function | ||
1304 | // if the function was in fact a lookup sentinel, we can either get a function or a table here | 1182 | // if the function was in fact a lookup sentinel, we can either get a function or a table here |
1305 | LUA_ASSERT(L1, lua_isfunction(L2, -1) || lua_istable(L2, -1)); | 1183 | LUA_ASSERT(L1, lua_isfunction(L2, -1) || lua_istable(L2, -1)); |
1306 | } | 1184 | } |
@@ -1311,8 +1189,7 @@ void InterCopyContext::copy_cached_func() const | |||
1311 | [[nodiscard]] bool InterCopyContext::push_cached_metatable() const | 1189 | [[nodiscard]] bool InterCopyContext::push_cached_metatable() const |
1312 | { | 1190 | { |
1313 | STACK_CHECK_START_REL(L1, 0); | 1191 | STACK_CHECK_START_REL(L1, 0); |
1314 | if (!lua_getmetatable(L1, L1_i)) // ... mt | 1192 | if (!lua_getmetatable(L1, L1_i)) { // L1: ... mt |
1315 | { | ||
1316 | STACK_CHECK(L1, 0); | 1193 | STACK_CHECK(L1, 0); |
1317 | return false; | 1194 | return false; |
1318 | } | 1195 | } |
@@ -1323,35 +1200,33 @@ void InterCopyContext::copy_cached_func() const | |||
1323 | STACK_CHECK_START_REL(L2, 0); | 1200 | STACK_CHECK_START_REL(L2, 0); |
1324 | STACK_GROW(L2, 4); | 1201 | STACK_GROW(L2, 4); |
1325 | // do we already know this metatable? | 1202 | // do we already know this metatable? |
1326 | push_registry_subtable(L2, kMtIdRegKey); // _R[kMtIdRegKey] | 1203 | push_registry_subtable(L2, kMtIdRegKey); // L2: _R[kMtIdRegKey] |
1327 | lua_pushinteger(L2, mt_id); // _R[kMtIdRegKey] id | 1204 | lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] id |
1328 | lua_rawget(L2, -2); // _R[kMtIdRegKey] mt|nil | 1205 | lua_rawget(L2, -2); // L2: _R[kMtIdRegKey] mt|nil |
1329 | STACK_CHECK(L2, 2); | 1206 | STACK_CHECK(L2, 2); |
1330 | 1207 | ||
1331 | if (lua_isnil(L2, -1)) | 1208 | if (lua_isnil(L2, -1)) { // L2 did not know the metatable |
1332 | { // L2 did not know the metatable | 1209 | lua_pop(L2, 1); // L2: _R[kMtIdRegKey] |
1333 | lua_pop(L2, 1); // _R[kMtIdRegKey] | ||
1334 | InterCopyContext const c{ U, L2, L1, L2_cache_i, SourceIndex{ lua_gettop(L1) }, VT::METATABLE, mode, name }; | 1210 | InterCopyContext const c{ U, L2, L1, L2_cache_i, SourceIndex{ lua_gettop(L1) }, VT::METATABLE, mode, name }; |
1335 | if (!c.inter_copy_one()) // _R[kMtIdRegKey] mt? | 1211 | if (!c.inter_copy_one()) { // L2: _R[kMtIdRegKey] mt? |
1336 | { | ||
1337 | raise_luaL_error(L1, "Error copying a metatable"); | 1212 | raise_luaL_error(L1, "Error copying a metatable"); |
1338 | } | 1213 | } |
1339 | 1214 | ||
1340 | STACK_CHECK(L2, 2); // _R[kMtIdRegKey] mt | 1215 | STACK_CHECK(L2, 2); // L2: _R[kMtIdRegKey] mt |
1341 | // mt_id -> metatable | 1216 | // mt_id -> metatable |
1342 | lua_pushinteger(L2, mt_id); // _R[kMtIdRegKey] mt id | 1217 | lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt id |
1343 | lua_pushvalue(L2, -2); // _R[kMtIdRegKey] mt id mt | 1218 | lua_pushvalue(L2, -2); // L2: _R[kMtIdRegKey] mt id mt |
1344 | lua_rawset(L2, -4); // _R[kMtIdRegKey] mt | 1219 | lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt |
1345 | 1220 | ||
1346 | // metatable -> mt_id | 1221 | // metatable -> mt_id |
1347 | lua_pushvalue(L2, -1); // _R[kMtIdRegKey] mt mt | 1222 | lua_pushvalue(L2, -1); // L2: _R[kMtIdRegKey] mt mt |
1348 | lua_pushinteger(L2, mt_id); // _R[kMtIdRegKey] mt mt id | 1223 | lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt mt id |
1349 | lua_rawset(L2, -4); // _R[kMtIdRegKey] mt | 1224 | lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt |
1350 | STACK_CHECK(L2, 2); | 1225 | STACK_CHECK(L2, 2); |
1351 | } | 1226 | } |
1352 | lua_remove(L2, -2); // mt | 1227 | lua_remove(L2, -2); // L2: mt |
1353 | 1228 | ||
1354 | lua_pop(L1, 1); // ... | 1229 | lua_pop(L1, 1); // L1: ... |
1355 | STACK_CHECK(L2, 1); | 1230 | STACK_CHECK(L2, 1); |
1356 | STACK_CHECK(L1, 0); | 1231 | STACK_CHECK(L1, 0); |
1357 | return true; | 1232 | return true; |
@@ -1366,49 +1241,40 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1366 | 1241 | ||
1367 | // For the key, only basic key types are copied over. others ignored | 1242 | // For the key, only basic key types are copied over. others ignored |
1368 | InterCopyContext c{ U, L2, L1, L2_cache_i, key_i, VT::KEY, mode, name }; | 1243 | InterCopyContext c{ U, L2, L1, L2_cache_i, key_i, VT::KEY, mode, name }; |
1369 | if (!c.inter_copy_one()) | 1244 | if (!c.inter_copy_one()) { |
1370 | { | ||
1371 | return; | 1245 | return; |
1372 | // we could raise an error instead of ignoring the table entry, like so: | 1246 | // we could raise an error instead of ignoring the table entry, like so: |
1373 | //raise_luaL_error(L1, "Unable to copy %s key '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", name, luaL_typename(L1, key_i)); | 1247 | // raise_luaL_error(L1, "Unable to copy %s key '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", name, luaL_typename(L1, key_i)); |
1374 | // maybe offer this possibility as a global configuration option, or a linda setting, or as a parameter of the call causing the transfer? | 1248 | // maybe offer this possibility as a global configuration option, or a linda setting, or as a parameter of the call causing the transfer? |
1375 | } | 1249 | } |
1376 | 1250 | ||
1377 | char* valPath{ nullptr }; | 1251 | char* valPath{ nullptr }; |
1378 | if (U->verboseErrors) | 1252 | if (U->verboseErrors) { |
1379 | { | ||
1380 | // for debug purposes, let's try to build a useful name | 1253 | // for debug purposes, let's try to build a useful name |
1381 | if (lua_type(L1, key_i) == LUA_TSTRING) | 1254 | if (lua_type(L1, key_i) == LUA_TSTRING) { |
1382 | { | ||
1383 | char const* key{ lua_tostring(L1, key_i) }; | 1255 | char const* key{ lua_tostring(L1, key_i) }; |
1384 | size_t const keyRawLen = lua_rawlen(L1, key_i); | 1256 | size_t const keyRawLen = lua_rawlen(L1, key_i); |
1385 | size_t const bufLen = strlen(name) + keyRawLen + 2; | 1257 | size_t const bufLen = strlen(name) + keyRawLen + 2; |
1386 | valPath = (char*) alloca( bufLen); | 1258 | valPath = (char*) alloca(bufLen); |
1387 | sprintf( valPath, "%s.%*s", name, (int) keyRawLen, key); | 1259 | sprintf(valPath, "%s.%*s", name, (int) keyRawLen, key); |
1388 | key = nullptr; | 1260 | key = nullptr; |
1389 | } | 1261 | } |
1390 | #if defined LUA_LNUM || LUA_VERSION_NUM >= 503 | 1262 | #if defined LUA_LNUM || LUA_VERSION_NUM >= 503 |
1391 | else if (lua_isinteger(L1, key_i)) | 1263 | else if (lua_isinteger(L1, key_i)) { |
1392 | { | ||
1393 | lua_Integer const key{ lua_tointeger(L1, key_i) }; | 1264 | lua_Integer const key{ lua_tointeger(L1, key_i) }; |
1394 | valPath = (char*) alloca(strlen(name) + 32 + 3); | 1265 | valPath = (char*) alloca(strlen(name) + 32 + 3); |
1395 | sprintf(valPath, "%s[" LUA_INTEGER_FMT "]", name, key); | 1266 | sprintf(valPath, "%s[" LUA_INTEGER_FMT "]", name, key); |
1396 | } | 1267 | } |
1397 | #endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 | 1268 | #endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 |
1398 | else if (lua_type(L1, key_i) == LUA_TNUMBER) | 1269 | else if (lua_type(L1, key_i) == LUA_TNUMBER) { |
1399 | { | ||
1400 | lua_Number const key{ lua_tonumber(L1, key_i) }; | 1270 | lua_Number const key{ lua_tonumber(L1, key_i) }; |
1401 | valPath = (char*) alloca(strlen(name) + 32 + 3); | 1271 | valPath = (char*) alloca(strlen(name) + 32 + 3); |
1402 | sprintf(valPath, "%s[" LUA_NUMBER_FMT "]", name, key); | 1272 | sprintf(valPath, "%s[" LUA_NUMBER_FMT "]", name, key); |
1403 | } | 1273 | } else if (lua_type(L1, key_i) == LUA_TLIGHTUSERDATA) { |
1404 | else if (lua_type( L1, key_i) == LUA_TLIGHTUSERDATA) | ||
1405 | { | ||
1406 | void* const key{ lua_touserdata(L1, key_i) }; | 1274 | void* const key{ lua_touserdata(L1, key_i) }; |
1407 | valPath = (char*) alloca(strlen(name) + 16 + 5); | 1275 | valPath = (char*) alloca(strlen(name) + 16 + 5); |
1408 | sprintf(valPath, "%s[U:%p]", name, key); | 1276 | sprintf(valPath, "%s[U:%p]", name, key); |
1409 | } | 1277 | } else if (lua_type(L1, key_i) == LUA_TBOOLEAN) { |
1410 | else if (lua_type( L1, key_i) == LUA_TBOOLEAN) | ||
1411 | { | ||
1412 | int const key{ lua_toboolean(L1, key_i) }; | 1278 | int const key{ lua_toboolean(L1, key_i) }; |
1413 | valPath = (char*) alloca(strlen(name) + 8); | 1279 | valPath = (char*) alloca(strlen(name) + 8); |
1414 | sprintf(valPath, "%s[%s]", name, key ? "true" : "false"); | 1280 | sprintf(valPath, "%s[%s]", name, key ? "true" : "false"); |
@@ -1418,132 +1284,114 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1418 | // Contents of metatables are copied with cache checking. important to detect loops. | 1284 | // Contents of metatables are copied with cache checking. important to detect loops. |
1419 | c.vt = VT::NORMAL; | 1285 | c.vt = VT::NORMAL; |
1420 | c.name = valPath ? valPath : name; | 1286 | c.name = valPath ? valPath : name; |
1421 | if (c.inter_copy_one()) | 1287 | if (c.inter_copy_one()) { |
1422 | { | 1288 | LUA_ASSERT(L1, lua_istable(L2, -3)); |
1423 | LUA_ASSERT(L1, lua_istable( L2, -3)); | 1289 | lua_rawset(L2, -3); // add to table (pops key & val) |
1424 | lua_rawset(L2, -3); // add to table (pops key & val) | 1290 | } else { |
1425 | } | ||
1426 | else | ||
1427 | { | ||
1428 | raise_luaL_error(L1, "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", valPath, luaL_typename(L1, val_i)); | 1291 | raise_luaL_error(L1, "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", valPath, luaL_typename(L1, val_i)); |
1429 | } | 1292 | } |
1430 | } | 1293 | } |
1431 | 1294 | ||
1432 | // ################################################################################################# | 1295 | // ################################################################################################# |
1433 | 1296 | ||
1434 | [[nodiscard]] bool InterCopyContext::copyclone() const | 1297 | [[nodiscard]] bool InterCopyContext::copyClonable() const |
1435 | { | 1298 | { |
1436 | SourceIndex const L1_i{ lua_absindex(L1, this->L1_i) }; | 1299 | SourceIndex const L1_i{ lua_absindex(L1, this->L1_i) }; |
1437 | void* const source{ lua_touserdata(L1, L1_i) }; | 1300 | void* const source{ lua_touserdata(L1, L1_i) }; |
1438 | 1301 | ||
1439 | STACK_CHECK_START_REL(L1, 0); // L1 (source) // L2 (destination) | 1302 | STACK_CHECK_START_REL(L1, 0); |
1440 | STACK_CHECK_START_REL(L2, 0); | 1303 | STACK_CHECK_START_REL(L2, 0); |
1441 | 1304 | ||
1442 | // Check if the source was already cloned during this copy | 1305 | // Check if the source was already cloned during this copy |
1443 | lua_pushlightuserdata(L2, source); // ... source | 1306 | lua_pushlightuserdata(L2, source); // L2: ... source |
1444 | lua_rawget(L2, L2_cache_i); // ... clone? | 1307 | lua_rawget(L2, L2_cache_i); // L2: ... clone? |
1445 | if (!lua_isnil(L2, -1)) | 1308 | if (!lua_isnil(L2, -1)) { |
1446 | { | ||
1447 | STACK_CHECK(L2, 1); | 1309 | STACK_CHECK(L2, 1); |
1448 | return true; | 1310 | return true; |
1449 | } | 1311 | } else { |
1450 | else | 1312 | lua_pop(L2, 1); // L2: ... |
1451 | { | ||
1452 | lua_pop(L2, 1); // ... | ||
1453 | } | 1313 | } |
1454 | STACK_CHECK(L2, 0); | 1314 | STACK_CHECK(L2, 0); |
1455 | 1315 | ||
1456 | // no metatable? -> not clonable | 1316 | // no metatable? -> not clonable |
1457 | if (!lua_getmetatable(L1, L1_i)) // ... mt? | 1317 | if (!lua_getmetatable(L1, L1_i)) { // L1: ... mt? |
1458 | { | ||
1459 | STACK_CHECK(L1, 0); | 1318 | STACK_CHECK(L1, 0); |
1460 | return false; | 1319 | return false; |
1461 | } | 1320 | } |
1462 | 1321 | ||
1463 | // no __lanesclone? -> not clonable | 1322 | // no __lanesclone? -> not clonable |
1464 | lua_getfield(L1, -1, "__lanesclone"); // ... mt __lanesclone? | 1323 | lua_getfield(L1, -1, "__lanesclone"); // L1: ... mt __lanesclone? |
1465 | if (lua_isnil(L1, -1)) | 1324 | if (lua_isnil(L1, -1)) { |
1466 | { | 1325 | lua_pop(L1, 2); // L1: ... |
1467 | lua_pop(L1, 2); // ... | ||
1468 | STACK_CHECK(L1, 0); | 1326 | STACK_CHECK(L1, 0); |
1469 | return false; | 1327 | return false; |
1470 | } | 1328 | } |
1471 | 1329 | ||
1472 | // we need to copy over the uservalues of the userdata as well | 1330 | // we need to copy over the uservalues of the userdata as well |
1473 | { | 1331 | { |
1474 | int const mt{ lua_absindex(L1, -2) }; // ... mt __lanesclone | 1332 | int const mt{ lua_absindex(L1, -2) }; // L1: ... mt __lanesclone |
1475 | size_t const userdata_size{ lua_rawlen(L1, L1_i) }; | 1333 | size_t const userdata_size{ lua_rawlen(L1, L1_i) }; |
1476 | // extract all the uservalues, but don't transfer them yet | 1334 | // extract all the uservalues, but don't transfer them yet |
1477 | int uvi = 0; | 1335 | int uvi = 0; |
1478 | while (lua_getiuservalue( L1, L1_i, ++ uvi) != LUA_TNONE) {} // ... mt __lanesclone [uv]+ nil | 1336 | while (lua_getiuservalue(L1, L1_i, ++uvi) != LUA_TNONE) {} // L1: ... mt __lanesclone [uv]+ nil |
1479 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now | 1337 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now |
1480 | lua_pop(L1, 1); // ... mt __lanesclone [uv]+ | 1338 | lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]+ |
1481 | -- uvi; | 1339 | --uvi; |
1482 | // create the clone userdata with the required number of uservalue slots | 1340 | // create the clone userdata with the required number of uservalue slots |
1483 | void* const clone{ lua_newuserdatauv(L2, userdata_size, uvi) }; // ... u | 1341 | void* const clone{ lua_newuserdatauv(L2, userdata_size, uvi) }; // L2: ... u |
1484 | // copy the metatable in the target state, and give it to the clone we put there | 1342 | // copy the metatable in the target state, and give it to the clone we put there |
1485 | InterCopyContext c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name }; | 1343 | InterCopyContext c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name }; |
1486 | if (c.inter_copy_one()) // ... u mt|sentinel | 1344 | if (c.inter_copy_one()) { // L2: ... u mt|sentinel |
1487 | { | 1345 | if (LookupMode::ToKeeper == mode) { // L2: ... u sentinel |
1488 | if (LookupMode::ToKeeper == mode) // ... u sentinel | ||
1489 | { | ||
1490 | LUA_ASSERT(L1, lua_tocfunction(L2, -1) == table_lookup_sentinel); | 1346 | LUA_ASSERT(L1, lua_tocfunction(L2, -1) == table_lookup_sentinel); |
1491 | // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn | 1347 | // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn |
1492 | lua_getupvalue(L2, -1, 1); // ... u sentinel fqn | 1348 | lua_getupvalue(L2, -1, 1); // L2: ... u sentinel fqn |
1493 | lua_remove(L2, -2); // ... u fqn | 1349 | lua_remove(L2, -2); // L2: ... u fqn |
1494 | lua_insert(L2, -2); // ... fqn u | 1350 | lua_insert(L2, -2); // L2: ... fqn u |
1495 | lua_pushcclosure(L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel | 1351 | lua_pushcclosure(L2, userdata_clone_sentinel, 2); // L2: ... userdata_clone_sentinel |
1496 | } | 1352 | } else { // from keeper or direct // L2: ... u mt |
1497 | else // from keeper or direct // ... u mt | ||
1498 | { | ||
1499 | LUA_ASSERT(L1, lua_istable(L2, -1)); | 1353 | LUA_ASSERT(L1, lua_istable(L2, -1)); |
1500 | lua_setmetatable(L2, -2); // ... u | 1354 | lua_setmetatable(L2, -2); // L2: ... u |
1501 | } | 1355 | } |
1502 | STACK_CHECK(L2, 1); | 1356 | STACK_CHECK(L2, 1); |
1503 | } | 1357 | } else { |
1504 | else | ||
1505 | { | ||
1506 | raise_luaL_error(L1, "Error copying a metatable"); | 1358 | raise_luaL_error(L1, "Error copying a metatable"); |
1507 | } | 1359 | } |
1508 | // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel | 1360 | // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel |
1509 | lua_pushlightuserdata( L2, source); // ... u source | 1361 | lua_pushlightuserdata(L2, source); // L2: ... u source |
1510 | lua_pushvalue( L2, -2); // ... u source u | 1362 | lua_pushvalue(L2, -2); // L2: ... u source u |
1511 | lua_rawset( L2, L2_cache_i); // ... u | 1363 | lua_rawset(L2, L2_cache_i); // L2: ... u |
1512 | // make sure we have the userdata now | 1364 | // make sure we have the userdata now |
1513 | if (LookupMode::ToKeeper == mode) // ... userdata_clone_sentinel | 1365 | if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel |
1514 | { | 1366 | lua_getupvalue(L2, -1, 2); // L2: ... userdata_clone_sentinel u |
1515 | lua_getupvalue(L2, -1, 2); // ... userdata_clone_sentinel u | ||
1516 | } | 1367 | } |
1517 | // assign uservalues | 1368 | // assign uservalues |
1518 | while (uvi > 0) | 1369 | while (uvi > 0) { |
1519 | { | ||
1520 | c.L1_i = SourceIndex{ lua_absindex(L1, -1) }; | 1370 | c.L1_i = SourceIndex{ lua_absindex(L1, -1) }; |
1521 | if (!c.inter_copy_one()) // ... u uv | 1371 | if (!c.inter_copy_one()) { // L2: ... u uv |
1522 | { | ||
1523 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); | 1372 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); |
1524 | } | 1373 | } |
1525 | lua_pop(L1, 1); // ... mt __lanesclone [uv]* | 1374 | lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]* |
1526 | // this pops the value from the stack | 1375 | // this pops the value from the stack |
1527 | lua_setiuservalue(L2, -2, uvi); // ... u | 1376 | lua_setiuservalue(L2, -2, uvi); // L2: ... u |
1528 | -- uvi; | 1377 | --uvi; |
1529 | } | 1378 | } |
1530 | // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination | 1379 | // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination |
1531 | if (LookupMode::ToKeeper == mode) // ... userdata_clone_sentinel u | 1380 | if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel u |
1532 | { | 1381 | lua_pop(L2, 1); // L2: ... userdata_clone_sentinel |
1533 | lua_pop(L2, 1); // ... userdata_clone_sentinel | ||
1534 | } | 1382 | } |
1535 | STACK_CHECK(L2, 1); | 1383 | STACK_CHECK(L2, 1); |
1536 | STACK_CHECK(L1, 2); | 1384 | STACK_CHECK(L1, 2); |
1537 | // call cloning function in source state to perform the actual memory cloning | 1385 | // call cloning function in source state to perform the actual memory cloning |
1538 | lua_pushlightuserdata(L1, clone); // ... mt __lanesclone clone | 1386 | lua_pushlightuserdata(L1, clone); // L1: ... mt __lanesclone clone |
1539 | lua_pushlightuserdata(L1, source); // ... mt __lanesclone clone source | 1387 | lua_pushlightuserdata(L1, source); // L1: ... mt __lanesclone clone source |
1540 | lua_pushinteger(L1, static_cast<lua_Integer>(userdata_size)); // ... mt __lanesclone clone source size | 1388 | lua_pushinteger(L1, static_cast<lua_Integer>(userdata_size)); // L1: ... mt __lanesclone clone source size |
1541 | lua_call(L1, 3, 0); // ... mt | 1389 | lua_call(L1, 3, 0); // L1: ... mt |
1542 | STACK_CHECK(L1, 1); | 1390 | STACK_CHECK(L1, 1); |
1543 | } | 1391 | } |
1544 | 1392 | ||
1545 | STACK_CHECK(L2, 1); | 1393 | STACK_CHECK(L2, 1); |
1546 | lua_pop(L1, 1); // ... | 1394 | lua_pop(L1, 1); // L1: ... |
1547 | STACK_CHECK(L1, 0); | 1395 | STACK_CHECK(L1, 0); |
1548 | return true; | 1396 | return true; |
1549 | } | 1397 | } |
@@ -1554,14 +1402,12 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1554 | { | 1402 | { |
1555 | STACK_CHECK_START_REL(L1, 0); | 1403 | STACK_CHECK_START_REL(L1, 0); |
1556 | STACK_CHECK_START_REL(L2, 0); | 1404 | STACK_CHECK_START_REL(L2, 0); |
1557 | if (vt == VT::KEY) | 1405 | if (vt == VT::KEY) { |
1558 | { | ||
1559 | return false; | 1406 | return false; |
1560 | } | 1407 | } |
1561 | 1408 | ||
1562 | // try clonable userdata first | 1409 | // try clonable userdata first |
1563 | if (copyclone()) | 1410 | if (copyClonable()) { |
1564 | { | ||
1565 | STACK_CHECK(L1, 0); | 1411 | STACK_CHECK(L1, 0); |
1566 | STACK_CHECK(L2, 1); | 1412 | STACK_CHECK(L2, 1); |
1567 | return true; | 1413 | return true; |
@@ -1572,8 +1418,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1572 | 1418 | ||
1573 | // Allow only deep userdata entities to be copied across | 1419 | // Allow only deep userdata entities to be copied across |
1574 | DEBUGSPEW_CODE(fprintf(stderr, "USERDATA\n")); | 1420 | DEBUGSPEW_CODE(fprintf(stderr, "USERDATA\n")); |
1575 | if (copydeep()) | 1421 | if (copyDeep()) { |
1576 | { | ||
1577 | STACK_CHECK(L1, 0); | 1422 | STACK_CHECK(L1, 0); |
1578 | STACK_CHECK(L2, 1); | 1423 | STACK_CHECK(L2, 1); |
1579 | return true; | 1424 | return true; |
@@ -1583,13 +1428,10 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1583 | STACK_CHECK(L2, 0); | 1428 | STACK_CHECK(L2, 0); |
1584 | 1429 | ||
1585 | // Not a deep or clonable full userdata | 1430 | // Not a deep or clonable full userdata |
1586 | if (U->demoteFullUserdata) // attempt demotion to light userdata | 1431 | if (U->demoteFullUserdata) { // attempt demotion to light userdata |
1587 | { | ||
1588 | void* const lud{ lua_touserdata(L1, L1_i) }; | 1432 | void* const lud{ lua_touserdata(L1, L1_i) }; |
1589 | lua_pushlightuserdata(L2, lud); | 1433 | lua_pushlightuserdata(L2, lud); |
1590 | } | 1434 | } else { // raise an error |
1591 | else // raise an error | ||
1592 | { | ||
1593 | raise_luaL_error(L1, "can't copy non-deep full userdata across lanes"); | 1435 | raise_luaL_error(L1, "can't copy non-deep full userdata across lanes"); |
1594 | } | 1436 | } |
1595 | 1437 | ||
@@ -1602,37 +1444,33 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1602 | 1444 | ||
1603 | [[nodiscard]] bool InterCopyContext::inter_copy_function() const | 1445 | [[nodiscard]] bool InterCopyContext::inter_copy_function() const |
1604 | { | 1446 | { |
1605 | if (vt == VT::KEY) | 1447 | if (vt == VT::KEY) { |
1606 | { | ||
1607 | return false; | 1448 | return false; |
1608 | } | 1449 | } |
1609 | 1450 | ||
1610 | STACK_CHECK_START_REL(L1, 0); // L1 (source) // L2 (destination) | 1451 | STACK_CHECK_START_REL(L1, 0); |
1611 | STACK_CHECK_START_REL(L2, 0); | 1452 | STACK_CHECK_START_REL(L2, 0); |
1612 | DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name)); | 1453 | DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name)); |
1613 | 1454 | ||
1614 | if (lua_tocfunction(L1, L1_i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata from a keeper | 1455 | if (lua_tocfunction(L1, L1_i) == userdata_clone_sentinel) { // we are actually copying a clonable full userdata from a keeper |
1615 | { | ||
1616 | // clone the full userdata again | 1456 | // clone the full userdata again |
1617 | 1457 | ||
1618 | // let's see if we already restored this userdata | 1458 | // let's see if we already restored this userdata |
1619 | lua_getupvalue(L1, L1_i, 2); // ... u | 1459 | lua_getupvalue(L1, L1_i, 2); // L1: ... u |
1620 | void* source = lua_touserdata(L1, -1); | 1460 | void* source = lua_touserdata(L1, -1); |
1621 | lua_pushlightuserdata(L2, source); // ... source | 1461 | lua_pushlightuserdata(L2, source); // L2: ... source |
1622 | lua_rawget(L2, L2_cache_i); // ... u? | 1462 | lua_rawget(L2, L2_cache_i); // L2: ... u? |
1623 | if (!lua_isnil(L2, -1)) | 1463 | if (!lua_isnil(L2, -1)) { |
1624 | { | 1464 | lua_pop(L1, 1); // L1: ... |
1625 | lua_pop(L1, 1); // ... | ||
1626 | STACK_CHECK(L1, 0); | 1465 | STACK_CHECK(L1, 0); |
1627 | STACK_CHECK(L2, 1); | 1466 | STACK_CHECK(L2, 1); |
1628 | return true; | 1467 | return true; |
1629 | } | 1468 | } |
1630 | lua_pop(L2, 1); // ... | 1469 | lua_pop(L2, 1); // L2: ... |
1631 | 1470 | ||
1632 | // userdata_clone_sentinel has 2 upvalues: the fqn of its metatable, and the userdata itself | 1471 | // userdata_clone_sentinel has 2 upvalues: the fqn of its metatable, and the userdata itself |
1633 | bool const found{ lookup_table() }; // ... mt? | 1472 | bool const found{ lookup_table() }; // L2: ... mt? |
1634 | if (!found) | 1473 | if (!found) { |
1635 | { | ||
1636 | STACK_CHECK(L2, 0); | 1474 | STACK_CHECK(L2, 0); |
1637 | return false; | 1475 | return false; |
1638 | } | 1476 | } |
@@ -1645,55 +1483,51 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1645 | { | 1483 | { |
1646 | // extract uservalues (don't transfer them yet) | 1484 | // extract uservalues (don't transfer them yet) |
1647 | int uvi = 0; | 1485 | int uvi = 0; |
1648 | while (lua_getiuservalue(L1, source_i, ++uvi) != LUA_TNONE) {} // ... u uv | 1486 | while (lua_getiuservalue(L1, source_i, ++uvi) != LUA_TNONE) {} // L1: ... u uv |
1649 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now | 1487 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now |
1650 | lua_pop(L1, 1); // ... u [uv]* | 1488 | lua_pop(L1, 1); // L1: ... u [uv]* |
1651 | --uvi; | 1489 | --uvi; |
1652 | STACK_CHECK(L1, uvi + 1); | 1490 | STACK_CHECK(L1, uvi + 1); |
1653 | // create the clone userdata with the required number of uservalue slots | 1491 | // create the clone userdata with the required number of uservalue slots |
1654 | clone = lua_newuserdatauv(L2, userdata_size, uvi); // ... mt u | 1492 | clone = lua_newuserdatauv(L2, userdata_size, uvi); // L2: ... mt u |
1655 | // add it in the cache | 1493 | // add it in the cache |
1656 | lua_pushlightuserdata(L2, source); // ... mt u source | 1494 | lua_pushlightuserdata(L2, source); // L2: ... mt u source |
1657 | lua_pushvalue(L2, -2); // ... mt u source u | 1495 | lua_pushvalue(L2, -2); // L2: ... mt u source u |
1658 | lua_rawset(L2, L2_cache_i); // ... mt u | 1496 | lua_rawset(L2, L2_cache_i); // L2: ... mt u |
1659 | // set metatable | 1497 | // set metatable |
1660 | lua_pushvalue(L2, -2); // ... mt u mt | 1498 | lua_pushvalue(L2, -2); // L2: ... mt u mt |
1661 | lua_setmetatable(L2, -2); // ... mt u | 1499 | lua_setmetatable(L2, -2); // L2: ... mt u |
1662 | // transfer and assign uservalues | 1500 | // transfer and assign uservalues |
1663 | InterCopyContext c{ *this }; | 1501 | InterCopyContext c{ *this }; |
1664 | while (uvi > 0) | 1502 | while (uvi > 0) { |
1665 | { | ||
1666 | c.L1_i = SourceIndex{ lua_absindex(L1, -1) }; | 1503 | c.L1_i = SourceIndex{ lua_absindex(L1, -1) }; |
1667 | if (!c.inter_copy_one()) // ... mt u uv | 1504 | if (!c.inter_copy_one()) { // L2: ... mt u uv |
1668 | { | ||
1669 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); | 1505 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); |
1670 | } | 1506 | } |
1671 | lua_pop(L1, 1); // ... u [uv]* | 1507 | lua_pop(L1, 1); // L1: ... u [uv]* |
1672 | // this pops the value from the stack | 1508 | // this pops the value from the stack |
1673 | lua_setiuservalue(L2, -2, uvi); // ... mt u | 1509 | lua_setiuservalue(L2, -2, uvi); // L2: ... mt u |
1674 | -- uvi; | 1510 | --uvi; |
1675 | } | 1511 | } |
1676 | // when we are done, all uservalues are popped from the stack, we can pop the source as well | 1512 | // when we are done, all uservalues are popped from the stack, we can pop the source as well |
1677 | lua_pop(L1, 1); // ... | 1513 | lua_pop(L1, 1); // L1: ... |
1678 | STACK_CHECK(L1, 0); | 1514 | STACK_CHECK(L1, 0); |
1679 | STACK_CHECK(L2, 2); // ... mt u | 1515 | STACK_CHECK(L2, 2); // L2: ... mt u |
1680 | } | 1516 | } |
1681 | // perform the custom cloning part | 1517 | // perform the custom cloning part |
1682 | lua_insert(L2, -2); // ... u mt | 1518 | lua_insert(L2, -2); // L2: ... u mt |
1683 | // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with | 1519 | // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with |
1684 | lua_getfield(L2, -1, "__lanesclone"); // ... u mt __lanesclone | 1520 | lua_getfield(L2, -1, "__lanesclone"); // L2: ... u mt __lanesclone |
1685 | lua_remove(L2, -2); // ... u __lanesclone | 1521 | lua_remove(L2, -2); // L2: ... u __lanesclone |
1686 | lua_pushlightuserdata(L2, clone); // ... u __lanesclone clone | 1522 | lua_pushlightuserdata(L2, clone); // L2: ... u __lanesclone clone |
1687 | lua_pushlightuserdata(L2, source); // ... u __lanesclone clone source | 1523 | lua_pushlightuserdata(L2, source); // L2: ... u __lanesclone clone source |
1688 | lua_pushinteger(L2, userdata_size); // ... u __lanesclone clone source size | 1524 | lua_pushinteger(L2, userdata_size); // L2: ... u __lanesclone clone source size |
1689 | // clone:__lanesclone(dest, source, size) | 1525 | // clone:__lanesclone(dest, source, size) |
1690 | lua_call(L2, 3, 0); // ... u | 1526 | lua_call(L2, 3, 0); // L2: ... u |
1691 | } | 1527 | } else { // regular function |
1692 | else // regular function | 1528 | DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name)); |
1693 | { | ||
1694 | DEBUGSPEW_CODE(fprintf( stderr, "FUNCTION %s\n", name)); | ||
1695 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1529 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1696 | copy_cached_func(); // ... f | 1530 | copy_cached_func(); // L2: ... f |
1697 | } | 1531 | } |
1698 | STACK_CHECK(L2, 1); | 1532 | STACK_CHECK(L2, 1); |
1699 | STACK_CHECK(L1, 0); | 1533 | STACK_CHECK(L1, 0); |
@@ -1704,8 +1538,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1704 | 1538 | ||
1705 | [[nodiscard]] bool InterCopyContext::inter_copy_table() const | 1539 | [[nodiscard]] bool InterCopyContext::inter_copy_table() const |
1706 | { | 1540 | { |
1707 | if (vt == VT::KEY) | 1541 | if (vt == VT::KEY) { |
1708 | { | ||
1709 | return false; | 1542 | return false; |
1710 | } | 1543 | } |
1711 | 1544 | ||
@@ -1717,24 +1550,22 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1717 | * First, let's try to see if this table is special (aka is it some table that we registered in our lookup databases during module registration?) | 1550 | * First, let's try to see if this table is special (aka is it some table that we registered in our lookup databases during module registration?) |
1718 | * Note that this table CAN be a module table, but we just didn't register it, in which case we'll send it through the table cloning mechanism | 1551 | * Note that this table CAN be a module table, but we just didn't register it, in which case we'll send it through the table cloning mechanism |
1719 | */ | 1552 | */ |
1720 | if (lookup_table()) | 1553 | if (lookup_table()) { |
1721 | { | ||
1722 | LUA_ASSERT(L1, lua_istable(L2, -1) || (lua_tocfunction(L2, -1) == table_lookup_sentinel)); // from lookup data. can also be table_lookup_sentinel if this is a table we know | 1554 | LUA_ASSERT(L1, lua_istable(L2, -1) || (lua_tocfunction(L2, -1) == table_lookup_sentinel)); // from lookup data. can also be table_lookup_sentinel if this is a table we know |
1723 | return true; | 1555 | return true; |
1724 | } | 1556 | } |
1725 | 1557 | ||
1726 | /* Check if we've already copied the same table from 'L1' (during this transmission), and | 1558 | /* Check if we've already copied the same table from 'L1' (during this transmission), and |
1727 | * reuse the old copy. This allows table upvalues shared by multiple | 1559 | * reuse the old copy. This allows table upvalues shared by multiple |
1728 | * local functions to point to the same table, also in the target. | 1560 | * local functions to point to the same table, also in the target. |
1729 | * Also, this takes care of cyclic tables and multiple references | 1561 | * Also, this takes care of cyclic tables and multiple references |
1730 | * to the same subtable. | 1562 | * to the same subtable. |
1731 | * | 1563 | * |
1732 | * Note: Even metatables need to go through this test; to detect | 1564 | * Note: Even metatables need to go through this test; to detect |
1733 | * loops such as those in required module tables (getmetatable(lanes).lanes == lanes) | 1565 | * loops such as those in required module tables (getmetatable(lanes).lanes == lanes) |
1734 | */ | 1566 | */ |
1735 | if (push_cached_table(L2, L2_cache_i, L1, L1_i)) | 1567 | if (push_cached_table(L2, L2_cache_i, L1, L1_i)) { |
1736 | { | 1568 | LUA_ASSERT(L1, lua_istable(L2, -1)); // from cache |
1737 | LUA_ASSERT(L1, lua_istable(L2, -1)); // from cache | ||
1738 | return true; | 1569 | return true; |
1739 | } | 1570 | } |
1740 | LUA_ASSERT(L1, lua_istable(L2, -1)); | 1571 | LUA_ASSERT(L1, lua_istable(L2, -1)); |
@@ -1743,19 +1574,17 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1743 | STACK_GROW(L2, 2); | 1574 | STACK_GROW(L2, 2); |
1744 | 1575 | ||
1745 | lua_pushnil(L1); // start iteration | 1576 | lua_pushnil(L1); // start iteration |
1746 | while (lua_next(L1, L1_i)) | 1577 | while (lua_next(L1, L1_i)) { |
1747 | { | ||
1748 | // need a function to prevent overflowing the stack with verboseErrors-induced alloca() | 1578 | // need a function to prevent overflowing the stack with verboseErrors-induced alloca() |
1749 | inter_copy_keyvaluepair(); | 1579 | inter_copy_keyvaluepair(); |
1750 | lua_pop(L1, 1); // pop value (next round) | 1580 | lua_pop(L1, 1); // pop value (next round) |
1751 | } | 1581 | } |
1752 | STACK_CHECK(L1, 0); | 1582 | STACK_CHECK(L1, 0); |
1753 | STACK_CHECK(L2, 1); | 1583 | STACK_CHECK(L2, 1); |
1754 | 1584 | ||
1755 | // Metatables are expected to be immutable, and copied only once. | 1585 | // Metatables are expected to be immutable, and copied only once. |
1756 | if (push_cached_metatable()) // ... t mt? | 1586 | if (push_cached_metatable()) { // L2: ... t mt? |
1757 | { | 1587 | lua_setmetatable(L2, -2); // L2: ... t |
1758 | lua_setmetatable(L2, -2); // ... t | ||
1759 | } | 1588 | } |
1760 | STACK_CHECK(L2, 1); | 1589 | STACK_CHECK(L2, 1); |
1761 | STACK_CHECK(L1, 0); | 1590 | STACK_CHECK(L1, 0); |
@@ -1786,8 +1615,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1786 | 1615 | ||
1787 | [[nodiscard]] bool InterCopyContext::inter_copy_nil() const | 1616 | [[nodiscard]] bool InterCopyContext::inter_copy_nil() const |
1788 | { | 1617 | { |
1789 | if (vt == VT::KEY) | 1618 | if (vt == VT::KEY) { |
1790 | { | ||
1791 | return false; | 1619 | return false; |
1792 | } | 1620 | } |
1793 | lua_pushnil(L2); | 1621 | lua_pushnil(L2); |
@@ -1798,15 +1626,13 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1798 | 1626 | ||
1799 | [[nodiscard]] bool InterCopyContext::inter_copy_number() const | 1627 | [[nodiscard]] bool InterCopyContext::inter_copy_number() const |
1800 | { | 1628 | { |
1801 | /* LNUM patch support (keeping integer accuracy) */ | 1629 | // LNUM patch support (keeping integer accuracy) |
1802 | #if defined LUA_LNUM || LUA_VERSION_NUM >= 503 | 1630 | #if defined LUA_LNUM || LUA_VERSION_NUM >= 503 |
1803 | if (lua_isinteger(L1, L1_i)) | 1631 | if (lua_isinteger(L1, L1_i)) { |
1804 | { | ||
1805 | lua_Integer const v{ lua_tointeger(L1, L1_i) }; | 1632 | lua_Integer const v{ lua_tointeger(L1, L1_i) }; |
1806 | DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", v)); | 1633 | DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", v)); |
1807 | lua_pushinteger(L2, v); | 1634 | lua_pushinteger(L2, v); |
1808 | } | 1635 | } else |
1809 | else | ||
1810 | #endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 | 1636 | #endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 |
1811 | { | 1637 | { |
1812 | lua_Number const v{ lua_tonumber(L1, L1_i) }; | 1638 | lua_Number const v{ lua_tonumber(L1, L1_i) }; |
@@ -1830,63 +1656,78 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1830 | // ################################################################################################# | 1656 | // ################################################################################################# |
1831 | 1657 | ||
1832 | /* | 1658 | /* |
1833 | * Copies a value from 'L1' state (at index 'i') to 'L2' state. Does not remove | 1659 | * Copies a value from 'L1' state (at index 'i') to 'L2' state. Does not remove |
1834 | * the original value. | 1660 | * the original value. |
1835 | * | 1661 | * |
1836 | * NOTE: Both the states must be solely in the current OS thread's possession. | 1662 | * NOTE: Both the states must be solely in the current OS thread's possession. |
1837 | * | 1663 | * |
1838 | * 'i' is an absolute index (no -1, ...) | 1664 | * 'i' is an absolute index (no -1, ...) |
1839 | * | 1665 | * |
1840 | * Returns true if value was pushed, false if its type is non-supported. | 1666 | * Returns true if value was pushed, false if its type is non-supported. |
1841 | */ | 1667 | */ |
1842 | [[nodiscard]] bool InterCopyContext::inter_copy_one() const | 1668 | [[nodiscard]] bool InterCopyContext::inter_copy_one() const |
1843 | { | 1669 | { |
1844 | static constexpr int kPODmask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING); | 1670 | static constexpr int kPODmask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING); |
1845 | STACK_GROW(L2, 1); | 1671 | STACK_GROW(L2, 1); |
1846 | STACK_CHECK_START_REL(L1, 0); // L1 // L2 | 1672 | STACK_CHECK_START_REL(L1, 0); |
1847 | STACK_CHECK_START_REL(L2, 0); // L1 // L2 | 1673 | STACK_CHECK_START_REL(L2, 0); |
1848 | 1674 | ||
1849 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END)); | 1675 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END(U))); |
1850 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1676 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1851 | 1677 | ||
1852 | LuaType val_type{ lua_type_as_enum(L1, L1_i) }; | 1678 | LuaType val_type{ lua_type_as_enum(L1, L1_i) }; |
1853 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s %s: " INDENT_END, lua_type_names[static_cast<int>(val_type)], vt_names[static_cast<int>(vt)])); | 1679 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s %s: " INDENT_END(U), lua_type_names[static_cast<int>(val_type)], vt_names[static_cast<int>(vt)])); |
1854 | 1680 | ||
1855 | // Non-POD can be skipped if its metatable contains { __lanesignore = true } | 1681 | // Non-POD can be skipped if its metatable contains { __lanesignore = true } |
1856 | if (((1 << static_cast<int>(val_type)) & kPODmask) == 0) | 1682 | if (((1 << static_cast<int>(val_type)) & kPODmask) == 0) { |
1857 | { | 1683 | if (lua_getmetatable(L1, L1_i)) { // L1: ... mt |
1858 | if (lua_getmetatable(L1, L1_i)) // ... mt | 1684 | lua_getfield(L1, -1, "__lanesignore"); // L1: ... mt ignore? |
1859 | { | 1685 | if (lua_isboolean(L1, -1) && lua_toboolean(L1, -1)) { |
1860 | lua_getfield(L1, -1, "__lanesignore"); // ... mt ignore? | 1686 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END(U))); |
1861 | if (lua_isboolean(L1, -1) && lua_toboolean(L1, -1)) | ||
1862 | { | ||
1863 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END)); | ||
1864 | val_type = LuaType::NIL; | 1687 | val_type = LuaType::NIL; |
1865 | } | 1688 | } |
1866 | lua_pop(L1, 2); // ... | 1689 | lua_pop(L1, 2); // L1: ... |
1867 | } | 1690 | } |
1868 | } | 1691 | } |
1869 | STACK_CHECK(L1, 0); | 1692 | STACK_CHECK(L1, 0); |
1870 | 1693 | ||
1871 | /* Lets push nil to L2 if the object should be ignored */ | 1694 | // Lets push nil to L2 if the object should be ignored |
1872 | bool ret{ true }; | 1695 | bool ret{ true }; |
1873 | switch (val_type) | 1696 | switch (val_type) { |
1874 | { | 1697 | // Basic types allowed both as values, and as table keys |
1875 | // Basic types allowed both as values, and as table keys | 1698 | case LuaType::BOOLEAN: |
1876 | case LuaType::BOOLEAN: ret = inter_copy_boolean(); break; | 1699 | ret = inter_copy_boolean(); |
1877 | case LuaType::NUMBER: ret = inter_copy_number(); break; | 1700 | break; |
1878 | case LuaType::STRING: ret = inter_copy_string(); break; | 1701 | case LuaType::NUMBER: |
1879 | case LuaType::LIGHTUSERDATA: ret = inter_copy_lightuserdata();break; | 1702 | ret = inter_copy_number(); |
1880 | 1703 | break; | |
1881 | // The following types are not allowed as table keys | 1704 | case LuaType::STRING: |
1882 | case LuaType::USERDATA: ret = inter_copy_userdata(); break; | 1705 | ret = inter_copy_string(); |
1883 | case LuaType::NIL: ret = inter_copy_nil(); break; | 1706 | break; |
1884 | case LuaType::FUNCTION: ret = inter_copy_function(); break; | 1707 | case LuaType::LIGHTUSERDATA: |
1885 | case LuaType::TABLE: ret = inter_copy_table(); break; | 1708 | ret = inter_copy_lightuserdata(); |
1886 | 1709 | break; | |
1887 | // The following types cannot be copied | 1710 | |
1888 | case LuaType::CDATA: [[fallthrough]]; | 1711 | // The following types are not allowed as table keys |
1889 | case LuaType::THREAD: ret = false; break; | 1712 | case LuaType::USERDATA: |
1713 | ret = inter_copy_userdata(); | ||
1714 | break; | ||
1715 | case LuaType::NIL: | ||
1716 | ret = inter_copy_nil(); | ||
1717 | break; | ||
1718 | case LuaType::FUNCTION: | ||
1719 | ret = inter_copy_function(); | ||
1720 | break; | ||
1721 | case LuaType::TABLE: | ||
1722 | ret = inter_copy_table(); | ||
1723 | break; | ||
1724 | |||
1725 | // The following types cannot be copied | ||
1726 | case LuaType::CDATA: | ||
1727 | [[fallthrough]]; | ||
1728 | case LuaType::THREAD: | ||
1729 | ret = false; | ||
1730 | break; | ||
1890 | } | 1731 | } |
1891 | 1732 | ||
1892 | STACK_CHECK(L2, ret ? 1 : 0); | 1733 | STACK_CHECK(L2, ret ? 1 : 0); |
@@ -1902,14 +1743,13 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1902 | { | 1743 | { |
1903 | LUA_ASSERT(L1, vt == VT::NORMAL); | 1744 | LUA_ASSERT(L1, vt == VT::NORMAL); |
1904 | 1745 | ||
1905 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy()\n" INDENT_END)); | 1746 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy()\n" INDENT_END(U))); |
1906 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1747 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1907 | 1748 | ||
1908 | int const top_L1{ lua_gettop(L1) }; | 1749 | int const top_L1{ lua_gettop(L1) }; |
1909 | if (n_ > top_L1) | 1750 | if (n_ > top_L1) { |
1910 | { | ||
1911 | // requesting to copy more than is available? | 1751 | // requesting to copy more than is available? |
1912 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END)); | 1752 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END(U))); |
1913 | return InterCopyResult::NotEnoughValues; | 1753 | return InterCopyResult::NotEnoughValues; |
1914 | } | 1754 | } |
1915 | 1755 | ||
@@ -1917,35 +1757,31 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1917 | STACK_GROW(L2, n_ + 1); | 1757 | STACK_GROW(L2, n_ + 1); |
1918 | 1758 | ||
1919 | /* | 1759 | /* |
1920 | * Make a cache table for the duration of this copy. Collects tables and | 1760 | * Make a cache table for the duration of this copy. Collects tables and |
1921 | * function entries, avoiding the same entries to be passed on as multiple | 1761 | * function entries, avoiding the same entries to be passed on as multiple |
1922 | * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner! | 1762 | * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner! |
1923 | */ | 1763 | */ |
1924 | int const top_L2{ lua_gettop(L2) }; // ... | 1764 | int const top_L2{ lua_gettop(L2) }; // L2: ... |
1925 | lua_newtable(L2); // ... cache | 1765 | lua_newtable(L2); // L2: ... cache |
1926 | 1766 | ||
1927 | char tmpBuf[16]; | 1767 | char tmpBuf[16]; |
1928 | char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" }; | 1768 | char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" }; |
1929 | InterCopyContext c{ U, L2, L1, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode, pBuf }; | 1769 | InterCopyContext c{ U, L2, L1, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode, pBuf }; |
1930 | bool copyok{ true }; | 1770 | bool copyok{ true }; |
1931 | STACK_CHECK_START_REL(L1, 0); | 1771 | STACK_CHECK_START_REL(L1, 0); |
1932 | for (int i{ top_L1 - n_ + 1 }, j{ 1 }; i <= top_L1; ++i, ++j) | 1772 | for (int i{ top_L1 - n_ + 1 }, j{ 1 }; i <= top_L1; ++i, ++j) { |
1933 | { | 1773 | if (U->verboseErrors) { |
1934 | if (U->verboseErrors) | ||
1935 | { | ||
1936 | sprintf(tmpBuf, "arg_%d", j); | 1774 | sprintf(tmpBuf, "arg_%d", j); |
1937 | } | 1775 | } |
1938 | c.L1_i = SourceIndex{ i }; | 1776 | c.L1_i = SourceIndex{ i }; |
1939 | copyok = c.inter_copy_one(); // ... cache {}n | 1777 | copyok = c.inter_copy_one(); // L2: ... cache {}n |
1940 | if (!copyok) | 1778 | if (!copyok) { |
1941 | { | ||
1942 | break; | 1779 | break; |
1943 | } | 1780 | } |
1944 | } | 1781 | } |
1945 | STACK_CHECK(L1, 0); | 1782 | STACK_CHECK(L1, 0); |
1946 | 1783 | ||
1947 | if (copyok) | 1784 | if (copyok) { |
1948 | { | ||
1949 | STACK_CHECK(L2, n_ + 1); | 1785 | STACK_CHECK(L2, n_ + 1); |
1950 | // Remove the cache table. Persistent caching would cause i.e. multiple | 1786 | // Remove the cache table. Persistent caching would cause i.e. multiple |
1951 | // messages passed in the same table to use the same table also in receiving end. | 1787 | // messages passed in the same table to use the same table also in receiving end. |
@@ -1964,7 +1800,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1964 | [[nodiscard]] InterCopyResult InterCopyContext::inter_move(int n_) const | 1800 | [[nodiscard]] InterCopyResult InterCopyContext::inter_move(int n_) const |
1965 | { | 1801 | { |
1966 | InterCopyResult const ret{ inter_copy(n_) }; | 1802 | InterCopyResult const ret{ inter_copy(n_) }; |
1967 | lua_pop( L1, n_); | 1803 | lua_pop(L1, n_); |
1968 | return ret; | 1804 | return ret; |
1969 | } | 1805 | } |
1970 | 1806 | ||
@@ -1976,22 +1812,19 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1976 | // else raise an error in L1 | 1812 | // else raise an error in L1 |
1977 | [[nodiscard]] InterCopyResult InterCopyContext::inter_copy_package() const | 1813 | [[nodiscard]] InterCopyResult InterCopyContext::inter_copy_package() const |
1978 | { | 1814 | { |
1979 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy_package()\n" INDENT_END)); | 1815 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy_package()\n" INDENT_END(U))); |
1980 | 1816 | ||
1981 | class OnExit | 1817 | class OnExit |
1982 | { | 1818 | { |
1983 | private: | 1819 | private: |
1984 | |||
1985 | lua_State* const L2; | 1820 | lua_State* const L2; |
1986 | int const top_L2; | 1821 | int const top_L2; |
1987 | DEBUGSPEW_CODE(DebugSpewIndentScope m_scope); | 1822 | DEBUGSPEW_CODE(DebugSpewIndentScope m_scope); |
1988 | 1823 | ||
1989 | public: | 1824 | public: |
1990 | |||
1991 | OnExit(Universe* U_, lua_State* L2_) | 1825 | OnExit(Universe* U_, lua_State* L2_) |
1992 | : L2{ L2_ } | 1826 | : L2{ L2_ } |
1993 | , top_L2{ lua_gettop(L2) } | 1827 | , top_L2{ lua_gettop(L2) } DEBUGSPEW_COMMA_PARAM(m_scope{ U_ }) |
1994 | DEBUGSPEW_COMMA_PARAM(m_scope{ U_ }) | ||
1995 | { | 1828 | { |
1996 | } | 1829 | } |
1997 | 1830 | ||
@@ -2002,21 +1835,18 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
2002 | } onExit{ U, L2 }; | 1835 | } onExit{ U, L2 }; |
2003 | 1836 | ||
2004 | STACK_CHECK_START_REL(L1, 0); | 1837 | STACK_CHECK_START_REL(L1, 0); |
2005 | if (lua_type_as_enum(L1, L1_i) != LuaType::TABLE) | 1838 | if (lua_type_as_enum(L1, L1_i) != LuaType::TABLE) { |
2006 | { | ||
2007 | lua_pushfstring(L1, "expected package as table, got %s", luaL_typename(L1, L1_i)); | 1839 | lua_pushfstring(L1, "expected package as table, got %s", luaL_typename(L1, L1_i)); |
2008 | STACK_CHECK(L1, 1); | 1840 | STACK_CHECK(L1, 1); |
2009 | // raise the error when copying from lane to lane, else just leave it on the stack to be raised later | 1841 | // raise the error when copying from lane to lane, else just leave it on the stack to be raised later |
2010 | if (mode == LookupMode::LaneBody) | 1842 | if (mode == LookupMode::LaneBody) { |
2011 | { | ||
2012 | raise_lua_error(L1); | 1843 | raise_lua_error(L1); |
2013 | } | 1844 | } |
2014 | return InterCopyResult::Error; | 1845 | return InterCopyResult::Error; |
2015 | } | 1846 | } |
2016 | lua_getglobal(L2, "package"); // TODO: use _R._LOADED.package instead of _G.package | 1847 | lua_getglobal(L2, "package"); // TODO: use _R._LOADED.package instead of _G.package |
2017 | if (lua_isnil(L2, -1)) // package library not loaded: do nothing | 1848 | if (lua_isnil(L2, -1)) { // package library not loaded: do nothing |
2018 | { | 1849 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END(U))); |
2019 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END)); | ||
2020 | STACK_CHECK(L1, 0); | 1850 | STACK_CHECK(L1, 0); |
2021 | return InterCopyResult::Success; | 1851 | return InterCopyResult::Success; |
2022 | } | 1852 | } |
@@ -2027,35 +1857,26 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
2027 | // users should provide an on_state_create function to setup custom loaders instead | 1857 | // users should provide an on_state_create function to setup custom loaders instead |
2028 | // don't copy package.preload in keeper states (they don't know how to translate functions) | 1858 | // don't copy package.preload in keeper states (they don't know how to translate functions) |
2029 | char const* entries[] = { "path", "cpath", (mode == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr }; | 1859 | char const* entries[] = { "path", "cpath", (mode == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr }; |
2030 | for (char const* const entry : entries) | 1860 | for (char const* const entry : entries) { |
2031 | { | 1861 | if (!entry) { |
2032 | if (!entry) | ||
2033 | { | ||
2034 | continue; | 1862 | continue; |
2035 | } | 1863 | } |
2036 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END, entry)); | 1864 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END(U), entry)); |
2037 | lua_getfield(L1, L1_i, entry); | 1865 | lua_getfield(L1, L1_i, entry); |
2038 | if (lua_isnil(L1, -1)) | 1866 | if (lua_isnil(L1, -1)) { |
2039 | { | ||
2040 | lua_pop(L1, 1); | 1867 | lua_pop(L1, 1); |
2041 | } | 1868 | } else { |
2042 | else | ||
2043 | { | ||
2044 | { | 1869 | { |
2045 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1870 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
2046 | result = inter_move(1); // moves the entry to L2 | 1871 | result = inter_move(1); // moves the entry to L2 |
2047 | STACK_CHECK(L1, 0); | 1872 | STACK_CHECK(L1, 0); |
2048 | } | 1873 | } |
2049 | if (result == InterCopyResult::Success) | 1874 | if (result == InterCopyResult::Success) { |
2050 | { | ||
2051 | lua_setfield(L2, -2, entry); // set package[entry] | 1875 | lua_setfield(L2, -2, entry); // set package[entry] |
2052 | } | 1876 | } else { |
2053 | else | ||
2054 | { | ||
2055 | lua_pushfstring(L1, "failed to copy package entry %s", entry); | 1877 | lua_pushfstring(L1, "failed to copy package entry %s", entry); |
2056 | // raise the error when copying from lane to lane, else just leave it on the stack to be raised later | 1878 | // raise the error when copying from lane to lane, else just leave it on the stack to be raised later |
2057 | if (mode == LookupMode::LaneBody) | 1879 | if (mode == LookupMode::LaneBody) { |
2058 | { | ||
2059 | raise_lua_error(L1); | 1880 | raise_lua_error(L1); |
2060 | } | 1881 | } |
2061 | lua_pop(L1, 1); | 1882 | lua_pop(L1, 1); |
diff --git a/src/tools.h b/src/tools.h index b58ae00..b22e4dd 100644 --- a/src/tools.h +++ b/src/tools.h | |||
@@ -55,8 +55,8 @@ class InterCopyContext | |||
55 | [[nodiscard]] bool push_cached_metatable() const; | 55 | [[nodiscard]] bool push_cached_metatable() const; |
56 | 56 | ||
57 | // for use in inter_copy_userdata | 57 | // for use in inter_copy_userdata |
58 | [[nodiscard]] bool copyclone() const; | 58 | [[nodiscard]] bool copyClonable() const; |
59 | [[nodiscard]] bool copydeep() const; | 59 | [[nodiscard]] bool copyDeep() const; |
60 | 60 | ||
61 | // copying a single Lua stack item | 61 | // copying a single Lua stack item |
62 | [[nodiscard]] bool inter_copy_boolean() const; | 62 | [[nodiscard]] bool inter_copy_boolean() const; |
@@ -79,8 +79,8 @@ class InterCopyContext | |||
79 | 79 | ||
80 | [[nodiscard]] int luaG_nameof(lua_State* L_); | 80 | [[nodiscard]] int luaG_nameof(lua_State* L_); |
81 | 81 | ||
82 | void populate_func_lookup_table(lua_State* L_, int _i, char const* _name); | 82 | void populate_func_lookup_table(lua_State* L_, int i_, char const* name_); |
83 | void initialize_allocator_function(Universe* U, lua_State* L_); | 83 | void initialize_allocator_function(Universe* U_, lua_State* L_); |
84 | 84 | ||
85 | // ################################################################################################# | 85 | // ################################################################################################# |
86 | 86 | ||
diff --git a/src/universe.cpp b/src/universe.cpp index 062d552..4dce427 100644 --- a/src/universe.cpp +++ b/src/universe.cpp | |||
@@ -60,8 +60,7 @@ Universe::Universe() | |||
60 | // the launched threads (even -2). | 60 | // the launched threads (even -2). |
61 | // | 61 | // |
62 | #ifdef LINUX_SCHED_RR | 62 | #ifdef LINUX_SCHED_RR |
63 | if (m_sudo) | 63 | if (m_sudo) { |
64 | { | ||
65 | struct sched_param sp; | 64 | struct sched_param sp; |
66 | sp.sched_priority = _PRIO_0; | 65 | sp.sched_priority = _PRIO_0; |
67 | PT_CALL(pthread_setschedparam(pthread_self(), SCHED_RR, &sp)); | 66 | PT_CALL(pthread_setschedparam(pthread_self(), SCHED_RR, &sp)); |
@@ -87,11 +86,11 @@ Universe* universe_create(lua_State* L_) | |||
87 | 86 | ||
88 | // ################################################################################################# | 87 | // ################################################################################################# |
89 | 88 | ||
90 | void universe_store(lua_State* L_, Universe* U) | 89 | void universe_store(lua_State* L_, Universe* U_) |
91 | { | 90 | { |
92 | LUA_ASSERT(L_, !U || universe_get(L_) == nullptr); | 91 | LUA_ASSERT(L_, !U_ || universe_get(L_) == nullptr); |
93 | STACK_CHECK_START_REL(L_, 0); | 92 | STACK_CHECK_START_REL(L_, 0); |
94 | kUniverseLightRegKey.setValue(L_, [U](lua_State* L_) { U ? lua_pushlightuserdata(L_, U) : lua_pushnil(L_); }); | 93 | kUniverseLightRegKey.setValue(L_, [U = U_](lua_State* L_) { U ? lua_pushlightuserdata(L_, U) : lua_pushnil(L_); }); |
95 | STACK_CHECK(L_, 0); | 94 | STACK_CHECK(L_, 0); |
96 | } | 95 | } |
97 | 96 | ||
diff --git a/src/universe.h b/src/universe.h index 2fd487e..c6c9c03 100644 --- a/src/universe.h +++ b/src/universe.h | |||
@@ -184,7 +184,7 @@ class Universe | |||
184 | 184 | ||
185 | [[nodiscard]] Universe* universe_get(lua_State* L_); | 185 | [[nodiscard]] Universe* universe_get(lua_State* L_); |
186 | [[nodiscard]] Universe* universe_create(lua_State* L_); | 186 | [[nodiscard]] Universe* universe_create(lua_State* L_); |
187 | void universe_store(lua_State* L_, Universe* U); | 187 | void universe_store(lua_State* L_, Universe* U_); |
188 | 188 | ||
189 | // ################################################################################################# | 189 | // ################################################################################################# |
190 | 190 | ||