aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-04-30 11:44:53 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-04-30 11:44:53 +0200
commit92ea4d16a274b4a7db0206fd74891a555f6501c9 (patch)
tree358a1b98f6d5c0985c2eaabc3c19b1b7db7327ba /src
parentd60a9fb712886880ec9630e744e1258ec3ed19b1 (diff)
downloadlanes-92ea4d16a274b4a7db0206fd74891a555f6501c9.tar.gz
lanes-92ea4d16a274b4a7db0206fd74891a555f6501c9.tar.bz2
lanes-92ea4d16a274b4a7db0206fd74891a555f6501c9.zip
Progressively applying the coding rules
Diffstat (limited to 'src')
-rw-r--r--src/cancel.cpp9
-rw-r--r--src/deep.cpp330
-rw-r--r--src/keeper.cpp655
-rw-r--r--src/keeper.h6
-rw-r--r--src/lanes.cpp1037
-rw-r--r--src/linda.cpp389
-rw-r--r--src/lindafactory.cpp24
-rw-r--r--src/macros_and_utils.h2
-rw-r--r--src/state.cpp327
-rw-r--r--src/state.h10
-rw-r--r--src/threading.cpp351
-rw-r--r--src/tools.cpp1501
-rw-r--r--src/tools.h8
-rw-r--r--src/universe.cpp9
-rw-r--r--src/universe.h2
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
57static constexpr RegistryUniqueKey kDeepLookupRegKey{ 0xC6788345703C6059ull }; 57static 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 */
63static constexpr RegistryUniqueKey kDeepProxyCacheRegKey{ 0xEBCD49AE1A3DD35Eull }; 63static 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 */
69static void set_deep_lookup(lua_State* L_) 69static 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 */
90static void get_deep_lookup(lua_State* L_) 90static 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_)
198char const* DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int nuv_, LookupMode mode_) 189char 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 */
350int DeepFactory::pushDeepUserdata(DestState L_, int nuv_) const 319int 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 */
395DeepPrelude* DeepFactory::toDeep(lua_State* L_, int index_) const 360DeepPrelude* 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 @@
56class keeper_fifo 56class 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_)
135static void fifo_peek(lua_State* L_, keeper_fifo* fifo_, int count_) 132static 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)
148static void fifo_pop( lua_State* L_, keeper_fifo* fifo_, int count_) 144static 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_)
283int keepercall_send(lua_State* L_) 275int 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_)
320int keepercall_receive(lua_State* L_) 308int 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_)
355int keepercall_receive_batched(lua_State* L_) 339int 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_)
389int keepercall_limit(lua_State* L_) 367int 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
425int keepercall_set(lua_State* L_) 401int 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_)
498int keepercall_get(lua_State* L_) 465int 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 [, ...]]
525int keepercall_count(lua_State* L_) 490int 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
607void close_keepers(Universe* U) 560void 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 */
654void init_keepers(Universe* U, lua_State* L_) 601void 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
768void Linda::releaseKeeper(Keeper* K_) const 706void 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
779void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mode_) 716void 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 */
814KeeperCallResult keeper_call(Universe* U, KeeperState K, keeper_api_t func_, lua_State* L_, void* linda_, int starting_index_) 745KeeperCallResult 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
40static constexpr UniqueKey kNilSentinel{ 0xC457D4EDDB05B5E4ull, "lanes.null" }; 40static constexpr UniqueKey kNilSentinel{ 0xC457D4EDDB05B5E4ull, "lanes.null" };
41 41
42void init_keepers(Universe* U, lua_State* L_); 42void init_keepers(Universe* U_, lua_State* L_);
43void close_keepers(Universe* U); 43void close_keepers(Universe* U_);
44 44
45void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mode_); 45void 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
61using KeeperCallResult = Unique<std::optional<int>>; 61using 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
174bool Lane::waitForCompletion(lua_Duration duration_) 172bool 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
188static void lane_main(Lane* lane); 187static void lane_main(Lane* lane);
189void Lane::startThread(int priority_) 188void 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
234static constexpr RegistryUniqueKey kFinalizerRegKey{ 0xFE936BFAA718FEEAull }; 236static 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.
258static void push_finalizers_table(lua_State* L_) 258static 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)
300static void push_stack_trace(lua_State* L_, int rc_, int stk_base_) 298static 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//
594LUAG_FUNC( set_singlethreaded) 563LUAG_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
636static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2ull }; // used as registry key 604static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2ull }; // used as registry key
637 605
638LUAG_FUNC( set_error_reporting) 606LUAG_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)
779LUAG_FUNC(set_thread_affinity) 740LUAG_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
799static struct errcode_name s_errcodes[] = 761static 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};
809static char const* get_errcode_name( int _code) 770static 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
822static void lane_main(Lane* lane_) 783static 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
920LUAG_FUNC(require) 877LUAG_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//
976LUAG_FUNC(lane_new) 933LUAG_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 */
1640LUAG_FUNC(now_secs) 1541LUAG_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*/
1654LUAG_FUNC(wakeup_conv) 1553LUAG_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
1707extern int LG_linda(lua_State* L_); 1605extern int LG_linda(lua_State* L_);
1708static struct luaL_Reg const lanes_functions[] = 1606static 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
1912void signal_handler(int signal) 1807void 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!
1923static volatile long s_ecoc_initCount = 0; 1819static volatile long s_ecoc_initCount = 0;
1924static volatile int s_ecoc_go_ahead = 0; 1820static volatile int s_ecoc_go_ahead = 0;
1925static void EnableCrashingOnCrashes(void) 1821static 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
1964LANES_API int luaopen_lanes_core( lua_State* L_) 1853// #################################################################################################
1854
1855LANES_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
2020LANES_API void luaopen_lanes_embedded( lua_State* L_, lua_CFunction _luaopen_lanes) 1911LANES_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
63Linda::~Linda() 63Linda::~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()
74void Linda::setName(char const* name_, size_t len_) 73void 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
99char const* Linda::getName() const 94char 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
130static void check_key_types(lua_State* L_, int start_, int end_) 122static 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 */
206LUAG_FUNC(linda_send) 191LUAG_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 */
369LUAG_FUNC(linda_receive) 334LUAG_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 */
530LUAG_FUNC(linda_set) 476LUAG_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 */
586LUAG_FUNC(linda_count) 524LUAG_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 */
608LUAG_FUNC(linda_get) 545LUAG_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 */
649LUAG_FUNC(linda_limit) 581LUAG_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 */
692LUAG_FUNC(linda_cancel) 619LUAG_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 */
736LUAG_FUNC(linda_deep) 654LUAG_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
753template <bool OPT> 671template <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
771LUAG_FUNC(linda_tostring) 690LUAG_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 */
785LUAG_FUNC(linda_concat) 704LUAG_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 */
813LUAG_FUNC(linda_dump) 729LUAG_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 */
87void serialize_require(DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L_) 86void 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
124static luaL_Reg const libs[] = 120static 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
162static void open1lib(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_, char const* name_, size_t len_) 157static 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)
193static void copy_one_time_settings(Universe* U, SourceState L1, DestState L2) 184static 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
218void initialize_on_state_create( Universe* U, lua_State* L_) 208void 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
251lua_State* create_state(Universe* U, lua_State* from_) 236lua_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
284void call_on_state_create(Universe* U, lua_State* L_, lua_State* from_, LookupMode mode_) 265void 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 */
334lua_State* luaG_newstate(Universe* U, SourceState from_, char const* libs_) 309lua_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 @@
6enum class LookupMode; 6enum class LookupMode;
7class Universe; 7class Universe;
8 8
9void serialize_require(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_); 9void 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
18void initialize_on_state_create(Universe* U, lua_State* L_); 18void initialize_on_state_create(Universe* U_, lua_State* L_);
19void call_on_state_create(Universe* U, lua_State* L_, lua_State* from_, LookupMode mode_); 19void 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) 88static 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
112static int const gs_prio_remap[] = 110static 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[] =
125void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) 122void 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_)
136void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, [[maybe_unused]] bool sudo_) 132void 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
147void THREAD_SET_AFFINITY(unsigned int aff) 142void 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)
161typedef struct tagTHREADNAME_INFO 155typedef 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
223static void _PT_FAIL( int rc, const char *name, const char *file, int line ) 213static 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
239static int const gs_prio_remap[] = 233static 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 */
48void push_registry_subtable_mode(lua_State* L_, RegistryUniqueKey key_, const char* mode_) 46void 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 */
84void push_registry_subtable(lua_State* L_, RegistryUniqueKey key_) 80void 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
92extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_) 88extern "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?
119void initialize_allocator_function(Universe* U, lua_State* L_) 112void 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
209FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) 190FuncSubType 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 */
272static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_, int _ctx_base, int _depth) 248static 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
347static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_, int _ctx_base, int _i, int _depth) 317static 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 */
472void populate_func_lookup_table(lua_State* L_, int i_, char const* name_) 428void 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
537static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; 486static 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) 847int luaG_nameof(lua_State* L_)
935 */
936int 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 */
985void InterCopyContext::lookup_native_func() const 891void 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()
1064static char const* lua_type_names[] = 962static 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};
1078static char const* vt_names[] = 975static 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
1105void InterCopyContext::copy_func() const 1002void 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 */
1257void InterCopyContext::copy_cached_func() const 1141void 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
82void populate_func_lookup_table(lua_State* L_, int _i, char const* _name); 82void populate_func_lookup_table(lua_State* L_, int i_, char const* name_);
83void initialize_allocator_function(Universe* U, lua_State* L_); 83void 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
90void universe_store(lua_State* L_, Universe* U) 89void 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_);
187void universe_store(lua_State* L_, Universe* U); 187void universe_store(lua_State* L_, Universe* U_);
188 188
189// ################################################################################################# 189// #################################################################################################
190 190