From 9c0a1bfc6eea9a2e68de7206d8d5af5188f22e59 Mon Sep 17 00:00:00 2001
From: Benoit Germain
Date: Mon, 6 May 2024 15:37:44 +0200
Subject: new lane generator option opt_tbl.name
---
deep_test/deeptest.lua | 5 +-
docs/index.html | 21 +++++---
src/lanes.cpp | 134 ++++++++++++++++++++++++++++++-------------------
src/lanes.lua | 22 ++++----
src/lanes_private.h | 2 +
5 files changed, 114 insertions(+), 70 deletions(-)
diff --git a/deep_test/deeptest.lua b/deep_test/deeptest.lua
index 09b638c..33de003 100644
--- a/deep_test/deeptest.lua
+++ b/deep_test/deeptest.lua
@@ -7,7 +7,7 @@ local dt = lanes.require "deep_test"
local test_deep = true
local test_clonable = true
local test_uvtype = "string"
-local nupvals = _VERSION == "Lua 5.4" and 3 or 1
+local nupvals = _VERSION == "Lua 5.4" and 2 or 1
local makeUserValue = function( obj_)
if test_uvtype == "string" then
@@ -43,8 +43,7 @@ local performTest = function( obj_)
obj_:setuv( 1, makeUserValue( obj_))
-- lua 5.4 supports multiple uservalues of arbitrary types
if nupvals > 1 then
- -- keep uv #2 as nil
- obj_:setuv( 3, "ENDUV")
+ obj_:setuv( 2, "ENDUV")
end
local t =
diff --git a/docs/index.html b/docs/index.html
index ed1c367..f08947e 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -684,7 +684,7 @@
| table |
Sets the globals table for the launched threads. This can be used for giving them constants. The key/value pairs of table are transfered in the lane globals after the libraries have been loaded and the modules required.
-
+
The global values of different lanes are in no manner connected; modifying one will only affect the particular lane.
|
@@ -698,11 +698,20 @@
These tables are built from the modules listed here. required must be a list of strings, each one being the name of a module to be required. Each module is required with require() before the lanes function is invoked.
So, from the required module's point of view, requiring it manually from inside the lane body or having it required this way doesn't change anything. From the lane body's point of view, the only difference is that a module not creating a global won't be accessible.
Therefore, a lane body will also have to require a module manually, but this won't do anything more (see Lua's require documentation).
-
+
ATTEMPTING TO TRANSFER A FUNCTION REGISTERED BY A MODULE NOT LISTED HERE WILL RAISE AN ERROR.
-
+
+
+ .name
+ |
+ string |
+
+ Name of the lane. If "auto", name is built from ar.short_src:ar.linedefined. Can be changed later from the inside of the lane with set_debug_threadname() (see below).
+ |
+
+
.gc_cb
|
@@ -719,9 +728,9 @@
The priority of lanes generated in the range -3..+3 (default is 0).
These values are a mapping over the actual priority range of the underlying implementation.
-
+
Implementation and dependability of priorities varies by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode.
-
+
A lane can also change its own thread priority dynamically with lanes.set_thread_priority().
|
@@ -732,7 +741,7 @@
table |
Specifying it when libs_str doesn't cause the package library to be loaded will generate an error.
-
+
If not specified, the created lane will receive the current values of package. Only path, cpath, preload and loaders (Lua 5.1)/searchers (Lua 5.2) are transfered.
|
diff --git a/src/lanes.cpp b/src/lanes.cpp
index e09956c..bce75a6 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -201,8 +201,8 @@ void Lane::startThread(int priority_)
*/
#define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it!
-// intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed
-static void securize_debug_threadname(lua_State* L_, Lane* lane_)
+// intern the debug name in the caller lua state so that the pointer remains valid after the lane's state is closed
+void Lane::securizeDebugName(lua_State* L_)
{
STACK_CHECK_START_REL(L_, 0);
STACK_GROW(L_, 3);
@@ -212,8 +212,8 @@ static void securize_debug_threadname(lua_State* L_, Lane* lane_)
// we don't care about the actual key, so long as it's unique and can't collide with anything.
lua_newtable(L_); // L_: lane ... {uv} {}
// Lua 5.1 can't do 'lane_->debugName = lua_pushstring(L_, lane_->debugName);'
- lua_pushstring(L_, lane_->debugName); // L_: lane ... {uv} {} name
- lane_->debugName = lua_tostring(L_, -1);
+ lua_pushstring(L_, debugName); // L_: lane ... {uv} {} name
+ debugName = lua_tostring(L_, -1);
lua_rawset(L_, -3); // L_: lane ... {uv}
lua_pop(L_, 1); // L_: lane
STACK_CHECK(L_, 0);
@@ -674,23 +674,36 @@ LUAG_FUNC(set_error_reporting)
// #################################################################################################
+void Lane::changeDebugName(int nameIdx_)
+{
+ // xxh64 of string "debugName" generated at https://www.pelock.com/products/hash-calculator
+ static constexpr RegistryUniqueKey hidden_regkey{ 0xA194E2645C57F6DDull };
+ nameIdx_ = lua_absindex(L, nameIdx_);
+ luaL_checktype(L, nameIdx_, LUA_TSTRING); // L: ... "name" ...
+ STACK_CHECK_START_REL(L, 0);
+ // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global...
+ hidden_regkey.setValue(L, [nameIdx = nameIdx_](lua_State* L_) { lua_pushvalue(L_, nameIdx); });// L: ... "name" ...
+ // keep a direct pointer on the string
+ debugName = lua_tostring(L, nameIdx_);
+ // to see VM name in Decoda debugger Virtual Machine window
+ lua_pushvalue(L, nameIdx_); // L: ... "name" ... "name"
+ lua_setglobal(L, "decoda_name"); // L: ... "name" ...
+ // and finally set the OS thread name
+ THREAD_SETNAME(debugName);
+ STACK_CHECK(L, 0);
+}
+
+// #################################################################################################
+
+// upvalue #1 is the lane userdata
LUAG_FUNC(set_debug_threadname)
{
- // fnv164 of string "debug_threadname" generated at https://www.pelock.com/products/hash-calculator
- constexpr RegistryUniqueKey hidden_regkey{ 0x79C0669AAAE04440ull };
// C s_lane structure is a light userdata upvalue
Lane* const lane{ lua_tolightuserdata(L_, lua_upvalueindex(1)) };
- luaL_checktype(L_, -1, LUA_TSTRING); // "name"
+ LUA_ASSERT(L_, L_ == lane->L); // this function is exported in a lane's state, therefore it is callable only from inside the Lane's state
lua_settop(L_, 1);
- STACK_CHECK_START_ABS(L_, 1);
- // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global...
- hidden_regkey.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); });
- STACK_CHECK(L_, 1);
- lane->debugName = lua_tostring(L_, -1);
- // keep a direct pointer on the string
- THREAD_SETNAME(lane->debugName);
- // to see VM name in Decoda debugger Virtual Machine window
- lua_setglobal(L_, "decoda_name"); //
+ STACK_CHECK_START_REL(L_, 0);
+ lane->changeDebugName(-1);
STACK_CHECK(L_, 0);
return 0;
}
@@ -911,13 +924,14 @@ static constexpr UniqueKey kLaneGC{ 0x5D6122141727F960ull };
// , [package_tbl]
// , [required_tbl]
// , [gc_cb_func]
+// , [name]
// [, ... args ...])
//
// Upvalues: metatable to use for 'lane_ud'
//
LUAG_FUNC(lane_new)
{
- // first 7 args: func libs priority globals package required gc_cb
+ // first 8 args: func libs priority globals package required gc_cb name
char const* const libs_str{ lua_tostring(L_, 2) };
bool const have_priority{ !lua_isnoneornil(L_, 3) };
int const priority{ have_priority ? static_cast(lua_tointeger(L_, 3)) : kThreadPrioDefault };
@@ -925,8 +939,9 @@ LUAG_FUNC(lane_new)
int const package_idx{ lua_isnoneornil(L_, 5) ? 0 : 5 };
int const required_idx{ lua_isnoneornil(L_, 6) ? 0 : 6 };
int const gc_cb_idx{ lua_isnoneornil(L_, 7) ? 0 : 7 };
+ int const name_idx{ lua_isnoneornil(L_, 8) ? 0 : 8 };
- static constexpr int kFixedArgsIdx{ 7 };
+ static constexpr int kFixedArgsIdx{ 8 };
int const nargs{ lua_gettop(L_) - kFixedArgsIdx };
Universe* const U{ universe_get(L_) };
LUA_ASSERT(L_, nargs >= 0);
@@ -942,7 +957,7 @@ LUAG_FUNC(lane_new)
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END(U)));
// populate with selected libraries at the same time.
- lua_State* const L2{ luaG_newstate(U, SourceState{ L_ }, libs_str) }; // L_: [7 args] ... L2:
+ lua_State* const L2{ luaG_newstate(U, SourceState{ L_ }, libs_str) }; // L_: [8 args] ... L2:
STACK_CHECK_START_REL(L2, 0);
// 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread)
@@ -957,14 +972,16 @@ LUAG_FUNC(lane_new)
lua_State* const m_L;
Lane* m_lane{ nullptr };
int const m_gc_cb_idx;
+ int const m_name_idx;
DEBUGSPEW_CODE(Universe* const U);
DEBUGSPEW_CODE(DebugSpewIndentScope m_scope);
public:
- OnExit(lua_State* L_, Lane* lane_, int gc_cb_idx_ DEBUGSPEW_COMMA_PARAM(Universe* U_))
+ OnExit(lua_State* L_, Lane* lane_, int gc_cb_idx_, int name_idx_ DEBUGSPEW_COMMA_PARAM(Universe* U_))
: m_L{ L_ }
, m_lane{ lane_ }
, m_gc_cb_idx{ gc_cb_idx_ }
+ , m_name_idx{ name_idx_ }
DEBUGSPEW_COMMA_PARAM(U{ U_ })
DEBUGSPEW_COMMA_PARAM(m_scope{ U_ })
{
@@ -1019,6 +1036,24 @@ LUAG_FUNC(lane_new)
}
lua_setiuservalue(m_L, -2, 1); // m_L: ... lane
+
+ lua_State* L2{ m_lane->L };
+ STACK_CHECK_START_REL(L2, 0);
+ char const* const debugName{ (m_name_idx > 0) ? lua_tostring(m_L, m_name_idx) : nullptr };
+ if (debugName)
+ {
+ if (strcmp(debugName, "auto") != 0) {
+ lua_pushstring(L2, debugName); // m_L: ... lane L2: ""
+ } else {
+ lua_Debug ar;
+ lua_pushvalue(m_L, 1); // m_L: ... lane func
+ lua_getinfo(m_L, ">S", &ar); // m_L: ... lane
+ lua_pushfstring(L2, "%s:%d", ar.short_src, ar.linedefined); // m_L: ... lane L2: ""
+ }
+ m_lane->changeDebugName(-1);
+ lua_pop(L2, 1); // m_L: ... lane L2:
+ }
+ STACK_CHECK(L2, 0);
STACK_CHECK(m_L, 1);
}
@@ -1029,7 +1064,7 @@ LUAG_FUNC(lane_new)
m_lane->ready.count_down();
m_lane = nullptr;
}
- } onExit{ L_, lane, gc_cb_idx DEBUGSPEW_COMMA_PARAM(U) };
+ } onExit{ L_, lane, gc_cb_idx, name_idx DEBUGSPEW_COMMA_PARAM(U) };
// launch the thread early, it will sync with a std::latch to parallelize OS thread warmup and L2 preparation
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END(U)));
lane->startThread(priority);
@@ -1038,11 +1073,6 @@ LUAG_FUNC(lane_new)
STACK_GROW(L_, 3);
STACK_CHECK_START_REL(L_, 0);
- // give a default "Lua" name to the thread to see VM name in Decoda debugger
- lua_pushfstring(L2, "Lane #%p", L2); // L_: [7 args] args... L2: ""
- lua_setglobal(L2, "decoda_name"); // L_: [7 args] args... L2:
- LUA_ASSERT(L_, lua_gettop(L2) == 0);
-
// package
if (package_idx != 0) {
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END(U)));
@@ -1062,8 +1092,8 @@ LUAG_FUNC(lane_new)
raise_luaL_error(L_, "expected required module list as a table, got %s", luaL_typename(L_, required_idx));
}
- lua_pushnil(L_); // L_: [7 args] args... nil L2:
- while (lua_next(L_, required_idx) != 0) { // L_: [7 args] args... n "modname" L2:
+ lua_pushnil(L_); // L_: [8 args] args... nil L2:
+ while (lua_next(L_, required_idx) != 0) { // L_: [8 args] args... n "modname" L2:
if (lua_type(L_, -1) != LUA_TSTRING || lua_type(L_, -2) != LUA_TNUMBER || lua_tonumber(L_, -2) != nbRequired) {
raise_luaL_error(L_, "required module list should be a list of strings");
} else {
@@ -1073,30 +1103,30 @@ LUAG_FUNC(lane_new)
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END(U), name));
// require the module in the target lane
- lua_getglobal(L2, "require"); // L_: [7 args] args... n "modname" L2: require()?
+ lua_getglobal(L2, "require"); // L_: [8 args] args... n "modname" L2: require()?
if (lua_isnil(L2, -1)) {
- lua_pop(L2, 1); // L_: [7 args] args... n "modname" L2:
+ lua_pop(L2, 1); // L_: [8 args] args... n "modname" L2:
raise_luaL_error(L_, "cannot pre-require modules without loading 'package' library first");
} else {
- lua_pushlstring(L2, name, len); // L_: [7 args] args... n "modname" L2: require() name
- if (lua_pcall(L2, 1, 1, 0) != LUA_OK) { // L_: [7 args] args... n "modname" L2: ret/errcode
+ lua_pushlstring(L2, name, len); // L_: [8 args] args... n "modname" L2: require() name
+ if (lua_pcall(L2, 1, 1, 0) != LUA_OK) { // L_: [8 args] args... n "modname" L2: ret/errcode
// propagate error to main state if any
InterCopyContext c{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} };
- std::ignore = c.inter_move(1); // L_: [7 args] args... n "modname" error L2:
+ std::ignore = c.inter_move(1); // L_: [8 args] args... n "modname" error L2:
raise_lua_error(L_);
}
- // here the module was successfully required // L_: [7 args] args... n "modname" L2: ret
+ // here the module was successfully required // L_: [8 args] args... n "modname" L2: ret
// after requiring the module, register the functions it exported in our name<->function database
populate_func_lookup_table(L2, -1, name);
- lua_pop(L2, 1); // L_: [7 args] args... n "modname" L2:
+ lua_pop(L2, 1); // L_: [8 args] args... n "modname" L2:
}
}
lua_pop(L_, 1); // L_: func libs priority globals package required gc_cb [... args ...] n
++nbRequired;
- } // L_: [7 args] args...
+ } // L_: [8 args] args...
}
STACK_CHECK(L_, 0);
- STACK_CHECK(L2, 0); // L_: [7 args] args... L2:
+ STACK_CHECK(L2, 0); // L_: [8 args] args... L2:
// Appending the specified globals to the global environment
// *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed...
@@ -1108,17 +1138,17 @@ LUAG_FUNC(lane_new)
}
DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
- lua_pushnil(L_); // L_: [7 args] args... nil L2:
+ lua_pushnil(L_); // L_: [8 args] args... nil L2:
// Lua 5.2 wants us to push the globals table on the stack
InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
- lua_pushglobaltable(L2); // L_: [7 args] args... nil L2: _G
- while (lua_next(L_, globals_idx)) { // L_: [7 args] args... k v L2: _G
- std::ignore = c.inter_copy(2); // L_: [7 args] args... k v L2: _G k v
+ lua_pushglobaltable(L2); // L_: [8 args] args... nil L2: _G
+ while (lua_next(L_, globals_idx)) { // L_: [8 args] args... k v L2: _G
+ std::ignore = c.inter_copy(2); // L_: [8 args] args... k v L2: _G k v
// assign it in L2's globals table
- lua_rawset(L2, -3); // L_: [7 args] args... k v L2: _G
- lua_pop(L_, 1); // L_: [7 args] args... k
- } // L_: [7 args] args...
- lua_pop(L2, 1); // L_: [7 args] args... L2:
+ lua_rawset(L2, -3); // L_: [8 args] args... k v L2: _G
+ lua_pop(L_, 1); // L_: [8 args] args... k
+ } // L_: [8 args] args...
+ lua_pop(L2, 1); // L_: [8 args] args... L2:
}
STACK_CHECK(L_, 0);
STACK_CHECK(L2, 0);
@@ -1128,16 +1158,16 @@ LUAG_FUNC(lane_new)
if (func_type == LuaType::FUNCTION) {
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END(U)));
DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
- lua_pushvalue(L_, 1); // L_: [7 args] args... func L2:
+ lua_pushvalue(L_, 1); // L_: [8 args] args... func L2:
InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
- InterCopyResult const res{ c.inter_move(1) }; // L_: [7 args] args... L2: func
+ InterCopyResult const res{ c.inter_move(1) }; // L_: [8 args] args... L2: func
if (res != InterCopyResult::Success) {
raise_luaL_error(L_, "tried to copy unsupported types");
}
} else if (func_type == LuaType::STRING) {
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: compile lane body\n" INDENT_END(U)));
// compile the string
- if (luaL_loadstring(L2, lua_tostring(L_, 1)) != 0) { // L_: [7 args] args... L2: func
+ if (luaL_loadstring(L2, lua_tostring(L_, 1)) != 0) { // L_: [8 args] args... L2: func
raise_luaL_error(L_, "error when parsing lane function code");
}
} else {
@@ -1152,7 +1182,7 @@ LUAG_FUNC(lane_new)
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END(U)));
DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
- InterCopyResult const res{ c.inter_move(nargs) }; // L_: [7 args] L2: func args...
+ InterCopyResult const res{ c.inter_move(nargs) }; // L_: [8 args] L2: func args...
if (res != InterCopyResult::Success) {
raise_luaL_error(L_, "tried to copy unsupported types");
}
@@ -1161,12 +1191,12 @@ LUAG_FUNC(lane_new)
LUA_ASSERT(L_, lua_gettop(L_) == kFixedArgsIdx);
// Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive).
- kLanePointerRegKey.setValue(L2, [lane](lua_State* L_) { lua_pushlightuserdata(L_, lane); }); // L_: [7 args] L2: func args...
+ kLanePointerRegKey.setValue(L2, [lane](lua_State* L_) { lua_pushlightuserdata(L_, lane); }); // L_: [8 args] L2: func args...
STACK_CHECK(L2, 1 + nargs);
STACK_CHECK_RESET_REL(L_, 0);
// all went well, the lane's thread can start working
- onExit.success(); // L_: [7 args] lane L2:
+ onExit.success(); // L_: [8 args] lane L2:
// we should have the lane userdata on top of the stack
STACK_CHECK(L_, 1);
return 1;
@@ -1298,7 +1328,7 @@ LUAG_FUNC(thread_join)
Universe* const U{ lane->U };
// debugName is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed
// so store it in the userdata uservalue at a key that can't possibly collide
- securize_debug_threadname(L_, lane);
+ lane->securizeDebugName(L_);
switch (lane->status) {
case Lane::Done:
{
diff --git a/src/lanes.lua b/src/lanes.lua
index 8a2592b..f95dddf 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -208,25 +208,29 @@ end
local opt_validators =
{
- priority = function(v_)
+ gc_cb = function(v_)
local tv = type(v_)
- return (tv == "number") and v_ or raise_option_error("priority", tv, v_)
+ return (tv == "function") and v_ or raise_option_error("gc_cb", tv, v_)
end,
globals = function(v_)
local tv = type(v_)
return (tv == "table") and v_ or raise_option_error("globals", tv, v_)
end,
+ name = function(v_)
+ local tv = type(v_)
+ return (tv == "string") and v_ or raise_option_error("name", tv, v_)
+ end,
package = function(v_)
local tv = type(v_)
return (tv == "table") and v_ or raise_option_error("package", tv, v_)
end,
- required = function(v_)
+ priority = function(v_)
local tv = type(v_)
- return (tv == "table") and v_ or raise_option_error("required", tv, v_)
+ return (tv == "number") and v_ or raise_option_error("priority", tv, v_)
end,
- gc_cb = function(v_)
+ required = function(v_)
local tv = type(v_)
- return (tv == "function") and v_ or raise_option_error("gc_cb", tv, v_)
+ return (tv == "table") and v_ or raise_option_error("required", tv, v_)
end
}
@@ -338,10 +342,10 @@ local gen = function(...)
end
local core_lane_new = assert(core.lane_new)
- local priority, globals, package, required, gc_cb = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb
+ local priority, globals, package, required, gc_cb, name = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb, opt.name
return function(...)
-- must pass functions args last else they will be truncated to the first one
- return core_lane_new(func, libs, priority, globals, package, required, gc_cb, ...)
+ return core_lane_new(func, libs, priority, globals, package, required, gc_cb, name, ...)
end
end -- gen()
@@ -569,7 +573,7 @@ local configure_timers = function()
end
end
end -- timer_body()
- timer_lane = gen("*", { package= {}, priority = core.max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility...
+ timer_lane = gen("*", { package= {}, priority = core.max_prio, name = "LanesTimer"}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility...
end -- first_time
-----
diff --git a/src/lanes_private.h b/src/lanes_private.h
index 01630ba..309b632 100644
--- a/src/lanes_private.h
+++ b/src/lanes_private.h
@@ -89,6 +89,8 @@ class Lane
[[nodiscard]] bool waitForCompletion(lua_Duration duration_);
void startThread(int priority_);
void pushThreadStatus(lua_State* L_);
+ void changeDebugName(int nameIdx_);
+ void securizeDebugName(lua_State* L_);
};
// xxh64 of string "kLanePointerRegKey" generated at https://www.pelock.com/products/hash-calculator
--
cgit v1.2.3-55-g6feb
From 45a579cdb43917e648e2e801432f6e7820004614 Mon Sep 17 00:00:00 2001
From: Benoit Germain
Date: Tue, 7 May 2024 09:23:12 +0200
Subject: API changes
* lanes.sleep accepts 'indefinitely'.
* settings.with_timers is false by default
---
docs/index.html | 6 +++---
src/lanes.lua | 6 ++++--
tests/track_lanes.lua | 50 +++++++++++++++++++++++++++++---------------------
3 files changed, 36 insertions(+), 26 deletions(-)
diff --git a/docs/index.html b/docs/index.html
index f08947e..e811074 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -311,7 +311,7 @@
If equal to false or nil, Lanes doesn't start the timer service, and the associated API will be absent from the interface (see below).
- Default is true.
+ Default is false.
|
@@ -1328,7 +1328,7 @@ events to a common Linda, but... :).
- Timers are implemented as a lane. They can be disabled by setting "with_timers" to nil or false to lanes.configure().
+ Timers are implemented as a lane. They can be enabled by setting "with_timers" to true in lanes.configure() settings.
@@ -1395,7 +1395,7 @@ events to a common Linda, but... :).
- void = lanes.sleep([seconds|false])
+ void = lanes.sleep(['indefinitely'|seconds|false])
|
diff --git a/src/lanes.lua b/src/lanes.lua
index f95dddf..caa8818 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -71,7 +71,7 @@ local default_params =
on_state_create = nil,
shutdown_timeout = 0.25,
shutdown_mode = "hard",
- with_timers = true,
+ with_timers = false,
track_lanes = false,
demote_full_userdata = nil,
verbose_errors = false,
@@ -626,7 +626,9 @@ end
-- PUBLIC LANES API
local sleep = function(seconds_)
seconds_ = seconds_ or 0.0 -- this causes false and nil to be a valid input, equivalent to 0.0, but that's ok
- if type(seconds_) ~= "number" then
+ if seconds_ == 'indefinitely' then
+ seconds_ = nil
+ elseif type(seconds_) ~= "number" then
error("invalid duration " .. string_format("%q", tostring(seconds_)))
end
-- receive data on a channel no-one ever sends anything, thus blocking for the specified duration
diff --git a/tests/track_lanes.lua b/tests/track_lanes.lua
index 46cfdad..daaa94c 100644
--- a/tests/track_lanes.lua
+++ b/tests/track_lanes.lua
@@ -1,22 +1,20 @@
local lanes = require "lanes" .configure{ with_timers = false, track_lanes = true}
-
-local wait
-do
- local linda = lanes.linda()
- wait = function( seconds_)
- linda:receive( seconds_, "dummy_key")
- end
-end
+local wait = lanes.sleep
print "hello"
-local track = function( title_)
+local track = function( title_, expected_count_)
print( title_)
- for k, v in pairs( lanes.threads())
+ local count = 0
+ local threads = lanes.threads()
+ for k, v in pairs(threads)
do
print( k, v.name, v.status)
+ count = count + 1
end
+ assert(count == expected_count_, "unexpected active lane count")
print( "\n")
+ return threads
end
local sleeper = function( name_, seconds_)
@@ -29,7 +27,7 @@ local sleeper = function( name_, seconds_)
set_debug_threadname( name_)
end
-- suspend the lane for the specified duration with a failed linda read
- wait( seconds_)
+ lanes.sleep(seconds_)
-- print( "exiting '" .. name_ .. "'")
end
@@ -39,7 +37,7 @@ end
local g = lanes.gen( "*", sleeper)
-- start a forever-waiting lane (nil timeout)
-g( "forever")
+g( "forever", 'indefinitely')
-- start a lane that will last 2 seconds
g( "two_seconds", 2)
@@ -47,18 +45,17 @@ g( "two_seconds", 2)
-- give a bit of time to reach the linda waiting call
wait( 0.1)
--- list the known lanes
-track( "============= START")
+-- list the known lanes (should be living lanes)
+local threads = track( "============= START", 2)
+-- two_seconds forever
+assert(threads[1].status == 'waiting' and threads[2].status == 'waiting')
-- wait until "two_seconds has completed"
wait(2.1)
-track( "============= two_seconds dead")
-
--- this will collect the completed lane (and remove it from the tracking queue)
--- collectgarbage()
-
--- track( "============= two_seconds dead (after collectgarbage)")
+local threads = track( "============= two_seconds dead", 2)
+-- two_seconds forever
+assert(threads[1].status == 'done' and threads[2].status == 'waiting')
-- start another lane that will last 2 seconds, with the same name
g( "two_seconds", 2)
@@ -67,6 +64,17 @@ g( "two_seconds", 2)
wait( 0.1)
-- list the known lanes
-track( "============= ANOTHER")
+-- unless garbage collector cleaned it, we should have 3 lanes
+local threads = track( "============= ANOTHER", 3)
+-- two_seconds #2 two_seconds #1 forever
+assert(threads[1].status == 'waiting' and threads[2].status == 'done' and threads[3].status == 'waiting')
+
+-- this will collect the completed lane (and remove it from the tracking queue)
+collectgarbage()
+
+-- list the known lanes
+local threads = track( "============= AFTER COLLECTGARBAGE", 2)
+-- two_seconds #2 forever
+assert(threads[1].status == 'waiting' and threads[2].status == 'waiting')
print "done"
\ No newline at end of file
--
cgit v1.2.3-55-g6feb
From 5c7ef34404d3367542275d76b49f276ab035639f Mon Sep 17 00:00:00 2001
From: Benoit Germain
Date: Tue, 7 May 2024 09:42:09 +0200
Subject: Some more code refactorization
---
Makefile | 13 ++++
deep_test/deep_test.vcxproj | 2 +-
deep_test/deep_test.vcxproj.filters | 6 +-
deep_test/deeptest.lua | 5 +-
src/compat.cpp | 2 +-
src/compat.h | 2 +
src/lanes.cpp | 135 +++++-------------------------------
src/lanes_private.h | 10 ++-
src/lindafactory.cpp | 5 +-
src/state.cpp | 107 +++++++++++++++-------------
src/tools.cpp | 2 +-
src/universe.cpp | 116 +++++++++++++++++++++++++------
src/universe.h | 36 ++++++++++
tests/timer.lua | 2 +-
14 files changed, 244 insertions(+), 199 deletions(-)
diff --git a/Makefile b/Makefile
index f21c18a..d149d6d 100644
--- a/Makefile
+++ b/Makefile
@@ -72,6 +72,7 @@ rock:
#--- Testing ---
#
test:
+ $(MAKE) appendud
$(MAKE) atexit
$(MAKE) atomic
$(MAKE) basic
@@ -94,6 +95,7 @@ test:
$(MAKE) recursive
$(MAKE) rupval
$(MAKE) timer
+ $(MAKE) track_lanes
appendud: tests/appendud.lua $(_TARGET_SO)
$(_PREFIX) $(LUA) $<
@@ -155,6 +157,9 @@ launchtest: tests/launchtest.lua $(_TARGET_SO)
linda_perf: tests/linda_perf.lua $(_TARGET_SO)
$(_PREFIX) $(LUA) $<
+manual_register: tests/manual_register.lua $(_TARGET_SO)
+ $(_PREFIX) $(LUA) $<
+
objects: tests/objects.lua $(_TARGET_SO)
$(_PREFIX) $(LUA) $<
@@ -179,6 +184,14 @@ pingpong: tests/pingpong.lua $(_TARGET_SO)
recursive: tests/recursive.lua $(_TARGET_SO)
$(_PREFIX) $(LUA) $<
+rupval: tests/rupval.lua $(_TARGET_SO)
+ $(_PREFIX) $(LUA) $<
+
+timer: tests/timer.lua $(_TARGET_SO)
+ $(_PREFIX) $(LUA) $<
+
+track_lanes: tests/track_lanes.lua $(_TARGET_SO)
+ $(_PREFIX) $(LUA) $<
#
# This tries to show out a bug which happens in lane cleanup (multicore CPU's only)
#
diff --git a/deep_test/deep_test.vcxproj b/deep_test/deep_test.vcxproj
index ddfad5d..175433b 100644
--- a/deep_test/deep_test.vcxproj
+++ b/deep_test/deep_test.vcxproj
@@ -710,13 +710,13 @@
-
+
diff --git a/deep_test/deep_test.vcxproj.filters b/deep_test/deep_test.vcxproj.filters
index 814301f..84527af 100644
--- a/deep_test/deep_test.vcxproj.filters
+++ b/deep_test/deep_test.vcxproj.filters
@@ -24,9 +24,6 @@
Lanes
-
- Lanes
-
Source Files
@@ -44,6 +41,9 @@
Lanes
+
+ Lanes
+
diff --git a/deep_test/deeptest.lua b/deep_test/deeptest.lua
index 33de003..09b638c 100644
--- a/deep_test/deeptest.lua
+++ b/deep_test/deeptest.lua
@@ -7,7 +7,7 @@ local dt = lanes.require "deep_test"
local test_deep = true
local test_clonable = true
local test_uvtype = "string"
-local nupvals = _VERSION == "Lua 5.4" and 2 or 1
+local nupvals = _VERSION == "Lua 5.4" and 3 or 1
local makeUserValue = function( obj_)
if test_uvtype == "string" then
@@ -43,7 +43,8 @@ local performTest = function( obj_)
obj_:setuv( 1, makeUserValue( obj_))
-- lua 5.4 supports multiple uservalues of arbitrary types
if nupvals > 1 then
- obj_:setuv( 2, "ENDUV")
+ -- keep uv #2 as nil
+ obj_:setuv( 3, "ENDUV")
end
local t =
diff --git a/src/compat.cpp b/src/compat.cpp
index 1d38917..336f716 100644
--- a/src/compat.cpp
+++ b/src/compat.cpp
@@ -29,7 +29,7 @@ LuaType luaG_getmodule(lua_State* L_, char const* name_)
// #################################################################################################
// Copied from Lua 5.2 loadlib.c
-static int luaL_getsubtable(lua_State* L_, int idx_, const char* fname_)
+int luaL_getsubtable(lua_State* L_, int idx_, const char* fname_)
{
lua_getfield(L_, idx_, fname_);
if (lua_istable(L_, -1))
diff --git a/src/compat.h b/src/compat.h
index 3a61268..bf22f10 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -68,6 +68,8 @@ inline int lua504_dump(lua_State* L_, lua_Writer writer_, void* data_, [[maybe_u
}
#define LUA_LOADED_TABLE "_LOADED" // // doesn't exist in Lua 5.1
+int luaL_getsubtable(lua_State* L_, int idx_, const char* fname_);
+
#endif // LUA_VERSION_NUM == 501
// #################################################################################################
diff --git a/src/lanes.cpp b/src/lanes.cpp
index bce75a6..90f0f9f 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -393,14 +393,6 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_)
// ########################################### Threads #############################################
// #################################################################################################
-//
-// Protects modifying the selfdestruct chain
-
-#define SELFDESTRUCT_END ((Lane*) (-1))
-//
-// The chain is ended by '(Lane*)(-1)', not nullptr:
-// 'selfdestructFirst -> ... -> ... -> (-1)'
-
/*
* Add the lane to selfdestruct chain; the ones still running at the end of the
* whole process will be cancelled.
@@ -446,99 +438,6 @@ static void selfdestruct_add(Lane* lane_)
// #################################################################################################
-// process end: cancel any still free-running threads
-[[nodiscard]] static int universe_gc(lua_State* L_)
-{
- Universe* const U{ lua_tofulluserdata(L_, 1) };
- lua_Duration const shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) };
- [[maybe_unused]] char const* const op_string{ lua_tostring(L_, lua_upvalueindex(2)) };
- CancelOp const op{ which_cancel_op(op_string) };
-
- if (U->selfdestructFirst != SELFDESTRUCT_END) {
- // Signal _all_ still running threads to exit (including the timer thread)
- {
- std::lock_guard guard{ U->selfdestructMutex };
- Lane* lane{ U->selfdestructFirst };
- lua_Duration timeout{ 1us };
- while (lane != SELFDESTRUCT_END) {
- // attempt the requested cancel with a small timeout.
- // if waiting on a linda, they will raise a cancel_error.
- // if a cancellation hook is desired, it will be installed to try to raise an error
- if (lane->thread.joinable()) {
- std::ignore = thread_cancel(lane, op, 1, timeout, true);
- }
- lane = lane->selfdestruct_next;
- }
- }
-
- // When noticing their cancel, the lanes will remove themselves from the selfdestruct chain.
- {
- std::chrono::time_point t_until{ std::chrono::steady_clock::now() + std::chrono::duration_cast(shutdown_timeout) };
-
- while (U->selfdestructFirst != SELFDESTRUCT_END) {
- // give threads time to act on their cancel
- std::this_thread::yield();
- // count the number of cancelled thread that didn't have the time to act yet
- int n{ 0 };
- {
- std::lock_guard guard{ U->selfdestructMutex };
- Lane* lane{ U->selfdestructFirst };
- while (lane != SELFDESTRUCT_END) {
- if (lane->cancelRequest != CancelRequest::None)
- ++n;
- lane = lane->selfdestruct_next;
- }
- }
- // if timeout elapsed, or we know all threads have acted, stop waiting
- std::chrono::time_point t_now = std::chrono::steady_clock::now();
- if (n == 0 || (t_now >= t_until)) {
- DEBUGSPEW_CODE(fprintf(stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout.count()));
- break;
- }
- }
- }
-
- // If some lanes are currently cleaning after themselves, wait until they are done.
- // They are no longer listed in the selfdestruct chain, but they still have to lua_close().
- while (U->selfdestructingCount.load(std::memory_order_acquire) > 0) {
- std::this_thread::yield();
- }
- }
-
- // If after all this, we still have some free-running lanes, it's an external user error, they should have stopped appropriately
- {
- std::lock_guard guard{ U->selfdestructMutex };
- Lane* lane{ U->selfdestructFirst };
- if (lane != SELFDESTRUCT_END) {
- // this causes a leak because we don't call U's destructor (which could be bad if the still running lanes are accessing it)
- raise_luaL_error(L_, "Zombie thread %s refuses to die!", lane->debugName);
- }
- }
-
- // no need to mutex-protect this as all threads in the universe are gone at that point
- if (U->timerLinda != nullptr) { // test in case some early internal error prevented Lanes from creating the deep timer
- [[maybe_unused]] int const prev_ref_count{ U->timerLinda->refcount.fetch_sub(1, std::memory_order_relaxed) };
- LUA_ASSERT(L_, prev_ref_count == 1); // this should be the last reference
- DeepFactory::DeleteDeepObject(L_, U->timerLinda);
- U->timerLinda = nullptr;
- }
-
- close_keepers(U);
-
- // remove the protected allocator, if any
- U->protectedAllocator.removeFrom(L_);
-
- U->Universe::~Universe();
-
- // universe is no longer available (nor necessary)
- // we need to do this in case some deep userdata objects were created before Lanes was initialized,
- // as potentially they will be garbage collected after Lanes at application shutdown
- universe_store(L_, nullptr);
- return 0;
-}
-
-// #################################################################################################
-
//---
// = _single( [cores_uint=1] )
//
@@ -1526,14 +1425,13 @@ LUAG_FUNC(threads)
Universe* const U{ universe_get(L_) };
// List _all_ still running threads
- //
std::lock_guard guard{ U->trackingMutex };
if (U->trackingFirst && U->trackingFirst != TRACKING_END) {
Lane* lane{ U->trackingFirst };
int index{ 0 };
lua_newtable(L_); // L_: {}
while (lane != TRACKING_END) {
- // insert a { name, status } tuple, so that several lanes with the same name can't clobber each other
+ // insert a { name='', status='' } tuple, so that several lanes with the same name can't clobber each other
lua_createtable(L_, 0, 2); // L_: {} {}
lua_pushstring(L_, lane->debugName); // L_: {} {} "name"
lua_setfield(L_, -2, "name"); // L_: {} {}
@@ -1622,17 +1520,20 @@ LUAG_FUNC(wakeup_conv)
// #################################################################################################
extern int LG_linda(lua_State* L_);
-static struct luaL_Reg const lanes_functions[] = {
- { "linda", LG_linda },
- { "now_secs", LG_now_secs },
- { "wakeup_conv", LG_wakeup_conv },
- { "set_thread_priority", LG_set_thread_priority },
- { "set_thread_affinity", LG_set_thread_affinity },
- { "nameof", luaG_nameof },
- { "register", LG_register },
- { "set_singlethreaded", LG_set_singlethreaded },
- { nullptr, nullptr }
-};
+
+namespace global {
+ static struct luaL_Reg const sLanesFunctions[] = {
+ { "linda", LG_linda },
+ { "now_secs", LG_now_secs },
+ { "wakeup_conv", LG_wakeup_conv },
+ { "set_thread_priority", LG_set_thread_priority },
+ { "set_thread_affinity", LG_set_thread_affinity },
+ { "nameof", luaG_nameof },
+ { "register", LG_register },
+ { "set_singlethreaded", LG_set_singlethreaded },
+ { nullptr, nullptr }
+ };
+} // namespace global
// #################################################################################################
@@ -1715,7 +1616,7 @@ LUAG_FUNC(configure)
lua_pushnil(L_); // L_: settings M nil
lua_setfield(L_, -2, "configure"); // L_: settings M
// add functions to the module's table
- luaG_registerlibfuncs(L_, lanes_functions);
+ luaG_registerlibfuncs(L_, global::sLanesFunctions);
#if HAVE_LANE_TRACKING()
// register core.threads() only if settings say it should be available
if (U->trackingFirst != nullptr) {
@@ -1739,7 +1640,7 @@ LUAG_FUNC(configure)
// prepare the metatable for threads
// contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname }
//
- if (luaL_newmetatable(L_, "Lane")) { // L_: settings M mt
+ if (luaL_newmetatable(L_, kLaneMetatableName)) { // L_: settings M mt
lua_pushcfunction(L_, lane_gc); // L_: settings M mt lane_gc
lua_setfield(L_, -2, "__gc"); // L_: settings M mt
lua_pushcfunction(L_, LG_thread_index); // L_: settings M mt LG_thread_index
@@ -1756,7 +1657,7 @@ LUAG_FUNC(configure)
lua_setfield(L_, -2, "get_debug_threadname"); // L_: settings M mt
lua_pushcfunction(L_, LG_thread_cancel); // L_: settings M mt LG_thread_cancel
lua_setfield(L_, -2, "cancel"); // L_: settings M mt
- lua_pushliteral(L_, "Lane"); // L_: settings M mt "Lane"
+ lua_pushliteral(L_, kLaneMetatableName); // L_: settings M mt "Lane"
lua_setfield(L_, -2, "__metatable"); // L_: settings M mt
}
diff --git a/src/lanes_private.h b/src/lanes_private.h
index 309b632..196a346 100644
--- a/src/lanes_private.h
+++ b/src/lanes_private.h
@@ -10,6 +10,14 @@
#include
#include
+// The chain is ended by '(Lane*)(-1)', not nullptr: 'selfdestructFirst -> ... -> ... -> (-1)'
+#define SELFDESTRUCT_END ((Lane*) (-1))
+
+// must be a #define instead of a constexpr to work with lua_pushliteral (until I templatize it)
+#define kLaneMetatableName "Lane"
+#define kLanesLibName "lanes"
+#define kLanesCoreLibName kLanesLibName ".core"
+
// NOTE: values to be changed by either thread, during execution, without
// locking, are marked "volatile"
//
@@ -102,5 +110,5 @@ static constexpr RegistryUniqueKey kLanePointerRegKey{ 0x2D8CF03FE9F0A51Aull };
//
[[nodiscard]] inline Lane* ToLane(lua_State* L_, int i_)
{
- return *(static_cast(luaL_checkudata(L_, i_, "Lane")));
+ return *(static_cast(luaL_checkudata(L_, i_, kLaneMetatableName)));
}
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp
index 1a8782e..0ec5a0a 100644
--- a/src/lindafactory.cpp
+++ b/src/lindafactory.cpp
@@ -34,6 +34,9 @@ THE SOFTWARE.
#include "linda.h"
+// must be a #define instead of a constexpr to work with lua_pushliteral (until I templatize it)
+#define kLindaMetatableName "Linda"
+
// #################################################################################################
void LindaFactory::createMetatable(lua_State* L_) const
@@ -45,7 +48,7 @@ void LindaFactory::createMetatable(lua_State* L_) const
lua_setfield(L_, -2, "__index");
// protect metatable from external access
- lua_pushliteral(L_, "Linda");
+ lua_pushliteral(L_, kLindaMetatableName);
lua_setfield(L_, -2, "__metatable");
// the linda functions
diff --git a/src/state.cpp b/src/state.cpp
index a3dfbcd..f894978 100644
--- a/src/state.cpp
+++ b/src/state.cpp
@@ -34,6 +34,7 @@ THE SOFTWARE.
#include "state.h"
#include "lanes.h"
+#include "lanes_private.h"
#include "tools.h"
#include "universe.h"
@@ -111,68 +112,71 @@ void serialize_require(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L_)
[[nodiscard]] static int require_lanes_core(lua_State* L_)
{
// leaves a copy of 'lanes.core' module table on the stack
- luaL_requiref(L_, "lanes.core", luaopen_lanes_core, 0);
+ luaL_requiref(L_, kLanesCoreLibName, luaopen_lanes_core, 0);
return 1;
}
// #################################################################################################
-static luaL_Reg const libs[] = {
- { LUA_LOADLIBNAME, luaopen_package },
- { LUA_TABLIBNAME, luaopen_table },
- { LUA_STRLIBNAME, luaopen_string },
- { LUA_MATHLIBNAME, luaopen_math },
+namespace global
+{
+ static luaL_Reg const sLibs[] = {
+ { "base", nullptr }, // ignore "base" (already acquired it)
+#if LUA_VERSION_NUM >= 502
+#ifdef luaopen_bit32
+ { LUA_BITLIBNAME, luaopen_bit32 },
+#endif
+ { LUA_COLIBNAME, luaopen_coroutine }, // Lua 5.2: coroutine is no longer a part of base!
+#else // LUA_VERSION_NUM
+ { LUA_COLIBNAME, nullptr }, // Lua 5.1: part of base package
+#endif // LUA_VERSION_NUM
+ { LUA_DBLIBNAME, luaopen_debug },
#ifndef PLATFORM_XBOX // no os/io libs on xbox
- { LUA_OSLIBNAME, luaopen_os },
- { LUA_IOLIBNAME, luaopen_io },
+ { LUA_IOLIBNAME, luaopen_io },
+ { LUA_OSLIBNAME, luaopen_os },
#endif // PLATFORM_XBOX
+ { LUA_LOADLIBNAME, luaopen_package },
+ { LUA_MATHLIBNAME, luaopen_math },
+ { LUA_STRLIBNAME, luaopen_string },
+ { LUA_TABLIBNAME, luaopen_table },
#if LUA_VERSION_NUM >= 503
- { LUA_UTF8LIBNAME, luaopen_utf8 },
+ { LUA_UTF8LIBNAME, luaopen_utf8 },
#endif
-#if LUA_VERSION_NUM >= 502
-#ifdef luaopen_bit32
- { LUA_BITLIBNAME, luaopen_bit32 },
-#endif
- { LUA_COLIBNAME, luaopen_coroutine }, // Lua 5.2: coroutine is no longer a part of base!
-#else // LUA_VERSION_NUM
- { LUA_COLIBNAME, nullptr }, // Lua 5.1: part of base package
-#endif // LUA_VERSION_NUM
- { LUA_DBLIBNAME, luaopen_debug },
#if LUAJIT_FLAVOR() != 0 // building against LuaJIT headers, add some LuaJIT-specific libs
- // #pragma message( "supporting JIT base libs")
- { LUA_BITLIBNAME, luaopen_bit },
- { LUA_JITLIBNAME, luaopen_jit },
- { LUA_FFILIBNAME, luaopen_ffi },
+ { LUA_BITLIBNAME, luaopen_bit },
+ { LUA_FFILIBNAME, luaopen_ffi },
+ { LUA_JITLIBNAME, luaopen_jit },
#endif // LUAJIT_FLAVOR()
- { LUA_DBLIBNAME, luaopen_debug },
- { "lanes.core", require_lanes_core }, // So that we can open it like any base library (possible since we have access to the init function)
- //
- { "base", nullptr }, // ignore "base" (already acquired it)
- { nullptr, nullptr }
-};
+ { kLanesCoreLibName, require_lanes_core }, // So that we can open it like any base library (possible since we have access to the init function)
+ //
+ { nullptr, nullptr }
+ };
+
+} // namespace global
// #################################################################################################
static void open1lib(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L_, char const* name_, size_t len_)
{
- for (int i{ 0 }; libs[i].name; ++i) {
- if (strncmp(name_, libs[i].name, len_) == 0) {
- lua_CFunction libfunc = libs[i].func;
- name_ = libs[i].name; // note that the provided name_ doesn't necessarily ends with '\0', hence len_
- if (libfunc != nullptr) {
- bool const isLanesCore{ libfunc == require_lanes_core }; // don't want to create a global for "lanes.core"
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "opening %.*s library\n" INDENT_END(U_), (int) len_, name_));
- STACK_CHECK_START_REL(L_, 0);
- // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack)
- luaL_requiref(L_, name_, libfunc, !isLanesCore);
- // lanes.core doesn't declare a global, so scan it here and now
- if (isLanesCore == true) {
- populate_func_lookup_table(L_, -1, name_);
- }
- lua_pop(L_, 1);
- STACK_CHECK(L_, 0);
+ for (int i{ 0 }; global::sLibs[i].name; ++i) {
+ if (strncmp(name_, global::sLibs[i].name, len_) == 0) {
+ lua_CFunction const libfunc{ global::sLibs[i].func };
+ if (!libfunc) {
+ continue;
+ }
+ name_ = global::sLibs[i].name; // note that the provided name_ doesn't necessarily ends with '\0', hence len_
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "opening %.*s library\n" INDENT_END(U_), (int) len_, name_));
+ STACK_CHECK_START_REL(L_, 0);
+ // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack)
+ bool const isLanesCore{ libfunc == require_lanes_core }; // don't want to create a global for "lanes.core"
+ luaL_requiref(L_, name_, libfunc, !isLanesCore); // L_: {lib}
+ // lanes.core doesn't declare a global, so scan it here and now
+ if (isLanesCore) {
+ populate_func_lookup_table(L_, -1, name_);
}
+ lua_pop(L_, 1); // L_:
+ STACK_CHECK(L_, 0);
break;
}
}
@@ -180,6 +184,14 @@ static void open1lib(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L_, char con
// #################################################################################################
+template
+static inline void open1lib(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L_, char const (&name_)[N])
+{
+ open1lib(DEBUGSPEW_PARAM_COMMA(U_) L_, name_, N - 1);
+}
+
+// #################################################################################################
+
// just like lua_xmove, args are (from, to)
static void copy_one_time_settings(Universe* U_, SourceState L1_, DestState L2_)
{
@@ -195,7 +207,7 @@ static void copy_one_time_settings(Universe* U_, SourceState L1_, DestState L2_)
// copy settings from from source to destination registry
InterCopyContext c{ U_, L2_, L1_, {}, {}, {}, {}, {} };
if (c.inter_move(1) != InterCopyResult::Success) { // L1_: L2_: config
- raise_luaL_error(L1_, "failed to copy settings when loading lanes.core");
+ raise_luaL_error(L1_, "failed to copy settings when loading " kLanesCoreLibName);
}
// set L2:_R[kConfigRegKey] = settings
kConfigRegKey.setValue(L2_, [](lua_State* L_) { lua_insert(L_, -2); }); // L1_: L2_: config
@@ -334,11 +346,10 @@ lua_State* luaG_newstate(Universe* U_, SourceState from_, char const* libs_)
// copy settings (for example because it may contain a Lua on_state_create function)
copy_one_time_settings(U_, from_, L);
- // 'lua.c' stops GC during initialization so perhaps its a good idea. :)
+ // 'lua.c' stops GC during initialization so perhaps it is a good idea. :)
lua_gc(L, LUA_GCSTOP, 0);
// Anything causes 'base' to be taken in
- //
if (libs_ != nullptr) {
// special "*" case (mainly to help with LuaJIT compatibility)
// as we are called from luaopen_lanes_core() already, and that would deadlock
@@ -346,7 +357,7 @@ lua_State* luaG_newstate(Universe* U_, SourceState from_, char const* libs_)
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "opening ALL standard libraries\n" INDENT_END(U_)));
luaL_openlibs(L);
// don't forget lanes.core for regular lane states
- open1lib(DEBUGSPEW_PARAM_COMMA(U_) L, "lanes.core", 10);
+ open1lib(DEBUGSPEW_PARAM_COMMA(U_) L, kLanesCoreLibName);
libs_ = nullptr; // done with libs
} else {
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "opening base library\n" INDENT_END(U_)));
diff --git a/src/tools.cpp b/src/tools.cpp
index 2d48552..73efda9 100644
--- a/src/tools.cpp
+++ b/src/tools.cpp
@@ -348,7 +348,7 @@ static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U_)
while (lua_next(L_, breadthFirstCache) != 0) { // L_: ... {i_} {bfc} k {}
DEBUGSPEW_CODE(char const* key = (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : "not a string");
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "table '%s'\n" INDENT_END(U_), key));
- DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U_ });
+ DEBUGSPEW_CODE(DebugSpewIndentScope scope2{ U_ });
// un-visit this table in case we do need to process it
lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {}
lua_rawget(L_, cache); // L_: ... {i_} {bfc} k {} n
diff --git a/src/universe.cpp b/src/universe.cpp
index bf64560..6adc314 100644
--- a/src/universe.cpp
+++ b/src/universe.cpp
@@ -1,11 +1,11 @@
/*
- * UNIVERSE.C Copyright (c) 2017, Benoit Germain
+ * UNIVERSE.CPP Copyright (c) 2017-2024, Benoit Germain
*/
/*
===============================================================================
-Copyright (C) 2017 Benoit Germain
+Copyright (C) 2017-2024 Benoit Germain
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -28,18 +28,14 @@ THE SOFTWARE.
===============================================================================
*/
-#include
-#include
-
#include "universe.h"
-#include "compat.h"
-#include "macros_and_utils.h"
-#include "uniquekey.h"
-// xxh64 of string "kUniverseFullRegKey" generated at https://www.pelock.com/products/hash-calculator
-static constexpr RegistryUniqueKey kUniverseFullRegKey{ 0x1C2D76870DD9DD9Full };
-// xxh64 of string "kUniverseLightRegKey" generated at https://www.pelock.com/products/hash-calculator
-static constexpr RegistryUniqueKey kUniverseLightRegKey{ 0x48BBE9CEAB0BA04Full };
+#include "cancel.h"
+#include "compat.h"
+#include "deep.h"
+#include "keeper.h"
+#include "lanes_private.h"
+#include "tools.h"
// #################################################################################################
@@ -72,7 +68,7 @@ Universe::Universe()
// #################################################################################################
// only called from the master state
-Universe* universe_create(lua_State* L_)
+[[nodiscard]] Universe* universe_create(lua_State* L_)
{
LUA_ASSERT(L_, universe_get(L_) == nullptr);
Universe* const U{ lua_newuserdatauv(L_, 0) }; // universe
@@ -86,20 +82,94 @@ Universe* universe_create(lua_State* L_)
// #################################################################################################
-void universe_store(lua_State* L_, Universe* U_)
+void Universe::terminateFreeRunningLanes(lua_State* L_, lua_Duration shutdownTimeout_, CancelOp op_)
{
- LUA_ASSERT(L_, !U_ || universe_get(L_) == nullptr);
- STACK_CHECK_START_REL(L_, 0);
- kUniverseLightRegKey.setValue(L_, [U = U_](lua_State* L_) { U ? lua_pushlightuserdata(L_, U) : lua_pushnil(L_); });
- STACK_CHECK(L_, 0);
+ if (selfdestructFirst != SELFDESTRUCT_END) {
+ // Signal _all_ still running threads to exit (including the timer thread)
+ {
+ std::lock_guard guard{ selfdestructMutex };
+ Lane* lane{ selfdestructFirst };
+ lua_Duration timeout{ 1us };
+ while (lane != SELFDESTRUCT_END) {
+ // attempt the requested cancel with a small timeout.
+ // if waiting on a linda, they will raise a cancel_error.
+ // if a cancellation hook is desired, it will be installed to try to raise an error
+ if (lane->thread.joinable()) {
+ std::ignore = thread_cancel(lane, op_, 1, timeout, true);
+ }
+ lane = lane->selfdestruct_next;
+ }
+ }
+
+ // When noticing their cancel, the lanes will remove themselves from the selfdestruct chain.
+ {
+ std::chrono::time_point t_until{ std::chrono::steady_clock::now() + std::chrono::duration_cast(shutdownTimeout_) };
+
+ while (selfdestructFirst != SELFDESTRUCT_END) {
+ // give threads time to act on their cancel
+ std::this_thread::yield();
+ // count the number of cancelled thread that didn't have the time to act yet
+ int n{ 0 };
+ {
+ std::lock_guard guard{ selfdestructMutex };
+ Lane* lane{ selfdestructFirst };
+ while (lane != SELFDESTRUCT_END) {
+ if (lane->cancelRequest != CancelRequest::None)
+ ++n;
+ lane = lane->selfdestruct_next;
+ }
+ }
+ // if timeout elapsed, or we know all threads have acted, stop waiting
+ std::chrono::time_point t_now = std::chrono::steady_clock::now();
+ if (n == 0 || (t_now >= t_until)) {
+ DEBUGSPEW_CODE(fprintf(stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdownTimeout_.count()));
+ break;
+ }
+ }
+ }
+
+ // If some lanes are currently cleaning after themselves, wait until they are done.
+ // They are no longer listed in the selfdestruct chain, but they still have to lua_close().
+ while (selfdestructingCount.load(std::memory_order_acquire) > 0) {
+ std::this_thread::yield();
+ }
+ }
+
+ // If after all this, we still have some free-running lanes, it's an external user error, they should have stopped appropriately
+ {
+ std::lock_guard guard{ selfdestructMutex };
+ Lane* lane{ selfdestructFirst };
+ if (lane != SELFDESTRUCT_END) {
+ // this causes a leak because we don't call U's destructor (which could be bad if the still running lanes are accessing it)
+ raise_luaL_error(L_, "Zombie thread %s refuses to die!", lane->debugName);
+ }
+ }
}
// #################################################################################################
-Universe* universe_get(lua_State* L_)
+// process end: cancel any still free-running threads
+int universe_gc(lua_State* L_)
{
- STACK_CHECK_START_REL(L_, 0);
- Universe* const universe{ kUniverseLightRegKey.readLightUserDataValue(L_) };
- STACK_CHECK(L_, 0);
- return universe;
+ lua_Duration const shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) };
+ [[maybe_unused]] char const* const op_string{ lua_tostring(L_, lua_upvalueindex(2)) };
+ Universe* const U{ lua_tofulluserdata(L_, 1) };
+ U->terminateFreeRunningLanes(L_, shutdown_timeout, which_cancel_op(op_string));
+
+ // no need to mutex-protect this as all threads in the universe are gone at that point
+ if (U->timerLinda != nullptr) { // test in case some early internal error prevented Lanes from creating the deep timer
+ [[maybe_unused]] int const prev_ref_count{ U->timerLinda->refcount.fetch_sub(1, std::memory_order_relaxed) };
+ LUA_ASSERT(L_, prev_ref_count == 1); // this should be the last reference
+ DeepFactory::DeleteDeepObject(L_, U->timerLinda);
+ U->timerLinda = nullptr;
+ }
+
+ close_keepers(U);
+
+ // remove the protected allocator, if any
+ U->protectedAllocator.removeFrom(L_);
+
+ U->Universe::~Universe();
+
+ return 0;
}
diff --git a/src/universe.h b/src/universe.h
index b2107af..58ddffc 100644
--- a/src/universe.h
+++ b/src/universe.h
@@ -11,12 +11,14 @@ extern "C"
#include "compat.h"
#include "macros_and_utils.h"
+#include "uniquekey.h"
#include
// #################################################################################################
// forwards
+enum class CancelOp;
struct DeepPrelude;
struct Keepers;
class Lane;
@@ -114,6 +116,13 @@ class ProtectedAllocator
// #################################################################################################
+// xxh64 of string "kUniverseFullRegKey" generated at https://www.pelock.com/products/hash-calculator
+static constexpr RegistryUniqueKey kUniverseFullRegKey{ 0x1C2D76870DD9DD9Full };
+// xxh64 of string "kUniverseLightRegKey" generated at https://www.pelock.com/products/hash-calculator
+static constexpr RegistryUniqueKey kUniverseLightRegKey{ 0x48BBE9CEAB0BA04Full };
+
+// #################################################################################################
+
// everything regarding the Lanes universe is stored in that global structure
// held as a full userdata in the master Lua state that required it for the first time
class Universe
@@ -154,6 +163,7 @@ class Universe
Lane* volatile trackingFirst{ nullptr }; // will change to TRACKING_END if we want to activate tracking
#endif // HAVE_LANE_TRACKING()
+ // Protects modifying the selfdestruct chain
std::mutex selfdestructMutex;
// require() serialization
@@ -178,6 +188,8 @@ class Universe
Universe(Universe&&) = delete;
Universe& operator=(Universe const&) = delete;
Universe& operator=(Universe&&) = delete;
+
+ void terminateFreeRunningLanes(lua_State* L_, lua_Duration shutdownTimeout_, CancelOp op_);
};
// #################################################################################################
@@ -211,3 +223,27 @@ class DebugSpewIndentScope
}
};
#endif // USE_DEBUG_SPEW()
+
+// #################################################################################################
+
+[[nodiscard]] inline Universe* universe_get(lua_State* L_)
+{
+ STACK_CHECK_START_REL(L_, 0);
+ Universe* const universe{ kUniverseLightRegKey.readLightUserDataValue(L_) };
+ STACK_CHECK(L_, 0);
+ return universe;
+}
+
+// #################################################################################################
+
+inline void universe_store(lua_State* L_, Universe* U_)
+{
+ LUA_ASSERT(L_, !U_ || universe_get(L_) == nullptr);
+ STACK_CHECK_START_REL(L_, 0);
+ kUniverseLightRegKey.setValue(L_, [U = U_](lua_State* L_) { U ? lua_pushlightuserdata(L_, U) : lua_pushnil(L_); });
+ STACK_CHECK(L_, 0);
+}
+
+// #################################################################################################
+
+int universe_gc(lua_State* L_);
diff --git a/tests/timer.lua b/tests/timer.lua
index 73ecb93..a633286 100644
--- a/tests/timer.lua
+++ b/tests/timer.lua
@@ -8,7 +8,7 @@
io.stderr:setvbuf "no"
-local lanes = require "lanes".configure()
+local lanes = require "lanes".configure{with_timers=true}
local linda= lanes.linda()
--
cgit v1.2.3-55-g6feb
From 6d271c5796eae14d1dc60e778435495ebfb540d8 Mon Sep 17 00:00:00 2001
From: Benoit Germain
Date: Tue, 7 May 2024 17:56:10 +0200
Subject: Linda API changes
* timeout clarifications (negative values are no longer accepted, use nil instead)
* linda(send, linda.null, key, ...) removed, if you want to send a nil, just do it as usual
---
docs/index.html | 51 +++++----
src/cancel.cpp | 35 +++---
src/cancel.h | 2 +-
src/lanes.cpp | 46 ++++----
src/lanes.lua | 17 +--
src/lanes_private.h | 2 +-
src/linda.cpp | 25 ++---
src/lindafactory.cpp | 1 +
src/universe.cpp | 3 +-
tests/basic.lua | 296 ++++++++++++++++++++++++++-------------------------
10 files changed, 250 insertions(+), 228 deletions(-)
diff --git a/docs/index.html b/docs/index.html
index e811074..3c9cbcf 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -953,26 +953,29 @@
- Waits until the lane finishes, or timeout seconds have passed. Returns nil, "timeout" on timeout, nil,err,stack_tbl if the lane hit an error, nil, "killed" if forcefully killed, or the return values of the lane.
+ timeout is an optional number >= 0 (the default if unspecified).
+
+ Waits until the lane finishes, or timeout seconds have passed.
+
+ Returns nil, "timeout" on timeout, nil,err,stack_tbl if the lane hit an error, nil, "killed" if forcefully killed, or the return values of the lane.
+
Unlike in reading the results in table fashion, errors are not propagated.
stack_tbl is a table describing where the error was thrown.
- In "extended" mode, stack_tbl is an array of tables containing info gathered with lua_getinfo() ("source","currentline","name","namewhat","what").
+ In "extended" mode, stack_tbl is an array of tables containing info gathered with lua_getinfo() ("source","currentline","name","namewhat","what").
- In "basic mode", stack_tbl is an array of "<filename>:<line>" strings. Use table.concat() to format it to your liking (or just ignore it).
+ In "basic" mode, stack_tbl is an array of "<filename>:<line>" strings. Use table.concat() to format it to your liking (or just ignore it).
- If you use :join, make sure your lane main function returns a non-nil value so you can tell timeout and error cases apart from succesful return (using the .status property may be risky, since it might change between a timed out join and the moment you read it).
+ If you use :join(), make sure your lane main function returns a non-nil value so you can tell timeout and error cases apart from succesful return (using the .status property may be risky, since it might change between a timed out join and the moment you read it).
-
-
- require "lanes".configure()
+ local lanes = require "lanes".configure()
f = lanes.gen(function() error "!!!" end)
a = f(1)
@@ -990,7 +993,7 @@
- require "lanes".configure()
+ local lanes = require "lanes".configure()
local sync_linda = lanes.linda()
f = lanes.gen(function() dostuff() sync_linda:send("done", true) end)
@@ -1012,7 +1015,10 @@
|
- cancel() sends a cancellation request to the lane.
+ timeout is an optional number >= 0. Defaults to 0 if left unspecified or nil.
+
+ cancel() sends a cancellation request to the lane.
+
First argument is a mode can be one of "hard", "soft", "call", "ret", "line", "count".
If mode is not specified, it defaults to "hard".
If wake_lane is true, the lane is also signalled so that execution returns from any pending linda operation. Linda operations detecting the cancellation request return lanes.cancel_error.
@@ -1056,17 +1062,17 @@
|
- The error call is used for throwing exceptions in Lua. What Lua does not offer, however, is scoped finalizers
+ The regular Lua error function is usable in lanes for throwing exceptions. What Lua does not offer, however, is scoped finalizers
that would get called when a certain block of instructions gets exited, whether through peaceful return or abrupt error.
- Since 2.0.3, Lanes registers a function set_finalizer in the lane's Lua state for doing this.
+ Lanes registers a function set_finalizer in the lane's Lua state for doing this.
Any functions given to it will be called in the lane Lua state, just prior to closing it. It is possible to set more than one finalizer. They are not called in any particular order.
- An error in a finalizer itself overrides the state of the regular chunk (in practise, it would be highly preferable not to have errors in finalizers). If one finalizer errors, the others may not get called.
+ An error in a finalizer itself overrides the state of the regular chunk (in practice, it would be highly preferable not to have errors in finalizers). If one finalizer errors, the others may not get called.
If a finalizer error occurs after an error in the lane body, then this new error replaces the previous one (including the full stack trace).
@@ -1103,18 +1109,18 @@
- require "lanes".configure()
+ local lanes = require "lanes".configure()
- local linda = lanes.linda()
+ local linda = lanes.linda("my linda")
local function loop(max)
for i = 1, max do
print("sending: " .. i)
- linda:send("x", i) -- linda as upvalue
+ linda:send("x", i) -- linda as upvalue of loop()
end
end
- a = lanes.gen("", loop)(10000)
+ lane_h = lanes.gen("", loop)(10000)
while true do
local key, val = linda:receive(3.0, "x") -- timeout in seconds
@@ -1124,6 +1130,8 @@
end
print(tostring(linda) .. " received: " .. val)
end
+
+ lane_h:join()
|
@@ -1147,7 +1155,7 @@
h = lanes.linda([opt_name, [opt_group]])
- [true|lanes.cancel_error] = h:send([timeout_secs,] [h.null,] key, ...)
+ [true|lanes.cancel_error] = h:send([timeout_secs,] key, ...)
[key, val]|[lanes.cancel_error] = h:receive([timeout_secs,] key [, ...])
@@ -1157,10 +1165,11 @@
|
- The send() and receive() methods use Linda keys as FIFO stacks (first in, first out). Timeouts are given in seconds (millisecond accuracy). If using numbers as the first Linda key, one must explicitly give nil as the timeout parameter to avoid ambiguities.
+ Timeouts are given in seconds (>= 0, millisecond accuracy) or nil. Timeout can be omitted only if the first key is not a number (then it's equivalent to an infinite duration).
+ The send() and receive() methods use Linda keys as FIFO stacks (first in, first out).
By default, stack sizes are unlimited but limits can be enforced using the limit() method. This can be useful to balance execution speeds in a producer/consumer scenario. Any negative value removes the limit.
A limit of 0 is allowed to block everything.
@@ -1181,7 +1190,7 @@
send() returns lanes.cancel_error if interrupted by a soft cancel request.
- If no data is provided after the key, send() raises an error. If provided with linda.null or lanes.null before the actual key and there is no data to send, send() sends a single nil.
+ If no data is provided after the key, send() raises an error.
Also, if linda.null or lanes.null is sent as data in a linda, it will be read as a nil.
@@ -1395,7 +1404,7 @@ events to a common Linda, but... :).
- void = lanes.sleep(['indefinitely'|seconds|false])
+ void = lanes.sleep(['indefinitely'|seconds|nil])
|
@@ -1775,7 +1784,7 @@ static MyDeepFactory g_MyDeepFactory;
- Data passing (parameters, upvalues, Linda messages) is generally fast, doing two binary state-to-state copies (from source state to hidden state, hidden state to target state). Remember that not only the function you specify but also its upvalues, their upvalues, etc. etc. will get copied.
- Lane startup is fast (1000's of lanes a second), depending on the number of standard libraries initialized. Initializing all standard libraries is about 3-4 times slower than having no standard libraries at all. If you throw in a lot of lanes per second, make sure you give them minimal necessary set of libraries.
- - Waiting Lindas are woken up (and execute some hidden Lua code) each time any key in the Lindas they are waiting for are changed. This may give essential slow-down (not measured, just a gut feeling) if a lot of Linda keys are used. Using separate Linda objects for logically separate issues will help (which is good practise anyhow).
+ - Waiting Lindas are woken up (and execute some hidden Lua code) each time any key in the Lindas they are waiting for are changed. This may give essential slow-down (not measured, just a gut feeling) if a lot of Linda keys are used. Using separate Linda objects for logically separate issues will help (which is good practice anyhow).
- Linda objects are light. The memory footprint is two OS-level signalling objects (HANDLE or pthread_cond_t) for each, plus one C pointer for the proxies per each Lua state using the Linda. Barely nothing.
- Timers are light. You can probably expect timers up to 0.01 second resolution to be useful, but that is very system specific. All timers are merged into one main timer state (see timer.lua); no OS side timers are utilized.
- If you are using a lot of Linda objects, it may be useful to try having more of these keeper states. By default, only one is used (see lanes.configure()).
diff --git a/src/cancel.cpp b/src/cancel.cpp
index dd848a7..fe1623b 100644
--- a/src/cancel.cpp
+++ b/src/cancel.cpp
@@ -107,7 +107,7 @@ LUAG_FUNC(cancel_test)
// #################################################################################################
-[[nodiscard]] static CancelResult thread_cancel_soft(Lane* lane_, lua_Duration duration_, bool wakeLane_)
+[[nodiscard]] static CancelResult thread_cancel_soft(Lane* lane_, std::chrono::time_point until_, bool wakeLane_)
{
lane_->cancelRequest = CancelRequest::Soft; // it's now signaled to stop
// negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own
@@ -118,12 +118,12 @@ LUAG_FUNC(cancel_test)
}
}
- return lane_->waitForCompletion(duration_) ? CancelResult::Cancelled : CancelResult::Timeout;
+ return lane_->waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout;
}
// #################################################################################################
-[[nodiscard]] static CancelResult thread_cancel_hard(Lane* lane_, lua_Duration duration_, bool wakeLane_)
+[[nodiscard]] static CancelResult thread_cancel_hard(Lane* lane_, std::chrono::time_point until_, bool wakeLane_)
{
lane_->cancelRequest = CancelRequest::Hard; // it's now signaled to stop
// lane_->thread.get_stop_source().request_stop();
@@ -134,13 +134,13 @@ LUAG_FUNC(cancel_test)
}
}
- CancelResult result{ lane_->waitForCompletion(duration_) ? CancelResult::Cancelled : CancelResult::Timeout };
+ CancelResult result{ lane_->waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout };
return result;
}
// #################################################################################################
-CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, lua_Duration duration_, bool wakeLane_)
+CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, std::chrono::time_point until_, bool wakeLane_)
{
// remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here
// We can read 'lane_->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN)
@@ -152,12 +152,12 @@ CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, lua_Durati
// signal the linda the wake up the thread so that it can react to the cancel query
// let us hope we never land here with a pointer on a linda that has been destroyed...
if (op_ == CancelOp::Soft) {
- return thread_cancel_soft(lane_, duration_, wakeLane_);
+ return thread_cancel_soft(lane_, until_, wakeLane_);
} else if (static_cast(op_) > static_cast(CancelOp::Soft)) {
lua_sethook(lane_->L, cancel_hook, static_cast(op_), hookCount_);
}
- return thread_cancel_hard(lane_, duration_, wakeLane_);
+ return thread_cancel_hard(lane_, until_, wakeLane_);
}
// #################################################################################################
@@ -200,7 +200,7 @@ CancelOp which_cancel_op(char const* opString_)
// #################################################################################################
-// bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, wake_lindas])
+// bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, wake_lane])
LUAG_FUNC(thread_cancel)
{
Lane* const lane{ ToLane(L_, 1) };
@@ -215,14 +215,19 @@ LUAG_FUNC(thread_cancel)
}
}
- lua_Duration wait_timeout{ 0.0 };
- if (lua_type(L_, 2) == LUA_TNUMBER) {
- wait_timeout = lua_Duration{ lua_tonumber(L_, 2) };
- lua_remove(L_, 2); // argument is processed, remove it
- if (wait_timeout.count() < 0.0) {
- raise_luaL_error(L_, "cancel timeout cannot be < 0");
+ std::chrono::time_point until{ std::chrono::time_point::max() };
+ if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion
+ lua_Duration const duration{ lua_tonumber(L_, 2) };
+ if (duration.count() >= 0.0) {
+ until = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration);
+ } else {
+ raise_luaL_argerror(L_, 2, "duration cannot be < 0");
}
+ lua_remove(L_, 2); // argument is processed, remove it
+ } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key
+ lua_remove(L_, 2); // argument is processed, remove it
}
+
// we wake by default in "hard" mode (remember that hook is hard too), but this can be turned off if desired
bool wake_lane{ op != CancelOp::Soft };
if (lua_gettop(L_) >= 2) {
@@ -233,7 +238,7 @@ LUAG_FUNC(thread_cancel)
lua_remove(L_, 2); // argument is processed, remove it
}
STACK_CHECK_START_REL(L_, 0);
- switch (thread_cancel(lane, op, hook_count, wait_timeout, wake_lane)) {
+ switch (thread_cancel(lane, op, hook_count, until, wake_lane)) {
default: // should never happen unless we added a case and forgot to handle it
LUA_ASSERT(L_, false);
break;
diff --git a/src/cancel.h b/src/cancel.h
index 3df5252..1918df3 100644
--- a/src/cancel.h
+++ b/src/cancel.h
@@ -49,7 +49,7 @@ enum class CancelOp
static constexpr UniqueKey kCancelError{ 0x0630345FEF912746ull, "lanes.cancel_error" }; // 'raise_cancel_error' sentinel
[[nodiscard]] CancelOp which_cancel_op(char const* opString_);
-[[nodiscard]] CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, lua_Duration secs_, bool wakeLane_);
+[[nodiscard]] CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, std::chrono::time_point until_, bool wakeLane_);
[[noreturn]] static inline void raise_cancel_error(lua_State* L_)
{
diff --git a/src/lanes.cpp b/src/lanes.cpp
index 90f0f9f..d211b6a 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -169,17 +169,12 @@ Lane::Lane(Universe* U_, lua_State* L_)
// #################################################################################################
-bool Lane::waitForCompletion(lua_Duration duration_)
+bool Lane::waitForCompletion(std::chrono::time_point until_)
{
- std::chrono::time_point until{ std::chrono::time_point::max() };
- if (duration_.count() >= 0.0) {
- until = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration_);
- }
-
std::unique_lock lock{ doneMutex };
// std::stop_token token{ thread.get_stop_token() };
// return doneCondVar.wait_until(lock, token, secs_, [this](){ return status >= Lane::Done; });
- return doneCondVar.wait_until(lock, until, [this]() { return status >= Lane::Done; });
+ return doneCondVar.wait_until(lock, until_, [this]() { return status >= Lane::Done; });
}
// #################################################################################################
@@ -1209,22 +1204,33 @@ void Lane::pushThreadStatus(lua_State* L_)
LUAG_FUNC(thread_join)
{
Lane* const lane{ ToLane(L_, 1) };
- lua_Duration const duration{ luaL_optnumber(L_, 2, -1.0) };
lua_State* const L2{ lane->L };
- bool const done{ !lane->thread.joinable() || lane->waitForCompletion(duration) };
+ std::chrono::time_point until{ std::chrono::time_point::max() };
+ if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion
+ lua_Duration const duration{ lua_tonumber(L_, 2) };
+ if (duration.count() >= 0.0) {
+ until = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration);
+ } else {
+ raise_luaL_argerror(L_, 2, "duration cannot be < 0");
+ }
+
+ } else if (!lua_isnoneornil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key
+ raise_luaL_argerror(L_, 2, "incorrect duration type");
+ }
+
+ bool const done{ !lane->thread.joinable() || lane->waitForCompletion(until) };
+ lua_settop(L_, 1); // L_: lane
if (!done || !L2) {
- STACK_GROW(L_, 2);
- lua_pushnil(L_); // L_: lane timeout? nil
- lua_pushliteral(L_, "timeout"); // L_: lane timeout? nil "timeout"
+ lua_pushnil(L_); // L_: lane nil
+ lua_pushliteral(L_, "timeout"); // L_: lane nil "timeout"
return 2;
}
- STACK_CHECK_START_REL(L_, 0);
+ STACK_CHECK_START_REL(L_, 0); // L_: lane
// Thread is Done/Error/Cancelled; all ours now
int ret{ 0 };
- Universe* const U{ lane->U };
// debugName is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed
// so store it in the userdata uservalue at a key that can't possibly collide
lane->securizeDebugName(L_);
@@ -1234,8 +1240,8 @@ LUAG_FUNC(thread_join)
int const n{ lua_gettop(L2) }; // whole L2 stack
if (
(n > 0) &&
- (InterCopyContext{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }.inter_move(n) != InterCopyResult::Success)
- ) { // L_: lane timeout? results L2:
+ (InterCopyContext{ lane->U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }.inter_move(n) != InterCopyResult::Success)
+ ) { // L_: lane results L2:
raise_luaL_error(L_, "tried to copy unsupported types");
}
ret = n;
@@ -1244,12 +1250,12 @@ LUAG_FUNC(thread_join)
case Lane::Error:
{
- int const n{ lua_gettop(L2) }; // L_: lane timeout? L2: "err" [trace]
+ int const n{ lua_gettop(L2) }; // L_: lane L2: "err" [trace]
STACK_GROW(L_, 3);
- lua_pushnil(L_); // L_: lane timeout? nil
+ lua_pushnil(L_); // L_: lane nil
// 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 ...
- InterCopyContext c{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} };
- if (c.inter_move(n) != InterCopyResult::Success) { // L_: lane timeout? nil "err" [trace] L2:
+ InterCopyContext c{ lane->U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} };
+ if (c.inter_move(n) != InterCopyResult::Success) { // L_: lane nil "err" [trace] L2:
raise_luaL_error(L_, "tried to copy unsupported types: %s", lua_tostring(L_, -n));
}
ret = 1 + n;
diff --git a/src/lanes.lua b/src/lanes.lua
index caa8818..0ab6661 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -51,16 +51,17 @@ local lanes = setmetatable({}, lanesMeta)
-- and 'table' visible.
--
local assert = assert(assert)
+local error = assert(error)
local io = assert(io)
+local pairs = assert(pairs)
local string_gmatch = assert(string.gmatch)
local string_format = assert(string.format)
local select = assert(select)
local setmetatable = assert(setmetatable)
local table_insert = assert(table.insert)
-local type = assert(type)
-local pairs = assert(pairs)
+local tonumber = assert(tonumber)
local tostring = assert(tostring)
-local error = assert(error)
+local type = assert(type)
-- #################################################################################################
@@ -625,10 +626,12 @@ end
--
-- PUBLIC LANES API
local sleep = function(seconds_)
- seconds_ = seconds_ or 0.0 -- this causes false and nil to be a valid input, equivalent to 0.0, but that's ok
- if seconds_ == 'indefinitely' then
- seconds_ = nil
- elseif type(seconds_) ~= "number" then
+ local type = type(seconds_)
+ if type == "string" then
+ seconds_ = (seconds_ ~= 'indefinitely') and tonumber(seconds_) or nil
+ elseif type == "nil" then
+ seconds_ = 0
+ elseif type ~= "number" then
error("invalid duration " .. string_format("%q", tostring(seconds_)))
end
-- receive data on a channel no-one ever sends anything, thus blocking for the specified duration
diff --git a/src/lanes_private.h b/src/lanes_private.h
index 196a346..a756c42 100644
--- a/src/lanes_private.h
+++ b/src/lanes_private.h
@@ -94,7 +94,7 @@ class Lane
Lane(Universe* U_, lua_State* L_);
~Lane();
- [[nodiscard]] bool waitForCompletion(lua_Duration duration_);
+ [[nodiscard]] bool waitForCompletion(std::chrono::time_point until_);
void startThread(int priority_);
void pushThreadStatus(lua_State* L_);
void changeDebugName(int nameIdx_);
diff --git a/src/linda.cpp b/src/linda.cpp
index bbfbd69..40ef6c7 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -180,7 +180,7 @@ int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_)
// #################################################################################################
/*
- * bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... )
+ * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...)
*
* Send one or more values to a Linda. If there is a limit, all values must fit.
*
@@ -192,25 +192,21 @@ LUAG_FUNC(linda_send)
{
auto send = [](lua_State* L_) {
Linda* const linda{ ToLinda(L_, 1) };
- std::chrono::time_point until{ std::chrono::time_point::max() };
int key_i{ 2 }; // index of first key, if timeout not there
+ std::chrono::time_point until{ std::chrono::time_point::max() };
if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion
lua_Duration const duration{ lua_tonumber(L_, 2) };
if (duration.count() >= 0.0) {
until = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration);
+ } else {
+ raise_luaL_argerror(L_, 2, "duration cannot be < 0");
}
++key_i;
} else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key
++key_i;
}
- bool const as_nil_sentinel{ kNilSentinel.equals(L_, key_i) }; // if not nullptr, send() will silently send a single nil if nothing is provided
- if (as_nil_sentinel) {
- // the real key to send data to is after the kNilSentinel marker
- ++key_i;
- }
-
// make sure the key is of a valid type
check_key_types(L_, key_i, key_i);
@@ -218,12 +214,7 @@ LUAG_FUNC(linda_send)
// make sure there is something to send
if (lua_gettop(L_) == key_i) {
- if (as_nil_sentinel) {
- // send a single nil if nothing is provided
- kNilSentinel.pushKey(L_);
- } else {
- raise_luaL_error(L_, "no data to send");
- }
+ raise_luaL_error(L_, "no data to send");
}
// convert nils to some special non-nil sentinel in sent values
@@ -322,7 +313,7 @@ LUAG_FUNC(linda_send)
/*
* 2 modes of operation
- * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] )
+ * [val, key]= linda_receive( linda_ud, [timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] )
* Consumes a single value from the Linda, in any key.
* Returns: received value (which is consumed from the slot), and the key which had it
@@ -335,13 +326,15 @@ LUAG_FUNC(linda_receive)
{
auto receive = [](lua_State* L_) {
Linda* const linda{ ToLinda(L_, 1) };
- std::chrono::time_point until{ std::chrono::time_point::max() };
int key_i{ 2 }; // index of first key, if timeout not there
+ std::chrono::time_point until{ std::chrono::time_point::max() };
if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion
lua_Duration const duration{ lua_tonumber(L_, 2) };
if (duration.count() >= 0.0) {
until = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration);
+ } else {
+ raise_luaL_argerror(L_, 2, "duration cannot be < 0");
}
++key_i;
} else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp
index 0ec5a0a..917d949 100644
--- a/src/lindafactory.cpp
+++ b/src/lindafactory.cpp
@@ -32,6 +32,7 @@ THE SOFTWARE.
#include "lindafactory.h"
+#include "lanes_private.h"
#include "linda.h"
// must be a #define instead of a constexpr to work with lua_pushliteral (until I templatize it)
diff --git a/src/universe.cpp b/src/universe.cpp
index 6adc314..becffdd 100644
--- a/src/universe.cpp
+++ b/src/universe.cpp
@@ -89,13 +89,12 @@ void Universe::terminateFreeRunningLanes(lua_State* L_, lua_Duration shutdownTim
{
std::lock_guard guard{ selfdestructMutex };
Lane* lane{ selfdestructFirst };
- lua_Duration timeout{ 1us };
while (lane != SELFDESTRUCT_END) {
// attempt the requested cancel with a small timeout.
// if waiting on a linda, they will raise a cancel_error.
// if a cancellation hook is desired, it will be installed to try to raise an error
if (lane->thread.joinable()) {
- std::ignore = thread_cancel(lane, op_, 1, timeout, true);
+ std::ignore = thread_cancel(lane, op_, 1, std::chrono::steady_clock::now() + 1us, true);
}
lane = lane->selfdestruct_next;
}
diff --git a/tests/basic.lua b/tests/basic.lua
index 1cf37e6..85a9889 100644
--- a/tests/basic.lua
+++ b/tests/basic.lua
@@ -8,16 +8,16 @@
--
local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure{ with_timers = false, internal_allocator = "libc"}
-print( "require_lanes_result:", require_lanes_result_1, require_lanes_result_2)
+print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2)
local lanes = require_lanes_result_1
local require_assert_result_1, require_assert_result_2 = require "assert" -- assert.fails()
-print( "require_assert_result:", require_assert_result_1, require_assert_result_2)
+print("require_assert_result:", require_assert_result_1, require_assert_result_2)
-local lanes_gen= assert( lanes.gen )
-local lanes_linda= assert( lanes.linda )
+local lanes_gen= assert(lanes.gen)
+local lanes_linda= assert(lanes.linda)
-local tostring= assert( tostring )
+local tostring= assert(tostring)
local function PRINT(...)
local str=""
@@ -29,8 +29,8 @@ local function PRINT(...)
end
end
-local gc_cb = function( name_, status_)
- PRINT( " ---> lane '" .. name_ .. "' collected with status " .. status_)
+local gc_cb = function(name_, status_)
+ PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'")
end
--gc_cb = nil
@@ -41,9 +41,9 @@ local tables_match
-- true if 'a' is a subtable of 'b'
--
-local function subtable( a, b )
+local function subtable(a, b)
--
- assert( type(a)=="table" and type(b)=="table" )
+ assert(type(a)=="table" and type(b)=="table")
for k,v in pairs(b) do
if type(v)~=type(a[k]) then
@@ -59,18 +59,18 @@ end
-- true when contents of 'a' and 'b' are identical
--
-tables_match= function( a, b )
- return subtable( a, b ) and subtable( b, a )
+tables_match= function(a, b)
+ return subtable(a, b) and subtable(b, a)
end
-- ##################################################################################################
-- ##################################################################################################
-- ##################################################################################################
-PRINT( "\n\n", "---=== Tasking (basic) ===---", "\n\n")
+PRINT("\n\n", "---=== Tasking (basic) ===---", "\n\n")
-local function task( a, b, c )
- set_debug_threadname( "task("..a..","..b..","..c..")")
+local function task(a, b, c)
+ set_debug_threadname("task("..a..","..b..","..c..")")
--error "111" -- testing error messages
assert(hey)
local v=0
@@ -80,20 +80,20 @@ local function task( a, b, c )
return v, hey
end
-local task_launch= lanes_gen( "", { globals={hey=true}, gc_cb = gc_cb}, task )
+local task_launch= lanes_gen("", { globals={hey=true}, gc_cb = gc_cb}, task)
-- base stdlibs, normal priority
-- 'task_launch' is a factory of multithreaded tasks, we can launch several:
-local lane1= task_launch( 100,200,3 )
-local lane2= task_launch( 200,300,4 )
+local lane1= task_launch(100,200,3)
+local lane2= task_launch(200,300,4)
-- At this stage, states may be "pending", "running" or "done"
local st1,st2= lane1.status, lane2.status
PRINT(st1,st2)
-assert( st1=="pending" or st1=="running" or st1=="done" )
-assert( st2=="pending" or st2=="running" or st2=="done" )
+assert(st1=="pending" or st1=="running" or st1=="done")
+assert(st2=="pending" or st2=="running" or st2=="done")
-- Accessing results ([1..N]) pends until they are available
--
@@ -101,14 +101,14 @@ PRINT("waiting...")
local v1, v1_hey= lane1[1], lane1[2]
local v2, v2_hey= lane2[1], lane2[2]
-PRINT( v1, v1_hey )
-assert( v1_hey == true )
+PRINT(v1, v1_hey)
+assert(v1_hey == true)
-PRINT( v2, v2_hey )
-assert( v2_hey == true )
+PRINT(v2, v2_hey)
+assert(v2_hey == true)
-assert( lane1.status == "done" )
-assert( lane1.status == "done" )
+assert(lane1.status == "done")
+assert(lane1.status == "done")
lane1, lane2 = nil
collectgarbage()
@@ -116,9 +116,9 @@ collectgarbage()
-- ##################################################################################################
-- ##################################################################################################
-PRINT( "\n\n", "---=== Tasking (cancelling) ===---", "\n\n")
+PRINT("\n\n", "---=== Tasking (cancelling) ===---", "\n\n")
-local task_launch2= lanes_gen( "", { globals={hey=true}, gc_cb = gc_cb}, task )
+local task_launch2= lanes_gen("", { globals={hey=true}, gc_cb = gc_cb}, task)
local N=999999999
local lane9= task_launch2(1,N,1) -- huuuuuuge...
@@ -129,7 +129,7 @@ local st
local t0= os.time()
while os.time()-t0 < 5 do
st= lane9.status
- io.stderr:write( (i==1) and st.." " or '.' )
+ io.stderr:write((i==1) and st.." " or '.')
if st~="pending" then break end
end
PRINT(" "..st)
@@ -138,36 +138,36 @@ if st=="error" then
local _= lane9[0] -- propagate the error here
end
if st=="done" then
- error( "Looping to "..N.." was not long enough (cannot test cancellation)" )
+ error("Looping to "..N.." was not long enough (cannot test cancellation)")
end
-assert( st=="running" )
+assert(st=="running")
-lane9:cancel( "count", 100) -- 0 timeout, 100 instructions count hook
+lane9:cancel("count", 100) -- 0 timeout, 100 instructions count hook
local t0= os.time()
while os.time()-t0 < 5 do
st= lane9.status
- io.stderr:write( (i==1) and st.." " or '.' )
+ io.stderr:write((i==1) and st.." " or '.')
if st~="running" then break end
end
PRINT(" "..st)
-assert( st == "cancelled" )
+assert(st == "cancelled")
-- cancellation of lanes waiting on a linda
local limited = lanes.linda("limited")
-limited:limit( "key", 1)
+limited:limit("key", 1)
-- [[################################################
-limited:send( "key", "hello") -- saturate linda
-for k, v in pairs( limited:dump()) do
- PRINT("limited[" .. tostring( k) .. "] = " .. tostring( v))
+limited:send("key", "hello") -- saturate linda
+for k, v in pairs(limited:dump()) do
+ PRINT("limited[" .. tostring(k) .. "] = " .. tostring(v))
end
local wait_send = function()
local a,b
- set_finalizer( function() print( "wait_send", a, b) end)
- a,b = limited:send( "key", "bybye") -- infinite timeout, returns only when lane is cancelled
+ set_finalizer(function() print("wait_send", a, b) end)
+ a,b = limited:send("key", "bybye") -- infinite timeout, returns only when lane is cancelled
end
-local wait_send_lane = lanes.gen( "*", wait_send)()
+local wait_send_lane = lanes.gen("*", wait_send)()
repeat until wait_send_lane.status == "waiting"
print "wait_send_lane is waiting"
wait_send_lane:cancel() -- hard cancel, 0 timeout
@@ -176,11 +176,11 @@ print "wait_send_lane is cancelled"
--################################################]]
local wait_receive = function()
local k, v
- set_finalizer( function() print( "wait_receive", k, v) end)
- k, v = limited:receive( "dummy") -- infinite timeout, returns only when lane is cancelled
+ set_finalizer(function() print("wait_receive", k, v) end)
+ k, v = limited:receive("dummy") -- infinite timeout, returns only when lane is cancelled
end
-local wait_receive_lane = lanes.gen( "*", wait_receive)()
+local wait_receive_lane = lanes.gen("*", wait_receive)()
repeat until wait_receive_lane.status == "waiting"
print "wait_receive_lane is waiting"
wait_receive_lane:cancel() -- hard cancel, 0 timeout
@@ -189,11 +189,11 @@ print "wait_receive_lane is cancelled"
--################################################]]
local wait_receive_batched = function()
local k, v1, v2
- set_finalizer( function() print( "wait_receive_batched", k, v1, v2) end)
- k, v1, v2 = limited:receive( limited.batched, "dummy", 2) -- infinite timeout, returns only when lane is cancelled
+ set_finalizer(function() print("wait_receive_batched", k, v1, v2) end)
+ k, v1, v2 = limited:receive(limited.batched, "dummy", 2) -- infinite timeout, returns only when lane is cancelled
end
-local wait_receive_batched_lane = lanes.gen( "*", wait_receive_batched)()
+local wait_receive_batched_lane = lanes.gen("*", wait_receive_batched)()
repeat until wait_receive_batched_lane.status == "waiting"
print "wait_receive_batched_lane is waiting"
wait_receive_batched_lane:cancel() -- hard cancel, 0 timeout
@@ -205,120 +205,126 @@ print "wait_receive_batched_lane is cancelled"
-- ##################################################################################################
-- ##################################################################################################
-PRINT( "\n\n", "---=== Communications ===---", "\n\n")
+PRINT("\n\n", "---=== Communications ===---", "\n\n")
local function WR(...) io.stderr:write(...) end
-local chunk= function( linda )
- set_debug_threadname "chunk"
- local function receive() return linda:receive( "->" ) end
- local function send(...) linda:send( "<-", ... ) end
+local chunk= function(linda)
+ local function receive() return linda:receive("->") end
+ local function send(...) linda:send("<-", ...) end
- WR( "Lane starts!\n" )
+ WR("Lane starts!\n")
local k,v
- k,v=receive(); WR( v.." received\n" ); assert( v==1 )
- k,v=receive(); WR( v.." received\n" ); assert( v==2 )
- k,v=receive(); WR( v.." received\n" ); assert( v==3 )
+ k,v=receive(); WR(v.." received\n"); assert(v==1)
+ k,v=receive(); WR(v.." received\n"); assert(v==2)
+ k,v=receive(); WR(v.." received\n"); assert(v==3)
+ k,v=receive(); WR(tostring(v).." received\n"); assert(v==nil)
- send( 1,2,3 ); WR( "1,2,3 sent\n" )
- send 'a'; WR( "'a' sent\n" )
- send { 'a', 'b', 'c', d=10 }; WR( "{'a','b','c',d=10} sent\n" )
+ send(1,2,3); WR("1,2,3 sent\n")
+ send 'a'; WR("'a' sent\n")
+ send(nil); WR("nil sent\n")
+ send { 'a', 'b', 'c', d=10 }; WR("{'a','b','c',d=10} sent\n")
- k,v=receive(); WR( v.." received\n" ); assert( v==4 )
+ k,v=receive(); WR(v.." received\n"); assert(v==4)
local subT1 = { "subT1"}
local subT2 = { "subT2"}
- send { subT1, subT2, subT1, subT2}; WR( "{ subT1, subT2, subT1, subT2} sent\n" )
+ send { subT1, subT2, subT1, subT2}; WR("{ subT1, subT2, subT1, subT2} sent\n")
- WR( "Lane ends!\n" )
+ WR("Lane ends!\n")
end
-local linda= lanes_linda("communications")
-assert( type(linda) == "userdata" )
+local linda = lanes_linda("communications")
+assert(type(linda) == "userdata" and tostring(linda) == "Linda: communications")
--
-- ["->"] master -> slave
-- ["<-"] slave <- master
local function PEEK() return linda:get("<-") end
-local function SEND(...) linda:send( "->", ... ) end
-local function RECEIVE() local k,v = linda:receive( 1, "<-" ) return v end
+local function SEND(...) linda:send("->", ...) end
+local function RECEIVE() local k,v = linda:receive(1, "<-") return v end
-local t= lanes_gen("io", {gc_cb = gc_cb}, chunk)(linda) -- prepare & launch
+local comms_lane = lanes_gen("io", {gc_cb = gc_cb, name = "auto"}, chunk)(linda) -- prepare & launch
-SEND(1); WR( "1 sent\n" )
-SEND(2); WR( "2 sent\n" )
+SEND(1); WR("1 sent\n")
+SEND(2); WR("2 sent\n")
+SEND(3); WR("3 sent\n")
for i=1,100 do
WR "."
- assert( PEEK() == nil ) -- nothing coming in, yet
+ lanes.sleep(0.0001)
+ assert(PEEK() == nil) -- nothing coming in, yet
end
-SEND(3); WR( "3 sent\n" )
+SEND(nil); WR("\nnil sent\n")
-local a,b,c= RECEIVE(), RECEIVE(), RECEIVE()
+local a,b,c = RECEIVE(), RECEIVE(), RECEIVE()
-print( "lane status: " .. t.status)
-if t.status == "error" then
- print( t:join())
+print("lane status: " .. comms_lane.status)
+if comms_lane.status == "error" then
+ print(comms_lane:join())
else
- WR( a..", "..b..", "..c.." received\n" )
+ WR(a..", "..b..", "..c.." received\n")
end
-assert( a==1 and b==2 and c==3 )
+assert(a==1 and b==2 and c==3)
-local a= RECEIVE(); WR( a.." received\n" )
-assert( a=='a' )
+local a = RECEIVE(); WR(a.." received\n")
+assert(a=='a')
-local a= RECEIVE(); WR( type(a).." received\n" )
-assert( tables_match( a, {'a','b','c',d=10} ) )
+local null = RECEIVE(); WR(tostring(null).." received\n")
+assert(null==nil)
-assert( PEEK() == nil )
+local out_t = RECEIVE(); WR(type(out_t).." received\n")
+assert(tables_match(out_t, {'a','b','c',d=10}))
+
+assert(PEEK() == nil)
SEND(4)
-local complex_table = RECEIVE(); WR( type(complex_table).." received\n" )
-assert( complex_table[1] == complex_table[3] and complex_table[2] == complex_table[4])
-WR( table.concat( {complex_table[1][1],complex_table[2][1],complex_table[3][1],complex_table[4][1]},", "))
+local complex_table = RECEIVE(); WR(type(complex_table).." received\n")
+assert(complex_table[1] == complex_table[3] and complex_table[2] == complex_table[4])
+WR(table.concat({complex_table[1][1],complex_table[2][1],complex_table[3][1],complex_table[4][1]},", "))
WR("collectgarbage")
-t = nil
+comms_lane = nil
collectgarbage()
-- wait
WR("waiting 1s")
-linda:receive( 1, "wait")
+lanes.sleep(1)
-- ##################################################################################################
-- ##################################################################################################
-- ##################################################################################################
-PRINT( "\n\n", "---=== Stdlib naming ===---", "\n\n")
+PRINT("\n\n", "---=== Stdlib naming ===---", "\n\n")
-local function dump_g( _x)
+local function dump_g(_x)
set_debug_threadname "dump_g"
assert(print)
- print( "### dumping _G for '" .. _x .. "'")
- for k, v in pairs( _G) do
- print( "\t" .. k .. ": " .. type( v))
+ print("### dumping _G for '" .. _x .. "'")
+ for k, v in pairs(_G) do
+ print("\t" .. k .. ": " .. type(v))
end
return true
end
-local function io_os_f( _x)
+local function io_os_f(_x)
set_debug_threadname "io_os_f"
assert(print)
- print( "### checking io and os libs existence for '" .. _x .. "'")
+ print("### checking io and os libs existence for '" .. _x .. "'")
assert(io)
assert(os)
return true
end
-local function coro_f( _x)
+local function coro_f(_x)
set_debug_threadname "coro_f"
assert(print)
- print( "### checking coroutine lib existence for '" .. _x .. "'")
+ print("### checking coroutine lib existence for '" .. _x .. "'")
assert(coroutine)
return true
end
-assert.fails( function() lanes_gen( "xxx", {gc_cb = gc_cb}, io_os_f ) end )
+assert.fails(function() lanes_gen("xxx", {gc_cb = gc_cb}, io_os_f) end)
local stdlib_naming_tests =
{
@@ -333,9 +339,9 @@ local stdlib_naming_tests =
{ "io,os,base", io_os_f},
}
-for _, t in ipairs( stdlib_naming_tests) do
- local f= lanes_gen( t[1], {gc_cb = gc_cb}, t[2]) -- any delimiter will do
- assert( f(t[1])[1] )
+for _, t in ipairs(stdlib_naming_tests) do
+ local f= lanes_gen(t[1], {gc_cb = gc_cb}, t[2]) -- any delimiter will do
+ assert(f(t[1])[1])
end
WR("collectgarbage")
@@ -345,17 +351,17 @@ collectgarbage()
-- ##################################################################################################
-- ##################################################################################################
-PRINT( "\n\n", "---=== Comms criss cross ===---", "\n\n")
+PRINT("\n\n", "---=== Comms criss cross ===---", "\n\n")
-- We make two identical lanes, which are using the same Linda channel.
--
-local tc= lanes_gen( "io", {gc_cb = gc_cb},
- function( linda, ch_in, ch_out )
- set_debug_threadname( "criss cross " .. ch_in .. " -> " .. ch_out)
+local tc= lanes_gen("io", {gc_cb = gc_cb},
+ function(linda, ch_in, ch_out)
+ set_debug_threadname("criss cross " .. ch_in .. " -> " .. ch_out)
local function STAGE(str)
- io.stderr:write( ch_in..": "..str.."\n" )
- linda:send( nil, ch_out, str )
- local k,v= linda:receive( nil, ch_in )
+ io.stderr:write(ch_in..": "..str.."\n")
+ linda:send(nil, ch_out, str)
+ local k,v= linda:receive(nil, ch_in)
assert(v==str)
end
STAGE("Hello")
@@ -378,103 +384,103 @@ collectgarbage()
-- ##################################################################################################
-- ##################################################################################################
-PRINT( "\n\n", "---=== Receive & send of code ===---", "\n\n")
+PRINT("\n\n", "---=== Receive & send of code ===---", "\n\n")
local upvalue="123"
-local function chunk2( linda )
- assert( upvalue=="123" ) -- even when running as separate thread
+local function chunk2(linda)
+ assert(upvalue=="123") -- even when running as separate thread
-- function name & line number should be there even as separate thread
--
local info= debug.getinfo(1) -- 1 = us
--
for k,v in pairs(info) do PRINT(k,v) end
- assert( info.nups == (_VERSION == "Lua 5.1" and 2 or 3) ) -- one upvalue + PRINT + _ENV (Lua 5.2 only)
- assert( info.what == "Lua" )
- --assert( info.name == "chunk2" ) -- name does not seem to come through
- assert( string.match( info.source, "^@.*basic.lua$" ) )
- assert( string.match( info.short_src, "^.*basic.lua$" ) )
+ assert(info.nups == (_VERSION == "Lua 5.1" and 2 or 3)) -- one upvalue + PRINT + _ENV (Lua 5.2 only)
+ assert(info.what == "Lua")
+ --assert(info.name == "chunk2") -- name does not seem to come through
+ assert(string.match(info.source, "^@.*basic.lua$"))
+ assert(string.match(info.short_src, "^.*basic.lua$"))
-- These vary so let's not be picky (they're there..)
--
- assert( info.linedefined > 200 ) -- start of 'chunk2'
- assert( info.currentline > info.linedefined ) -- line of 'debug.getinfo'
- assert( info.lastlinedefined > info.currentline ) -- end of 'chunk2'
- local k,func= linda:receive( "down" )
- assert( type(func)=="function" )
- assert( k=="down" )
+ assert(info.linedefined > 200) -- start of 'chunk2'
+ assert(info.currentline > info.linedefined) -- line of 'debug.getinfo'
+ assert(info.lastlinedefined > info.currentline) -- end of 'chunk2'
+ local k,func= linda:receive("down")
+ assert(type(func)=="function")
+ assert(k=="down")
func(linda)
- local k,str= linda:receive( "down" )
- assert( str=="ok" )
+ local k,str= linda:receive("down")
+ assert(str=="ok")
- linda:send( "up", function() return ":)" end, "ok2" )
+ linda:send("up", function() return ":)" end, "ok2")
end
local linda= lanes.linda("linda")
-local t2= lanes_gen( "debug,string,io", {gc_cb = gc_cb}, chunk2 )(linda) -- prepare & launch
-linda:send( "down", function(linda) linda:send( "up", "ready!" ) end,
- "ok" )
+local t2= lanes_gen("debug,string,io", {gc_cb = gc_cb}, chunk2)(linda) -- prepare & launch
+linda:send("down", function(linda) linda:send("up", "ready!") end,
+ "ok")
-- wait to see if the tiny function gets executed
--
-local k,s= linda:receive( 1, "up" )
+local k,s= linda:receive(1, "up")
if t2.status == "error" then
- print( "t2 error: " , t2:join())
+ print("t2 error: " , t2:join())
end
PRINT(s)
-assert( s=="ready!" )
+assert(s=="ready!")
-- returns of the 'chunk2' itself
--
-local k,f= linda:receive( "up" )
-assert( type(f)=="function" )
+local k,f= linda:receive("up")
+assert(type(f)=="function")
local s2= f()
-assert( s2==":)" )
+assert(s2==":)")
-local k,ok2= linda:receive( "up" )
-assert( ok2 == "ok2" )
+local k,ok2= linda:receive("up")
+assert(ok2 == "ok2")
-- ##################################################################################################
-- ##################################################################################################
-- ##################################################################################################
-PRINT( "\n\n", "---=== :join test ===---", "\n\n")
+PRINT("\n\n", "---=== :join test ===---", "\n\n")
-- NOTE: 'unpack()' cannot be used on the lane handle; it will always return nil
-- (unless [1..n] has been read earlier, in which case it would seemingly
-- work).
-local S= lanes_gen( "table", {gc_cb = gc_cb},
+local S= lanes_gen("table", {gc_cb = gc_cb},
function(arg)
set_debug_threadname "join test lane"
- set_finalizer( function() end)
+ set_finalizer(function() end)
aux= {}
for i, v in ipairs(arg) do
table.insert (aux, 1, v)
end
-- unpack was renamed table.unpack in Lua 5.2: cater for both!
return (unpack or table.unpack)(aux)
-end )
+end)
h= S { 12, 13, 14 } -- execution starts, h[1..3] will get the return values
--- wait a bit so that the lane hasa chance to set its debug name
-linda:receive(0.5, "gloupti")
-print( "joining with '" .. h:get_debug_threadname() .. "'")
+-- wait a bit so that the lane has a chance to set its debug name
+lanes.sleep(0.5)
+print("joining with '" .. h:get_debug_threadname() .. "'")
local a,b,c,d= h:join()
if h.status == "error" then
- print( h:get_debug_threadname(), "error: " , a, b, c, d)
+ print(h:get_debug_threadname(), "error: " , a, b, c, d)
else
- print( h:get_debug_threadname(), a,b,c,d)
+ print(h:get_debug_threadname(), a,b,c,d)
assert(a==14)
assert(b==13)
assert(c==12)
assert(d==nil)
end
-local nameof_type, nameof_name = lanes.nameof( print)
-PRINT( "name of " .. nameof_type .. " print = '" .. nameof_name .. "'")
+local nameof_type, nameof_name = lanes.nameof(print)
+PRINT("name of " .. nameof_type .. " print = '" .. nameof_name .. "'")
--
io.stderr:write "Done! :)\n"
--
cgit v1.2.3-55-g6feb
From 5276c1d2f35bb8df9b3fd0933cf5d1de1b9133ed Mon Sep 17 00:00:00 2001
From: Benoit Germain
Date: Mon, 13 May 2024 15:03:12 +0200
Subject: Move InterCopyContext implementation in a separate file
---
lanes-4.0.0-0.rockspec | 1 +
src/Makefile | 18 +-
src/cancel.cpp | 2 -
src/compat.cpp | 7 +-
src/deep.cpp | 56 +-
src/deep.h | 10 +-
src/intercopycontext.cpp | 1296 +++++++++++++++++++++++++++++++++++++++++++++
src/intercopycontext.h | 77 +++
src/keeper.cpp | 5 +-
src/keeper.h | 1 -
src/lanes.cpp | 81 ++-
src/linda.cpp | 4 -
src/lindafactory.cpp | 1 -
src/macros_and_utils.h | 2 +-
src/state.cpp | 1 +
src/tools.cpp | 1309 +---------------------------------------------
src/tools.h | 78 +--
src/universe.cpp | 3 -
src/universe.h | 1 -
19 files changed, 1489 insertions(+), 1464 deletions(-)
create mode 100644 src/intercopycontext.cpp
create mode 100644 src/intercopycontext.h
diff --git a/lanes-4.0.0-0.rockspec b/lanes-4.0.0-0.rockspec
index 76982f1..ccfaf86 100644
--- a/lanes-4.0.0-0.rockspec
+++ b/lanes-4.0.0-0.rockspec
@@ -63,6 +63,7 @@ build = {
"src/cancel.cpp",
"src/compat.cpp",
"src/deep.cpp",
+ "src/intercopycontext.cpp",
"src/keeper.cpp",
"src/lanes.cpp",
"src/linda.cpp",
diff --git a/src/Makefile b/src/Makefile
index 06bbcd0..bbe7b23 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -7,7 +7,7 @@
MODULE=lanes
-SRC=lanes.c cancel.cpp compat.cpp threading.cpp tools.cpp state.cpp linda.cpp lindafactory.cpp deep.cpp keeper.cpp universe.cpp
+SRC=lanes.c cancel.cpp compat.cpp threading.cpp tools.cpp state.cpp linda.cpp lindafactory.cpp deep.cpp keeper.cpp universe.cpp intercopycontext.cpp
OBJ=$(SRC:.c=.o)
@@ -124,17 +124,17 @@ all: $(MODULE)/core.$(_SO)
# Note: Don't put $(LUA_LIBS) ahead of $^; MSYS will not like that (I think)
#
$(MODULE_DIR)/core.$(_SO): $(OBJ)
- mkdir -p $(MODULE_DIR)
- $(CC) $(LIBFLAG) $^ $(LIBS) $(LUA_LIBS) -o $@
+ mkdir -p $(MODULE_DIR)
+ $(CC) $(LIBFLAG) $^ $(LIBS) $(LUA_LIBS) -o $@
clean:
- -rm -rf $(MODULE)/core.$(_SO) *.o *.map
+ -rm -rf $(MODULE)/core.$(_SO) *.o *.map
#---
# NSLU2 "slug" Linux ARM
#
nslu2:
- $(MAKE) all CFLAGS="$(CFLAGS) -I/opt/include -L/opt/lib -D_GNU_SOURCE -lpthread"
+ $(MAKE) all CFLAGS="$(CFLAGS) -I/opt/include -L/opt/lib -D_GNU_SOURCE -lpthread"
#---
# Cross compiling to Win32 (MinGW on OS X Intel)
@@ -150,15 +150,15 @@ MINGW_GCC=mingw32-gcc
# i686-pc-mingw32-gcc
win32: $(WIN32_LUA51)/include/lua.h
- $(MAKE) build CC=$(MINGW_GCC) \
+ $(MAKE) build CC=$(MINGW_GCC) \
LUA_FLAGS=-I$(WIN32_LUA51)/include \
LUA_LIBS="-L$(WIN32_LUA51) -llua51" \
_SO=dll \
SO_FLAGS=-shared
$(WIN32_LUA51)/include/lua.h:
- @echo "Usage: make win32 WIN32_LUA51="
- @echo " [MINGW_GCC=...mingw32-gcc]"
- @false
+ @echo "Usage: make win32 WIN32_LUA51="
+ @echo " [MINGW_GCC=...mingw32-gcc]"
+ @false
.PROXY: all clean nslu2 win32
diff --git a/src/cancel.cpp b/src/cancel.cpp
index fe1623b..8356169 100644
--- a/src/cancel.cpp
+++ b/src/cancel.cpp
@@ -36,8 +36,6 @@ THE SOFTWARE.
#include "cancel.h"
#include "lanes_private.h"
-#include "threading.h"
-#include "tools.h"
// #################################################################################################
// #################################################################################################
diff --git a/src/compat.cpp b/src/compat.cpp
index 336f716..4e8025e 100644
--- a/src/compat.cpp
+++ b/src/compat.cpp
@@ -1,9 +1,12 @@
+#include "compat.h"
+
+#include "macros_and_utils.h"
+
+
// #################################################################################################
// ###################################### Lua 5.1 / 5.2 / 5.3 ######################################
// #################################################################################################
-#include "compat.h"
-#include "macros_and_utils.h"
// #################################################################################################
diff --git a/src/deep.cpp b/src/deep.cpp
index 6570e55..e0c2a39 100644
--- a/src/deep.cpp
+++ b/src/deep.cpp
@@ -34,10 +34,7 @@ THE SOFTWARE.
#include "deep.h"
-#include "compat.h"
#include "tools.h"
-#include "uniquekey.h"
-#include "universe.h"
#include
#include
@@ -105,7 +102,7 @@ static void LookupDeep(lua_State* L_)
// #################################################################################################
// Return the registered factory for 'index' (deep userdata proxy), or nullptr if 'index' is not a deep userdata proxy.
-[[nodiscard]] static inline DeepFactory* LookupFactory(lua_State* L_, int index_, LookupMode mode_)
+[[nodiscard]] DeepFactory* LookupFactory(lua_State* L_, int index_, LookupMode mode_)
{
// when looking inside a keeper, we are 100% sure the object is a deep userdata
if (mode_ == LookupMode::FromKeeper) {
@@ -386,54 +383,3 @@ DeepPrelude* DeepFactory::toDeep(lua_State* L_, int index_) const
DeepPrelude** const proxy{ lua_tofulluserdata(L_, index_) };
return *proxy;
}
-
-// #################################################################################################
-
-// Copy deep userdata between two separate Lua states (from L1 to L2)
-// Returns false if not a deep userdata, else true (unless an error occured)
-[[nodiscard]] bool InterCopyContext::tryCopyDeep() const
-{
- DeepFactory* const factory{ LookupFactory(L1, L1_i, mode) };
- if (factory == nullptr) {
- return false; // not a deep userdata
- }
-
- STACK_CHECK_START_REL(L1, 0);
- STACK_CHECK_START_REL(L2, 0);
-
- // extract all uservalues of the source. unfortunately, the only way to know their count is to iterate until we fail
- int nuv = 0;
- while (lua_getiuservalue(L1, L1_i, nuv + 1) != LUA_TNONE) { // L1: ... u [uv]* nil
- ++nuv;
- }
- // last call returned TNONE and pushed nil, that we don't need
- lua_pop(L1, 1); // L1: ... u [uv]*
- STACK_CHECK(L1, nuv);
-
- DeepPrelude* const u{ *lua_tofulluserdata(L1, L1_i) };
- char const* errmsg{ DeepFactory::PushDeepProxy(L2, u, nuv, mode) }; // L1: ... u [uv]* L2: u
- if (errmsg != nullptr) {
- raise_luaL_error(getErrL(), errmsg);
- }
-
- // transfer all uservalues of the source in the destination
- {
- InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, name };
- int const clone_i{ lua_gettop(L2) };
- while (nuv) {
- c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
- if (!c.inter_copy_one()) { // L1: ... u [uv]* L2: u uv
- raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
- }
- lua_pop(L1, 1); // L1: ... u [uv]*
- // this pops the value from the stack
- lua_setiuservalue(L2, clone_i, nuv); // L2: u
- --nuv;
- }
- }
-
- STACK_CHECK(L2, 1);
- STACK_CHECK(L1, 0);
-
- return true;
-}
\ No newline at end of file
diff --git a/src/deep.h b/src/deep.h
index 784952c..41df86f 100644
--- a/src/deep.h
+++ b/src/deep.h
@@ -20,15 +20,9 @@ extern "C"
#include
// forwards
+enum class LookupMode;
class Universe;
-enum class LookupMode
-{
- LaneBody, // send the lane body directly from the source to the destination lane. keep this one first so that it's the value we get when we default-construct
- ToKeeper, // send a function from a lane to a keeper state
- FromKeeper // send a function from a keeper state to a lane
-};
-
// #################################################################################################
// xxh64 of string "kDeepVersion_1" generated at https://www.pelock.com/products/hash-calculator
@@ -80,3 +74,5 @@ class DeepFactory
static void DeleteDeepObject(lua_State* L_, DeepPrelude* o_);
[[nodiscard]] static char const* PushDeepProxy(DestState L_, DeepPrelude* o_, int nuv_, LookupMode mode_);
};
+
+[[nodiscard]] DeepFactory* LookupFactory(lua_State* L_, int index_, LookupMode mode_);
diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp
new file mode 100644
index 0000000..07fcd77
--- /dev/null
+++ b/src/intercopycontext.cpp
@@ -0,0 +1,1296 @@
+/*
+===============================================================================
+
+Copyright (C) 2024 benoit Germain
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+===============================================================================
+*/
+
+#include "intercopycontext.h"
+
+#include "deep.h"
+#include "universe.h"
+
+// #################################################################################################
+
+// Lua 5.4.3 style of dumping (see lstrlib.c)
+// we have to do it that way because we can't unbalance the stack between buffer operations
+// namely, this means we can't push a function on top of the stack *after* we initialize the buffer!
+// luckily, this also works with earlier Lua versions
+[[nodiscard]] static int buf_writer(lua_State* L_, void const* b_, size_t size_, void* ud_)
+{
+ luaL_Buffer* const B{ static_cast(ud_) };
+ if (!B->L) {
+ luaL_buffinit(L_, B);
+ }
+ luaL_addlstring(B, static_cast(b_), size_);
+ return 0;
+}
+
+// #################################################################################################
+
+// function sentinel used to transfer native functions from/to keeper states
+[[nodiscard]] static int func_lookup_sentinel(lua_State* L_)
+{
+ raise_luaL_error(L_, "function lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1)));
+}
+
+// #################################################################################################
+
+// function sentinel used to transfer native table from/to keeper states
+[[nodiscard]] static int table_lookup_sentinel(lua_State* L_)
+{
+ raise_luaL_error(L_, "table lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1)));
+}
+
+// #################################################################################################
+
+// function sentinel used to transfer cloned full userdata from/to keeper states
+[[nodiscard]] static int userdata_clone_sentinel(lua_State* L_)
+{
+ raise_luaL_error(L_, "userdata clone sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1)));
+}
+
+// #################################################################################################
+
+// retrieve the name of a function/table in the lookup database
+[[nodiscard]] static char const* find_lookup_name(lua_State* L_, int i_, LookupMode mode_, char const* upName_, size_t* len_)
+{
+ LUA_ASSERT(L_, lua_isfunction(L_, i_) || lua_istable(L_, i_)); // L_: ... v ...
+ STACK_CHECK_START_REL(L_, 0);
+ STACK_GROW(L_, 3); // up to 3 slots are necessary on error
+ if (mode_ == LookupMode::FromKeeper) {
+ lua_CFunction f = lua_tocfunction(L_, i_); // should *always* be one of the function sentinels
+ if (f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel) {
+ lua_getupvalue(L_, i_, 1); // L_: ... v ... "f.q.n"
+ } else {
+ // if this is not a sentinel, this is some user-created table we wanted to lookup
+ LUA_ASSERT(L_, nullptr == f && lua_istable(L_, i_));
+ // push anything that will convert to nullptr string
+ lua_pushnil(L_); // L_: ... v ... nil
+ }
+ } else {
+ // fetch the name from the source state's lookup table
+ kLookupRegKey.pushValue(L_); // L_: ... v ... {}
+ STACK_CHECK(L_, 1);
+ LUA_ASSERT(L_, lua_istable(L_, -1));
+ lua_pushvalue(L_, i_); // L_: ... v ... {} v
+ lua_rawget(L_, -2); // L_: ... v ... {} "f.q.n"
+ }
+ char const* fqn{ lua_tolstring(L_, -1, len_) };
+ DEBUGSPEW_CODE(Universe* const U = universe_get(L_));
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END(U), fqn));
+ // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database
+ lua_pop(L_, (mode_ == LookupMode::FromKeeper) ? 1 : 2); // L_: ... v ...
+ STACK_CHECK(L_, 0);
+ if (nullptr == fqn && !lua_istable(L_, i_)) { // raise an error if we try to send an unknown function (but not for tables)
+ *len_ = 0; // just in case
+ // try to discover the name of the function we want to send
+ lua_getglobal(L_, "decoda_name"); // L_: ... v ... decoda_name
+ char const* from{ lua_tostring(L_, -1) };
+ lua_pushcfunction(L_, luaG_nameof); // L_: ... v ... decoda_name luaG_nameof
+ lua_pushvalue(L_, i_); // L_: ... v ... decoda_name luaG_nameof t
+ lua_call(L_, 1, 2); // L_: ... v ... decoda_name "type" "name"|nil
+ char const* typewhat{ (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : luaL_typename(L_, -2) };
+ // second return value can be nil if the table was not found
+ // probable reason: the function was removed from the source Lua state before Lanes was required.
+ char const *what, *gotchaA, *gotchaB;
+ if (lua_isnil(L_, -1)) {
+ gotchaA = " referenced by";
+ gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)";
+ what = upName_;
+ } else {
+ gotchaA = "";
+ gotchaB = "";
+ what = (lua_type(L_, -1) == LUA_TSTRING) ? lua_tostring(L_, -1) : luaL_typename(L_, -1);
+ }
+ raise_luaL_error(L_, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB);
+ }
+ STACK_CHECK(L_, 0);
+ return fqn;
+}
+
+// #################################################################################################
+
+/*---=== Inter-state copying ===---*/
+
+// xxh64 of string "kMtIdRegKey" generated at https://www.pelock.com/products/hash-calculator
+static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull };
+
+// get a unique ID for metatable at [i].
+[[nodiscard]] static lua_Integer get_mt_id(Universe* U_, lua_State* L_, int idx_)
+{
+ idx_ = lua_absindex(L_, idx_);
+
+ STACK_GROW(L_, 3);
+
+ STACK_CHECK_START_REL(L_, 0);
+ std::ignore = kMtIdRegKey.getSubTable(L_, 0, 0); // L_: ... _R[kMtIdRegKey]
+ lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] {mt}
+ lua_rawget(L_, -2); // L_: ... _R[kMtIdRegKey] mtk?
+
+ lua_Integer id{ lua_tointeger(L_, -1) }; // 0 for nil
+ lua_pop(L_, 1); // L_: ... _R[kMtIdRegKey]
+ STACK_CHECK(L_, 1);
+
+ if (id == 0) {
+ id = U_->nextMetatableId.fetch_add(1, std::memory_order_relaxed);
+
+ // Create two-way references: id_uint <-> table
+ lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] {mt}
+ lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] {mt} id
+ lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey]
+
+ lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] id
+ lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] id {mt}
+ lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey]
+ }
+ lua_pop(L_, 1); // L_: ...
+ STACK_CHECK(L_, 0);
+
+ return id;
+}
+
+// #################################################################################################
+
+// Copy a function over, which has not been found in the cache.
+// L2 has the cache key for this function at the top of the stack
+void InterCopyContext::copy_func() const
+{
+ LUA_ASSERT(L1, L2_cache_i != 0); // L2: ... {cache} ... p
+ STACK_GROW(L1, 2);
+ STACK_CHECK_START_REL(L1, 0);
+
+ // 'lua_dump()' needs the function at top of stack
+ // if already on top of the stack, no need to push again
+ bool const needToPush{ L1_i != lua_gettop(L1) };
+ if (needToPush) {
+ lua_pushvalue(L1, L1_i); // L1: ... f
+ }
+
+ //
+ // "value returned is the error code returned by the last call
+ // to the writer" (and we only return 0)
+ // not sure this could ever fail but for memory shortage reasons
+ // last parameter is Lua 5.4-specific (no stripping)
+ luaL_Buffer B;
+ B.L = nullptr;
+ if (lua504_dump(L1, buf_writer, &B, 0) != 0) {
+ raise_luaL_error(getErrL(), "internal error: function dump failed.");
+ }
+
+ // pushes dumped string on 'L1'
+ luaL_pushresult(&B); // L1: ... f b
+
+ // if not pushed, no need to pop
+ if (needToPush) {
+ lua_remove(L1, -2); // L1: ... b
+ }
+
+ // transfer the bytecode, then the upvalues, to create a similar closure
+ {
+ char const* fname = nullptr;
+#define LOG_FUNC_INFO 0
+#if LOG_FUNC_INFO
+ // "To get information about a function you push it onto the
+ // stack and start the what string with the character '>'."
+ //
+ {
+ lua_Debug ar;
+ lua_pushvalue(L1, L1_i); // L1: ... b f
+ // fills 'fname' 'namewhat' and 'linedefined', pops function
+ lua_getinfo(L1, ">nS", &ar); // L1: ... b
+ fname = ar.namewhat;
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "FNAME: %s @ %d" INDENT_END(U), ar.short_src, ar.linedefined)); // just gives nullptr
+ }
+#endif // LOG_FUNC_INFO
+ {
+ size_t sz;
+ char const* s = lua_tolstring(L1, -1, &sz); // L1: ... b
+ LUA_ASSERT(L1, s && sz);
+ STACK_GROW(L2, 2);
+ // Note: Line numbers seem to be taken precisely from the
+ // original function. 'fname' is not used since the chunk
+ // is precompiled (it seems...).
+ //
+ // TBD: Can we get the function's original name through, as well?
+ //
+ if (luaL_loadbuffer(L2, s, sz, fname) != 0) { // L2: ... {cache} ... p function
+ // chunk is precompiled so only LUA_ERRMEM can happen
+ // "Otherwise, it pushes an error message"
+ //
+ STACK_GROW(L1, 1);
+ raise_luaL_error(getErrL(), "%s: %s", fname, lua_tostring(L2, -1));
+ }
+ // remove the dumped string
+ lua_pop(L1, 1); // ...
+ // now set the cache as soon as we can.
+ // this is necessary if one of the function's upvalues references it indirectly
+ // we need to find it in the cache even if it isn't fully transfered yet
+ lua_insert(L2, -2); // L2: ... {cache} ... function p
+ lua_pushvalue(L2, -2); // L2: ... {cache} ... function p function
+ // cache[p] = function
+ lua_rawset(L2, L2_cache_i); // L2: ... {cache} ... function
+ }
+ STACK_CHECK(L1, 0);
+
+ /* push over any upvalues; references to this function will come from
+ * cache so we don't end up in eternal loop.
+ * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy!
+ * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state!
+ */
+ int n{ 0 };
+ {
+ InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} };
+#if LUA_VERSION_NUM >= 502
+ // Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default)
+ // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state...
+ // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table
+ lua_pushglobaltable(L1); // L1: ... _G
+#endif // LUA_VERSION_NUM
+ for (n = 0; (c.name = lua_getupvalue(L1, L1_i, 1 + n)) != nullptr; ++n) { // L1: ... _G up[n]
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END(U), n, c.name));
+#if LUA_VERSION_NUM >= 502
+ if (lua_rawequal(L1, -1, -2)) { // is the upvalue equal to the global table?
+ DEBUGSPEW_CODE(fprintf(stderr, "pushing destination global scope\n"));
+ lua_pushglobaltable(L2); // L2: ... {cache} ... function
+ } else
+#endif // LUA_VERSION_NUM
+ {
+ DEBUGSPEW_CODE(fprintf(stderr, "copying value\n"));
+ c.L1_i = SourceIndex{ lua_gettop(L1) };
+ if (!c.inter_copy_one()) { // L2: ... {cache} ... function
+ raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
+ }
+ }
+ lua_pop(L1, 1); // L1: ... _G
+ }
+#if LUA_VERSION_NUM >= 502
+ lua_pop(L1, 1); // L1: ...
+#endif // LUA_VERSION_NUM
+ }
+ // L2: ... {cache} ... function + 'n' upvalues (>=0)
+
+ STACK_CHECK(L1, 0);
+
+ // Set upvalues (originally set to 'nil' by 'lua_load')
+ for (int const func_index{ lua_gettop(L2) - n }; n > 0; --n) {
+ char const* rc{ lua_setupvalue(L2, func_index, n) }; // L2: ... {cache} ... function
+ //
+ // "assigns the value at the top of the stack to the upvalue and returns its name.
+ // It also pops the value from the stack."
+
+ LUA_ASSERT(L1, rc); // not having enough slots?
+ }
+ // once all upvalues have been set we are left
+ // with the function at the top of the stack // L2: ... {cache} ... function
+ }
+ STACK_CHECK(L1, 0);
+}
+
+// #################################################################################################
+
+// Push a looked-up native/LuaJIT function.
+void InterCopyContext::lookup_native_func() const
+{
+ // get the name of the function we want to send
+ size_t len;
+ char const* const fqn{ find_lookup_name(L1, L1_i, mode, name, &len) };
+ // push the equivalent function in the destination's stack, retrieved from the lookup table
+ STACK_CHECK_START_REL(L2, 0);
+ STACK_GROW(L2, 3); // up to 3 slots are necessary on error
+ switch (mode) {
+ default: // shouldn't happen, in theory...
+ raise_luaL_error(getErrL(), "internal error: unknown lookup mode");
+ break;
+
+ case LookupMode::ToKeeper:
+ // push a sentinel closure that holds the lookup name as upvalue
+ lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: "f.q.n"
+ lua_pushcclosure(L2, func_lookup_sentinel, 1); // L1: ... f ... L2: f
+ break;
+
+ case LookupMode::LaneBody:
+ case LookupMode::FromKeeper:
+ kLookupRegKey.pushValue(L2); // L1: ... f ... L2: {}
+ STACK_CHECK(L2, 1);
+ LUA_ASSERT(L1, lua_istable(L2, -1));
+ lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: {} "f.q.n"
+ lua_rawget(L2, -2); // L1: ... f ... L2: {} f
+ // nil means we don't know how to transfer stuff: user should do something
+ // anything other than function or table should not happen!
+ if (!lua_isfunction(L2, -1) && !lua_istable(L2, -1)) {
+ lua_getglobal(L1, "decoda_name"); // L1: ... f ... decoda_name
+ char const* const from{ lua_tostring(L1, -1) };
+ lua_pop(L1, 1); // L1: ... f ...
+ lua_getglobal(L2, "decoda_name"); // L1: ... f ... L2: {} f decoda_name
+ char const* const to{ lua_tostring(L2, -1) };
+ lua_pop(L2, 1); // L2: {} f
+ // 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
+ raise_luaL_error(
+ getErrL(),
+ "%s%s: function '%s' not found in %s destination transfer database.",
+ lua_isnil(L2, -1) ? "" : "INTERNAL ERROR IN ",
+ from ? from : "main",
+ fqn,
+ to ? to : "main");
+ return;
+ }
+ lua_remove(L2, -2); // L2: f
+ break;
+
+ /* keep it in case I need it someday, who knows...
+ case LookupMode::RawFunctions:
+ {
+ int n;
+ char const* upname;
+ lua_CFunction f = lua_tocfunction( L, i);
+ // copy upvalues
+ for (n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != nullptr; ++ n) {
+ luaG_inter_move( U, L, L2, 1, mode_); // L2: [up[,up ...]]
+ }
+ lua_pushcclosure( L2, f, n); // L2:
+ }
+ break;
+ */
+ }
+ STACK_CHECK(L2, 1);
+}
+
+// #################################################################################################
+
+// Check if we've already copied the same function from 'L1', and reuse the old copy.
+// Always pushes a function to 'L2'.
+void InterCopyContext::copy_cached_func() const
+{
+ FuncSubType const funcSubType{ luaG_getfuncsubtype(L1, L1_i) };
+ if (funcSubType == FuncSubType::Bytecode) {
+ void* const aspointer = const_cast(lua_topointer(L1, L1_i));
+ // TBD: Merge this and same code for tables
+ LUA_ASSERT(L1, L2_cache_i != 0);
+
+ STACK_GROW(L2, 2);
+
+ // L2_cache[id_str]= function
+ //
+ STACK_CHECK_START_REL(L2, 0);
+
+ // We don't need to use the from state ('L1') in ID since the life span
+ // is only for the duration of a copy (both states are locked).
+
+ // push a light userdata uniquely representing the function
+ lua_pushlightuserdata(L2, aspointer); // L2: ... {cache} ... p
+
+ // fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1));
+
+ lua_pushvalue(L2, -1); // L2: ... {cache} ... p p
+ lua_rawget(L2, L2_cache_i); // L2: ... {cache} ... p function|nil|true
+
+ if (lua_isnil(L2, -1)) { // function is unknown
+ lua_pop(L2, 1); // L2: ... {cache} ... p
+
+ // Set to 'true' for the duration of creation; need to find self-references
+ // via upvalues
+ //
+ // pushes a copy of the func, stores a reference in the cache
+ copy_func(); // L2: ... {cache} ... function
+ } else { // found function in the cache
+ lua_remove(L2, -2); // L2: ... {cache} ... function
+ }
+ STACK_CHECK(L2, 1);
+ LUA_ASSERT(L1, lua_isfunction(L2, -1));
+ } else { // function is native/LuaJIT: no need to cache
+ lookup_native_func(); // L2: ... {cache} ... function
+ // if the function was in fact a lookup sentinel, we can either get a function or a table here
+ LUA_ASSERT(L1, lua_isfunction(L2, -1) || lua_istable(L2, -1));
+ }
+}
+
+// #################################################################################################
+
+// Push a looked-up table, or nothing if we found nothing
+[[nodiscard]] bool InterCopyContext::lookup_table() const
+{
+ // get the name of the table we want to send
+ size_t len;
+ char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len);
+ if (nullptr == fqn) { // name not found, it is some user-created table
+ return false;
+ }
+ // push the equivalent table in the destination's stack, retrieved from the lookup table
+ STACK_CHECK_START_REL(L2, 0);
+ STACK_GROW(L2, 3); // up to 3 slots are necessary on error
+ switch (mode) {
+ default: // shouldn't happen, in theory...
+ raise_luaL_error(getErrL(), "internal error: unknown lookup mode");
+ break;
+
+ case LookupMode::ToKeeper:
+ // push a sentinel closure that holds the lookup name as upvalue
+ lua_pushlstring(L2, fqn, len); // L1: ... t ... L2: "f.q.n"
+ lua_pushcclosure(L2, table_lookup_sentinel, 1); // L1: ... t ... L2: f
+ break;
+
+ case LookupMode::LaneBody:
+ case LookupMode::FromKeeper:
+ kLookupRegKey.pushValue(L2); // L1: ... t ... L2: {}
+ STACK_CHECK(L2, 1);
+ LUA_ASSERT(L1, lua_istable(L2, -1));
+ lua_pushlstring(L2, fqn, len); // L2: {} "f.q.n"
+ lua_rawget(L2, -2); // L2: {} t
+ // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead)
+ // but not when we extract something out of a keeper, as there is nothing to clone!
+ if (lua_isnil(L2, -1) && mode == LookupMode::LaneBody) {
+ lua_pop(L2, 2); // L1: ... t ... L2:
+ STACK_CHECK(L2, 0);
+ return false;
+ } else if (!lua_istable(L2, -1)) { // this can happen if someone decides to replace same already registered item (for a example a standard lib function) with a table
+ lua_getglobal(L1, "decoda_name"); // L1: ... t ... decoda_name
+ char const* from{ lua_tostring(L1, -1) };
+ lua_pop(L1, 1); // L1: ... t ...
+ lua_getglobal(L2, "decoda_name"); // L1: ... t ... L2: {} t decoda_name
+ char const* to{ lua_tostring(L2, -1) };
+ lua_pop(L2, 1); // L1: ... t ... L2: {} t
+ raise_luaL_error(
+ getErrL(),
+ "%s: source table '%s' found as %s in %s destination transfer database.",
+ from ? from : "main",
+ fqn,
+ lua_typename(L2, lua_type_as_enum(L2, -1)),
+ to ? to : "main");
+ }
+ lua_remove(L2, -2); // L1: ... t ... L2: t
+ break;
+ }
+ STACK_CHECK(L2, 1);
+ return true;
+}
+
+// #################################################################################################
+
+void InterCopyContext::inter_copy_keyvaluepair() const
+{
+ SourceIndex const val_i{ lua_gettop(L1) };
+ SourceIndex const key_i{ val_i - 1 };
+
+ // For the key, only basic key types are copied over. others ignored
+ InterCopyContext c{ U, L2, L1, L2_cache_i, key_i, VT::KEY, mode, name };
+ if (!c.inter_copy_one()) {
+ return;
+ // we could raise an error instead of ignoring the table entry, like so:
+ // 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));
+ // maybe offer this possibility as a global configuration option, or a linda setting, or as a parameter of the call causing the transfer?
+ }
+
+ char* valPath{ nullptr };
+ if (U->verboseErrors) {
+ // for debug purposes, let's try to build a useful name
+ if (lua_type(L1, key_i) == LUA_TSTRING) {
+ char const* key{ lua_tostring(L1, key_i) };
+ size_t const keyRawLen = lua_rawlen(L1, key_i);
+ size_t const bufLen = strlen(name) + keyRawLen + 2;
+ valPath = (char*) alloca(bufLen);
+ sprintf(valPath, "%s.%*s", name, (int) keyRawLen, key);
+ key = nullptr;
+ }
+#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
+ else if (lua_isinteger(L1, key_i)) {
+ lua_Integer const key{ lua_tointeger(L1, key_i) };
+ valPath = (char*) alloca(strlen(name) + 32 + 3);
+ sprintf(valPath, "%s[" LUA_INTEGER_FMT "]", name, key);
+ }
+#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
+ else if (lua_type(L1, key_i) == LUA_TNUMBER) {
+ lua_Number const key{ lua_tonumber(L1, key_i) };
+ valPath = (char*) alloca(strlen(name) + 32 + 3);
+ sprintf(valPath, "%s[" LUA_NUMBER_FMT "]", name, key);
+ } else if (lua_type(L1, key_i) == LUA_TLIGHTUSERDATA) {
+ void* const key{ lua_touserdata(L1, key_i) };
+ valPath = (char*) alloca(strlen(name) + 16 + 5);
+ sprintf(valPath, "%s[U:%p]", name, key);
+ } else if (lua_type(L1, key_i) == LUA_TBOOLEAN) {
+ int const key{ lua_toboolean(L1, key_i) };
+ valPath = (char*) alloca(strlen(name) + 8);
+ sprintf(valPath, "%s[%s]", name, key ? "true" : "false");
+ }
+ }
+ c.L1_i = SourceIndex{ val_i };
+ // Contents of metatables are copied with cache checking. important to detect loops.
+ c.vt = VT::NORMAL;
+ c.name = valPath ? valPath : name;
+ if (c.inter_copy_one()) {
+ LUA_ASSERT(L1, lua_istable(L2, -3));
+ lua_rawset(L2, -3); // add to table (pops key & val)
+ } else {
+ raise_luaL_error(getErrL(), "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", valPath, luaL_typename(L1, val_i));
+ }
+}
+
+// #################################################################################################
+
+[[nodiscard]] bool InterCopyContext::push_cached_metatable() const
+{
+ STACK_CHECK_START_REL(L1, 0);
+ if (!lua_getmetatable(L1, L1_i)) { // L1: ... mt
+ STACK_CHECK(L1, 0);
+ return false;
+ }
+ STACK_CHECK(L1, 1);
+
+ lua_Integer const mt_id{ get_mt_id(U, L1, -1) }; // Unique id for the metatable
+
+ STACK_CHECK_START_REL(L2, 0);
+ STACK_GROW(L2, 4);
+ // do we already know this metatable?
+ std::ignore = kMtIdRegKey.getSubTable(L2, 0, 0); // L2: _R[kMtIdRegKey]
+ lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] id
+ lua_rawget(L2, -2); // L2: _R[kMtIdRegKey] mt|nil
+ STACK_CHECK(L2, 2);
+
+ if (lua_isnil(L2, -1)) { // L2 did not know the metatable
+ lua_pop(L2, 1); // L2: _R[kMtIdRegKey]
+ InterCopyContext const c{ U, L2, L1, L2_cache_i, SourceIndex{ lua_gettop(L1) }, VT::METATABLE, mode, name };
+ if (!c.inter_copy_one()) { // L2: _R[kMtIdRegKey] mt?
+ raise_luaL_error(getErrL(), "Error copying a metatable");
+ }
+
+ STACK_CHECK(L2, 2); // L2: _R[kMtIdRegKey] mt
+ // mt_id -> metatable
+ lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt id
+ lua_pushvalue(L2, -2); // L2: _R[kMtIdRegKey] mt id mt
+ lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt
+
+ // metatable -> mt_id
+ lua_pushvalue(L2, -1); // L2: _R[kMtIdRegKey] mt mt
+ lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt mt id
+ lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt
+ STACK_CHECK(L2, 2);
+ }
+ lua_remove(L2, -2); // L2: mt
+
+ lua_pop(L1, 1); // L1: ...
+ STACK_CHECK(L2, 1);
+ STACK_CHECK(L1, 0);
+ return true;
+}
+
+// #################################################################################################
+
+// Check if we've already copied the same table from 'L1', and reuse the old copy. This allows table upvalues shared by multiple
+// local functions to point to the same table, also in the target.
+// Always pushes a table to 'L2'.
+// Returns true if the table was cached (no need to fill it!); false if it's a virgin.
+[[nodiscard]] bool InterCopyContext::push_cached_table() const
+{
+ void const* p{ lua_topointer(L1, L1_i) };
+
+ LUA_ASSERT(L1, L2_cache_i != 0);
+ STACK_GROW(L2, 3);
+ STACK_CHECK_START_REL(L2, 0);
+
+ // We don't need to use the from state ('L1') in ID since the life span
+ // is only for the duration of a copy (both states are locked).
+ // push a light userdata uniquely representing the table
+ lua_pushlightuserdata(L2, const_cast(p)); // L1: ... t ... L2: ... p
+
+ // fprintf(stderr, "<< ID: %s >>\n", lua_tostring(L2, -1));
+
+ lua_rawget(L2, L2_cache_i); // L1: ... t ... L2: ... {cached|nil}
+ bool const not_found_in_cache{ lua_isnil(L2, -1) };
+ if (not_found_in_cache) {
+ // create a new entry in the cache
+ lua_pop(L2, 1); // L1: ... t ... L2: ...
+ lua_newtable(L2); // L1: ... t ... L2: ... {}
+ lua_pushlightuserdata(L2, const_cast(p)); // L1: ... t ... L2: ... {} p
+ lua_pushvalue(L2, -2); // L1: ... t ... L2: ... {} p {}
+ lua_rawset(L2, L2_cache_i); // L1: ... t ... L2: ... {}
+ }
+ STACK_CHECK(L2, 1);
+ LUA_ASSERT(L1, lua_istable(L2, -1));
+ return !not_found_in_cache;
+}
+
+// #################################################################################################
+
+[[nodiscard]] bool InterCopyContext::tryCopyClonable() const
+{
+ SourceIndex const L1i{ lua_absindex(L1, L1_i) };
+ void* const source{ lua_touserdata(L1, L1i) };
+
+ STACK_CHECK_START_REL(L1, 0);
+ STACK_CHECK_START_REL(L2, 0);
+
+ // Check if the source was already cloned during this copy
+ lua_pushlightuserdata(L2, source); // L2: ... source
+ lua_rawget(L2, L2_cache_i); // L2: ... clone?
+ if (!lua_isnil(L2, -1)) {
+ STACK_CHECK(L2, 1);
+ return true;
+ } else {
+ lua_pop(L2, 1); // L2: ...
+ }
+ STACK_CHECK(L2, 0);
+
+ // no metatable? -> not clonable
+ if (!lua_getmetatable(L1, L1i)) { // L1: ... mt?
+ STACK_CHECK(L1, 0);
+ return false;
+ }
+
+ // no __lanesclone? -> not clonable
+ lua_getfield(L1, -1, "__lanesclone"); // L1: ... mt __lanesclone?
+ if (lua_isnil(L1, -1)) {
+ lua_pop(L1, 2); // L1: ...
+ STACK_CHECK(L1, 0);
+ return false;
+ }
+
+ // we need to copy over the uservalues of the userdata as well
+ {
+ int const mt{ lua_absindex(L1, -2) }; // L1: ... mt __lanesclone
+ size_t const userdata_size{ lua_rawlen(L1, L1i) };
+ // extract all the uservalues, but don't transfer them yet
+ int uvi = 0;
+ while (lua_getiuservalue(L1, L1i, ++uvi) != LUA_TNONE) {} // L1: ... mt __lanesclone [uv]+ nil
+ // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
+ lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]+
+ --uvi;
+ // create the clone userdata with the required number of uservalue slots
+ void* const clone{ lua_newuserdatauv(L2, userdata_size, uvi) }; // L2: ... u
+ // copy the metatable in the target state, and give it to the clone we put there
+ InterCopyContext c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name };
+ if (c.inter_copy_one()) { // L2: ... u mt|sentinel
+ if (LookupMode::ToKeeper == mode) { // L2: ... u sentinel
+ LUA_ASSERT(L1, lua_tocfunction(L2, -1) == table_lookup_sentinel);
+ // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn
+ lua_getupvalue(L2, -1, 1); // L2: ... u sentinel fqn
+ lua_remove(L2, -2); // L2: ... u fqn
+ lua_insert(L2, -2); // L2: ... fqn u
+ lua_pushcclosure(L2, userdata_clone_sentinel, 2); // L2: ... userdata_clone_sentinel
+ } else { // from keeper or direct // L2: ... u mt
+ LUA_ASSERT(L1, lua_istable(L2, -1));
+ lua_setmetatable(L2, -2); // L2: ... u
+ }
+ STACK_CHECK(L2, 1);
+ } else {
+ raise_luaL_error(getErrL(), "Error copying a metatable");
+ }
+ // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel
+ lua_pushlightuserdata(L2, source); // L2: ... u source
+ lua_pushvalue(L2, -2); // L2: ... u source u
+ lua_rawset(L2, L2_cache_i); // L2: ... u
+ // make sure we have the userdata now
+ if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel
+ lua_getupvalue(L2, -1, 2); // L2: ... userdata_clone_sentinel u
+ }
+ // assign uservalues
+ while (uvi > 0) {
+ c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
+ if (!c.inter_copy_one()) { // L2: ... u uv
+ raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
+ }
+ lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]*
+ // this pops the value from the stack
+ lua_setiuservalue(L2, -2, uvi); // L2: ... u
+ --uvi;
+ }
+ // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination
+ if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel u
+ lua_pop(L2, 1); // L2: ... userdata_clone_sentinel
+ }
+ STACK_CHECK(L2, 1);
+ STACK_CHECK(L1, 2);
+ // call cloning function in source state to perform the actual memory cloning
+ lua_pushlightuserdata(L1, clone); // L1: ... mt __lanesclone clone
+ lua_pushlightuserdata(L1, source); // L1: ... mt __lanesclone clone source
+ lua_pushinteger(L1, static_cast(userdata_size)); // L1: ... mt __lanesclone clone source size
+ lua_call(L1, 3, 0); // L1: ... mt
+ STACK_CHECK(L1, 1);
+ }
+
+ STACK_CHECK(L2, 1);
+ lua_pop(L1, 1); // L1: ...
+ STACK_CHECK(L1, 0);
+ return true;
+}
+
+// #################################################################################################
+
+// Copy deep userdata between two separate Lua states (from L1 to L2)
+// Returns false if not a deep userdata, else true (unless an error occured)
+[[nodiscard]] bool InterCopyContext::tryCopyDeep() const
+{
+ DeepFactory* const factory{ LookupFactory(L1, L1_i, mode) };
+ if (factory == nullptr) {
+ return false; // not a deep userdata
+ }
+
+ STACK_CHECK_START_REL(L1, 0);
+ STACK_CHECK_START_REL(L2, 0);
+
+ // extract all uservalues of the source. unfortunately, the only way to know their count is to iterate until we fail
+ int nuv = 0;
+ while (lua_getiuservalue(L1, L1_i, nuv + 1) != LUA_TNONE) { // L1: ... u [uv]* nil
+ ++nuv;
+ }
+ // last call returned TNONE and pushed nil, that we don't need
+ lua_pop(L1, 1); // L1: ... u [uv]*
+ STACK_CHECK(L1, nuv);
+
+ DeepPrelude* const u{ *lua_tofulluserdata(L1, L1_i) };
+ char const* errmsg{ DeepFactory::PushDeepProxy(L2, u, nuv, mode) }; // L1: ... u [uv]* L2: u
+ if (errmsg != nullptr) {
+ raise_luaL_error(getErrL(), errmsg);
+ }
+
+ // transfer all uservalues of the source in the destination
+ {
+ InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, name };
+ int const clone_i{ lua_gettop(L2) };
+ while (nuv) {
+ c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
+ if (!c.inter_copy_one()) { // L1: ... u [uv]* L2: u uv
+ raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
+ }
+ lua_pop(L1, 1); // L1: ... u [uv]*
+ // this pops the value from the stack
+ lua_setiuservalue(L2, clone_i, nuv); // L2: u
+ --nuv;
+ }
+ }
+
+ STACK_CHECK(L2, 1);
+ STACK_CHECK(L1, 0);
+
+ return true;
+}
+
+// #################################################################################################
+
+[[nodiscard]] bool InterCopyContext::inter_copy_boolean() const
+{
+ int const v{ lua_toboolean(L1, L1_i) };
+ DEBUGSPEW_CODE(fprintf(stderr, "%s\n", v ? "true" : "false"));
+ lua_pushboolean(L2, v);
+ return true;
+}
+
+// #################################################################################################
+
+[[nodiscard]] bool InterCopyContext::inter_copy_function() const
+{
+ if (vt == VT::KEY) {
+ return false;
+ }
+
+ STACK_CHECK_START_REL(L1, 0);
+ STACK_CHECK_START_REL(L2, 0);
+ DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name));
+
+ if (lua_tocfunction(L1, L1_i) == userdata_clone_sentinel) { // we are actually copying a clonable full userdata from a keeper
+ // clone the full userdata again
+
+ // let's see if we already restored this userdata
+ lua_getupvalue(L1, L1_i, 2); // L1: ... u
+ void* source = lua_touserdata(L1, -1);
+ lua_pushlightuserdata(L2, source); // L2: ... source
+ lua_rawget(L2, L2_cache_i); // L2: ... u?
+ if (!lua_isnil(L2, -1)) {
+ lua_pop(L1, 1); // L1: ...
+ STACK_CHECK(L1, 0);
+ STACK_CHECK(L2, 1);
+ return true;
+ }
+ lua_pop(L2, 1); // L2: ...
+
+ // userdata_clone_sentinel has 2 upvalues: the fqn of its metatable, and the userdata itself
+ bool const found{ lookup_table() }; // L2: ... mt?
+ if (!found) {
+ STACK_CHECK(L2, 0);
+ return false;
+ }
+ // 'L1_i' slot was the proxy closure, but from now on we operate onthe actual userdata we extracted from it
+ SourceIndex const source_i{ lua_gettop(L1) };
+ source = lua_touserdata(L1, -1);
+ void* clone{ nullptr };
+ // get the number of bytes to allocate for the clone
+ size_t const userdata_size{ lua_rawlen(L1, -1) };
+ {
+ // extract uservalues (don't transfer them yet)
+ int uvi = 0;
+ while (lua_getiuservalue(L1, source_i, ++uvi) != LUA_TNONE) {} // L1: ... u uv
+ // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
+ lua_pop(L1, 1); // L1: ... u [uv]*
+ --uvi;
+ STACK_CHECK(L1, uvi + 1);
+ // create the clone userdata with the required number of uservalue slots
+ clone = lua_newuserdatauv(L2, userdata_size, uvi); // L2: ... mt u
+ // add it in the cache
+ lua_pushlightuserdata(L2, source); // L2: ... mt u source
+ lua_pushvalue(L2, -2); // L2: ... mt u source u
+ lua_rawset(L2, L2_cache_i); // L2: ... mt u
+ // set metatable
+ lua_pushvalue(L2, -2); // L2: ... mt u mt
+ lua_setmetatable(L2, -2); // L2: ... mt u
+ // transfer and assign uservalues
+ InterCopyContext c{ *this };
+ while (uvi > 0) {
+ c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
+ if (!c.inter_copy_one()) { // L2: ... mt u uv
+ raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
+ }
+ lua_pop(L1, 1); // L1: ... u [uv]*
+ // this pops the value from the stack
+ lua_setiuservalue(L2, -2, uvi); // L2: ... mt u
+ --uvi;
+ }
+ // when we are done, all uservalues are popped from the stack, we can pop the source as well
+ lua_pop(L1, 1); // L1: ...
+ STACK_CHECK(L1, 0);
+ STACK_CHECK(L2, 2); // L2: ... mt u
+ }
+ // perform the custom cloning part
+ lua_insert(L2, -2); // L2: ... u mt
+ // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with
+ lua_getfield(L2, -1, "__lanesclone"); // L2: ... u mt __lanesclone
+ lua_remove(L2, -2); // L2: ... u __lanesclone
+ lua_pushlightuserdata(L2, clone); // L2: ... u __lanesclone clone
+ lua_pushlightuserdata(L2, source); // L2: ... u __lanesclone clone source
+ lua_pushinteger(L2, userdata_size); // L2: ... u __lanesclone clone source size
+ // clone:__lanesclone(dest, source, size)
+ lua_call(L2, 3, 0); // L2: ... u
+ } else { // regular function
+ DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name));
+ DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
+ copy_cached_func(); // L2: ... f
+ }
+ STACK_CHECK(L2, 1);
+ STACK_CHECK(L1, 0);
+ return true;
+}
+
+// #################################################################################################
+
+[[nodiscard]] bool InterCopyContext::inter_copy_lightuserdata() const
+{
+ void* const p{ lua_touserdata(L1, L1_i) };
+ DEBUGSPEW_CODE(fprintf(stderr, "%p\n", p));
+ lua_pushlightuserdata(L2, p);
+ return true;
+}
+
+// #################################################################################################
+
+[[nodiscard]] bool InterCopyContext::inter_copy_nil() const
+{
+ if (vt == VT::KEY) {
+ return false;
+ }
+ lua_pushnil(L2);
+ return true;
+}
+
+// #################################################################################################
+
+[[nodiscard]] bool InterCopyContext::inter_copy_number() const
+{
+ // LNUM patch support (keeping integer accuracy)
+#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
+ if (lua_isinteger(L1, L1_i)) {
+ lua_Integer const v{ lua_tointeger(L1, L1_i) };
+ DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", v));
+ lua_pushinteger(L2, v);
+ } else
+#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
+ {
+ lua_Number const v{ lua_tonumber(L1, L1_i) };
+ DEBUGSPEW_CODE(fprintf(stderr, LUA_NUMBER_FMT "\n", v));
+ lua_pushnumber(L2, v);
+ }
+ return true;
+}
+
+// #################################################################################################
+
+[[nodiscard]] bool InterCopyContext::inter_copy_string() const
+{
+ size_t len;
+ char const* const s{ lua_tolstring(L1, L1_i, &len) };
+ DEBUGSPEW_CODE(fprintf(stderr, "'%s'\n", s));
+ lua_pushlstring(L2, s, len);
+ return true;
+}
+
+// #################################################################################################
+
+[[nodiscard]] bool InterCopyContext::inter_copy_table() const
+{
+ if (vt == VT::KEY) {
+ return false;
+ }
+
+ STACK_CHECK_START_REL(L1, 0);
+ STACK_CHECK_START_REL(L2, 0);
+ DEBUGSPEW_CODE(fprintf(stderr, "TABLE %s\n", name));
+
+ /*
+ * 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?)
+ * 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
+ */
+ if (lookup_table()) {
+ 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
+ return true;
+ }
+
+ /* Check if we've already copied the same table from 'L1' (during this transmission), and
+ * reuse the old copy. This allows table upvalues shared by multiple
+ * local functions to point to the same table, also in the target.
+ * Also, this takes care of cyclic tables and multiple references
+ * to the same subtable.
+ *
+ * Note: Even metatables need to go through this test; to detect
+ * loops such as those in required module tables (getmetatable(lanes).lanes == lanes)
+ */
+ if (push_cached_table()) {
+ LUA_ASSERT(L1, lua_istable(L2, -1)); // from cache
+ return true;
+ }
+ LUA_ASSERT(L1, lua_istable(L2, -1));
+
+ STACK_GROW(L1, 2);
+ STACK_GROW(L2, 2);
+
+ lua_pushnil(L1); // start iteration
+ while (lua_next(L1, L1_i)) {
+ // need a function to prevent overflowing the stack with verboseErrors-induced alloca()
+ inter_copy_keyvaluepair();
+ lua_pop(L1, 1); // pop value (next round)
+ }
+ STACK_CHECK(L1, 0);
+ STACK_CHECK(L2, 1);
+
+ // Metatables are expected to be immutable, and copied only once.
+ if (push_cached_metatable()) { // L2: ... t mt?
+ lua_setmetatable(L2, -2); // L2: ... t
+ }
+ STACK_CHECK(L2, 1);
+ STACK_CHECK(L1, 0);
+ return true;
+}
+
+// #################################################################################################
+
+[[nodiscard]] bool InterCopyContext::inter_copy_userdata() const
+{
+ STACK_CHECK_START_REL(L1, 0);
+ STACK_CHECK_START_REL(L2, 0);
+ if (vt == VT::KEY) {
+ return false;
+ }
+
+ // try clonable userdata first
+ if (tryCopyClonable()) {
+ STACK_CHECK(L1, 0);
+ STACK_CHECK(L2, 1);
+ return true;
+ }
+
+ STACK_CHECK(L1, 0);
+ STACK_CHECK(L2, 0);
+
+ // Allow only deep userdata entities to be copied across
+ DEBUGSPEW_CODE(fprintf(stderr, "USERDATA\n"));
+ if (tryCopyDeep()) {
+ STACK_CHECK(L1, 0);
+ STACK_CHECK(L2, 1);
+ return true;
+ }
+
+ STACK_CHECK(L1, 0);
+ STACK_CHECK(L2, 0);
+
+ // Not a deep or clonable full userdata
+ if (U->demoteFullUserdata) { // attempt demotion to light userdata
+ void* const lud{ lua_touserdata(L1, L1_i) };
+ lua_pushlightuserdata(L2, lud);
+ } else { // raise an error
+ raise_luaL_error(getErrL(), "can't copy non-deep full userdata across lanes");
+ }
+
+ STACK_CHECK(L2, 1);
+ STACK_CHECK(L1, 0);
+ return true;
+}
+
+// #################################################################################################
+
+#if USE_DEBUG_SPEW()
+static char const* lua_type_names[] = {
+ "LUA_TNIL"
+ , "LUA_TBOOLEAN"
+ , "LUA_TLIGHTUSERDATA"
+ , "LUA_TNUMBER"
+ , "LUA_TSTRING"
+ , "LUA_TTABLE"
+ , "LUA_TFUNCTION"
+ , "LUA_TUSERDATA"
+ , "LUA_TTHREAD"
+ , "" // not really a type
+ , "LUA_TJITCDATA" // LuaJIT specific
+};
+static char const* vt_names[] = {
+ "VT::NORMAL"
+ , "VT::KEY"
+ , "VT::METATABLE"
+};
+#endif // USE_DEBUG_SPEW()
+
+/*
+ * Copies a value from 'L1' state (at index 'i') to 'L2' state. Does not remove
+ * the original value.
+ *
+ * NOTE: Both the states must be solely in the current OS thread's possession.
+ *
+ * 'i' is an absolute index (no -1, ...)
+ *
+ * Returns true if value was pushed, false if its type is non-supported.
+ */
+[[nodiscard]] bool InterCopyContext::inter_copy_one() const
+{
+ static constexpr int kPODmask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING);
+ STACK_GROW(L2, 1);
+ STACK_CHECK_START_REL(L1, 0);
+ STACK_CHECK_START_REL(L2, 0);
+
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END(U)));
+ DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
+
+ LuaType val_type{ lua_type_as_enum(L1, L1_i) };
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s %s: " INDENT_END(U), lua_type_names[static_cast(val_type)], vt_names[static_cast(vt)]));
+
+ // Non-POD can be skipped if its metatable contains { __lanesignore = true }
+ if (((1 << static_cast(val_type)) & kPODmask) == 0) {
+ if (lua_getmetatable(L1, L1_i)) { // L1: ... mt
+ lua_getfield(L1, -1, "__lanesignore"); // L1: ... mt ignore?
+ if (lua_isboolean(L1, -1) && lua_toboolean(L1, -1)) {
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END(U)));
+ val_type = LuaType::NIL;
+ }
+ lua_pop(L1, 2); // L1: ...
+ }
+ }
+ STACK_CHECK(L1, 0);
+
+ // Lets push nil to L2 if the object should be ignored
+ bool ret{ true };
+ switch (val_type) {
+ // Basic types allowed both as values, and as table keys
+ case LuaType::BOOLEAN:
+ ret = inter_copy_boolean();
+ break;
+ case LuaType::NUMBER:
+ ret = inter_copy_number();
+ break;
+ case LuaType::STRING:
+ ret = inter_copy_string();
+ break;
+ case LuaType::LIGHTUSERDATA:
+ ret = inter_copy_lightuserdata();
+ break;
+
+ // The following types are not allowed as table keys
+ case LuaType::USERDATA:
+ ret = inter_copy_userdata();
+ break;
+ case LuaType::NIL:
+ ret = inter_copy_nil();
+ break;
+ case LuaType::FUNCTION:
+ ret = inter_copy_function();
+ break;
+ case LuaType::TABLE:
+ ret = inter_copy_table();
+ break;
+
+ // The following types cannot be copied
+ case LuaType::CDATA:
+ [[fallthrough]];
+ case LuaType::THREAD:
+ ret = false;
+ break;
+ }
+
+ STACK_CHECK(L2, ret ? 1 : 0);
+ STACK_CHECK(L1, 0);
+ return ret;
+}
+
+// #################################################################################################
+
+// transfers stuff from L1->_G["package"] to L2->_G["package"]
+// returns InterCopyResult::Success if everything is fine
+// returns InterCopyResult::Error if pushed an error message in L1
+// else raise an error in L1
+[[nodiscard]] InterCopyResult InterCopyContext::inter_copy_package() const
+{
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy_package()\n" INDENT_END(U)));
+
+ class OnExit
+ {
+ private:
+ lua_State* const L2;
+ int const top_L2;
+ DEBUGSPEW_CODE(DebugSpewIndentScope m_scope);
+
+ public:
+ OnExit(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L2_)
+ : L2{ L2_ }
+ , top_L2{ lua_gettop(L2) } DEBUGSPEW_COMMA_PARAM(m_scope{ U_ })
+ {
+ }
+
+ ~OnExit()
+ {
+ lua_settop(L2, top_L2);
+ }
+ } onExit{ DEBUGSPEW_PARAM_COMMA(U) L2 };
+
+ STACK_CHECK_START_REL(L1, 0);
+ if (lua_type_as_enum(L1, L1_i) != LuaType::TABLE) {
+ lua_pushfstring(L1, "expected package as table, got %s", luaL_typename(L1, L1_i));
+ STACK_CHECK(L1, 1);
+ // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
+ if (mode == LookupMode::LaneBody) {
+ raise_lua_error(getErrL()); // that's ok, getErrL() is L1 in that case
+ }
+ return InterCopyResult::Error;
+ }
+ if (luaG_getmodule(L2, LUA_LOADLIBNAME) == LuaType::NIL) { // package library not loaded: do nothing
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END(U)));
+ STACK_CHECK(L1, 0);
+ return InterCopyResult::Success;
+ }
+
+ InterCopyResult result{ InterCopyResult::Success };
+ // package.loaders is renamed package.searchers in Lua 5.2
+ // but don't copy it anyway, as the function names change depending on the slot index!
+ // users should provide an on_state_create function to setup custom loaders instead
+ // don't copy package.preload in keeper states (they don't know how to translate functions)
+ char const* entries[] = { "path", "cpath", (mode == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr };
+ for (char const* const entry : entries) {
+ if (!entry) {
+ continue;
+ }
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END(U), entry));
+ lua_getfield(L1, L1_i, entry);
+ if (lua_isnil(L1, -1)) {
+ lua_pop(L1, 1);
+ } else {
+ {
+ DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
+ result = inter_move(1); // moves the entry to L2
+ STACK_CHECK(L1, 0);
+ }
+ if (result == InterCopyResult::Success) {
+ lua_setfield(L2, -2, entry); // set package[entry]
+ } else {
+ lua_pushfstring(L1, "failed to copy package entry %s", entry);
+ // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
+ if (mode == LookupMode::LaneBody) {
+ raise_lua_error(getErrL());
+ }
+ lua_pop(L1, 1);
+ break;
+ }
+ }
+ }
+ STACK_CHECK(L1, 0);
+ return result;
+}
+
+// #################################################################################################
+
+// Akin to 'lua_xmove' but copies values between _any_ Lua states.
+// NOTE: Both the states must be solely in the current OS thread's possession.
+[[nodiscard]] InterCopyResult InterCopyContext::inter_copy(int n_) const
+{
+ LUA_ASSERT(L1, vt == VT::NORMAL);
+
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy()\n" INDENT_END(U)));
+ DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
+
+ int const top_L1{ lua_gettop(L1) };
+ if (n_ > top_L1) {
+ // requesting to copy more than is available?
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END(U)));
+ return InterCopyResult::NotEnoughValues;
+ }
+
+ STACK_CHECK_START_REL(L2, 0);
+ STACK_GROW(L2, n_ + 1);
+
+ /*
+ * Make a cache table for the duration of this copy. Collects tables and
+ * function entries, avoiding the same entries to be passed on as multiple
+ * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner!
+ */
+ int const top_L2{ lua_gettop(L2) }; // L2: ...
+ lua_newtable(L2); // L2: ... cache
+
+ char tmpBuf[16];
+ char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" };
+ InterCopyContext c{ U, L2, L1, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode, pBuf };
+ bool copyok{ true };
+ STACK_CHECK_START_REL(L1, 0);
+ for (int i{ top_L1 - n_ + 1 }, j{ 1 }; i <= top_L1; ++i, ++j) {
+ if (U->verboseErrors) {
+ sprintf(tmpBuf, "arg_%d", j);
+ }
+ c.L1_i = SourceIndex{ i };
+ copyok = c.inter_copy_one(); // L2: ... cache {}n
+ if (!copyok) {
+ break;
+ }
+ }
+ STACK_CHECK(L1, 0);
+
+ if (copyok) {
+ STACK_CHECK(L2, n_ + 1);
+ // Remove the cache table. Persistent caching would cause i.e. multiple
+ // messages passed in the same table to use the same table also in receiving end.
+ lua_remove(L2, top_L2 + 1);
+ return InterCopyResult::Success;
+ }
+
+ // error -> pop everything from the target state stack
+ lua_settop(L2, top_L2);
+ STACK_CHECK(L2, 0);
+ return InterCopyResult::Error;
+}
+
+// #################################################################################################
+
+[[nodiscard]] InterCopyResult InterCopyContext::inter_move(int n_) const
+{
+ InterCopyResult const ret{ inter_copy(n_) };
+ lua_pop(L1, n_);
+ return ret;
+}
diff --git a/src/intercopycontext.h b/src/intercopycontext.h
new file mode 100644
index 0000000..28e1ead
--- /dev/null
+++ b/src/intercopycontext.h
@@ -0,0 +1,77 @@
+#pragma once
+
+#include "tools.h"
+
+// forwards
+class Universe;
+
+// #################################################################################################
+
+enum class VT
+{
+ NORMAL, // keep this one first so that it's the value we get when we default-construct
+ KEY,
+ METATABLE
+};
+
+enum class InterCopyResult
+{
+ Success,
+ NotEnoughValues,
+ Error
+};
+
+// #################################################################################################
+
+using CacheIndex = Unique;
+using SourceIndex = Unique;
+class InterCopyContext
+{
+ public:
+ Universe* const U;
+ DestState const L2;
+ SourceState const L1;
+ CacheIndex const L2_cache_i;
+ SourceIndex L1_i; // that one can change when we reuse the context
+ VT vt; // that one can change when we reuse the context
+ LookupMode const mode;
+ char const* name; // that one can change when we reuse the context
+
+ private:
+ // when mode == LookupMode::FromKeeper, L1 is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error
+ // whon mode != LookupMode::FromKeeper, L1 is not a keeper state, therefore L1 is the state where we want to raise the error
+ lua_State* getErrL() const { return (mode == LookupMode::FromKeeper) ? L2 : L1; }
+
+ // for use in copy_cached_func
+ void copy_func() const;
+ void lookup_native_func() const;
+
+ // for use in inter_copy_function
+ void copy_cached_func() const;
+ [[nodiscard]] bool lookup_table() const;
+
+ // for use in inter_copy_table
+ void inter_copy_keyvaluepair() const;
+ [[nodiscard]] bool push_cached_metatable() const;
+ [[nodiscard]] bool push_cached_table() const;
+
+ // for use in inter_copy_userdata
+ [[nodiscard]] bool tryCopyClonable() const;
+ [[nodiscard]] bool tryCopyDeep() const;
+
+ // copying a single Lua stack item
+ [[nodiscard]] bool inter_copy_boolean() const;
+ [[nodiscard]] bool inter_copy_function() const;
+ [[nodiscard]] bool inter_copy_lightuserdata() const;
+ [[nodiscard]] bool inter_copy_nil() const;
+ [[nodiscard]] bool inter_copy_number() const;
+ [[nodiscard]] bool inter_copy_string() const;
+ [[nodiscard]] bool inter_copy_table() const;
+ [[nodiscard]] bool inter_copy_userdata() const;
+
+ public:
+ [[nodiscard]] bool inter_copy_one() const;
+ [[nodiscard]] InterCopyResult inter_copy_package() const;
+ [[nodiscard]] InterCopyResult inter_copy(int n_) const;
+ [[nodiscard]] InterCopyResult inter_move(int n_) const;
+};
diff --git a/src/keeper.cpp b/src/keeper.cpp
index bb510f4..7367d0c 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -39,12 +39,9 @@
*/
#include "keeper.h"
-#include "compat.h"
+#include "intercopycontext.h"
#include "linda.h"
#include "state.h"
-#include "tools.h"
-#include "uniquekey.h"
-#include "universe.h"
#include
#include
diff --git a/src/keeper.h b/src/keeper.h
index 37642fd..8f30720 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -9,7 +9,6 @@ extern "C"
}
#endif // __cplusplus
-#include "threading.h"
#include "uniquekey.h"
#include
diff --git a/src/lanes.cpp b/src/lanes.cpp
index d211b6a..ee40ffa 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -81,7 +81,8 @@ THE SOFTWARE.
#include "lanes.h"
-#include "compat.h"
+#include "deep.h"
+#include "intercopycontext.h"
#include "keeper.h"
#include "lanes_private.h"
#include "state.h"
@@ -1521,6 +1522,84 @@ LUAG_FUNC(wakeup_conv)
return 1;
}
+// #################################################################################################
+// ################################### custom allocator support ####################################
+// #################################################################################################
+
+// same as PUC-Lua l_alloc
+extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_)
+{
+ if (nsize_ == 0) {
+ free(ptr_);
+ return nullptr;
+ } else {
+ return realloc(ptr_, nsize_);
+ }
+}
+
+// #################################################################################################
+
+[[nodiscard]] static int luaG_provide_protected_allocator(lua_State* L_)
+{
+ Universe* const U{ universe_get(L_) };
+ // push a new full userdata on the stack, giving access to the universe's protected allocator
+ [[maybe_unused]] AllocatorDefinition* const def{ new (L_) AllocatorDefinition{ U->protectedAllocator.makeDefinition() } };
+ return 1;
+}
+
+// #################################################################################################
+
+// called once at the creation of the universe (therefore L is the master Lua state everything originates from)
+// Do I need to disable this when compiling for LuaJIT to prevent issues?
+static void initialize_allocator_function(Universe* U_, lua_State* L_)
+{
+ STACK_CHECK_START_REL(L_, 1); // L_: settings
+ lua_getfield(L_, -1, "allocator"); // L_: settings allocator|nil|"protected"
+ if (!lua_isnil(L_, -1)) {
+ // store C function pointer in an internal variable
+ U_->provideAllocator = lua_tocfunction(L_, -1); // L_: settings allocator
+ if (U_->provideAllocator != nullptr) {
+ // make sure the function doesn't have upvalues
+ char const* upname = lua_getupvalue(L_, -1, 1); // L_: settings allocator upval?
+ if (upname != nullptr) { // should be "" for C functions with upvalues if any
+ raise_luaL_error(L_, "config.allocator() shouldn't have upvalues");
+ }
+ // remove this C function from the config table so that it doesn't cause problems
+ // when we transfer the config table in newly created Lua states
+ lua_pushnil(L_); // L_: settings allocator nil
+ lua_setfield(L_, -3, "allocator"); // L_: settings allocator
+ } else if (lua_type(L_, -1) == LUA_TSTRING) { // should be "protected"
+ LUA_ASSERT(L_, strcmp(lua_tostring(L_, -1), "protected") == 0);
+ // set the original allocator to call from inside protection by the mutex
+ U_->protectedAllocator.initFrom(L_);
+ U_->protectedAllocator.installIn(L_);
+ // before a state is created, this function will be called to obtain the allocator
+ U_->provideAllocator = luaG_provide_protected_allocator;
+ }
+ } else {
+ // just grab whatever allocator was provided to lua_newstate
+ U_->protectedAllocator.initFrom(L_);
+ }
+ lua_pop(L_, 1); // L_: settings
+ STACK_CHECK(L_, 1);
+
+ lua_getfield(L_, -1, "internal_allocator"); // L_: settings "libc"|"allocator"
+ {
+ char const* allocator = lua_tostring(L_, -1);
+ if (strcmp(allocator, "libc") == 0) {
+ U_->internalAllocator = AllocatorDefinition{ libc_lua_Alloc, nullptr };
+ } else if (U_->provideAllocator == luaG_provide_protected_allocator) {
+ // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case.
+ U_->internalAllocator = U_->protectedAllocator.makeDefinition();
+ } else {
+ // no protection required, just use whatever we have as-is.
+ U_->internalAllocator = U_->protectedAllocator;
+ }
+ }
+ lua_pop(L_, 1); // L_: settings
+ STACK_CHECK(L_, 1);
+}
+
// #################################################################################################
// ######################################## Module linkage #########################################
// #################################################################################################
diff --git a/src/linda.cpp b/src/linda.cpp
index 40ef6c7..07911b3 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -32,13 +32,9 @@ THE SOFTWARE.
#include "linda.h"
-#include "compat.h"
-#include "keeper.h"
#include "lanes_private.h"
#include "lindafactory.h"
-#include "threading.h"
#include "tools.h"
-#include "universe.h"
#include
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp
index 917d949..0ec5a0a 100644
--- a/src/lindafactory.cpp
+++ b/src/lindafactory.cpp
@@ -32,7 +32,6 @@ THE SOFTWARE.
#include "lindafactory.h"
-#include "lanes_private.h"
#include "linda.h"
// must be a #define instead of a constexpr to work with lua_pushliteral (until I templatize it)
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h
index a1f6cba..b8f04b6 100644
--- a/src/macros_and_utils.h
+++ b/src/macros_and_utils.h
@@ -61,7 +61,7 @@ template
// #################################################################################################
-#define USE_DEBUG_SPEW() 0
+#define USE_DEBUG_SPEW() 1
#if USE_DEBUG_SPEW()
#define INDENT_BEGIN "%.*s "
#define INDENT_END(U_) , (U_ ? U_->debugspewIndentDepth.load(std::memory_order_relaxed) : 0), DebugSpewIndentScope::debugspew_indent
diff --git a/src/state.cpp b/src/state.cpp
index f894978..7252885 100644
--- a/src/state.cpp
+++ b/src/state.cpp
@@ -33,6 +33,7 @@ THE SOFTWARE.
#include "state.h"
+#include "intercopycontext.h"
#include "lanes.h"
#include "lanes_private.h"
#include "tools.h"
diff --git a/src/tools.cpp b/src/tools.cpp
index 73efda9..2623da6 100644
--- a/src/tools.cpp
+++ b/src/tools.cpp
@@ -1,5 +1,6 @@
/*
- * TOOLS.C Copyright (c) 2002-10, Asko Kauppi
+ * TOOLS.CPP Copyright (c) 2002-10, Asko Kauppi
+ * Copyright (C) 2010-24, Benoit Germain
*
* Lua tools to support Lanes.
*/
@@ -8,7 +9,7 @@
===============================================================================
Copyright (C) 2002-10 Asko Kauppi
- 2011-17 benoit Germain
+ 2011-24 benoit Germain
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -42,82 +43,6 @@ static constexpr RegistryUniqueKey kLookupCacheRegKey{ 0x9BF75F84E54B691Bull };
// #################################################################################################
-// same as PUC-Lua l_alloc
-extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_)
-{
- if (nsize_ == 0) {
- free(ptr_);
- return nullptr;
- } else {
- return realloc(ptr_, nsize_);
- }
-}
-
-// #################################################################################################
-
-[[nodiscard]] static int luaG_provide_protected_allocator(lua_State* L_)
-{
- Universe* const U{ universe_get(L_) };
- // push a new full userdata on the stack, giving access to the universe's protected allocator
- [[maybe_unused]] AllocatorDefinition* const def{ new (L_) AllocatorDefinition{ U->protectedAllocator.makeDefinition() } };
- return 1;
-}
-
-// #################################################################################################
-
-// called once at the creation of the universe (therefore L is the master Lua state everything originates from)
-// Do I need to disable this when compiling for LuaJIT to prevent issues?
-void initialize_allocator_function(Universe* U_, lua_State* L_)
-{
- STACK_CHECK_START_REL(L_, 1); // L_: settings
- lua_getfield(L_, -1, "allocator"); // L_: settings allocator|nil|"protected"
- if (!lua_isnil(L_, -1)) {
- // store C function pointer in an internal variable
- U_->provideAllocator = lua_tocfunction(L_, -1); // L_: settings allocator
- if (U_->provideAllocator != nullptr) {
- // make sure the function doesn't have upvalues
- char const* upname = lua_getupvalue(L_, -1, 1); // L_: settings allocator upval?
- if (upname != nullptr) { // should be "" for C functions with upvalues if any
- raise_luaL_error(L_, "config.allocator() shouldn't have upvalues");
- }
- // remove this C function from the config table so that it doesn't cause problems
- // when we transfer the config table in newly created Lua states
- lua_pushnil(L_); // L_: settings allocator nil
- lua_setfield(L_, -3, "allocator"); // L_: settings allocator
- } else if (lua_type(L_, -1) == LUA_TSTRING) { // should be "protected"
- LUA_ASSERT(L_, strcmp(lua_tostring(L_, -1), "protected") == 0);
- // set the original allocator to call from inside protection by the mutex
- U_->protectedAllocator.initFrom(L_);
- U_->protectedAllocator.installIn(L_);
- // before a state is created, this function will be called to obtain the allocator
- U_->provideAllocator = luaG_provide_protected_allocator;
- }
- } else {
- // just grab whatever allocator was provided to lua_newstate
- U_->protectedAllocator.initFrom(L_);
- }
- lua_pop(L_, 1); // L_: settings
- STACK_CHECK(L_, 1);
-
- lua_getfield(L_, -1, "internal_allocator"); // L_: settings "libc"|"allocator"
- {
- char const* allocator = lua_tostring(L_, -1);
- if (strcmp(allocator, "libc") == 0) {
- U_->internalAllocator = AllocatorDefinition{ libc_lua_Alloc, nullptr };
- } else if (U_->provideAllocator == luaG_provide_protected_allocator) {
- // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case.
- U_->internalAllocator = U_->protectedAllocator.makeDefinition();
- } else {
- // no protection required, just use whatever we have as-is.
- U_->internalAllocator = U_->protectedAllocator;
- }
- }
- lua_pop(L_, 1); // L_: settings
- STACK_CHECK(L_, 1);
-}
-
-// #################################################################################################
-
[[nodiscard]] static int dummy_writer([[maybe_unused]] lua_State* L_, [[maybe_unused]] void const* p_, [[maybe_unused]] size_t sz_, [[maybe_unused]] void* ud_)
{
return 666;
@@ -138,14 +63,7 @@ void initialize_allocator_function(Universe* U_, lua_State* L_)
* +-----------------+----------+------------+----------+
*/
-enum class FuncSubType
-{
- Bytecode,
- Native,
- FastJIT
-};
-
-FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i)
+[[nodiscard]] FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i)
{
if (lua_tocfunction(L_, _i)) { // nullptr for LuaJIT-fast && bytecode functions
return FuncSubType::Native;
@@ -430,227 +348,6 @@ void populate_func_lookup_table(lua_State* L_, int i_, char const* name_)
// #################################################################################################
-/*---=== Inter-state copying ===---*/
-
-// xxh64 of string "kMtIdRegKey" generated at https://www.pelock.com/products/hash-calculator
-static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull };
-
-// get a unique ID for metatable at [i].
-[[nodiscard]] static lua_Integer get_mt_id(Universe* U_, lua_State* L_, int idx_)
-{
- idx_ = lua_absindex(L_, idx_);
-
- STACK_GROW(L_, 3);
-
- STACK_CHECK_START_REL(L_, 0);
- std::ignore = kMtIdRegKey.getSubTable(L_, 0, 0); // L_: ... _R[kMtIdRegKey]
- lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] {mt}
- lua_rawget(L_, -2); // L_: ... _R[kMtIdRegKey] mtk?
-
- lua_Integer id{ lua_tointeger(L_, -1) }; // 0 for nil
- lua_pop(L_, 1); // L_: ... _R[kMtIdRegKey]
- STACK_CHECK(L_, 1);
-
- if (id == 0) {
- id = U_->nextMetatableId.fetch_add(1, std::memory_order_relaxed);
-
- // Create two-way references: id_uint <-> table
- lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] {mt}
- lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] {mt} id
- lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey]
-
- lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] id
- lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] id {mt}
- lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey]
- }
- lua_pop(L_, 1); // L_: ...
- STACK_CHECK(L_, 0);
-
- return id;
-}
-
-// #################################################################################################
-
-// function sentinel used to transfer native functions from/to keeper states
-[[nodiscard]] static int func_lookup_sentinel(lua_State* L_)
-{
- raise_luaL_error(L_, "function lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1)));
-}
-
-// #################################################################################################
-
-// function sentinel used to transfer native table from/to keeper states
-[[nodiscard]] static int table_lookup_sentinel(lua_State* L_)
-{
- raise_luaL_error(L_, "table lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1)));
-}
-
-// #################################################################################################
-
-// function sentinel used to transfer cloned full userdata from/to keeper states
-[[nodiscard]] static int userdata_clone_sentinel(lua_State* L_)
-{
- raise_luaL_error(L_, "userdata clone sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1)));
-}
-
-// #################################################################################################
-
-// retrieve the name of a function/table in the lookup database
-[[nodiscard]] static char const* find_lookup_name(lua_State* L_, int i_, LookupMode mode_, char const* upName_, size_t* len_)
-{
- LUA_ASSERT(L_, lua_isfunction(L_, i_) || lua_istable(L_, i_)); // L_: ... v ...
- STACK_CHECK_START_REL(L_, 0);
- STACK_GROW(L_, 3); // up to 3 slots are necessary on error
- if (mode_ == LookupMode::FromKeeper) {
- lua_CFunction f = lua_tocfunction(L_, i_); // should *always* be one of the function sentinels
- if (f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel) {
- lua_getupvalue(L_, i_, 1); // L_: ... v ... "f.q.n"
- } else {
- // if this is not a sentinel, this is some user-created table we wanted to lookup
- LUA_ASSERT(L_, nullptr == f && lua_istable(L_, i_));
- // push anything that will convert to nullptr string
- lua_pushnil(L_); // L_: ... v ... nil
- }
- } else {
- // fetch the name from the source state's lookup table
- kLookupRegKey.pushValue(L_); // L_: ... v ... {}
- STACK_CHECK(L_, 1);
- LUA_ASSERT(L_, lua_istable(L_, -1));
- lua_pushvalue(L_, i_); // L_: ... v ... {} v
- lua_rawget(L_, -2); // L_: ... v ... {} "f.q.n"
- }
- char const* fqn{ lua_tolstring(L_, -1, len_) };
- DEBUGSPEW_CODE(Universe* const U = universe_get(L_));
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END(U), fqn));
- // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database
- lua_pop(L_, (mode_ == LookupMode::FromKeeper) ? 1 : 2); // L_: ... v ...
- STACK_CHECK(L_, 0);
- if (nullptr == fqn && !lua_istable(L_, i_)) { // raise an error if we try to send an unknown function (but not for tables)
- *len_ = 0; // just in case
- // try to discover the name of the function we want to send
- lua_getglobal(L_, "decoda_name"); // L_: ... v ... decoda_name
- char const* from{ lua_tostring(L_, -1) };
- lua_pushcfunction(L_, luaG_nameof); // L_: ... v ... decoda_name luaG_nameof
- lua_pushvalue(L_, i_); // L_: ... v ... decoda_name luaG_nameof t
- lua_call(L_, 1, 2); // L_: ... v ... decoda_name "type" "name"|nil
- char const* typewhat{ (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : luaL_typename(L_, -2) };
- // second return value can be nil if the table was not found
- // probable reason: the function was removed from the source Lua state before Lanes was required.
- char const *what, *gotchaA, *gotchaB;
- if (lua_isnil(L_, -1)) {
- gotchaA = " referenced by";
- gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)";
- what = upName_;
- } else {
- gotchaA = "";
- gotchaB = "";
- what = (lua_type(L_, -1) == LUA_TSTRING) ? lua_tostring(L_, -1) : luaL_typename(L_, -1);
- }
- raise_luaL_error(L_, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB);
- }
- STACK_CHECK(L_, 0);
- return fqn;
-}
-
-// #################################################################################################
-
-// Push a looked-up table, or nothing if we found nothing
-[[nodiscard]] bool InterCopyContext::lookup_table() const
-{
- // get the name of the table we want to send
- size_t len;
- char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len);
- if (nullptr == fqn) { // name not found, it is some user-created table
- return false;
- }
- // push the equivalent table in the destination's stack, retrieved from the lookup table
- STACK_CHECK_START_REL(L2, 0);
- STACK_GROW(L2, 3); // up to 3 slots are necessary on error
- switch (mode) {
- default: // shouldn't happen, in theory...
- raise_luaL_error(getErrL(), "internal error: unknown lookup mode");
- break;
-
- case LookupMode::ToKeeper:
- // push a sentinel closure that holds the lookup name as upvalue
- lua_pushlstring(L2, fqn, len); // L1: ... t ... L2: "f.q.n"
- lua_pushcclosure(L2, table_lookup_sentinel, 1); // L1: ... t ... L2: f
- break;
-
- case LookupMode::LaneBody:
- case LookupMode::FromKeeper:
- kLookupRegKey.pushValue(L2); // L1: ... t ... L2: {}
- STACK_CHECK(L2, 1);
- LUA_ASSERT(L1, lua_istable(L2, -1));
- lua_pushlstring(L2, fqn, len); // L2: {} "f.q.n"
- lua_rawget(L2, -2); // L2: {} t
- // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead)
- // but not when we extract something out of a keeper, as there is nothing to clone!
- if (lua_isnil(L2, -1) && mode == LookupMode::LaneBody) {
- lua_pop(L2, 2); // L1: ... t ... L2:
- STACK_CHECK(L2, 0);
- return false;
- } else if (!lua_istable(L2, -1)) { // this can happen if someone decides to replace same already registered item (for a example a standard lib function) with a table
- lua_getglobal(L1, "decoda_name"); // L1: ... t ... decoda_name
- char const* from{ lua_tostring(L1, -1) };
- lua_pop(L1, 1); // L1: ... t ...
- lua_getglobal(L2, "decoda_name"); // L1: ... t ... L2: {} t decoda_name
- char const* to{ lua_tostring(L2, -1) };
- lua_pop(L2, 1); // L1: ... t ... L2: {} t
- raise_luaL_error(
- getErrL(),
- "%s: source table '%s' found as %s in %s destination transfer database.",
- from ? from : "main",
- fqn,
- lua_typename(L2, lua_type_as_enum(L2, -1)),
- to ? to : "main"
- );
- }
- lua_remove(L2, -2); // L1: ... t ... L2: t
- break;
- }
- STACK_CHECK(L2, 1);
- return true;
-}
-
-// #################################################################################################
-
-// Check if we've already copied the same table from 'L1', and reuse the old copy. This allows table upvalues shared by multiple
-// local functions to point to the same table, also in the target.
-// Always pushes a table to 'L2'.
-// Returns true if the table was cached (no need to fill it!); false if it's a virgin.
-[[nodiscard]] bool InterCopyContext::push_cached_table() const
-{
- void const* p{ lua_topointer(L1, L1_i) };
-
- LUA_ASSERT(L1, L2_cache_i != 0);
- STACK_GROW(L2, 3);
- STACK_CHECK_START_REL(L2, 0);
-
- // We don't need to use the from state ('L1') in ID since the life span
- // is only for the duration of a copy (both states are locked).
- // push a light userdata uniquely representing the table
- lua_pushlightuserdata(L2, const_cast(p)); // L1: ... t ... L2: ... p
-
- // fprintf(stderr, "<< ID: %s >>\n", lua_tostring(L2, -1));
-
- lua_rawget(L2, L2_cache_i); // L1: ... t ... L2: ... {cached|nil}
- bool const not_found_in_cache{ lua_isnil(L2, -1) };
- if (not_found_in_cache) {
- // create a new entry in the cache
- lua_pop(L2, 1); // L1: ... t ... L2: ...
- lua_newtable(L2); // L1: ... t ... L2: ... {}
- lua_pushlightuserdata(L2, const_cast(p)); // L1: ... t ... L2: ... {} p
- lua_pushvalue(L2, -2); // L1: ... t ... L2: ... {} p {}
- lua_rawset(L2, L2_cache_i); // L1: ... t ... L2: ... {}
- }
- STACK_CHECK(L2, 1);
- LUA_ASSERT(L1, lua_istable(L2, -1));
- return !not_found_in_cache;
-}
-
-// #################################################################################################
-
// Return some name helping to identify an object
[[nodiscard]] static int DiscoverObjectNameRecur(lua_State* L_, int shortest_, int depth_)
{
@@ -826,1001 +523,3 @@ int luaG_nameof(lua_State* L_)
lua_replace(L_, -3); // L_: "type" "result"
return 2;
}
-
-// #################################################################################################
-
-// Push a looked-up native/LuaJIT function.
-void InterCopyContext::lookup_native_func() const
-{
- // get the name of the function we want to send
- size_t len;
- char const* const fqn{ find_lookup_name(L1, L1_i, mode, name, &len) };
- // push the equivalent function in the destination's stack, retrieved from the lookup table
- STACK_CHECK_START_REL(L2, 0);
- STACK_GROW(L2, 3); // up to 3 slots are necessary on error
- switch (mode) {
- default: // shouldn't happen, in theory...
- raise_luaL_error(getErrL(), "internal error: unknown lookup mode");
- break;
-
- case LookupMode::ToKeeper:
- // push a sentinel closure that holds the lookup name as upvalue
- lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: "f.q.n"
- lua_pushcclosure(L2, func_lookup_sentinel, 1); // L1: ... f ... L2: f
- break;
-
- case LookupMode::LaneBody:
- case LookupMode::FromKeeper:
- kLookupRegKey.pushValue(L2); // L1: ... f ... L2: {}
- STACK_CHECK(L2, 1);
- LUA_ASSERT(L1, lua_istable(L2, -1));
- lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: {} "f.q.n"
- lua_rawget(L2, -2); // L1: ... f ... L2: {} f
- // nil means we don't know how to transfer stuff: user should do something
- // anything other than function or table should not happen!
- if (!lua_isfunction(L2, -1) && !lua_istable(L2, -1)) {
- lua_getglobal(L1, "decoda_name"); // L1: ... f ... decoda_name
- char const* const from{ lua_tostring(L1, -1) };
- lua_pop(L1, 1); // L1: ... f ...
- lua_getglobal(L2, "decoda_name"); // L1: ... f ... L2: {} f decoda_name
- char const* const to{ lua_tostring(L2, -1) };
- lua_pop(L2, 1); // L2: {} f
- // 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
- raise_luaL_error(
- getErrL()
- , "%s%s: function '%s' not found in %s destination transfer database."
- , lua_isnil(L2, -1) ? "" : "INTERNAL ERROR IN "
- , from ? from : "main"
- , fqn
- , to ? to : "main"
- );
- return;
- }
- lua_remove(L2, -2); // L2: f
- break;
-
- /* keep it in case I need it someday, who knows...
- case LookupMode::RawFunctions:
- {
- int n;
- char const* upname;
- lua_CFunction f = lua_tocfunction( L, i);
- // copy upvalues
- for (n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != nullptr; ++ n) {
- luaG_inter_move( U, L, L2, 1, mode_); // L2: [up[,up ...]]
- }
- lua_pushcclosure( L2, f, n); // L2:
- }
- break;
- */
- }
- STACK_CHECK(L2, 1);
-}
-
-// #################################################################################################
-
-#if USE_DEBUG_SPEW()
-static char const* lua_type_names[] = {
- "LUA_TNIL"
- , "LUA_TBOOLEAN"
- , "LUA_TLIGHTUSERDATA"
- , "LUA_TNUMBER"
- , "LUA_TSTRING"
- , "LUA_TTABLE"
- , "LUA_TFUNCTION"
- , "LUA_TUSERDATA"
- , "LUA_TTHREAD"
- , "" // not really a type
- , "LUA_TJITCDATA" // LuaJIT specific
-};
-static char const* vt_names[] = {
- "VT::NORMAL"
- , "VT::KEY"
- , "VT::METATABLE"
-};
-#endif // USE_DEBUG_SPEW()
-
-// #################################################################################################
-
-// Lua 5.4.3 style of dumping (see lstrlib.c)
-// we have to do it that way because we can't unbalance the stack between buffer operations
-// namely, this means we can't push a function on top of the stack *after* we initialize the buffer!
-// luckily, this also works with earlier Lua versions
-[[nodiscard]] static int buf_writer(lua_State* L_, void const* b_, size_t size_, void* ud_)
-{
- luaL_Buffer* const B{ static_cast(ud_) };
- if (!B->L) {
- luaL_buffinit(L_, B);
- }
- luaL_addlstring(B, static_cast(b_), size_);
- return 0;
-}
-
-// #################################################################################################
-
-// Copy a function over, which has not been found in the cache.
-// L2 has the cache key for this function at the top of the stack
-void InterCopyContext::copy_func() const
-{
- LUA_ASSERT(L1, L2_cache_i != 0); // L2: ... {cache} ... p
- STACK_GROW(L1, 2);
- STACK_CHECK_START_REL(L1, 0);
-
- // 'lua_dump()' needs the function at top of stack
- // if already on top of the stack, no need to push again
- bool const needToPush{ L1_i != lua_gettop(L1) };
- if (needToPush) {
- lua_pushvalue(L1, L1_i); // L1: ... f
- }
-
- //
- // "value returned is the error code returned by the last call
- // to the writer" (and we only return 0)
- // not sure this could ever fail but for memory shortage reasons
- // last parameter is Lua 5.4-specific (no stripping)
- luaL_Buffer B;
- B.L = nullptr;
- if (lua504_dump(L1, buf_writer, &B, 0) != 0) {
- raise_luaL_error(getErrL(), "internal error: function dump failed.");
- }
-
- // pushes dumped string on 'L1'
- luaL_pushresult(&B); // L1: ... f b
-
- // if not pushed, no need to pop
- if (needToPush) {
- lua_remove(L1, -2); // L1: ... b
- }
-
- // transfer the bytecode, then the upvalues, to create a similar closure
- {
- char const* fname = nullptr;
-#define LOG_FUNC_INFO 0
-#if LOG_FUNC_INFO
- // "To get information about a function you push it onto the
- // stack and start the what string with the character '>'."
- //
- {
- lua_Debug ar;
- lua_pushvalue(L1, L1_i); // L1: ... b f
- // fills 'fname' 'namewhat' and 'linedefined', pops function
- lua_getinfo(L1, ">nS", &ar); // L1: ... b
- fname = ar.namewhat;
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "FNAME: %s @ %d" INDENT_END(U), ar.short_src, ar.linedefined)); // just gives nullptr
- }
-#endif // LOG_FUNC_INFO
- {
- size_t sz;
- char const* s = lua_tolstring(L1, -1, &sz); // L1: ... b
- LUA_ASSERT(L1, s && sz);
- STACK_GROW(L2, 2);
- // Note: Line numbers seem to be taken precisely from the
- // original function. 'fname' is not used since the chunk
- // is precompiled (it seems...).
- //
- // TBD: Can we get the function's original name through, as well?
- //
- if (luaL_loadbuffer(L2, s, sz, fname) != 0) { // L2: ... {cache} ... p function
- // chunk is precompiled so only LUA_ERRMEM can happen
- // "Otherwise, it pushes an error message"
- //
- STACK_GROW(L1, 1);
- raise_luaL_error(getErrL(), "%s: %s", fname, lua_tostring(L2, -1));
- }
- // remove the dumped string
- lua_pop(L1, 1); // ...
- // now set the cache as soon as we can.
- // this is necessary if one of the function's upvalues references it indirectly
- // we need to find it in the cache even if it isn't fully transfered yet
- lua_insert(L2, -2); // L2: ... {cache} ... function p
- lua_pushvalue(L2, -2); // L2: ... {cache} ... function p function
- // cache[p] = function
- lua_rawset(L2, L2_cache_i); // L2: ... {cache} ... function
- }
- STACK_CHECK(L1, 0);
-
- /* push over any upvalues; references to this function will come from
- * cache so we don't end up in eternal loop.
- * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy!
- * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state!
- */
- int n{ 0 };
- {
- InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} };
-#if LUA_VERSION_NUM >= 502
- // Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default)
- // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state...
- // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table
- lua_pushglobaltable(L1); // L1: ... _G
-#endif // LUA_VERSION_NUM
- for (n = 0; (c.name = lua_getupvalue(L1, L1_i, 1 + n)) != nullptr; ++n) { // L1: ... _G up[n]
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END(U), n, c.name));
-#if LUA_VERSION_NUM >= 502
- if (lua_rawequal(L1, -1, -2)) { // is the upvalue equal to the global table?
- DEBUGSPEW_CODE(fprintf(stderr, "pushing destination global scope\n"));
- lua_pushglobaltable(L2); // L2: ... {cache} ... function
- } else
-#endif // LUA_VERSION_NUM
- {
- DEBUGSPEW_CODE(fprintf(stderr, "copying value\n"));
- c.L1_i = SourceIndex{ lua_gettop(L1) };
- if (!c.inter_copy_one()) { // L2: ... {cache} ... function
- raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
- }
- }
- lua_pop(L1, 1); // L1: ... _G
- }
-#if LUA_VERSION_NUM >= 502
- lua_pop(L1, 1); // L1: ...
-#endif // LUA_VERSION_NUM
- }
- // L2: ... {cache} ... function + 'n' upvalues (>=0)
-
- STACK_CHECK(L1, 0);
-
- // Set upvalues (originally set to 'nil' by 'lua_load')
- for (int const func_index{ lua_gettop(L2) - n }; n > 0; --n) {
- char const* rc{ lua_setupvalue(L2, func_index, n) }; // L2: ... {cache} ... function
- //
- // "assigns the value at the top of the stack to the upvalue and returns its name.
- // It also pops the value from the stack."
-
- LUA_ASSERT(L1, rc); // not having enough slots?
- }
- // once all upvalues have been set we are left
- // with the function at the top of the stack // L2: ... {cache} ... function
- }
- STACK_CHECK(L1, 0);
-}
-
-// #################################################################################################
-
-// Check if we've already copied the same function from 'L1', and reuse the old copy.
-// Always pushes a function to 'L2'.
-void InterCopyContext::copy_cached_func() const
-{
- FuncSubType const funcSubType{ luaG_getfuncsubtype(L1, L1_i) };
- if (funcSubType == FuncSubType::Bytecode) {
- void* const aspointer = const_cast(lua_topointer(L1, L1_i));
- // TBD: Merge this and same code for tables
- LUA_ASSERT(L1, L2_cache_i != 0);
-
- STACK_GROW(L2, 2);
-
- // L2_cache[id_str]= function
- //
- STACK_CHECK_START_REL(L2, 0);
-
- // We don't need to use the from state ('L1') in ID since the life span
- // is only for the duration of a copy (both states are locked).
-
- // push a light userdata uniquely representing the function
- lua_pushlightuserdata(L2, aspointer); // L2: ... {cache} ... p
-
- // fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1));
-
- lua_pushvalue(L2, -1); // L2: ... {cache} ... p p
- lua_rawget(L2, L2_cache_i); // L2: ... {cache} ... p function|nil|true
-
- if (lua_isnil(L2, -1)) { // function is unknown
- lua_pop(L2, 1); // L2: ... {cache} ... p
-
- // Set to 'true' for the duration of creation; need to find self-references
- // via upvalues
- //
- // pushes a copy of the func, stores a reference in the cache
- copy_func(); // L2: ... {cache} ... function
- } else { // found function in the cache
- lua_remove(L2, -2); // L2: ... {cache} ... function
- }
- STACK_CHECK(L2, 1);
- LUA_ASSERT(L1, lua_isfunction(L2, -1));
- } else { // function is native/LuaJIT: no need to cache
- lookup_native_func(); // L2: ... {cache} ... function
- // if the function was in fact a lookup sentinel, we can either get a function or a table here
- LUA_ASSERT(L1, lua_isfunction(L2, -1) || lua_istable(L2, -1));
- }
-}
-
-// #################################################################################################
-
-[[nodiscard]] bool InterCopyContext::push_cached_metatable() const
-{
- STACK_CHECK_START_REL(L1, 0);
- if (!lua_getmetatable(L1, L1_i)) { // L1: ... mt
- STACK_CHECK(L1, 0);
- return false;
- }
- STACK_CHECK(L1, 1);
-
- lua_Integer const mt_id{ get_mt_id(U, L1, -1) }; // Unique id for the metatable
-
- STACK_CHECK_START_REL(L2, 0);
- STACK_GROW(L2, 4);
- // do we already know this metatable?
- std::ignore = kMtIdRegKey.getSubTable(L2, 0, 0); // L2: _R[kMtIdRegKey]
- lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] id
- lua_rawget(L2, -2); // L2: _R[kMtIdRegKey] mt|nil
- STACK_CHECK(L2, 2);
-
- if (lua_isnil(L2, -1)) { // L2 did not know the metatable
- lua_pop(L2, 1); // L2: _R[kMtIdRegKey]
- InterCopyContext const c{ U, L2, L1, L2_cache_i, SourceIndex{ lua_gettop(L1) }, VT::METATABLE, mode, name };
- if (!c.inter_copy_one()) { // L2: _R[kMtIdRegKey] mt?
- raise_luaL_error(getErrL(), "Error copying a metatable");
- }
-
- STACK_CHECK(L2, 2); // L2: _R[kMtIdRegKey] mt
- // mt_id -> metatable
- lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt id
- lua_pushvalue(L2, -2); // L2: _R[kMtIdRegKey] mt id mt
- lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt
-
- // metatable -> mt_id
- lua_pushvalue(L2, -1); // L2: _R[kMtIdRegKey] mt mt
- lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt mt id
- lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt
- STACK_CHECK(L2, 2);
- }
- lua_remove(L2, -2); // L2: mt
-
- lua_pop(L1, 1); // L1: ...
- STACK_CHECK(L2, 1);
- STACK_CHECK(L1, 0);
- return true;
-}
-
-// #################################################################################################
-
-void InterCopyContext::inter_copy_keyvaluepair() const
-{
- SourceIndex const val_i{ lua_gettop(L1) };
- SourceIndex const key_i{ val_i - 1 };
-
- // For the key, only basic key types are copied over. others ignored
- InterCopyContext c{ U, L2, L1, L2_cache_i, key_i, VT::KEY, mode, name };
- if (!c.inter_copy_one()) {
- return;
- // we could raise an error instead of ignoring the table entry, like so:
- // 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));
- // maybe offer this possibility as a global configuration option, or a linda setting, or as a parameter of the call causing the transfer?
- }
-
- char* valPath{ nullptr };
- if (U->verboseErrors) {
- // for debug purposes, let's try to build a useful name
- if (lua_type(L1, key_i) == LUA_TSTRING) {
- char const* key{ lua_tostring(L1, key_i) };
- size_t const keyRawLen = lua_rawlen(L1, key_i);
- size_t const bufLen = strlen(name) + keyRawLen + 2;
- valPath = (char*) alloca(bufLen);
- sprintf(valPath, "%s.%*s", name, (int) keyRawLen, key);
- key = nullptr;
- }
-#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
- else if (lua_isinteger(L1, key_i)) {
- lua_Integer const key{ lua_tointeger(L1, key_i) };
- valPath = (char*) alloca(strlen(name) + 32 + 3);
- sprintf(valPath, "%s[" LUA_INTEGER_FMT "]", name, key);
- }
-#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
- else if (lua_type(L1, key_i) == LUA_TNUMBER) {
- lua_Number const key{ lua_tonumber(L1, key_i) };
- valPath = (char*) alloca(strlen(name) + 32 + 3);
- sprintf(valPath, "%s[" LUA_NUMBER_FMT "]", name, key);
- } else if (lua_type(L1, key_i) == LUA_TLIGHTUSERDATA) {
- void* const key{ lua_touserdata(L1, key_i) };
- valPath = (char*) alloca(strlen(name) + 16 + 5);
- sprintf(valPath, "%s[U:%p]", name, key);
- } else if (lua_type(L1, key_i) == LUA_TBOOLEAN) {
- int const key{ lua_toboolean(L1, key_i) };
- valPath = (char*) alloca(strlen(name) + 8);
- sprintf(valPath, "%s[%s]", name, key ? "true" : "false");
- }
- }
- c.L1_i = SourceIndex{ val_i };
- // Contents of metatables are copied with cache checking. important to detect loops.
- c.vt = VT::NORMAL;
- c.name = valPath ? valPath : name;
- if (c.inter_copy_one()) {
- LUA_ASSERT(L1, lua_istable(L2, -3));
- lua_rawset(L2, -3); // add to table (pops key & val)
- } else {
- raise_luaL_error(getErrL(), "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", valPath, luaL_typename(L1, val_i));
- }
-}
-
-// #################################################################################################
-
-[[nodiscard]] bool InterCopyContext::tryCopyClonable() const
-{
- SourceIndex const L1i{ lua_absindex(L1, L1_i) };
- void* const source{ lua_touserdata(L1, L1i) };
-
- STACK_CHECK_START_REL(L1, 0);
- STACK_CHECK_START_REL(L2, 0);
-
- // Check if the source was already cloned during this copy
- lua_pushlightuserdata(L2, source); // L2: ... source
- lua_rawget(L2, L2_cache_i); // L2: ... clone?
- if (!lua_isnil(L2, -1)) {
- STACK_CHECK(L2, 1);
- return true;
- } else {
- lua_pop(L2, 1); // L2: ...
- }
- STACK_CHECK(L2, 0);
-
- // no metatable? -> not clonable
- if (!lua_getmetatable(L1, L1i)) { // L1: ... mt?
- STACK_CHECK(L1, 0);
- return false;
- }
-
- // no __lanesclone? -> not clonable
- lua_getfield(L1, -1, "__lanesclone"); // L1: ... mt __lanesclone?
- if (lua_isnil(L1, -1)) {
- lua_pop(L1, 2); // L1: ...
- STACK_CHECK(L1, 0);
- return false;
- }
-
- // we need to copy over the uservalues of the userdata as well
- {
- int const mt{ lua_absindex(L1, -2) }; // L1: ... mt __lanesclone
- size_t const userdata_size{ lua_rawlen(L1, L1i) };
- // extract all the uservalues, but don't transfer them yet
- int uvi = 0;
- while (lua_getiuservalue(L1, L1i, ++uvi) != LUA_TNONE) {} // L1: ... mt __lanesclone [uv]+ nil
- // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
- lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]+
- --uvi;
- // create the clone userdata with the required number of uservalue slots
- void* const clone{ lua_newuserdatauv(L2, userdata_size, uvi) }; // L2: ... u
- // copy the metatable in the target state, and give it to the clone we put there
- InterCopyContext c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name };
- if (c.inter_copy_one()) { // L2: ... u mt|sentinel
- if (LookupMode::ToKeeper == mode) { // L2: ... u sentinel
- LUA_ASSERT(L1, lua_tocfunction(L2, -1) == table_lookup_sentinel);
- // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn
- lua_getupvalue(L2, -1, 1); // L2: ... u sentinel fqn
- lua_remove(L2, -2); // L2: ... u fqn
- lua_insert(L2, -2); // L2: ... fqn u
- lua_pushcclosure(L2, userdata_clone_sentinel, 2); // L2: ... userdata_clone_sentinel
- } else { // from keeper or direct // L2: ... u mt
- LUA_ASSERT(L1, lua_istable(L2, -1));
- lua_setmetatable(L2, -2); // L2: ... u
- }
- STACK_CHECK(L2, 1);
- } else {
- raise_luaL_error(getErrL(), "Error copying a metatable");
- }
- // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel
- lua_pushlightuserdata(L2, source); // L2: ... u source
- lua_pushvalue(L2, -2); // L2: ... u source u
- lua_rawset(L2, L2_cache_i); // L2: ... u
- // make sure we have the userdata now
- if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel
- lua_getupvalue(L2, -1, 2); // L2: ... userdata_clone_sentinel u
- }
- // assign uservalues
- while (uvi > 0) {
- c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
- if (!c.inter_copy_one()) { // L2: ... u uv
- raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
- }
- lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]*
- // this pops the value from the stack
- lua_setiuservalue(L2, -2, uvi); // L2: ... u
- --uvi;
- }
- // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination
- if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel u
- lua_pop(L2, 1); // L2: ... userdata_clone_sentinel
- }
- STACK_CHECK(L2, 1);
- STACK_CHECK(L1, 2);
- // call cloning function in source state to perform the actual memory cloning
- lua_pushlightuserdata(L1, clone); // L1: ... mt __lanesclone clone
- lua_pushlightuserdata(L1, source); // L1: ... mt __lanesclone clone source
- lua_pushinteger(L1, static_cast(userdata_size)); // L1: ... mt __lanesclone clone source size
- lua_call(L1, 3, 0); // L1: ... mt
- STACK_CHECK(L1, 1);
- }
-
- STACK_CHECK(L2, 1);
- lua_pop(L1, 1); // L1: ...
- STACK_CHECK(L1, 0);
- return true;
-}
-
-// #################################################################################################
-
-[[nodiscard]] bool InterCopyContext::inter_copy_userdata() const
-{
- STACK_CHECK_START_REL(L1, 0);
- STACK_CHECK_START_REL(L2, 0);
- if (vt == VT::KEY) {
- return false;
- }
-
- // try clonable userdata first
- if (tryCopyClonable()) {
- STACK_CHECK(L1, 0);
- STACK_CHECK(L2, 1);
- return true;
- }
-
- STACK_CHECK(L1, 0);
- STACK_CHECK(L2, 0);
-
- // Allow only deep userdata entities to be copied across
- DEBUGSPEW_CODE(fprintf(stderr, "USERDATA\n"));
- if (tryCopyDeep()) {
- STACK_CHECK(L1, 0);
- STACK_CHECK(L2, 1);
- return true;
- }
-
- STACK_CHECK(L1, 0);
- STACK_CHECK(L2, 0);
-
- // Not a deep or clonable full userdata
- if (U->demoteFullUserdata) { // attempt demotion to light userdata
- void* const lud{ lua_touserdata(L1, L1_i) };
- lua_pushlightuserdata(L2, lud);
- } else { // raise an error
- raise_luaL_error(getErrL(), "can't copy non-deep full userdata across lanes");
- }
-
- STACK_CHECK(L2, 1);
- STACK_CHECK(L1, 0);
- return true;
-}
-
-// #################################################################################################
-
-[[nodiscard]] bool InterCopyContext::inter_copy_function() const
-{
- if (vt == VT::KEY) {
- return false;
- }
-
- STACK_CHECK_START_REL(L1, 0);
- STACK_CHECK_START_REL(L2, 0);
- DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name));
-
- if (lua_tocfunction(L1, L1_i) == userdata_clone_sentinel) { // we are actually copying a clonable full userdata from a keeper
- // clone the full userdata again
-
- // let's see if we already restored this userdata
- lua_getupvalue(L1, L1_i, 2); // L1: ... u
- void* source = lua_touserdata(L1, -1);
- lua_pushlightuserdata(L2, source); // L2: ... source
- lua_rawget(L2, L2_cache_i); // L2: ... u?
- if (!lua_isnil(L2, -1)) {
- lua_pop(L1, 1); // L1: ...
- STACK_CHECK(L1, 0);
- STACK_CHECK(L2, 1);
- return true;
- }
- lua_pop(L2, 1); // L2: ...
-
- // userdata_clone_sentinel has 2 upvalues: the fqn of its metatable, and the userdata itself
- bool const found{ lookup_table() }; // L2: ... mt?
- if (!found) {
- STACK_CHECK(L2, 0);
- return false;
- }
- // 'L1_i' slot was the proxy closure, but from now on we operate onthe actual userdata we extracted from it
- SourceIndex const source_i{ lua_gettop(L1) };
- source = lua_touserdata(L1, -1);
- void* clone{ nullptr };
- // get the number of bytes to allocate for the clone
- size_t const userdata_size{ lua_rawlen(L1, -1) };
- {
- // extract uservalues (don't transfer them yet)
- int uvi = 0;
- while (lua_getiuservalue(L1, source_i, ++uvi) != LUA_TNONE) {} // L1: ... u uv
- // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
- lua_pop(L1, 1); // L1: ... u [uv]*
- --uvi;
- STACK_CHECK(L1, uvi + 1);
- // create the clone userdata with the required number of uservalue slots
- clone = lua_newuserdatauv(L2, userdata_size, uvi); // L2: ... mt u
- // add it in the cache
- lua_pushlightuserdata(L2, source); // L2: ... mt u source
- lua_pushvalue(L2, -2); // L2: ... mt u source u
- lua_rawset(L2, L2_cache_i); // L2: ... mt u
- // set metatable
- lua_pushvalue(L2, -2); // L2: ... mt u mt
- lua_setmetatable(L2, -2); // L2: ... mt u
- // transfer and assign uservalues
- InterCopyContext c{ *this };
- while (uvi > 0) {
- c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
- if (!c.inter_copy_one()) { // L2: ... mt u uv
- raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
- }
- lua_pop(L1, 1); // L1: ... u [uv]*
- // this pops the value from the stack
- lua_setiuservalue(L2, -2, uvi); // L2: ... mt u
- --uvi;
- }
- // when we are done, all uservalues are popped from the stack, we can pop the source as well
- lua_pop(L1, 1); // L1: ...
- STACK_CHECK(L1, 0);
- STACK_CHECK(L2, 2); // L2: ... mt u
- }
- // perform the custom cloning part
- lua_insert(L2, -2); // L2: ... u mt
- // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with
- lua_getfield(L2, -1, "__lanesclone"); // L2: ... u mt __lanesclone
- lua_remove(L2, -2); // L2: ... u __lanesclone
- lua_pushlightuserdata(L2, clone); // L2: ... u __lanesclone clone
- lua_pushlightuserdata(L2, source); // L2: ... u __lanesclone clone source
- lua_pushinteger(L2, userdata_size); // L2: ... u __lanesclone clone source size
- // clone:__lanesclone(dest, source, size)
- lua_call(L2, 3, 0); // L2: ... u
- } else { // regular function
- DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name));
- DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
- copy_cached_func(); // L2: ... f
- }
- STACK_CHECK(L2, 1);
- STACK_CHECK(L1, 0);
- return true;
-}
-
-// #################################################################################################
-
-[[nodiscard]] bool InterCopyContext::inter_copy_table() const
-{
- if (vt == VT::KEY) {
- return false;
- }
-
- STACK_CHECK_START_REL(L1, 0);
- STACK_CHECK_START_REL(L2, 0);
- DEBUGSPEW_CODE(fprintf(stderr, "TABLE %s\n", name));
-
- /*
- * 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?)
- * 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
- */
- if (lookup_table()) {
- 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
- return true;
- }
-
- /* Check if we've already copied the same table from 'L1' (during this transmission), and
- * reuse the old copy. This allows table upvalues shared by multiple
- * local functions to point to the same table, also in the target.
- * Also, this takes care of cyclic tables and multiple references
- * to the same subtable.
- *
- * Note: Even metatables need to go through this test; to detect
- * loops such as those in required module tables (getmetatable(lanes).lanes == lanes)
- */
- if (push_cached_table()) {
- LUA_ASSERT(L1, lua_istable(L2, -1)); // from cache
- return true;
- }
- LUA_ASSERT(L1, lua_istable(L2, -1));
-
- STACK_GROW(L1, 2);
- STACK_GROW(L2, 2);
-
- lua_pushnil(L1); // start iteration
- while (lua_next(L1, L1_i)) {
- // need a function to prevent overflowing the stack with verboseErrors-induced alloca()
- inter_copy_keyvaluepair();
- lua_pop(L1, 1); // pop value (next round)
- }
- STACK_CHECK(L1, 0);
- STACK_CHECK(L2, 1);
-
- // Metatables are expected to be immutable, and copied only once.
- if (push_cached_metatable()) { // L2: ... t mt?
- lua_setmetatable(L2, -2); // L2: ... t
- }
- STACK_CHECK(L2, 1);
- STACK_CHECK(L1, 0);
- return true;
-}
-
-// #################################################################################################
-
-[[nodiscard]] bool InterCopyContext::inter_copy_boolean() const
-{
- int const v{ lua_toboolean(L1, L1_i) };
- DEBUGSPEW_CODE(fprintf(stderr, "%s\n", v ? "true" : "false"));
- lua_pushboolean(L2, v);
- return true;
-}
-
-// #################################################################################################
-
-[[nodiscard]] bool InterCopyContext::inter_copy_lightuserdata() const
-{
- void* const p{ lua_touserdata(L1, L1_i) };
- DEBUGSPEW_CODE(fprintf(stderr, "%p\n", p));
- lua_pushlightuserdata(L2, p);
- return true;
-}
-
-// #################################################################################################
-
-[[nodiscard]] bool InterCopyContext::inter_copy_nil() const
-{
- if (vt == VT::KEY) {
- return false;
- }
- lua_pushnil(L2);
- return true;
-}
-
-// #################################################################################################
-
-[[nodiscard]] bool InterCopyContext::inter_copy_number() const
-{
- // LNUM patch support (keeping integer accuracy)
-#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
- if (lua_isinteger(L1, L1_i)) {
- lua_Integer const v{ lua_tointeger(L1, L1_i) };
- DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", v));
- lua_pushinteger(L2, v);
- } else
-#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
- {
- lua_Number const v{ lua_tonumber(L1, L1_i) };
- DEBUGSPEW_CODE(fprintf(stderr, LUA_NUMBER_FMT "\n", v));
- lua_pushnumber(L2, v);
- }
- return true;
-}
-
-// #################################################################################################
-
-[[nodiscard]] bool InterCopyContext::inter_copy_string() const
-{
- size_t len;
- char const* const s{ lua_tolstring(L1, L1_i, &len) };
- DEBUGSPEW_CODE(fprintf(stderr, "'%s'\n", s));
- lua_pushlstring(L2, s, len);
- return true;
-}
-
-// #################################################################################################
-
-/*
- * Copies a value from 'L1' state (at index 'i') to 'L2' state. Does not remove
- * the original value.
- *
- * NOTE: Both the states must be solely in the current OS thread's possession.
- *
- * 'i' is an absolute index (no -1, ...)
- *
- * Returns true if value was pushed, false if its type is non-supported.
- */
-[[nodiscard]] bool InterCopyContext::inter_copy_one() const
-{
- static constexpr int kPODmask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING);
- STACK_GROW(L2, 1);
- STACK_CHECK_START_REL(L1, 0);
- STACK_CHECK_START_REL(L2, 0);
-
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END(U)));
- DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
-
- LuaType val_type{ lua_type_as_enum(L1, L1_i) };
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s %s: " INDENT_END(U), lua_type_names[static_cast(val_type)], vt_names[static_cast(vt)]));
-
- // Non-POD can be skipped if its metatable contains { __lanesignore = true }
- if (((1 << static_cast(val_type)) & kPODmask) == 0) {
- if (lua_getmetatable(L1, L1_i)) { // L1: ... mt
- lua_getfield(L1, -1, "__lanesignore"); // L1: ... mt ignore?
- if (lua_isboolean(L1, -1) && lua_toboolean(L1, -1)) {
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END(U)));
- val_type = LuaType::NIL;
- }
- lua_pop(L1, 2); // L1: ...
- }
- }
- STACK_CHECK(L1, 0);
-
- // Lets push nil to L2 if the object should be ignored
- bool ret{ true };
- switch (val_type) {
- // Basic types allowed both as values, and as table keys
- case LuaType::BOOLEAN:
- ret = inter_copy_boolean();
- break;
- case LuaType::NUMBER:
- ret = inter_copy_number();
- break;
- case LuaType::STRING:
- ret = inter_copy_string();
- break;
- case LuaType::LIGHTUSERDATA:
- ret = inter_copy_lightuserdata();
- break;
-
- // The following types are not allowed as table keys
- case LuaType::USERDATA:
- ret = inter_copy_userdata();
- break;
- case LuaType::NIL:
- ret = inter_copy_nil();
- break;
- case LuaType::FUNCTION:
- ret = inter_copy_function();
- break;
- case LuaType::TABLE:
- ret = inter_copy_table();
- break;
-
- // The following types cannot be copied
- case LuaType::CDATA:
- [[fallthrough]];
- case LuaType::THREAD:
- ret = false;
- break;
- }
-
- STACK_CHECK(L2, ret ? 1 : 0);
- STACK_CHECK(L1, 0);
- return ret;
-}
-
-// #################################################################################################
-
-// Akin to 'lua_xmove' but copies values between _any_ Lua states.
-// NOTE: Both the states must be solely in the current OS thread's possession.
-[[nodiscard]] InterCopyResult InterCopyContext::inter_copy(int n_) const
-{
- LUA_ASSERT(L1, vt == VT::NORMAL);
-
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy()\n" INDENT_END(U)));
- DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
-
- int const top_L1{ lua_gettop(L1) };
- if (n_ > top_L1) {
- // requesting to copy more than is available?
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END(U)));
- return InterCopyResult::NotEnoughValues;
- }
-
- STACK_CHECK_START_REL(L2, 0);
- STACK_GROW(L2, n_ + 1);
-
- /*
- * Make a cache table for the duration of this copy. Collects tables and
- * function entries, avoiding the same entries to be passed on as multiple
- * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner!
- */
- int const top_L2{ lua_gettop(L2) }; // L2: ...
- lua_newtable(L2); // L2: ... cache
-
- char tmpBuf[16];
- char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" };
- InterCopyContext c{ U, L2, L1, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode, pBuf };
- bool copyok{ true };
- STACK_CHECK_START_REL(L1, 0);
- for (int i{ top_L1 - n_ + 1 }, j{ 1 }; i <= top_L1; ++i, ++j) {
- if (U->verboseErrors) {
- sprintf(tmpBuf, "arg_%d", j);
- }
- c.L1_i = SourceIndex{ i };
- copyok = c.inter_copy_one(); // L2: ... cache {}n
- if (!copyok) {
- break;
- }
- }
- STACK_CHECK(L1, 0);
-
- if (copyok) {
- STACK_CHECK(L2, n_ + 1);
- // Remove the cache table. Persistent caching would cause i.e. multiple
- // messages passed in the same table to use the same table also in receiving end.
- lua_remove(L2, top_L2 + 1);
- return InterCopyResult::Success;
- }
-
- // error -> pop everything from the target state stack
- lua_settop(L2, top_L2);
- STACK_CHECK(L2, 0);
- return InterCopyResult::Error;
-}
-
-// #################################################################################################
-
-[[nodiscard]] InterCopyResult InterCopyContext::inter_move(int n_) const
-{
- InterCopyResult const ret{ inter_copy(n_) };
- lua_pop(L1, n_);
- return ret;
-}
-
-// #################################################################################################
-
-// transfers stuff from L1->_G["package"] to L2->_G["package"]
-// returns InterCopyResult::Success if everything is fine
-// returns InterCopyResult::Error if pushed an error message in L1
-// else raise an error in L1
-[[nodiscard]] InterCopyResult InterCopyContext::inter_copy_package() const
-{
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy_package()\n" INDENT_END(U)));
-
- class OnExit
- {
- private:
- lua_State* const L2;
- int const top_L2;
- DEBUGSPEW_CODE(DebugSpewIndentScope m_scope);
-
- public:
- OnExit(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L2_)
- : L2{ L2_ }
- , top_L2{ lua_gettop(L2) } DEBUGSPEW_COMMA_PARAM(m_scope{ U_ })
- {
- }
-
- ~OnExit()
- {
- lua_settop(L2, top_L2);
- }
- } onExit{ DEBUGSPEW_PARAM_COMMA(U) L2 };
-
- STACK_CHECK_START_REL(L1, 0);
- if (lua_type_as_enum(L1, L1_i) != LuaType::TABLE) {
- lua_pushfstring(L1, "expected package as table, got %s", luaL_typename(L1, L1_i));
- STACK_CHECK(L1, 1);
- // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
- if (mode == LookupMode::LaneBody) {
- raise_lua_error(getErrL()); // that's ok, getErrL() is L1 in that case
- }
- return InterCopyResult::Error;
- }
- if (luaG_getmodule(L2, LUA_LOADLIBNAME) == LuaType::NIL) { // package library not loaded: do nothing
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END(U)));
- STACK_CHECK(L1, 0);
- return InterCopyResult::Success;
- }
-
- InterCopyResult result{ InterCopyResult::Success };
- // package.loaders is renamed package.searchers in Lua 5.2
- // but don't copy it anyway, as the function names change depending on the slot index!
- // users should provide an on_state_create function to setup custom loaders instead
- // don't copy package.preload in keeper states (they don't know how to translate functions)
- char const* entries[] = { "path", "cpath", (mode == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr };
- for (char const* const entry : entries) {
- if (!entry) {
- continue;
- }
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END(U), entry));
- lua_getfield(L1, L1_i, entry);
- if (lua_isnil(L1, -1)) {
- lua_pop(L1, 1);
- } else {
- {
- DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
- result = inter_move(1); // moves the entry to L2
- STACK_CHECK(L1, 0);
- }
- if (result == InterCopyResult::Success) {
- lua_setfield(L2, -2, entry); // set package[entry]
- } else {
- lua_pushfstring(L1, "failed to copy package entry %s", entry);
- // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
- if (mode == LookupMode::LaneBody) {
- raise_lua_error(getErrL());
- }
- lua_pop(L1, 1);
- break;
- }
- }
- }
- STACK_CHECK(L1, 0);
- return result;
-}
diff --git a/src/tools.h b/src/tools.h
index 53d3a99..be76fd9 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -1,88 +1,30 @@
#pragma once
-#include "deep.h"
-#include "macros_and_utils.h"
+#include "uniquekey.h"
-// forwards
class Universe;
-// #################################################################################################
-
-enum class VT
+enum class LookupMode
{
- NORMAL, // keep this one first so that it's the value we get when we default-construct
- KEY,
- METATABLE
+ LaneBody, // send the lane body directly from the source to the destination lane. keep this one first so that it's the value we get when we default-construct
+ ToKeeper, // send a function from a lane to a keeper state
+ FromKeeper // send a function from a keeper state to a lane
};
-enum class InterCopyResult
+enum class FuncSubType
{
- Success,
- NotEnoughValues,
- Error
+ Bytecode,
+ Native,
+ FastJIT
};
-// #################################################################################################
-
-using CacheIndex = Unique;
-using SourceIndex = Unique;
-class InterCopyContext
-{
- public:
- Universe* const U;
- DestState const L2;
- SourceState const L1;
- CacheIndex const L2_cache_i;
- SourceIndex L1_i; // that one can change when we reuse the context
- VT vt; // that one can change when we reuse the context
- LookupMode const mode;
- char const* name; // that one can change when we reuse the context
-
- private:
- // when mode == LookupMode::FromKeeper, L1 is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error
- // whon mode != LookupMode::FromKeeper, L1 is not a keeper state, therefore L1 is the state where we want to raise the error
- lua_State* getErrL() const { return (mode == LookupMode::FromKeeper) ? L2 : L1; }
-
- // for use in copy_cached_func
- void copy_func() const;
- void lookup_native_func() const;
-
- // for use in inter_copy_function
- void copy_cached_func() const;
- [[nodiscard]] bool lookup_table() const;
-
- // for use in inter_copy_table
- void inter_copy_keyvaluepair() const;
- [[nodiscard]] bool push_cached_metatable() const;
- [[nodiscard]] bool push_cached_table() const;
-
- // for use in inter_copy_userdata
- [[nodiscard]] bool tryCopyClonable() const;
- [[nodiscard]] bool tryCopyDeep() const;
-
- // copying a single Lua stack item
- [[nodiscard]] bool inter_copy_boolean() const;
- [[nodiscard]] bool inter_copy_function() const;
- [[nodiscard]] bool inter_copy_lightuserdata() const;
- [[nodiscard]] bool inter_copy_nil() const;
- [[nodiscard]] bool inter_copy_number() const;
- [[nodiscard]] bool inter_copy_string() const;
- [[nodiscard]] bool inter_copy_table() const;
- [[nodiscard]] bool inter_copy_userdata() const;
-
- public:
- [[nodiscard]] bool inter_copy_one() const;
- [[nodiscard]] InterCopyResult inter_copy_package() const;
- [[nodiscard]] InterCopyResult inter_copy(int n_) const;
- [[nodiscard]] InterCopyResult inter_move(int n_) const;
-};
+[[nodiscard]] FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i);
// #################################################################################################
[[nodiscard]] int luaG_nameof(lua_State* L_);
void populate_func_lookup_table(lua_State* L_, int i_, char const* name_);
-void initialize_allocator_function(Universe* U_, lua_State* L_);
// #################################################################################################
diff --git a/src/universe.cpp b/src/universe.cpp
index becffdd..a5c28f0 100644
--- a/src/universe.cpp
+++ b/src/universe.cpp
@@ -30,12 +30,9 @@ THE SOFTWARE.
#include "universe.h"
-#include "cancel.h"
-#include "compat.h"
#include "deep.h"
#include "keeper.h"
#include "lanes_private.h"
-#include "tools.h"
// #################################################################################################
diff --git a/src/universe.h b/src/universe.h
index 58ddffc..3adba4d 100644
--- a/src/universe.h
+++ b/src/universe.h
@@ -9,7 +9,6 @@ extern "C"
}
#endif // __cplusplus
-#include "compat.h"
#include "macros_and_utils.h"
#include "uniquekey.h"
--
cgit v1.2.3-55-g6feb
From 399d4437d7347148952b2a175babe053191bec29 Mon Sep 17 00:00:00 2001
From: Benoit Germain
Date: Mon, 13 May 2024 15:29:52 +0200
Subject: deep_test vcproj tweaks
---
deep_test/deep_test.vcxproj | 6 +++---
deep_test/deep_test.vcxproj.filters | 14 +++++++-------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/deep_test/deep_test.vcxproj b/deep_test/deep_test.vcxproj
index 175433b..43b7ce0 100644
--- a/deep_test/deep_test.vcxproj
+++ b/deep_test/deep_test.vcxproj
@@ -709,15 +709,15 @@
-
-
+
+
+
-
diff --git a/deep_test/deep_test.vcxproj.filters b/deep_test/deep_test.vcxproj.filters
index 84527af..4bd26fd 100644
--- a/deep_test/deep_test.vcxproj.filters
+++ b/deep_test/deep_test.vcxproj.filters
@@ -21,9 +21,6 @@
Lanes
-
- Lanes
-
Source Files
@@ -32,16 +29,19 @@
Lanes
-
+
Lanes
-
+
Lanes
-
+
Lanes
-
+
+ Lanes
+
+
Lanes
--
cgit v1.2.3-55-g6feb
From 89598b602f57f17aabb8d9dbd2d87eb5176160f6 Mon Sep 17 00:00:00 2001
From: Benoit Germain
Date: Mon, 13 May 2024 16:26:02 +0200
Subject: Progressively applying the coding rules
---
src/cancel.cpp | 70 ++++-----
src/compat.cpp | 12 +-
src/deep.cpp | 64 ++++----
src/intercopycontext.cpp | 399 +++++++++++++++++++++++------------------------
src/keeper.cpp | 328 +++++++++++++++++++-------------------
src/macros_and_utils.h | 10 +-
6 files changed, 442 insertions(+), 441 deletions(-)
diff --git a/src/cancel.cpp b/src/cancel.cpp
index 8356169..b297c85 100644
--- a/src/cancel.cpp
+++ b/src/cancel.cpp
@@ -51,9 +51,9 @@ THE SOFTWARE.
*/
[[nodiscard]] static inline CancelRequest cancel_test(lua_State* L_)
{
- Lane* const lane{ kLanePointerRegKey.readLightUserDataValue(L_) };
+ Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue(L_) };
// 'lane' is nullptr for the original main state (and no-one can cancel that)
- return lane ? lane->cancelRequest : CancelRequest::None;
+ return _lane ? _lane->cancelRequest : CancelRequest::None;
}
// #################################################################################################
@@ -66,8 +66,8 @@ THE SOFTWARE.
//
LUAG_FUNC(cancel_test)
{
- CancelRequest test{ cancel_test(L_) };
- lua_pushboolean(L_, test != CancelRequest::None);
+ CancelRequest _test{ cancel_test(L_) };
+ lua_pushboolean(L_, _test != CancelRequest::None);
return 1;
}
@@ -110,9 +110,9 @@ LUAG_FUNC(cancel_test)
lane_->cancelRequest = CancelRequest::Soft; // it's now signaled to stop
// negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own
if (wakeLane_) { // wake the thread so that execution returns from any pending linda operation if desired
- std::condition_variable* const waiting_on{ lane_->waiting_on };
- if (lane_->status == Lane::Waiting && waiting_on != nullptr) {
- waiting_on->notify_all();
+ std::condition_variable* const _waiting_on{ lane_->waiting_on };
+ if (lane_->status == Lane::Waiting && _waiting_on != nullptr) {
+ _waiting_on->notify_all();
}
}
@@ -126,9 +126,9 @@ LUAG_FUNC(cancel_test)
lane_->cancelRequest = CancelRequest::Hard; // it's now signaled to stop
// lane_->thread.get_stop_source().request_stop();
if (wakeLane_) { // wake the thread so that execution returns from any pending linda operation if desired
- std::condition_variable* waiting_on = lane_->waiting_on;
- if (lane_->status == Lane::Waiting && waiting_on != nullptr) {
- waiting_on->notify_all();
+ std::condition_variable* const _waiting_on{ lane_->waiting_on };
+ if (lane_->status == Lane::Waiting && _waiting_on != nullptr) {
+ _waiting_on->notify_all();
}
}
@@ -163,21 +163,21 @@ CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hookCount_, std::chron
CancelOp which_cancel_op(char const* opString_)
{
- CancelOp op{ CancelOp::Invalid };
+ CancelOp _op{ CancelOp::Invalid };
if (strcmp(opString_, "hard") == 0) {
- op = CancelOp::Hard;
+ _op = CancelOp::Hard;
} else if (strcmp(opString_, "soft") == 0) {
- op = CancelOp::Soft;
+ _op = CancelOp::Soft;
} else if (strcmp(opString_, "call") == 0) {
- op = CancelOp::MaskCall;
+ _op = CancelOp::MaskCall;
} else if (strcmp(opString_, "ret") == 0) {
- op = CancelOp::MaskRet;
+ _op = CancelOp::MaskRet;
} else if (strcmp(opString_, "line") == 0) {
- op = CancelOp::MaskLine;
+ _op = CancelOp::MaskLine;
} else if (strcmp(opString_, "count") == 0) {
- op = CancelOp::MaskCount;
+ _op = CancelOp::MaskCount;
}
- return op;
+ return _op;
}
// #################################################################################################
@@ -185,13 +185,13 @@ CancelOp which_cancel_op(char const* opString_)
[[nodiscard]] static CancelOp which_cancel_op(lua_State* L_, int idx_)
{
if (lua_type(L_, idx_) == LUA_TSTRING) {
- char const* const str{ lua_tostring(L_, idx_) };
- CancelOp op{ which_cancel_op(str) };
+ char const* const _str{ lua_tostring(L_, idx_) };
+ CancelOp _op{ which_cancel_op(_str) };
lua_remove(L_, idx_); // argument is processed, remove it
- if (op == CancelOp::Invalid) {
- raise_luaL_error(L_, "invalid hook option %s", str);
+ if (_op == CancelOp::Invalid) {
+ raise_luaL_error(L_, "invalid hook option %s", _str);
}
- return op;
+ return _op;
}
return CancelOp::Hard;
}
@@ -201,23 +201,23 @@ CancelOp which_cancel_op(char const* opString_)
// bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, wake_lane])
LUAG_FUNC(thread_cancel)
{
- Lane* const lane{ ToLane(L_, 1) };
- CancelOp const op{ which_cancel_op(L_, 2) }; // this removes the op string from the stack
+ Lane* const _lane{ ToLane(L_, 1) };
+ CancelOp const _op{ which_cancel_op(L_, 2) }; // this removes the op string from the stack
- int hook_count{ 0 };
- if (static_cast(op) > static_cast(CancelOp::Soft)) { // hook is requested
- hook_count = static_cast(luaL_checkinteger(L_, 2));
+ int _hook_count{ 0 };
+ if (static_cast(_op) > static_cast(CancelOp::Soft)) { // hook is requested
+ _hook_count = static_cast(luaL_checkinteger(L_, 2));
lua_remove(L_, 2); // argument is processed, remove it
- if (hook_count < 1) {
+ if (_hook_count < 1) {
raise_luaL_error(L_, "hook count cannot be < 1");
}
}
- std::chrono::time_point until{ std::chrono::time_point::max() };
+ std::chrono::time_point _until{ std::chrono::time_point::max() };
if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion
lua_Duration const duration{ lua_tonumber(L_, 2) };
if (duration.count() >= 0.0) {
- until = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration);
+ _until = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration);
} else {
raise_luaL_argerror(L_, 2, "duration cannot be < 0");
}
@@ -227,16 +227,16 @@ LUAG_FUNC(thread_cancel)
}
// we wake by default in "hard" mode (remember that hook is hard too), but this can be turned off if desired
- bool wake_lane{ op != CancelOp::Soft };
+ bool _wake_lane{ _op != CancelOp::Soft };
if (lua_gettop(L_) >= 2) {
if (!lua_isboolean(L_, 2)) {
raise_luaL_error(L_, "wake_lindas parameter is not a boolean");
}
- wake_lane = lua_toboolean(L_, 2);
+ _wake_lane = lua_toboolean(L_, 2);
lua_remove(L_, 2); // argument is processed, remove it
}
STACK_CHECK_START_REL(L_, 0);
- switch (thread_cancel(lane, op, hook_count, until, wake_lane)) {
+ switch (thread_cancel(_lane, _op, _hook_count, _until, _wake_lane)) {
default: // should never happen unless we added a case and forgot to handle it
LUA_ASSERT(L_, false);
break;
@@ -248,7 +248,7 @@ LUAG_FUNC(thread_cancel)
case CancelResult::Cancelled:
lua_pushboolean(L_, 1); // true
- lane->pushThreadStatus(L_); // true status
+ _lane->pushThreadStatus(L_); // true status
break;
}
STACK_CHECK(L_, 2);
diff --git a/src/compat.cpp b/src/compat.cpp
index 4e8025e..b45cce2 100644
--- a/src/compat.cpp
+++ b/src/compat.cpp
@@ -14,15 +14,15 @@
LuaType luaG_getmodule(lua_State* L_, char const* name_)
{
STACK_CHECK_START_REL(L_, 0);
- LuaType type{ static_cast(lua503_getfield(L_, LUA_REGISTRYINDEX, LUA_LOADED_TABLE)) };// L_: _R._LOADED|nil
- if (type != LuaType::TABLE) { // L_: _R._LOADED|nil
+ LuaType _type{ static_cast(lua503_getfield(L_, LUA_REGISTRYINDEX, LUA_LOADED_TABLE)) };// L_: _R._LOADED|nil
+ if (_type != LuaType::TABLE) { // L_: _R._LOADED|nil
STACK_CHECK(L_, 1);
- return type;
+ return _type;
}
- type = static_cast(lua503_getfield(L_, -1, name_)); // L_: _R._LOADED {module}|nil
- lua_remove(L_, -2); // L_: {module}|nil
+ _type = static_cast(lua503_getfield(L_, -1, name_)); // L_: _R._LOADED {module}|nil
+ lua_remove(L_, -2); // L_: {module}|nil
STACK_CHECK(L_, 1);
- return type;
+ return _type;
}
// #################################################################################################
diff --git a/src/deep.cpp b/src/deep.cpp
index e0c2a39..9474666 100644
--- a/src/deep.cpp
+++ b/src/deep.cpp
@@ -59,6 +59,8 @@ static constexpr RegistryUniqueKey kDeepLookupRegKey{ 0xC6788345703C6059ull };
*/
static constexpr RegistryUniqueKey kDeepProxyCacheRegKey{ 0xEBCD49AE1A3DD35Eull };
+// #################################################################################################
+
/*
* Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists.
* Pops the both values off the stack.
@@ -106,9 +108,9 @@ static void LookupDeep(lua_State* L_)
{
// when looking inside a keeper, we are 100% sure the object is a deep userdata
if (mode_ == LookupMode::FromKeeper) {
- DeepPrelude* const proxy{ *lua_tofulluserdata(L_, index_) };
+ DeepPrelude* const _proxy{ *lua_tofulluserdata(L_, index_) };
// we can (and must) cast and fetch the internally stored factory
- return &proxy->factory;
+ return &_proxy->factory;
} else {
// essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/factory database
// it is the only way to ensure that the userdata is indeed a deep userdata!
@@ -123,10 +125,10 @@ static void LookupDeep(lua_State* L_)
// replace metatable with the factory pointer, if it is actually a deep userdata
LookupDeep(L_); // L_: deep ... factory|nil
- DeepFactory* const ret{ lua_tolightuserdata(L_, -1) }; // nullptr if not a userdata
+ DeepFactory* const _ret{ lua_tolightuserdata(L_, -1) }; // nullptr if not a userdata
lua_pop(L_, 1);
STACK_CHECK(L_, 0);
- return ret;
+ return _ret;
}
}
@@ -149,12 +151,12 @@ void DeepFactory::DeleteDeepObject(lua_State* L_, DeepPrelude* o_)
*/
[[nodiscard]] static int deep_userdata_gc(lua_State* L_)
{
- DeepPrelude* const* const proxy{ lua_tofulluserdata(L_, 1) };
- DeepPrelude* const p{ *proxy };
+ DeepPrelude* const* const _proxy{ lua_tofulluserdata(L_, 1) };
+ DeepPrelude* const _p{ *_proxy };
// can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded
// in that case, we are not multithreaded and locking isn't necessary anyway
- bool const isLastRef{ p->refcount.fetch_sub(1, std::memory_order_relaxed) == 1 };
+ bool const isLastRef{ _p->refcount.fetch_sub(1, std::memory_order_relaxed) == 1 };
if (isLastRef) {
// retrieve wrapped __gc
@@ -166,7 +168,7 @@ void DeepFactory::DeleteDeepObject(lua_State* L_, DeepPrelude* o_)
// need an empty stack in case we are GC_ing from a Keeper, so that empty stack checks aren't triggered
lua_pop(L_, 2); // L_:
}
- DeepFactory::DeleteDeepObject(L_, p);
+ DeepFactory::DeleteDeepObject(L_, _p);
}
return 0;
}
@@ -219,9 +221,9 @@ char const* DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int
STACK_CHECK_START_REL(L_, 0);
// a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4)
- DeepPrelude** const proxy{ lua_newuserdatauv(L_, nuv_) }; // L_: DPC proxy
- LUA_ASSERT(L_, proxy);
- *proxy = prelude_;
+ DeepPrelude** const _proxy{ lua_newuserdatauv(L_, nuv_) }; // L_: DPC proxy
+ LUA_ASSERT(L_, _proxy);
+ *_proxy = prelude_;
prelude_->refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data
// Get/create metatable for 'factory' (in this state)
@@ -231,13 +233,13 @@ char const* DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int
if (lua_isnil(L_, -1)) { // No metatable yet.
lua_pop(L_, 1); // L_: DPC proxy
- int const oldtop{ lua_gettop(L_) };
+ int const _oldtop{ lua_gettop(L_) };
// 1 - make one and register it
if (mode_ != LookupMode::ToKeeper) {
factory.createMetatable(L_); // L_: DPC proxy metatable
- if (lua_gettop(L_) - oldtop != 1 || !lua_istable(L_, -1)) {
+ if (lua_gettop(L_) - _oldtop != 1 || !lua_istable(L_, -1)) {
// factory didn't push exactly 1 value, or the value it pushed is not a table: ERROR!
- lua_settop(L_, oldtop); // L_: DPC proxy X
+ lua_settop(L_, _oldtop); // L_: DPC proxy X
lua_pop(L_, 3); // L_:
return "Bad DeepFactory::createMetatable overload: unexpected pushed value";
}
@@ -262,12 +264,12 @@ char const* DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int
factory.storeDeepLookup(L_);
// 2 - cause the target state to require the module that exported the factory
- if (char const* const modname{ factory.moduleName() }; modname) { // we actually got a module name
+ if (char const* const _modname{ factory.moduleName() }; _modname) { // we actually got a module name
// L.registry._LOADED exists without having registered the 'package' library.
lua_getglobal(L_, "require"); // DPC proxy metatable require()
// check that the module is already loaded (or being loaded, we are happy either way)
if (lua_isfunction(L_, -1)) {
- lua_pushstring(L_, modname); // L_: DPC proxy metatable require() "module"
+ lua_pushstring(L_, _modname); // L_: DPC proxy metatable require() "module"
lua_getfield(L_, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); // L_: DPC proxy metatable require() "module" _R._LOADED
if (lua_istable(L_, -1)) {
lua_pushvalue(L_, -2); // L_: DPC proxy metatable require() "module" _R._LOADED "module"
@@ -280,7 +282,7 @@ char const* DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int
require_result = lua_pcall(L_, 1, 0, 0); // L_: DPC proxy metatable error?
if (require_result != LUA_OK) {
// failed, return the error message
- lua_pushfstring(L_, "error while requiring '%s' identified by DeepFactory::moduleName: ", modname);
+ lua_pushfstring(L_, "error while requiring '%s' identified by DeepFactory::moduleName: ", _modname);
lua_insert(L_, -2); // L_: DPC proxy metatable prefix error
lua_concat(L_, 2); // L_: DPC proxy metatable error
return lua_tostring(L_, -1);
@@ -334,30 +336,30 @@ int DeepFactory::pushDeepUserdata(DestState L_, int nuv_) const
{
STACK_GROW(L_, 1);
STACK_CHECK_START_REL(L_, 0);
- int const oldtop{ lua_gettop(L_) };
- DeepPrelude* const prelude{ newDeepObjectInternal(L_) };
- if (prelude == nullptr) {
+ int const _oldtop{ lua_gettop(L_) };
+ DeepPrelude* const _prelude{ newDeepObjectInternal(L_) };
+ if (_prelude == nullptr) {
raise_luaL_error(L_, "DeepFactory::newDeepObjectInternal failed to create deep userdata (out of memory)");
}
- if (prelude->magic != kDeepVersion) {
+ if (_prelude->magic != kDeepVersion) {
// just in case, don't leak the newly allocated deep userdata object
- deleteDeepObjectInternal(L_, prelude);
+ deleteDeepObjectInternal(L_, _prelude);
raise_luaL_error(L_, "Bad Deep Factory: kDeepVersion is incorrect, rebuild your implementation with the latest deep implementation");
}
- LUA_ASSERT(L_, prelude->refcount.load(std::memory_order_relaxed) == 0); // 'DeepFactory::PushDeepProxy' will lift it to 1
- LUA_ASSERT(L_, &prelude->factory == this);
+ LUA_ASSERT(L_, _prelude->refcount.load(std::memory_order_relaxed) == 0); // 'DeepFactory::PushDeepProxy' will lift it to 1
+ LUA_ASSERT(L_, &_prelude->factory == this);
- if (lua_gettop(L_) - oldtop != 0) {
+ if (lua_gettop(L_) - _oldtop != 0) {
// just in case, don't leak the newly allocated deep userdata object
- deleteDeepObjectInternal(L_, prelude);
+ deleteDeepObjectInternal(L_, _prelude);
raise_luaL_error(L_, "Bad DeepFactory::newDeepObjectInternal overload: should not push anything on the stack");
}
- char const* const errmsg{ DeepFactory::PushDeepProxy(L_, prelude, nuv_, LookupMode::LaneBody) }; // proxy
- if (errmsg != nullptr) {
- raise_luaL_error(L_, errmsg);
+ char const* const _err{ DeepFactory::PushDeepProxy(L_, _prelude, nuv_, LookupMode::LaneBody) }; // proxy
+ if (_err != nullptr) {
+ raise_luaL_error(L_, _err);
}
STACK_CHECK(L_, 1);
return 1;
@@ -380,6 +382,6 @@ DeepPrelude* DeepFactory::toDeep(lua_State* L_, int index_) const
}
STACK_CHECK(L_, 0);
- DeepPrelude** const proxy{ lua_tofulluserdata(L_, index_) };
- return *proxy;
+ DeepPrelude** const _proxy{ lua_tofulluserdata(L_, index_) };
+ return *_proxy;
}
diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp
index 07fcd77..10d620e 100644
--- a/src/intercopycontext.cpp
+++ b/src/intercopycontext.cpp
@@ -37,11 +37,11 @@ THE SOFTWARE.
// luckily, this also works with earlier Lua versions
[[nodiscard]] static int buf_writer(lua_State* L_, void const* b_, size_t size_, void* ud_)
{
- luaL_Buffer* const B{ static_cast(ud_) };
- if (!B->L) {
- luaL_buffinit(L_, B);
+ luaL_Buffer* const _B{ static_cast(ud_) };
+ if (!_B->L) {
+ luaL_buffinit(L_, _B);
}
- luaL_addlstring(B, static_cast(b_), size_);
+ luaL_addlstring(_B, static_cast(b_), size_);
return 0;
}
@@ -78,12 +78,12 @@ THE SOFTWARE.
STACK_CHECK_START_REL(L_, 0);
STACK_GROW(L_, 3); // up to 3 slots are necessary on error
if (mode_ == LookupMode::FromKeeper) {
- lua_CFunction f = lua_tocfunction(L_, i_); // should *always* be one of the function sentinels
- if (f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel) {
+ lua_CFunction const _f{ lua_tocfunction(L_, i_) }; // should *always* be one of the function sentinels
+ if (_f == func_lookup_sentinel || _f == table_lookup_sentinel || _f == userdata_clone_sentinel) {
lua_getupvalue(L_, i_, 1); // L_: ... v ... "f.q.n"
} else {
// if this is not a sentinel, this is some user-created table we wanted to lookup
- LUA_ASSERT(L_, nullptr == f && lua_istable(L_, i_));
+ LUA_ASSERT(L_, nullptr == _f && lua_istable(L_, i_));
// push anything that will convert to nullptr string
lua_pushnil(L_); // L_: ... v ... nil
}
@@ -95,13 +95,13 @@ THE SOFTWARE.
lua_pushvalue(L_, i_); // L_: ... v ... {} v
lua_rawget(L_, -2); // L_: ... v ... {} "f.q.n"
}
- char const* fqn{ lua_tolstring(L_, -1, len_) };
+ char const* _fqn{ lua_tolstring(L_, -1, len_) };
DEBUGSPEW_CODE(Universe* const U = universe_get(L_));
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END(U), fqn));
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END(U), _fqn));
// popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database
lua_pop(L_, (mode_ == LookupMode::FromKeeper) ? 1 : 2); // L_: ... v ...
STACK_CHECK(L_, 0);
- if (nullptr == fqn && !lua_istable(L_, i_)) { // raise an error if we try to send an unknown function (but not for tables)
+ if (nullptr == _fqn && !lua_istable(L_, i_)) { // raise an error if we try to send an unknown function (but not for tables)
*len_ = 0; // just in case
// try to discover the name of the function we want to send
lua_getglobal(L_, "decoda_name"); // L_: ... v ... decoda_name
@@ -125,7 +125,7 @@ THE SOFTWARE.
raise_luaL_error(L_, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB);
}
STACK_CHECK(L_, 0);
- return fqn;
+ return _fqn;
}
// #################################################################################################
@@ -147,26 +147,26 @@ static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull };
lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] {mt}
lua_rawget(L_, -2); // L_: ... _R[kMtIdRegKey] mtk?
- lua_Integer id{ lua_tointeger(L_, -1) }; // 0 for nil
+ lua_Integer _id{ lua_tointeger(L_, -1) }; // 0 for nil
lua_pop(L_, 1); // L_: ... _R[kMtIdRegKey]
STACK_CHECK(L_, 1);
- if (id == 0) {
- id = U_->nextMetatableId.fetch_add(1, std::memory_order_relaxed);
+ if (_id == 0) {
+ _id = U_->nextMetatableId.fetch_add(1, std::memory_order_relaxed);
// Create two-way references: id_uint <-> table
lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] {mt}
- lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] {mt} id
+ lua_pushinteger(L_, _id); // L_: ... _R[kMtIdRegKey] {mt} id
lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey]
- lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] id
+ lua_pushinteger(L_, _id); // L_: ... _R[kMtIdRegKey] id
lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] id {mt}
lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey]
}
lua_pop(L_, 1); // L_: ...
STACK_CHECK(L_, 0);
- return id;
+ return _id;
}
// #################################################################################################
@@ -181,8 +181,8 @@ void InterCopyContext::copy_func() const
// 'lua_dump()' needs the function at top of stack
// if already on top of the stack, no need to push again
- bool const needToPush{ L1_i != lua_gettop(L1) };
- if (needToPush) {
+ bool const _needToPush{ L1_i != lua_gettop(L1) };
+ if (_needToPush) {
lua_pushvalue(L1, L1_i); // L1: ... f
}
@@ -191,8 +191,7 @@ void InterCopyContext::copy_func() const
// to the writer" (and we only return 0)
// not sure this could ever fail but for memory shortage reasons
// last parameter is Lua 5.4-specific (no stripping)
- luaL_Buffer B;
- B.L = nullptr;
+ luaL_Buffer B{};
if (lua504_dump(L1, buf_writer, &B, 0) != 0) {
raise_luaL_error(getErrL(), "internal error: function dump failed.");
}
@@ -201,7 +200,7 @@ void InterCopyContext::copy_func() const
luaL_pushresult(&B); // L1: ... f b
// if not pushed, no need to pop
- if (needToPush) {
+ if (_needToPush) {
lua_remove(L1, -2); // L1: ... b
}
@@ -214,18 +213,18 @@ void InterCopyContext::copy_func() const
// stack and start the what string with the character '>'."
//
{
- lua_Debug ar;
+ lua_Debug _ar;
lua_pushvalue(L1, L1_i); // L1: ... b f
// fills 'fname' 'namewhat' and 'linedefined', pops function
- lua_getinfo(L1, ">nS", &ar); // L1: ... b
- fname = ar.namewhat;
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "FNAME: %s @ %d" INDENT_END(U), ar.short_src, ar.linedefined)); // just gives nullptr
+ lua_getinfo(L1, ">nS", &_ar); // L1: ... b
+ fname = _ar.namewhat;
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "FNAME: %s @ %d" INDENT_END(U), _ar.short_src, _ar.linedefined)); // just gives nullptr
}
#endif // LOG_FUNC_INFO
{
- size_t sz;
- char const* s = lua_tolstring(L1, -1, &sz); // L1: ... b
- LUA_ASSERT(L1, s && sz);
+ size_t _sz;
+ char const* _s{ lua_tolstring(L1, -1, &_sz) }; // L1: ... b
+ LUA_ASSERT(L1, _s && _sz);
STACK_GROW(L2, 2);
// Note: Line numbers seem to be taken precisely from the
// original function. 'fname' is not used since the chunk
@@ -233,7 +232,7 @@ void InterCopyContext::copy_func() const
//
// TBD: Can we get the function's original name through, as well?
//
- if (luaL_loadbuffer(L2, s, sz, fname) != 0) { // L2: ... {cache} ... p function
+ if (luaL_loadbuffer(L2, _s, _sz, fname) != 0) { // L2: ... {cache} ... p function
// chunk is precompiled so only LUA_ERRMEM can happen
// "Otherwise, it pushes an error message"
//
@@ -259,15 +258,15 @@ void InterCopyContext::copy_func() const
*/
int n{ 0 };
{
- InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} };
+ InterCopyContext _c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} };
#if LUA_VERSION_NUM >= 502
// Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default)
// Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state...
// -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table
lua_pushglobaltable(L1); // L1: ... _G
#endif // LUA_VERSION_NUM
- for (n = 0; (c.name = lua_getupvalue(L1, L1_i, 1 + n)) != nullptr; ++n) { // L1: ... _G up[n]
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END(U), n, c.name));
+ for (n = 0; (_c.name = lua_getupvalue(L1, L1_i, 1 + n)) != nullptr; ++n) { // L1: ... _G up[n]
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END(U), n, _c.name));
#if LUA_VERSION_NUM >= 502
if (lua_rawequal(L1, -1, -2)) { // is the upvalue equal to the global table?
DEBUGSPEW_CODE(fprintf(stderr, "pushing destination global scope\n"));
@@ -276,8 +275,8 @@ void InterCopyContext::copy_func() const
#endif // LUA_VERSION_NUM
{
DEBUGSPEW_CODE(fprintf(stderr, "copying value\n"));
- c.L1_i = SourceIndex{ lua_gettop(L1) };
- if (!c.inter_copy_one()) { // L2: ... {cache} ... function
+ _c.L1_i = SourceIndex{ lua_gettop(L1) };
+ if (!_c.inter_copy_one()) { // L2: ... {cache} ... function
raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
}
}
@@ -292,13 +291,13 @@ void InterCopyContext::copy_func() const
STACK_CHECK(L1, 0);
// Set upvalues (originally set to 'nil' by 'lua_load')
- for (int const func_index{ lua_gettop(L2) - n }; n > 0; --n) {
- char const* rc{ lua_setupvalue(L2, func_index, n) }; // L2: ... {cache} ... function
+ for (int const _func_index{ lua_gettop(L2) - n }; n > 0; --n) {
+ char const* _rc{ lua_setupvalue(L2, _func_index, n) }; // L2: ... {cache} ... function
//
// "assigns the value at the top of the stack to the upvalue and returns its name.
// It also pops the value from the stack."
- LUA_ASSERT(L1, rc); // not having enough slots?
+ LUA_ASSERT(L1, _rc); // not having enough slots?
}
// once all upvalues have been set we are left
// with the function at the top of the stack // L2: ... {cache} ... function
@@ -312,8 +311,8 @@ void InterCopyContext::copy_func() const
void InterCopyContext::lookup_native_func() const
{
// get the name of the function we want to send
- size_t len;
- char const* const fqn{ find_lookup_name(L1, L1_i, mode, name, &len) };
+ size_t _len;
+ char const* const _fqn{ find_lookup_name(L1, L1_i, mode, name, &_len) };
// push the equivalent function in the destination's stack, retrieved from the lookup table
STACK_CHECK_START_REL(L2, 0);
STACK_GROW(L2, 3); // up to 3 slots are necessary on error
@@ -324,7 +323,7 @@ void InterCopyContext::lookup_native_func() const
case LookupMode::ToKeeper:
// push a sentinel closure that holds the lookup name as upvalue
- lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: "f.q.n"
+ lua_pushlstring(L2, _fqn, _len); // L1: ... f ... L2: "f.q.n"
lua_pushcclosure(L2, func_lookup_sentinel, 1); // L1: ... f ... L2: f
break;
@@ -333,25 +332,25 @@ void InterCopyContext::lookup_native_func() const
kLookupRegKey.pushValue(L2); // L1: ... f ... L2: {}
STACK_CHECK(L2, 1);
LUA_ASSERT(L1, lua_istable(L2, -1));
- lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: {} "f.q.n"
+ lua_pushlstring(L2, _fqn, _len); // L1: ... f ... L2: {} "f.q.n"
lua_rawget(L2, -2); // L1: ... f ... L2: {} f
// nil means we don't know how to transfer stuff: user should do something
// anything other than function or table should not happen!
if (!lua_isfunction(L2, -1) && !lua_istable(L2, -1)) {
lua_getglobal(L1, "decoda_name"); // L1: ... f ... decoda_name
- char const* const from{ lua_tostring(L1, -1) };
+ char const* const _from{ lua_tostring(L1, -1) };
lua_pop(L1, 1); // L1: ... f ...
lua_getglobal(L2, "decoda_name"); // L1: ... f ... L2: {} f decoda_name
- char const* const to{ lua_tostring(L2, -1) };
+ char const* const _to{ lua_tostring(L2, -1) };
lua_pop(L2, 1); // L2: {} f
// 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
raise_luaL_error(
getErrL(),
"%s%s: function '%s' not found in %s destination transfer database.",
lua_isnil(L2, -1) ? "" : "INTERNAL ERROR IN ",
- from ? from : "main",
- fqn,
- to ? to : "main");
+ _from ? _from : "main",
+ _fqn,
+ _to ? _to : "main");
return;
}
lua_remove(L2, -2); // L2: f
@@ -381,10 +380,10 @@ void InterCopyContext::lookup_native_func() const
// Always pushes a function to 'L2'.
void InterCopyContext::copy_cached_func() const
{
- FuncSubType const funcSubType{ luaG_getfuncsubtype(L1, L1_i) };
- if (funcSubType == FuncSubType::Bytecode) {
- void* const aspointer = const_cast(lua_topointer(L1, L1_i));
- // TBD: Merge this and same code for tables
+ FuncSubType const _funcSubType{ luaG_getfuncsubtype(L1, L1_i) };
+ if (_funcSubType == FuncSubType::Bytecode) {
+ void* const _aspointer{ const_cast(lua_topointer(L1, L1_i)) };
+ // TODO: Merge this and same code for tables
LUA_ASSERT(L1, L2_cache_i != 0);
STACK_GROW(L2, 2);
@@ -397,7 +396,7 @@ void InterCopyContext::copy_cached_func() const
// is only for the duration of a copy (both states are locked).
// push a light userdata uniquely representing the function
- lua_pushlightuserdata(L2, aspointer); // L2: ... {cache} ... p
+ lua_pushlightuserdata(L2, _aspointer); // L2: ... {cache} ... p
// fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1));
@@ -430,9 +429,9 @@ void InterCopyContext::copy_cached_func() const
[[nodiscard]] bool InterCopyContext::lookup_table() const
{
// get the name of the table we want to send
- size_t len;
- char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len);
- if (nullptr == fqn) { // name not found, it is some user-created table
+ size_t _len;
+ char const* const _fqn{ find_lookup_name(L1, L1_i, mode, name, &_len) };
+ if (nullptr == _fqn) { // name not found, it is some user-created table
return false;
}
// push the equivalent table in the destination's stack, retrieved from the lookup table
@@ -445,7 +444,7 @@ void InterCopyContext::copy_cached_func() const
case LookupMode::ToKeeper:
// push a sentinel closure that holds the lookup name as upvalue
- lua_pushlstring(L2, fqn, len); // L1: ... t ... L2: "f.q.n"
+ lua_pushlstring(L2, _fqn, _len); // L1: ... t ... L2: "f.q.n"
lua_pushcclosure(L2, table_lookup_sentinel, 1); // L1: ... t ... L2: f
break;
@@ -454,7 +453,7 @@ void InterCopyContext::copy_cached_func() const
kLookupRegKey.pushValue(L2); // L1: ... t ... L2: {}
STACK_CHECK(L2, 1);
LUA_ASSERT(L1, lua_istable(L2, -1));
- lua_pushlstring(L2, fqn, len); // L2: {} "f.q.n"
+ lua_pushlstring(L2, _fqn, _len); // L2: {} "f.q.n"
lua_rawget(L2, -2); // L2: {} t
// we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead)
// but not when we extract something out of a keeper, as there is nothing to clone!
@@ -473,7 +472,7 @@ void InterCopyContext::copy_cached_func() const
getErrL(),
"%s: source table '%s' found as %s in %s destination transfer database.",
from ? from : "main",
- fqn,
+ _fqn,
lua_typename(L2, lua_type_as_enum(L2, -1)),
to ? to : "main");
}
@@ -488,12 +487,12 @@ void InterCopyContext::copy_cached_func() const
void InterCopyContext::inter_copy_keyvaluepair() const
{
- SourceIndex const val_i{ lua_gettop(L1) };
- SourceIndex const key_i{ val_i - 1 };
+ SourceIndex const _val_i{ lua_gettop(L1) };
+ SourceIndex const _key_i{ _val_i - 1 };
// For the key, only basic key types are copied over. others ignored
- InterCopyContext c{ U, L2, L1, L2_cache_i, key_i, VT::KEY, mode, name };
- if (!c.inter_copy_one()) {
+ InterCopyContext _c{ U, L2, L1, L2_cache_i, _key_i, VT::KEY, mode, name };
+ if (!_c.inter_copy_one()) {
return;
// we could raise an error instead of ignoring the table entry, like so:
// 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));
@@ -503,44 +502,44 @@ void InterCopyContext::inter_copy_keyvaluepair() const
char* valPath{ nullptr };
if (U->verboseErrors) {
// for debug purposes, let's try to build a useful name
- if (lua_type(L1, key_i) == LUA_TSTRING) {
- char const* key{ lua_tostring(L1, key_i) };
- size_t const keyRawLen = lua_rawlen(L1, key_i);
+ if (lua_type(L1, _key_i) == LUA_TSTRING) {
+ char const* key{ lua_tostring(L1, _key_i) };
+ size_t const keyRawLen = lua_rawlen(L1, _key_i);
size_t const bufLen = strlen(name) + keyRawLen + 2;
valPath = (char*) alloca(bufLen);
sprintf(valPath, "%s.%*s", name, (int) keyRawLen, key);
key = nullptr;
}
#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
- else if (lua_isinteger(L1, key_i)) {
- lua_Integer const key{ lua_tointeger(L1, key_i) };
+ else if (lua_isinteger(L1, _key_i)) {
+ lua_Integer const key{ lua_tointeger(L1, _key_i) };
valPath = (char*) alloca(strlen(name) + 32 + 3);
sprintf(valPath, "%s[" LUA_INTEGER_FMT "]", name, key);
}
#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
- else if (lua_type(L1, key_i) == LUA_TNUMBER) {
- lua_Number const key{ lua_tonumber(L1, key_i) };
+ else if (lua_type(L1, _key_i) == LUA_TNUMBER) {
+ lua_Number const key{ lua_tonumber(L1, _key_i) };
valPath = (char*) alloca(strlen(name) + 32 + 3);
sprintf(valPath, "%s[" LUA_NUMBER_FMT "]", name, key);
- } else if (lua_type(L1, key_i) == LUA_TLIGHTUSERDATA) {
- void* const key{ lua_touserdata(L1, key_i) };
+ } else if (lua_type(L1, _key_i) == LUA_TLIGHTUSERDATA) {
+ void* const key{ lua_touserdata(L1, _key_i) };
valPath = (char*) alloca(strlen(name) + 16 + 5);
sprintf(valPath, "%s[U:%p]", name, key);
- } else if (lua_type(L1, key_i) == LUA_TBOOLEAN) {
- int const key{ lua_toboolean(L1, key_i) };
+ } else if (lua_type(L1, _key_i) == LUA_TBOOLEAN) {
+ int const key{ lua_toboolean(L1, _key_i) };
valPath = (char*) alloca(strlen(name) + 8);
sprintf(valPath, "%s[%s]", name, key ? "true" : "false");
}
}
- c.L1_i = SourceIndex{ val_i };
+ _c.L1_i = SourceIndex{ _val_i };
// Contents of metatables are copied with cache checking. important to detect loops.
- c.vt = VT::NORMAL;
- c.name = valPath ? valPath : name;
- if (c.inter_copy_one()) {
+ _c.vt = VT::NORMAL;
+ _c.name = valPath ? valPath : name;
+ if (_c.inter_copy_one()) {
LUA_ASSERT(L1, lua_istable(L2, -3));
lua_rawset(L2, -3); // add to table (pops key & val)
} else {
- raise_luaL_error(getErrL(), "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", valPath, luaL_typename(L1, val_i));
+ raise_luaL_error(getErrL(), "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", valPath, luaL_typename(L1, _val_i));
}
}
@@ -555,13 +554,13 @@ void InterCopyContext::inter_copy_keyvaluepair() const
}
STACK_CHECK(L1, 1);
- lua_Integer const mt_id{ get_mt_id(U, L1, -1) }; // Unique id for the metatable
+ lua_Integer const _mt_id{ get_mt_id(U, L1, -1) }; // Unique id for the metatable
STACK_CHECK_START_REL(L2, 0);
STACK_GROW(L2, 4);
// do we already know this metatable?
std::ignore = kMtIdRegKey.getSubTable(L2, 0, 0); // L2: _R[kMtIdRegKey]
- lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] id
+ lua_pushinteger(L2, _mt_id); // L2: _R[kMtIdRegKey] id
lua_rawget(L2, -2); // L2: _R[kMtIdRegKey] mt|nil
STACK_CHECK(L2, 2);
@@ -574,13 +573,13 @@ void InterCopyContext::inter_copy_keyvaluepair() const
STACK_CHECK(L2, 2); // L2: _R[kMtIdRegKey] mt
// mt_id -> metatable
- lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt id
+ lua_pushinteger(L2, _mt_id); // L2: _R[kMtIdRegKey] mt id
lua_pushvalue(L2, -2); // L2: _R[kMtIdRegKey] mt id mt
lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt
// metatable -> mt_id
lua_pushvalue(L2, -1); // L2: _R[kMtIdRegKey] mt mt
- lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt mt id
+ lua_pushinteger(L2, _mt_id); // L2: _R[kMtIdRegKey] mt mt id
lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt
STACK_CHECK(L2, 2);
}
@@ -600,7 +599,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const
// Returns true if the table was cached (no need to fill it!); false if it's a virgin.
[[nodiscard]] bool InterCopyContext::push_cached_table() const
{
- void const* p{ lua_topointer(L1, L1_i) };
+ void const* const _p{ lua_topointer(L1, L1_i) };
LUA_ASSERT(L1, L2_cache_i != 0);
STACK_GROW(L2, 3);
@@ -609,37 +608,37 @@ void InterCopyContext::inter_copy_keyvaluepair() const
// We don't need to use the from state ('L1') in ID since the life span
// is only for the duration of a copy (both states are locked).
// push a light userdata uniquely representing the table
- lua_pushlightuserdata(L2, const_cast(p)); // L1: ... t ... L2: ... p
+ lua_pushlightuserdata(L2, const_cast(_p)); // L1: ... t ... L2: ... p
// fprintf(stderr, "<< ID: %s >>\n", lua_tostring(L2, -1));
lua_rawget(L2, L2_cache_i); // L1: ... t ... L2: ... {cached|nil}
- bool const not_found_in_cache{ lua_isnil(L2, -1) };
- if (not_found_in_cache) {
+ bool const _not_found_in_cache{ lua_isnil(L2, -1) };
+ if (_not_found_in_cache) {
// create a new entry in the cache
lua_pop(L2, 1); // L1: ... t ... L2: ...
lua_newtable(L2); // L1: ... t ... L2: ... {}
- lua_pushlightuserdata(L2, const_cast(p)); // L1: ... t ... L2: ... {} p
+ lua_pushlightuserdata(L2, const_cast(_p)); // L1: ... t ... L2: ... {} p
lua_pushvalue(L2, -2); // L1: ... t ... L2: ... {} p {}
lua_rawset(L2, L2_cache_i); // L1: ... t ... L2: ... {}
}
STACK_CHECK(L2, 1);
LUA_ASSERT(L1, lua_istable(L2, -1));
- return !not_found_in_cache;
+ return !_not_found_in_cache;
}
// #################################################################################################
[[nodiscard]] bool InterCopyContext::tryCopyClonable() const
{
- SourceIndex const L1i{ lua_absindex(L1, L1_i) };
- void* const source{ lua_touserdata(L1, L1i) };
+ SourceIndex const _L1_i{ lua_absindex(L1, L1_i) };
+ void* const _source{ lua_touserdata(L1, _L1_i) };
STACK_CHECK_START_REL(L1, 0);
STACK_CHECK_START_REL(L2, 0);
// Check if the source was already cloned during this copy
- lua_pushlightuserdata(L2, source); // L2: ... source
+ lua_pushlightuserdata(L2, _source); // L2: ... source
lua_rawget(L2, L2_cache_i); // L2: ... clone?
if (!lua_isnil(L2, -1)) {
STACK_CHECK(L2, 1);
@@ -650,7 +649,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const
STACK_CHECK(L2, 0);
// no metatable? -> not clonable
- if (!lua_getmetatable(L1, L1i)) { // L1: ... mt?
+ if (!lua_getmetatable(L1, _L1_i)) { // L1: ... mt?
STACK_CHECK(L1, 0);
return false;
}
@@ -666,18 +665,18 @@ void InterCopyContext::inter_copy_keyvaluepair() const
// we need to copy over the uservalues of the userdata as well
{
int const mt{ lua_absindex(L1, -2) }; // L1: ... mt __lanesclone
- size_t const userdata_size{ lua_rawlen(L1, L1i) };
+ size_t const userdata_size{ lua_rawlen(L1, _L1_i) };
// extract all the uservalues, but don't transfer them yet
- int uvi = 0;
- while (lua_getiuservalue(L1, L1i, ++uvi) != LUA_TNONE) {} // L1: ... mt __lanesclone [uv]+ nil
+ int _uvi{ 0 };
+ while (lua_getiuservalue(L1, _L1_i, ++_uvi) != LUA_TNONE) {} // L1: ... mt __lanesclone [uv]+ nil
// when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]+
- --uvi;
+ --_uvi;
// create the clone userdata with the required number of uservalue slots
- void* const clone{ lua_newuserdatauv(L2, userdata_size, uvi) }; // L2: ... u
+ void* const _clone{ lua_newuserdatauv(L2, userdata_size, _uvi) }; // L2: ... u
// copy the metatable in the target state, and give it to the clone we put there
- InterCopyContext c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name };
- if (c.inter_copy_one()) { // L2: ... u mt|sentinel
+ InterCopyContext _c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name };
+ if (_c.inter_copy_one()) { // L2: ... u mt|sentinel
if (LookupMode::ToKeeper == mode) { // L2: ... u sentinel
LUA_ASSERT(L1, lua_tocfunction(L2, -1) == table_lookup_sentinel);
// we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn
@@ -694,7 +693,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const
raise_luaL_error(getErrL(), "Error copying a metatable");
}
// first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel
- lua_pushlightuserdata(L2, source); // L2: ... u source
+ lua_pushlightuserdata(L2, _source); // L2: ... u source
lua_pushvalue(L2, -2); // L2: ... u source u
lua_rawset(L2, L2_cache_i); // L2: ... u
// make sure we have the userdata now
@@ -702,15 +701,15 @@ void InterCopyContext::inter_copy_keyvaluepair() const
lua_getupvalue(L2, -1, 2); // L2: ... userdata_clone_sentinel u
}
// assign uservalues
- while (uvi > 0) {
- c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
- if (!c.inter_copy_one()) { // L2: ... u uv
+ while (_uvi > 0) {
+ _c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
+ if (!_c.inter_copy_one()) { // L2: ... u uv
raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
}
lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]*
// this pops the value from the stack
- lua_setiuservalue(L2, -2, uvi); // L2: ... u
- --uvi;
+ lua_setiuservalue(L2, -2, _uvi); // L2: ... u
+ --_uvi;
}
// when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination
if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel u
@@ -719,8 +718,8 @@ void InterCopyContext::inter_copy_keyvaluepair() const
STACK_CHECK(L2, 1);
STACK_CHECK(L1, 2);
// call cloning function in source state to perform the actual memory cloning
- lua_pushlightuserdata(L1, clone); // L1: ... mt __lanesclone clone
- lua_pushlightuserdata(L1, source); // L1: ... mt __lanesclone clone source
+ lua_pushlightuserdata(L1, _clone); // L1: ... mt __lanesclone clone
+ lua_pushlightuserdata(L1, _source); // L1: ... mt __lanesclone clone source
lua_pushinteger(L1, static_cast(userdata_size)); // L1: ... mt __lanesclone clone source size
lua_call(L1, 3, 0); // L1: ... mt
STACK_CHECK(L1, 1);
@@ -738,8 +737,8 @@ void InterCopyContext::inter_copy_keyvaluepair() const
// Returns false if not a deep userdata, else true (unless an error occured)
[[nodiscard]] bool InterCopyContext::tryCopyDeep() const
{
- DeepFactory* const factory{ LookupFactory(L1, L1_i, mode) };
- if (factory == nullptr) {
+ DeepFactory* const _factory{ LookupFactory(L1, L1_i, mode) };
+ if (_factory == nullptr) {
return false; // not a deep userdata
}
@@ -747,33 +746,33 @@ void InterCopyContext::inter_copy_keyvaluepair() const
STACK_CHECK_START_REL(L2, 0);
// extract all uservalues of the source. unfortunately, the only way to know their count is to iterate until we fail
- int nuv = 0;
- while (lua_getiuservalue(L1, L1_i, nuv + 1) != LUA_TNONE) { // L1: ... u [uv]* nil
- ++nuv;
+ int _nuv = 0;
+ while (lua_getiuservalue(L1, L1_i, _nuv + 1) != LUA_TNONE) { // L1: ... u [uv]* nil
+ ++_nuv;
}
// last call returned TNONE and pushed nil, that we don't need
lua_pop(L1, 1); // L1: ... u [uv]*
- STACK_CHECK(L1, nuv);
+ STACK_CHECK(L1, _nuv);
DeepPrelude* const u{ *lua_tofulluserdata(L1, L1_i) };
- char const* errmsg{ DeepFactory::PushDeepProxy(L2, u, nuv, mode) }; // L1: ... u [uv]* L2: u
+ char const* errmsg{ DeepFactory::PushDeepProxy(L2, u, _nuv, mode) }; // L1: ... u [uv]* L2: u
if (errmsg != nullptr) {
raise_luaL_error(getErrL(), errmsg);
}
// transfer all uservalues of the source in the destination
{
- InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, name };
- int const clone_i{ lua_gettop(L2) };
- while (nuv) {
- c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
- if (!c.inter_copy_one()) { // L1: ... u [uv]* L2: u uv
+ InterCopyContext _c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, name };
+ int const _clone_i{ lua_gettop(L2) };
+ while (_nuv) {
+ _c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
+ if (!_c.inter_copy_one()) { // L1: ... u [uv]* L2: u uv
raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
}
lua_pop(L1, 1); // L1: ... u [uv]*
// this pops the value from the stack
- lua_setiuservalue(L2, clone_i, nuv); // L2: u
- --nuv;
+ lua_setiuservalue(L2, _clone_i, _nuv); // L2: u
+ --_nuv;
}
}
@@ -787,9 +786,9 @@ void InterCopyContext::inter_copy_keyvaluepair() const
[[nodiscard]] bool InterCopyContext::inter_copy_boolean() const
{
- int const v{ lua_toboolean(L1, L1_i) };
- DEBUGSPEW_CODE(fprintf(stderr, "%s\n", v ? "true" : "false"));
- lua_pushboolean(L2, v);
+ int const _v{ lua_toboolean(L1, L1_i) };
+ DEBUGSPEW_CODE(fprintf(stderr, "%s\n", _v ? "true" : "false"));
+ lua_pushboolean(L2, _v);
return true;
}
@@ -810,8 +809,8 @@ void InterCopyContext::inter_copy_keyvaluepair() const
// let's see if we already restored this userdata
lua_getupvalue(L1, L1_i, 2); // L1: ... u
- void* source = lua_touserdata(L1, -1);
- lua_pushlightuserdata(L2, source); // L2: ... source
+ void* _source{ lua_touserdata(L1, -1) };
+ lua_pushlightuserdata(L2, _source); // L2: ... source
lua_rawget(L2, L2_cache_i); // L2: ... u?
if (!lua_isnil(L2, -1)) {
lua_pop(L1, 1); // L1: ...
@@ -829,22 +828,22 @@ void InterCopyContext::inter_copy_keyvaluepair() const
}
// 'L1_i' slot was the proxy closure, but from now on we operate onthe actual userdata we extracted from it
SourceIndex const source_i{ lua_gettop(L1) };
- source = lua_touserdata(L1, -1);
- void* clone{ nullptr };
+ _source = lua_touserdata(L1, -1);
+ void* _clone{ nullptr };
// get the number of bytes to allocate for the clone
size_t const userdata_size{ lua_rawlen(L1, -1) };
{
// extract uservalues (don't transfer them yet)
- int uvi = 0;
- while (lua_getiuservalue(L1, source_i, ++uvi) != LUA_TNONE) {} // L1: ... u uv
+ int _uvi = 0;
+ while (lua_getiuservalue(L1, source_i, ++_uvi) != LUA_TNONE) {} // L1: ... u uv
// when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
lua_pop(L1, 1); // L1: ... u [uv]*
- --uvi;
- STACK_CHECK(L1, uvi + 1);
+ --_uvi;
+ STACK_CHECK(L1, _uvi + 1);
// create the clone userdata with the required number of uservalue slots
- clone = lua_newuserdatauv(L2, userdata_size, uvi); // L2: ... mt u
+ _clone = lua_newuserdatauv(L2, userdata_size, _uvi); // L2: ... mt u
// add it in the cache
- lua_pushlightuserdata(L2, source); // L2: ... mt u source
+ lua_pushlightuserdata(L2, _source); // L2: ... mt u source
lua_pushvalue(L2, -2); // L2: ... mt u source u
lua_rawset(L2, L2_cache_i); // L2: ... mt u
// set metatable
@@ -852,15 +851,15 @@ void InterCopyContext::inter_copy_keyvaluepair() const
lua_setmetatable(L2, -2); // L2: ... mt u
// transfer and assign uservalues
InterCopyContext c{ *this };
- while (uvi > 0) {
+ while (_uvi > 0) {
c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
if (!c.inter_copy_one()) { // L2: ... mt u uv
raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
}
lua_pop(L1, 1); // L1: ... u [uv]*
// this pops the value from the stack
- lua_setiuservalue(L2, -2, uvi); // L2: ... mt u
- --uvi;
+ lua_setiuservalue(L2, -2, _uvi); // L2: ... mt u
+ --_uvi;
}
// when we are done, all uservalues are popped from the stack, we can pop the source as well
lua_pop(L1, 1); // L1: ...
@@ -868,12 +867,12 @@ void InterCopyContext::inter_copy_keyvaluepair() const
STACK_CHECK(L2, 2); // L2: ... mt u
}
// perform the custom cloning part
- lua_insert(L2, -2); // L2: ... u mt
+ lua_insert(L2, -2); // L2: ... u mt
// __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with
lua_getfield(L2, -1, "__lanesclone"); // L2: ... u mt __lanesclone
lua_remove(L2, -2); // L2: ... u __lanesclone
- lua_pushlightuserdata(L2, clone); // L2: ... u __lanesclone clone
- lua_pushlightuserdata(L2, source); // L2: ... u __lanesclone clone source
+ lua_pushlightuserdata(L2, _clone); // L2: ... u __lanesclone clone
+ lua_pushlightuserdata(L2, _source); // L2: ... u __lanesclone clone source
lua_pushinteger(L2, userdata_size); // L2: ... u __lanesclone clone source size
// clone:__lanesclone(dest, source, size)
lua_call(L2, 3, 0); // L2: ... u
@@ -891,9 +890,9 @@ void InterCopyContext::inter_copy_keyvaluepair() const
[[nodiscard]] bool InterCopyContext::inter_copy_lightuserdata() const
{
- void* const p{ lua_touserdata(L1, L1_i) };
- DEBUGSPEW_CODE(fprintf(stderr, "%p\n", p));
- lua_pushlightuserdata(L2, p);
+ void* const _p{ lua_touserdata(L1, L1_i) };
+ DEBUGSPEW_CODE(fprintf(stderr, "%p\n", _p));
+ lua_pushlightuserdata(L2, _p);
return true;
}
@@ -915,15 +914,15 @@ void InterCopyContext::inter_copy_keyvaluepair() const
// LNUM patch support (keeping integer accuracy)
#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
if (lua_isinteger(L1, L1_i)) {
- lua_Integer const v{ lua_tointeger(L1, L1_i) };
- DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", v));
- lua_pushinteger(L2, v);
+ lua_Integer const _v{ lua_tointeger(L1, L1_i) };
+ DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", _v));
+ lua_pushinteger(L2, _v);
} else
#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
{
- lua_Number const v{ lua_tonumber(L1, L1_i) };
- DEBUGSPEW_CODE(fprintf(stderr, LUA_NUMBER_FMT "\n", v));
- lua_pushnumber(L2, v);
+ lua_Number const _v{ lua_tonumber(L1, L1_i) };
+ DEBUGSPEW_CODE(fprintf(stderr, LUA_NUMBER_FMT "\n", _v));
+ lua_pushnumber(L2, _v);
}
return true;
}
@@ -932,10 +931,10 @@ void InterCopyContext::inter_copy_keyvaluepair() const
[[nodiscard]] bool InterCopyContext::inter_copy_string() const
{
- size_t len;
- char const* const s{ lua_tolstring(L1, L1_i, &len) };
- DEBUGSPEW_CODE(fprintf(stderr, "'%s'\n", s));
- lua_pushlstring(L2, s, len);
+ size_t _len;
+ char const* const _s{ lua_tolstring(L1, L1_i, &_len) };
+ DEBUGSPEW_CODE(fprintf(stderr, "'%s'\n", _s));
+ lua_pushlstring(L2, _s, _len);
return true;
}
@@ -1029,8 +1028,8 @@ void InterCopyContext::inter_copy_keyvaluepair() const
// Not a deep or clonable full userdata
if (U->demoteFullUserdata) { // attempt demotion to light userdata
- void* const lud{ lua_touserdata(L1, L1_i) };
- lua_pushlightuserdata(L2, lud);
+ void* const _lud{ lua_touserdata(L1, L1_i) };
+ lua_pushlightuserdata(L2, _lud);
} else { // raise an error
raise_luaL_error(getErrL(), "can't copy non-deep full userdata across lanes");
}
@@ -1083,16 +1082,16 @@ static char const* vt_names[] = {
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END(U)));
DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
- LuaType val_type{ lua_type_as_enum(L1, L1_i) };
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s %s: " INDENT_END(U), lua_type_names[static_cast(val_type)], vt_names[static_cast(vt)]));
+ LuaType _val_type{ lua_type_as_enum(L1, L1_i) };
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s %s: " INDENT_END(U), lua_type_names[static_cast(_val_type)], vt_names[static_cast(vt)]));
// Non-POD can be skipped if its metatable contains { __lanesignore = true }
- if (((1 << static_cast(val_type)) & kPODmask) == 0) {
+ if (((1 << static_cast(_val_type)) & kPODmask) == 0) {
if (lua_getmetatable(L1, L1_i)) { // L1: ... mt
lua_getfield(L1, -1, "__lanesignore"); // L1: ... mt ignore?
if (lua_isboolean(L1, -1) && lua_toboolean(L1, -1)) {
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END(U)));
- val_type = LuaType::NIL;
+ _val_type = LuaType::NIL;
}
lua_pop(L1, 2); // L1: ...
}
@@ -1100,47 +1099,47 @@ static char const* vt_names[] = {
STACK_CHECK(L1, 0);
// Lets push nil to L2 if the object should be ignored
- bool ret{ true };
- switch (val_type) {
+ bool _ret{ true };
+ switch (_val_type) {
// Basic types allowed both as values, and as table keys
case LuaType::BOOLEAN:
- ret = inter_copy_boolean();
+ _ret = inter_copy_boolean();
break;
case LuaType::NUMBER:
- ret = inter_copy_number();
+ _ret = inter_copy_number();
break;
case LuaType::STRING:
- ret = inter_copy_string();
+ _ret = inter_copy_string();
break;
case LuaType::LIGHTUSERDATA:
- ret = inter_copy_lightuserdata();
+ _ret = inter_copy_lightuserdata();
break;
// The following types are not allowed as table keys
case LuaType::USERDATA:
- ret = inter_copy_userdata();
+ _ret = inter_copy_userdata();
break;
case LuaType::NIL:
- ret = inter_copy_nil();
+ _ret = inter_copy_nil();
break;
case LuaType::FUNCTION:
- ret = inter_copy_function();
+ _ret = inter_copy_function();
break;
case LuaType::TABLE:
- ret = inter_copy_table();
+ _ret = inter_copy_table();
break;
// The following types cannot be copied
case LuaType::CDATA:
[[fallthrough]];
case LuaType::THREAD:
- ret = false;
+ _ret = false;
break;
}
- STACK_CHECK(L2, ret ? 1 : 0);
+ STACK_CHECK(L2, _ret ? 1 : 0);
STACK_CHECK(L1, 0);
- return ret;
+ return _ret;
}
// #################################################################################################
@@ -1189,30 +1188,30 @@ static char const* vt_names[] = {
return InterCopyResult::Success;
}
- InterCopyResult result{ InterCopyResult::Success };
+ InterCopyResult _result{ InterCopyResult::Success };
// package.loaders is renamed package.searchers in Lua 5.2
// but don't copy it anyway, as the function names change depending on the slot index!
// users should provide an on_state_create function to setup custom loaders instead
// don't copy package.preload in keeper states (they don't know how to translate functions)
- char const* entries[] = { "path", "cpath", (mode == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr };
- for (char const* const entry : entries) {
- if (!entry) {
+ char const* _entries[] = { "path", "cpath", (mode == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr };
+ for (char const* const _entry : _entries) {
+ if (!_entry) {
continue;
}
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END(U), entry));
- lua_getfield(L1, L1_i, entry);
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END(U), _entry));
+ lua_getfield(L1, L1_i, _entry);
if (lua_isnil(L1, -1)) {
lua_pop(L1, 1);
} else {
{
DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
- result = inter_move(1); // moves the entry to L2
+ _result = inter_move(1); // moves the entry to L2
STACK_CHECK(L1, 0);
}
- if (result == InterCopyResult::Success) {
- lua_setfield(L2, -2, entry); // set package[entry]
+ if (_result == InterCopyResult::Success) {
+ lua_setfield(L2, -2, _entry); // set package[entry]
} else {
- lua_pushfstring(L1, "failed to copy package entry %s", entry);
+ lua_pushfstring(L1, "failed to copy package entry %s", _entry);
// raise the error when copying from lane to lane, else just leave it on the stack to be raised later
if (mode == LookupMode::LaneBody) {
raise_lua_error(getErrL());
@@ -1223,7 +1222,7 @@ static char const* vt_names[] = {
}
}
STACK_CHECK(L1, 0);
- return result;
+ return _result;
}
// #################################################################################################
@@ -1237,8 +1236,8 @@ static char const* vt_names[] = {
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy()\n" INDENT_END(U)));
DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
- int const top_L1{ lua_gettop(L1) };
- if (n_ > top_L1) {
+ int const _top_L1{ lua_gettop(L1) };
+ if (n_ > _top_L1) {
// requesting to copy more than is available?
DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END(U)));
return InterCopyResult::NotEnoughValues;
@@ -1252,36 +1251,36 @@ static char const* vt_names[] = {
* function entries, avoiding the same entries to be passed on as multiple
* copies. ESSENTIAL i.e. for handling upvalue tables in the right manner!
*/
- int const top_L2{ lua_gettop(L2) }; // L2: ...
+ int const _top_L2{ lua_gettop(L2) }; // L2: ...
lua_newtable(L2); // L2: ... cache
- char tmpBuf[16];
- char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" };
- InterCopyContext c{ U, L2, L1, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode, pBuf };
- bool copyok{ true };
+ char _tmpBuf[16];
+ char const* const _pBuf{ U->verboseErrors ? _tmpBuf : "?" };
+ InterCopyContext _c{ U, L2, L1, CacheIndex{ _top_L2 + 1 }, {}, VT::NORMAL, mode, _pBuf };
+ bool _copyok{ true };
STACK_CHECK_START_REL(L1, 0);
- for (int i{ top_L1 - n_ + 1 }, j{ 1 }; i <= top_L1; ++i, ++j) {
+ for (int i{ _top_L1 - n_ + 1 }, j{ 1 }; i <= _top_L1; ++i, ++j) {
if (U->verboseErrors) {
- sprintf(tmpBuf, "arg_%d", j);
+ sprintf(_tmpBuf, "arg_%d", j);
}
- c.L1_i = SourceIndex{ i };
- copyok = c.inter_copy_one(); // L2: ... cache {}n
- if (!copyok) {
+ _c.L1_i = SourceIndex{ i };
+ _copyok = _c.inter_copy_one(); // L2: ... cache {}n
+ if (!_copyok) {
break;
}
}
STACK_CHECK(L1, 0);
- if (copyok) {
+ if (_copyok) {
STACK_CHECK(L2, n_ + 1);
// Remove the cache table. Persistent caching would cause i.e. multiple
// messages passed in the same table to use the same table also in receiving end.
- lua_remove(L2, top_L2 + 1);
+ lua_remove(L2, _top_L2 + 1); // L2: ... {}n
return InterCopyResult::Success;
}
// error -> pop everything from the target state stack
- lua_settop(L2, top_L2);
+ lua_settop(L2, _top_L2);
STACK_CHECK(L2, 0);
return InterCopyResult::Error;
}
@@ -1290,7 +1289,7 @@ static char const* vt_names[] = {
[[nodiscard]] InterCopyResult InterCopyContext::inter_move(int n_) const
{
- InterCopyResult const ret{ inter_copy(n_) };
+ InterCopyResult const _ret{ inter_copy(n_) };
lua_pop(L1, n_);
- return ret;
+ return _ret;
}
diff --git a/src/keeper.cpp b/src/keeper.cpp
index 7367d0c..39d2e85 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -76,15 +76,15 @@ static constexpr int kContentsTableIndex{ 1 };
// replaces the fifo ud by its uservalue on the stack
[[nodiscard]] static keeper_fifo* prepare_fifo_access(lua_State* L_, int idx_)
{
- keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, idx_) };
- if (fifo != nullptr) {
+ keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, idx_) };
+ if (_fifo != nullptr) {
idx_ = lua_absindex(L_, idx_);
STACK_GROW(L_, 1);
// we can replace the fifo userdata in the stack without fear of it being GCed, there are other references around
lua_getiuservalue(L_, idx_, kContentsTableIndex);
lua_replace(L_, idx_);
}
- return fifo;
+ return _fifo;
}
// #################################################################################################
@@ -95,12 +95,12 @@ static constexpr int kContentsTableIndex{ 1 };
{
STACK_GROW(L_, 2);
STACK_CHECK_START_REL(L_, 0);
- keeper_fifo* const fifo{ new (L_) keeper_fifo{} };
+ keeper_fifo* const _fifo{ new (L_) keeper_fifo{} };
STACK_CHECK(L_, 1);
lua_newtable(L_);
lua_setiuservalue(L_, -2, kContentsTableIndex);
STACK_CHECK(L_, 1);
- return fifo;
+ return _fifo;
}
// #################################################################################################
@@ -109,12 +109,12 @@ static constexpr int kContentsTableIndex{ 1 };
// out: nothing, removes all pushed values from the stack
static void fifo_push(lua_State* L_, keeper_fifo* fifo_, int count_)
{
- int const idx{ lua_gettop(L_) - count_ };
- int const start{ fifo_->first + fifo_->count - 1 };
+ int const _idx{ lua_gettop(L_) - count_ };
+ int const _start{ fifo_->first + fifo_->count - 1 };
// pop all additional arguments, storing them in the fifo
- for (int i = count_; i >= 1; --i) {
+ for (int _i = count_; _i >= 1; --_i) {
// store in the fifo the value at the top of the stack at the specified index, popping it from the stack
- lua_rawseti(L_, idx, start + i);
+ lua_rawseti(L_, _idx, _start + _i);
}
fifo_->count += count_;
}
@@ -129,8 +129,8 @@ static void fifo_push(lua_State* L_, keeper_fifo* fifo_, int count_)
static void fifo_peek(lua_State* L_, keeper_fifo* fifo_, int count_)
{
STACK_GROW(L_, count_);
- for (int i = 0; i < count_; ++i) {
- lua_rawgeti(L_, 1, (fifo_->first + i));
+ for (int _i = 0; _i < count_; ++_i) {
+ lua_rawgeti(L_, 1, (fifo_->first + _i));
}
}
@@ -141,32 +141,32 @@ static void fifo_peek(lua_State* L_, keeper_fifo* fifo_, int count_)
static void fifo_pop(lua_State* L_, keeper_fifo* fifo_, int count_)
{
LUA_ASSERT(L_, lua_istable(L_, -1));
- int const fifo_idx{ lua_gettop(L_) }; // L_: ... fifotbl
+ int const _fifo_idx{ lua_gettop(L_) }; // L_: ... fifotbl
// each iteration pushes a value on the stack!
STACK_GROW(L_, count_ + 2);
// skip first item, we will push it last
for (int i = 1; i < count_; ++i) {
int const at{ fifo_->first + i };
// push item on the stack
- lua_rawgeti(L_, fifo_idx, at); // L_: ... fifotbl val
+ lua_rawgeti(L_, _fifo_idx, at); // L_: ... fifotbl val
// remove item from the fifo
lua_pushnil(L_); // L_: ... fifotbl val nil
- lua_rawseti(L_, fifo_idx, at); // L_: ... fifotbl val
+ lua_rawseti(L_, _fifo_idx, at); // L_: ... fifotbl val
}
// now process first item
{
int const at{ fifo_->first };
- lua_rawgeti(L_, fifo_idx, at); // L_: ... fifotbl vals val
+ lua_rawgeti(L_, _fifo_idx, at); // L_: ... fifotbl vals val
lua_pushnil(L_); // L_: ... fifotbl vals val nil
- lua_rawseti(L_, fifo_idx, at); // L_: ... fifotbl vals val
- lua_replace(L_, fifo_idx); // L_: ... vals
+ lua_rawseti(L_, _fifo_idx, at); // L_: ... fifotbl vals val
+ lua_replace(L_, _fifo_idx); // L_: ... vals
}
// avoid ever-growing indexes by resetting each time we detect the fifo is empty
{
- int const new_count{ fifo_->count - count_ };
- fifo_->first = (new_count == 0) ? 1 : (fifo_->first + count_);
- fifo_->count = new_count;
+ int const _new_count{ fifo_->count - count_ };
+ fifo_->first = (_new_count == 0) ? 1 : (fifo_->first + count_);
+ fifo_->count = _new_count;
}
}
@@ -202,34 +202,34 @@ static void push_table(lua_State* L_, int idx_)
// only used by linda:dump() and linda:__towatch() for debugging purposes
int keeper_push_linda_storage(Linda& linda_, DestState L_)
{
- Keeper* const K{ linda_.whichKeeper() };
- SourceState const KL{ K ? K->L : nullptr };
- if (KL == nullptr)
+ Keeper* const _K{ linda_.whichKeeper() };
+ SourceState const _KL{ _K ? _K->L : nullptr };
+ if (_KL == nullptr)
return 0;
- STACK_GROW(KL, 4);
- STACK_CHECK_START_REL(KL, 0);
- kFifosRegKey.pushValue(KL); // KL: fifos L_:
- lua_pushlightuserdata(KL, &linda_); // KL: fifos ud L_:
- lua_rawget(KL, -2); // KL: fifos storage L_:
- lua_remove(KL, -2); // KL: storage L_:
- if (!lua_istable(KL, -1)) {
- lua_pop(KL, 1); // KL: L_:
- STACK_CHECK(KL, 0);
+ STACK_GROW(_KL, 4);
+ STACK_CHECK_START_REL(_KL, 0);
+ kFifosRegKey.pushValue(_KL); // KL: fifos L_:
+ lua_pushlightuserdata(_KL, &linda_); // KL: fifos ud L_:
+ lua_rawget(_KL, -2); // KL: fifos storage L_:
+ lua_remove(_KL, -2); // KL: storage L_:
+ if (!lua_istable(_KL, -1)) {
+ lua_pop(_KL, 1); // KL: L_:
+ STACK_CHECK(_KL, 0);
return 0;
}
// move data from keeper to destination state
STACK_GROW(L_, 5);
STACK_CHECK_START_REL(L_, 0);
lua_newtable(L_); // KL: storage L_: out
- InterCopyContext c{ linda_.U, L_, KL, {}, {}, {}, LookupMode::FromKeeper, {} };
- lua_pushnil(KL); // KL: storage nil L_: out
- while (lua_next(KL, -2)) { // KL: storage key fifo L_: out
- keeper_fifo* fifo = prepare_fifo_access(KL, -1); // KL: storage key fifotbl L_: out
- lua_pushvalue(KL, -2); // KL: storage key fifotbl key L_: out
- std::ignore = c.inter_move(1); // KL: storage key fifotbl L_: out key
+ InterCopyContext _c{ linda_.U, L_, _KL, {}, {}, {}, LookupMode::FromKeeper, {} };
+ lua_pushnil(_KL); // KL: storage nil L_: out
+ while (lua_next(_KL, -2)) { // KL: storage key fifo L_: out
+ keeper_fifo* fifo = prepare_fifo_access(_KL, -1); // KL: storage key fifotbl L_: out
+ lua_pushvalue(_KL, -2); // KL: storage key fifotbl key L_: out
+ std::ignore = _c.inter_move(1); // KL: storage key fifotbl L_: out key
STACK_CHECK(L_, 2);
lua_newtable(L_); // KL: storage key L_: out key keyout
- std::ignore = c.inter_move(1); // KL: storage key L_: out key keyout fifotbl
+ std::ignore = _c.inter_move(1); // KL: storage key L_: out key keyout fifotbl
lua_pushinteger(L_, fifo->first); // KL: storage key L_: out key keyout fifotbl first
STACK_CHECK(L_, 5);
lua_setfield(L_, -3, "first"); // KL: storage key L_: out key keyout fifotbl
@@ -244,8 +244,8 @@ int keeper_push_linda_storage(Linda& linda_, DestState L_)
STACK_CHECK(L_, 1);
} // KL_: storage L_: out
STACK_CHECK(L_, 1);
- lua_pop(KL, 1); // KL: L_: out
- STACK_CHECK(KL, 0);
+ lua_pop(_KL, 1); // KL: L_: out
+ STACK_CHECK(_KL, 0);
return 1;
}
@@ -271,7 +271,7 @@ int keepercall_clear(lua_State* L_)
// out: true|false
int keepercall_send(lua_State* L_)
{
- int const n{ lua_gettop(L_) - 2 };
+ int const _n{ lua_gettop(L_) - 2 };
push_table(L_, 1); // L_: ud key ... fifos
// get the fifo associated to this key in this linda, create it if it doesn't exist
lua_pushvalue(L_, 2); // L_: ud key ... fifos key
@@ -284,14 +284,14 @@ int keepercall_send(lua_State* L_)
lua_rawset(L_, -4); // L_: ud key ... fifos fifo
}
lua_remove(L_, -2); // L_: ud key ... fifo
- keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) };
- if (fifo->limit >= 0 && fifo->count + n > fifo->limit) {
+ keeper_fifo* _fifo{ keeper_fifo::getPtr(L_, -1) };
+ if (_fifo->limit >= 0 && _fifo->count + _n > _fifo->limit) {
lua_settop(L_, 0); // L_:
lua_pushboolean(L_, 0); // L_:false
} else {
- fifo = prepare_fifo_access(L_, -1); // L_: ud fifotbl
+ _fifo = prepare_fifo_access(L_, -1); // L_: ud fifotbl
lua_replace(L_, 2); // L_: ud fifotbl ...
- fifo_push(L_, fifo, n); // L_: ud fifotbl
+ fifo_push(L_, _fifo, _n); // L_: ud fifotbl
lua_settop(L_, 0); // L_:
lua_pushboolean(L_, 1); // L_: true
}
@@ -304,19 +304,19 @@ int keepercall_send(lua_State* L_)
// out: (key, val) or nothing
int keepercall_receive(lua_State* L_)
{
- int const top{ lua_gettop(L_) };
+ int const _top{ lua_gettop(L_) };
push_table(L_, 1); // L_: ud keys fifos
lua_replace(L_, 1); // L_: fifos keys
- for (int i = 2; i <= top; ++i) {
- lua_pushvalue(L_, i); // L_: fifos keys key[i]
+ for (int _i = 2; _i <= _top; ++_i) {
+ lua_pushvalue(L_, _i); // L_: fifos keys key[i]
lua_rawget(L_, 1); // L_: fifos keys fifo
- keeper_fifo* const fifo{ prepare_fifo_access(L_, -1) }; // L_: fifos keys fifotbl
- if (fifo != nullptr && fifo->count > 0) {
- fifo_pop(L_, fifo, 1); // L_: fifos keys val
+ keeper_fifo* const _fifo{ prepare_fifo_access(L_, -1) }; // L_: fifos keys fifotbl
+ if (_fifo != nullptr && _fifo->count > 0) {
+ fifo_pop(L_, _fifo, 1); // L_: fifos keys val
if (!lua_isnil(L_, -1)) {
lua_replace(L_, 1); // L_: val keys
- lua_settop(L_, i); // L_: val keys key[i]
- if (i != 2) {
+ lua_settop(L_, _i); // L_: val keys key[i]
+ if (_i != 2) {
lua_replace(L_, 2); // L_: val key keys
lua_settop(L_, 2); // L_: val key
}
@@ -324,7 +324,7 @@ int keepercall_receive(lua_State* L_)
return 2;
}
}
- lua_settop(L_, top); // L_: data keys
+ lua_settop(L_, _top); // L_: data keys
}
// nothing to receive
return 0;
@@ -335,9 +335,9 @@ int keepercall_receive(lua_State* L_)
// in: linda_ud key mincount [maxcount]
int keepercall_receive_batched(lua_State* L_)
{
- int const min_count{ static_cast(lua_tointeger(L_, 3)) };
- if (min_count > 0) {
- int const max_count{ static_cast(luaL_optinteger(L_, 4, min_count)) };
+ int const _min_count{ static_cast(lua_tointeger(L_, 3)) };
+ if (_min_count > 0) {
+ int const _max_count{ static_cast(luaL_optinteger(L_, 4, _min_count)) };
lua_settop(L_, 2); // L_: ud key
lua_insert(L_, 1); // L_: key ud
push_table(L_, 2); // L_: key ud fifos
@@ -345,9 +345,9 @@ int keepercall_receive_batched(lua_State* L_)
lua_pushvalue(L_, 1); // L_: key fifos key
lua_rawget(L_, 2); // L_: key fifos fifo
lua_remove(L_, 2); // L_: key fifo
- keeper_fifo* const fifo{ prepare_fifo_access(L_, 2) }; // L_: key fifotbl
- if (fifo != nullptr && fifo->count >= min_count) {
- fifo_pop(L_, fifo, std::min(max_count, fifo->count)); // L_: key ...
+ keeper_fifo* const _fifo{ prepare_fifo_access(L_, 2) }; // L_: key fifotbl
+ if (_fifo != nullptr && _fifo->count >= _min_count) {
+ fifo_pop(L_, _fifo, std::min(_max_count, _fifo->count)); // L_: key ...
} else {
lua_settop(L_, 0); // L_:
}
@@ -363,16 +363,16 @@ int keepercall_receive_batched(lua_State* L_)
// out: true or nil
int keepercall_limit(lua_State* L_)
{
- int const limit{ static_cast(lua_tointeger(L_, 3)) };
+ int const _limit{ static_cast(lua_tointeger(L_, 3)) };
push_table(L_, 1); // L_: ud key n fifos
lua_replace(L_, 1); // L_: fifos key n
lua_pop(L_, 1); // L_: fifos key
lua_pushvalue(L_, -1); // L_: fifos key key
lua_rawget(L_, -3); // L_: fifos key fifo|nil
- keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) };
- if (fifo == nullptr) { // L_: fifos key nil
+ keeper_fifo* _fifo{ keeper_fifo::getPtr(L_, -1) };
+ if (_fifo == nullptr) { // L_: fifos key nil
lua_pop(L_, 1); // L_: fifos key
- fifo = fifo_new(KeeperState{ L_ }); // L_: fifos key fifo
+ _fifo = fifo_new(KeeperState{ L_ }); // L_: fifos key fifo
lua_rawset(L_, -3); // L_: fifos
}
// remove any clutter on the stack
@@ -380,13 +380,13 @@ int keepercall_limit(lua_State* L_)
// return true if we decide that blocked threads waiting to write on that key should be awakened
// this is the case if we detect the key was full but it is no longer the case
if (
- ((fifo->limit >= 0) && (fifo->count >= fifo->limit)) // the key was full if limited and count exceeded the previous limit
- && ((limit < 0) || (fifo->count < limit)) // the key is not full if unlimited or count is lower than the new limit
+ ((_fifo->limit >= 0) && (_fifo->count >= _fifo->limit)) // the key was full if limited and count exceeded the previous limit
+ && ((_limit < 0) || (_fifo->count < _limit)) // the key is not full if unlimited or count is lower than the new limit
) {
lua_pushboolean(L_, 1); // L_: true
}
// set the new limit
- fifo->limit = limit;
+ _fifo->limit = _limit;
// return 0 or 1 value
return lua_gettop(L_);
}
@@ -397,7 +397,7 @@ int keepercall_limit(lua_State* L_)
// out: true if the linda was full but it's no longer the case, else nothing
int keepercall_set(lua_State* L_)
{
- bool should_wake_writers{ false };
+ bool _should_wake_writers{ false };
STACK_GROW(L_, 6);
// retrieve fifos associated with the linda
@@ -409,28 +409,28 @@ int keepercall_set(lua_State* L_)
lua_pushvalue(L_, -1); // L_: fifos key key
lua_rawget(L_, 1); // L_: fifos key fifo|nil
// empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged!
- keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) };
- if (fifo != nullptr) { // might be nullptr if we set a nonexistent key to nil // L_: fifos key fifo
- if (fifo->limit < 0) { // fifo limit value is the default (unlimited): we can totally remove it
+ keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, -1) };
+ if (_fifo != nullptr) { // might be nullptr if we set a nonexistent key to nil // L_: fifos key fifo
+ if (_fifo->limit < 0) { // fifo limit value is the default (unlimited): we can totally remove it
lua_pop(L_, 1); // L_: fifos key
lua_pushnil(L_); // L_: fifos key nil
lua_rawset(L_, -3); // L_: fifos
} else {
// we create room if the fifo was full but it is no longer the case
- should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit);
+ _should_wake_writers = (_fifo->limit > 0) && (_fifo->count >= _fifo->limit);
lua_remove(L_, -2); // L_: fifos fifo
lua_newtable(L_); // L_: fifos fifo {}
lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos fifo
- fifo->first = 1;
- fifo->count = 0;
+ _fifo->first = 1;
+ _fifo->count = 0;
}
}
} else { // set/replace contents stored at the specified key?
- int const count{ lua_gettop(L_) - 2 }; // number of items we want to store
+ int const _count{ lua_gettop(L_) - 2 }; // number of items we want to store
lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] key
lua_rawget(L_, 1); // L_: fifos key [val [, ...]] fifo|nil
- keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) };
- if (fifo == nullptr) { // can be nullptr if we store a value at a new key // fifos key [val [, ...]] nil
+ keeper_fifo* _fifo{ keeper_fifo::getPtr(L_, -1) };
+ if (_fifo == nullptr) { // can be nullptr if we store a value at a new key // fifos key [val [, ...]] nil
// no need to wake writers in that case, because a writer can't wait on an inexistent key
lua_pop(L_, 1); // L_: fifos key [val [, ...]]
std::ignore = fifo_new(KeeperState{ L_ }); // L_: fifos key [val [, ...]] fifo
@@ -440,19 +440,19 @@ int keepercall_set(lua_State* L_)
} else { // L_: fifos key [val [, ...]] fifo
// the fifo exists, we just want to update its contents
// we create room if the fifo was full but it is no longer the case
- should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit) && (count < fifo->limit);
+ _should_wake_writers = (_fifo->limit > 0) && (_fifo->count >= _fifo->limit) && (_count < _fifo->limit);
// empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged!
lua_newtable(L_); // L_: fifos key [val [, ...]] fifo {}
lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos key [val [, ...]] fifo
- fifo->first = 1;
- fifo->count = 0;
+ _fifo->first = 1;
+ _fifo->count = 0;
}
- fifo = prepare_fifo_access(L_, -1); // L_: fifos key [val [, ...]] fifotbl
+ _fifo = prepare_fifo_access(L_, -1); // L_: fifos key [val [, ...]] fifotbl
// move the fifo below the values we want to store
lua_insert(L_, 3); // L_: fifos key fifotbl [val [, ...]]
- fifo_push(L_, fifo, count); // L_: fifos key fifotbl
+ fifo_push(L_, _fifo, _count); // L_: fifos key fifotbl
}
- return should_wake_writers ? (lua_pushboolean(L_, 1), 1) : 0;
+ return _should_wake_writers ? (lua_pushboolean(L_, 1), 1) : 0;
}
// #################################################################################################
@@ -461,21 +461,21 @@ int keepercall_set(lua_State* L_)
// out: at most values
int keepercall_get(lua_State* L_)
{
- int count{ 1 };
+ int _count{ 1 };
if (lua_gettop(L_) == 3) { // L_: ud key count
- count = static_cast(lua_tointeger(L_, 3));
+ _count = static_cast(lua_tointeger(L_, 3));
lua_pop(L_, 1); // L_: ud key
}
push_table(L_, 1); // L_: ud key fifos
lua_replace(L_, 1); // L_: fifos key
lua_rawget(L_, 1); // L_: fifos fifo
- keeper_fifo* const fifo{ prepare_fifo_access(L_, -1) }; // L_: fifos fifotbl
- if (fifo != nullptr && fifo->count > 0) {
+ keeper_fifo* const _fifo{ prepare_fifo_access(L_, -1) }; // L_: fifos fifotbl
+ if (_fifo != nullptr && _fifo->count > 0) {
lua_remove(L_, 1); // L_: fifotbl
- count = std::min(count, fifo->count);
+ _count = std::min(_count, _fifo->count);
// read value off the fifo
- fifo_peek(L_, fifo, count); // L_: fifotbl ...
- return count;
+ fifo_peek(L_, _fifo, _count); // L_: fifotbl ...
+ return _count;
}
// no fifo was ever registered for this key, or it is empty
return 0;
@@ -494,10 +494,10 @@ int keepercall_count(lua_State* L_)
lua_replace(L_, 1); // L_: out fifos
lua_pushnil(L_); // L_: out fifos nil
while (lua_next(L_, 2)) { // L_: out fifos key fifo
- keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) };
+ keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, -1) };
lua_pop(L_, 1); // L_: out fifos key
lua_pushvalue(L_, -1); // L_: out fifos key key
- lua_pushinteger(L_, fifo->count); // L_: out fifos key key count
+ lua_pushinteger(L_, _fifo->count); // L_: out fifos key key count
lua_rawset(L_, -5); // L_: out fifos key
}
lua_pop(L_, 1); // L_: out
@@ -510,8 +510,8 @@ int keepercall_count(lua_State* L_)
if (lua_isnil(L_, -1)) { // L_: the key is unknown // L_: fifos nil
lua_remove(L_, -2); // L_: nil
} else { // the key is known // L_: fifos fifo
- keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) };
- lua_pushinteger(L_, fifo->count); // L_: fifos fifo count
+ keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, -1) };
+ lua_pushinteger(L_, _fifo->count); // L_: fifos fifo count
lua_replace(L_, -3); // L_: count fifo
lua_pop(L_, 1); // L_: count
}
@@ -526,10 +526,10 @@ int keepercall_count(lua_State* L_)
while (lua_gettop(L_) > 2) {
lua_pushvalue(L_, -1); // L_: out fifos keys... key
lua_rawget(L_, 2); // L_: out fifos keys... fifo|nil
- keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) };
+ keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, -1) };
lua_pop(L_, 1); // L_: out fifos keys...
- if (fifo != nullptr) { // L_: the key is known
- lua_pushinteger(L_, fifo->count); // L_: out fifos keys... count
+ if (_fifo != nullptr) { // L_: the key is known
+ lua_pushinteger(L_, _fifo->count); // L_: out fifos keys... count
lua_rawset(L_, 1); // L_: out fifos keys...
} else { // the key is unknown
lua_pop(L_, 1); // L_: out fifos keys...
@@ -557,27 +557,27 @@ int keepercall_count(lua_State* L_)
void close_keepers(Universe* U_)
{
if (U_->keepers != nullptr) {
- int nbKeepers{ U_->keepers->nb_keepers };
+ int _nbKeepers{ U_->keepers->nb_keepers };
// 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
// 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
// in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success
// which is early-outed with a U->keepers->nbKeepers null-check
U_->keepers->nb_keepers = 0;
- for (int i = 0; i < nbKeepers; ++i) {
- lua_State* const K{ U_->keepers->keeper_array[i].L };
- U_->keepers->keeper_array[i].L = KeeperState{ nullptr };
+ for (int _i = 0; _i < _nbKeepers; ++_i) {
+ lua_State* const K{ U_->keepers->keeper_array[_i].L };
+ U_->keepers->keeper_array[_i].L = KeeperState{ nullptr };
if (K != nullptr) {
lua_close(K);
} else {
// detected partial init: destroy only the mutexes that got initialized properly
- nbKeepers = i;
+ _nbKeepers = _i;
}
}
- for (int i = 0; i < nbKeepers; ++i) {
- U_->keepers->keeper_array[i].~Keeper();
+ for (int _i = 0; _i < _nbKeepers; ++_i) {
+ U_->keepers->keeper_array[_i].~Keeper();
}
// free the keeper bookkeeping structure
- U_->internalAllocator.free(U_->keepers, sizeof(Keepers) + (nbKeepers - 1) * sizeof(Keeper));
+ U_->internalAllocator.free(U_->keepers, sizeof(Keepers) + (_nbKeepers - 1) * sizeof(Keeper));
U_->keepers = nullptr;
}
}
@@ -600,10 +600,10 @@ void init_keepers(Universe* U_, lua_State* L_)
LUA_ASSERT(L_, lua_gettop(L_) == 1 && lua_istable(L_, 1));
STACK_CHECK_START_REL(L_, 0); // L_: settings
lua_getfield(L_, 1, "nb_keepers"); // L_: settings nb_keepers
- int const nb_keepers{ static_cast(lua_tointeger(L_, -1)) };
+ int const _nb_keepers{ static_cast(lua_tointeger(L_, -1)) };
lua_pop(L_, 1); // L_: settings
- if (nb_keepers < 1) {
- raise_luaL_error(L_, "Bad number of keepers (%d)", nb_keepers);
+ if (_nb_keepers < 1) {
+ raise_luaL_error(L_, "Bad number of keepers (%d)", _nb_keepers);
}
STACK_CHECK(L_, 0);
@@ -614,51 +614,51 @@ void init_keepers(Universe* U_, lua_State* L_)
// Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states
{
- size_t const bytes = sizeof(Keepers) + (nb_keepers - 1) * sizeof(Keeper);
+ size_t const bytes = sizeof(Keepers) + (_nb_keepers - 1) * sizeof(Keeper);
U_->keepers = static_cast(U_->internalAllocator.alloc(bytes));
if (U_->keepers == nullptr) {
raise_luaL_error(L_, "init_keepers() failed while creating keeper array; out of memory");
}
U_->keepers->Keepers::Keepers();
U_->keepers->gc_threshold = keepers_gc_threshold;
- U_->keepers->nb_keepers = nb_keepers;
+ U_->keepers->nb_keepers = _nb_keepers;
- for (int i = 0; i < nb_keepers; ++i) {
- U_->keepers->keeper_array[i].Keeper::Keeper();
+ for (int _i = 0; _i < _nb_keepers; ++_i) {
+ U_->keepers->keeper_array[_i].Keeper::Keeper();
}
}
- for (int i = 0; i < nb_keepers; ++i) {
+ for (int _i = 0; _i < _nb_keepers; ++_i) {
// note that we will leak K if we raise an error later
- KeeperState const K{ create_state(U_, L_) }; // L_: settings K:
- if (K == nullptr) {
+ KeeperState const _K{ create_state(U_, L_) }; // L_: settings K:
+ if (_K == nullptr) {
raise_luaL_error(L_, "init_keepers() failed while creating keeper states; out of memory");
}
- U_->keepers->keeper_array[i].L = K;
+ U_->keepers->keeper_array[_i].L = _K;
if (U_->keepers->gc_threshold >= 0) {
- lua_gc(K, LUA_GCSTOP, 0);
+ lua_gc(_K, LUA_GCSTOP, 0);
}
- STACK_CHECK_START_ABS(K, 0);
+ STACK_CHECK_START_ABS(_K, 0);
// copy the universe pointer in the keeper itself
- universe_store(K, U_);
- STACK_CHECK(K, 0);
+ universe_store(_K, U_);
+ STACK_CHECK(_K, 0);
// make sure 'package' is initialized in keeper states, so that we have require()
// this because this is needed when transferring deep userdata object
- luaL_requiref(K, LUA_LOADLIBNAME, luaopen_package, 1); // L_: settings K: package
- lua_pop(K, 1); // L_: settings K:
- STACK_CHECK(K, 0);
- serialize_require(DEBUGSPEW_PARAM_COMMA(U_) K);
- STACK_CHECK(K, 0);
+ luaL_requiref(_K, LUA_LOADLIBNAME, luaopen_package, 1); // L_: settings K: package
+ lua_pop(_K, 1); // L_: settings K:
+ STACK_CHECK(_K, 0);
+ serialize_require(DEBUGSPEW_PARAM_COMMA(U_) _K);
+ STACK_CHECK(_K, 0);
// copy package.path and package.cpath from the source state
if (luaG_getmodule(L_, LUA_LOADLIBNAME) != LuaType::NIL) { // L_: settings package K:
// when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately
- InterCopyContext c{ U_, DestState{ K }, SourceState{ L_ }, {}, SourceIndex{ lua_absindex(L_, -1) }, {}, LookupMode::ToKeeper, {} };
- if (c.inter_copy_package() != InterCopyResult::Success) { // L_: settings ... error_msg K:
+ InterCopyContext _c{ U_, DestState{ _K }, SourceState{ L_ }, {}, SourceIndex{ lua_absindex(L_, -1) }, {}, LookupMode::ToKeeper, {} };
+ if (_c.inter_copy_package() != InterCopyResult::Success) { // L_: settings ... error_msg K:
// if something went wrong, the error message is at the top of the stack
lua_remove(L_, -2); // L_: settings error_msg
raise_lua_error(L_);
@@ -666,19 +666,19 @@ void init_keepers(Universe* U_, lua_State* L_)
}
lua_pop(L_, 1); // L_: settings K:
STACK_CHECK(L_, 0);
- STACK_CHECK(K, 0);
+ STACK_CHECK(_K, 0);
// attempt to call on_state_create(), if we have one and it is a C function
// (only support a C function because we can't transfer executable Lua code in keepers)
// will raise an error in L_ in case of problem
- callOnStateCreate(U_, K, L_, LookupMode::ToKeeper);
+ callOnStateCreate(U_, _K, L_, LookupMode::ToKeeper);
// to see VM name in Decoda debugger
- lua_pushfstring(K, "Keeper #%d", i + 1); // L_: settings K: "Keeper #n"
- lua_setglobal(K, "decoda_name"); // L_: settings K:
+ lua_pushfstring(_K, "Keeper #%d", _i + 1); // L_: settings K: "Keeper #n"
+ lua_setglobal(_K, "decoda_name"); // L_: settings K:
// create the fifos table in the keeper state
- kFifosRegKey.setValue(K, [](lua_State* L_) { lua_newtable(L_); }); // L_: settings K:
- STACK_CHECK(K, 0);
+ kFifosRegKey.setValue(_K, [](lua_State* L_) { lua_newtable(L_); }); // L_: settings K:
+ STACK_CHECK(_K, 0);
}
STACK_CHECK(L_, 0);
}
@@ -687,12 +687,12 @@ void init_keepers(Universe* U_, lua_State* L_)
Keeper* Linda::acquireKeeper() const
{
- int const nbKeepers{ U->keepers->nb_keepers };
+ int const _nbKeepers{ U->keepers->nb_keepers };
// can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers)
- if (nbKeepers) {
- Keeper* const K{ &U->keepers->keeper_array[keeperIndex] };
- K->mutex.lock();
- return K;
+ if (_nbKeepers) {
+ Keeper* const _K{ &U->keepers->keeper_array[keeperIndex] };
+ _K->mutex.lock();
+ return _K;
}
return nullptr;
}
@@ -711,17 +711,17 @@ void Linda::releaseKeeper(Keeper* K_) const
void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mode_)
{
- int const n{ lua_gettop(L_) };
- for (int i = start_; i <= n; ++i) {
+ int const _n{ lua_gettop(L_) };
+ for (int _i = start_; _i <= _n; ++_i) {
if (mode_ == LookupMode::ToKeeper) {
- if (lua_isnil(L_, i)) {
+ if (lua_isnil(L_, _i)) {
kNilSentinel.pushKey(L_);
- lua_replace(L_, i);
+ lua_replace(L_, _i);
}
} else {
- if (kNilSentinel.equals(L_, i)) {
+ if (kNilSentinel.equals(L_, _i)) {
lua_pushnil(L_);
- lua_replace(L_, i);
+ lua_replace(L_, _i);
}
}
}
@@ -740,21 +740,21 @@ void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mod
*/
KeeperCallResult keeper_call(Universe* U_, KeeperState K_, keeper_api_t func_, lua_State* L_, void* linda_, int starting_index_)
{
- KeeperCallResult result;
- int const args{ starting_index_ ? (lua_gettop(L_) - starting_index_ + 1) : 0 }; // L: ... args... K_:
- int const top_K{ lua_gettop(K_) };
+ KeeperCallResult _result{};
+ int const _args{ starting_index_ ? (lua_gettop(L_) - starting_index_ + 1) : 0 }; // L: ... args... K_:
+ int const _top_K{ lua_gettop(K_) };
// if we didn't do anything wrong, the keeper stack should be clean
- LUA_ASSERT(L_, top_K == 0);
+ LUA_ASSERT(L_, _top_K == 0);
STACK_GROW(K_, 2);
PUSH_KEEPER_FUNC(K_, func_); // L: ... args... K_: func_
lua_pushlightuserdata(K_, linda_); // L: ... args... K_: func_ linda
if (
- (args == 0) ||
- (InterCopyContext{ U_, DestState{ K_ }, SourceState{ L_ }, {}, {}, {}, LookupMode::ToKeeper, {} }.inter_copy(args) == InterCopyResult::Success)
+ (_args == 0) ||
+ (InterCopyContext{ U_, DestState{ K_ }, SourceState{ L_ }, {}, {}, {}, LookupMode::ToKeeper, {} }.inter_copy(_args) == InterCopyResult::Success)
) { // L: ... args... K_: func_ linda args...
- lua_call(K_, 1 + args, LUA_MULTRET); // L: ... args... K_: result...
- int const retvals{ lua_gettop(K_) - top_K };
+ lua_call(K_, 1 + _args, LUA_MULTRET); // L: ... args... K_: result...
+ int const retvals{ lua_gettop(K_) - _top_K };
// note that this can raise a lua error while the keeper state (and its mutex) is acquired
// this may interrupt a lane, causing the destruction of the underlying OS thread
// after this, another lane making use of this keeper can get an error code from the mutex-locking function
@@ -763,29 +763,29 @@ KeeperCallResult keeper_call(Universe* U_, KeeperState K_, keeper_api_t func_, l
(retvals == 0) ||
(InterCopyContext{ U_, DestState{ L_ }, SourceState{ K_ }, {}, {}, {}, LookupMode::FromKeeper, {} }.inter_move(retvals) == InterCopyResult::Success)
) { // L: ... args... result... K_: result...
- result.emplace(retvals);
+ _result.emplace(retvals);
}
}
// whatever happens, restore the stack to where it was at the origin
- lua_settop(K_, top_K); // L: ... args... result... K_:
+ lua_settop(K_, _top_K); // L: ... args... result... K_:
// 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
if (func_ != KEEPER_API(clear)) [[unlikely]] {
// since keeper state GC is stopped, let's run a step once in a while if required
- int const gc_threshold{ U_->keepers->gc_threshold };
- if (gc_threshold == 0) [[unlikely]] {
+ int const _gc_threshold{ U_->keepers->gc_threshold };
+ if (_gc_threshold == 0) [[unlikely]] {
lua_gc(K_, LUA_GCSTEP, 0);
- } else if (gc_threshold > 0) [[likely]] {
- int const gc_usage{ lua_gc(K_, LUA_GCCOUNT, 0) };
- if (gc_usage >= gc_threshold) {
+ } else if (_gc_threshold > 0) [[likely]] {
+ int const _gc_usage{ lua_gc(K_, LUA_GCCOUNT, 0) };
+ if (_gc_usage >= _gc_threshold) {
lua_gc(K_, LUA_GCCOLLECT, 0);
- int const gc_usage_after{ lua_gc(K_, LUA_GCCOUNT, 0) };
- if (gc_usage_after > gc_threshold) [[unlikely]] {
- raise_luaL_error(L_, "Keeper GC threshold is too low, need at least %d", gc_usage_after);
+ int const _gc_usage_after{ lua_gc(K_, LUA_GCCOUNT, 0) };
+ if (_gc_usage_after > _gc_threshold) [[unlikely]] {
+ raise_luaL_error(L_, "Keeper GC threshold is too low, need at least %d", _gc_usage_after);
}
}
}
}
- return result;
+ return _result;
}
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h
index b8f04b6..a8738a8 100644
--- a/src/macros_and_utils.h
+++ b/src/macros_and_utils.h
@@ -159,26 +159,26 @@ class StackChecker
};
#define STACK_CHECK_START_REL(L, offset_) \
- StackChecker stackChecker_##L \
+ StackChecker _stackChecker_##L \
{ \
L, StackChecker::Relative{ offset_ }, __FILE__, __LINE__ \
}
#define STACK_CHECK_START_ABS(L, offset_) \
- StackChecker stackChecker_##L \
+ StackChecker _stackChecker_##L \
{ \
L, StackChecker::Absolute{ offset_ }, __FILE__, __LINE__ \
}
#define STACK_CHECK_RESET_REL(L, offset_) \
- stackChecker_##L = StackChecker \
+ _stackChecker_##L = StackChecker \
{ \
L, StackChecker::Relative{ offset_ }, __FILE__, __LINE__ \
}
#define STACK_CHECK_RESET_ABS(L, offset_) \
- stackChecker_##L = StackChecker \
+ _stackChecker_##L = StackChecker \
{ \
L, StackChecker::Absolute{ offset_ }, __FILE__, __LINE__ \
}
-#define STACK_CHECK(L, offset_) stackChecker_##L.check(offset_, __FILE__, __LINE__)
+#define STACK_CHECK(L, offset_) _stackChecker_##L.check(offset_, __FILE__, __LINE__)
#endif // NDEBUG
--
cgit v1.2.3-55-g6feb
From 6b1911b8a3eb7305e6225485191d5e3bccb3b25c Mon Sep 17 00:00:00 2001
From: Benoit Germain
Date: Mon, 13 May 2024 18:00:21 +0200
Subject: Progressively applying the coding rules
---
src/keeper.cpp | 10 +-
src/keeper.h | 2 +-
src/lanes.cpp | 662 ++++++++++++++++++++++++-------------------------
src/lanesconf.h | 2 +-
src/linda.cpp | 464 +++++++++++++++++-----------------
src/lindafactory.cpp | 40 +--
src/macros_and_utils.h | 2 +-
src/state.cpp | 133 +++++-----
src/tools.cpp | 124 ++++-----
src/universe.cpp | 66 ++---
10 files changed, 751 insertions(+), 754 deletions(-)
diff --git a/src/keeper.cpp b/src/keeper.cpp
index 39d2e85..dcfa2ec 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -738,9 +738,9 @@ void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mod
*
* Returns: number of return values (pushed to 'L'), unset in case of error
*/
-KeeperCallResult keeper_call(Universe* U_, KeeperState K_, keeper_api_t func_, lua_State* L_, void* linda_, int starting_index_)
+KeeperCallResult keeper_call(KeeperState K_, keeper_api_t func_, lua_State* L_, Linda* linda_, int starting_index_)
{
- KeeperCallResult _result{};
+ KeeperCallResult _result;
int const _args{ starting_index_ ? (lua_gettop(L_) - starting_index_ + 1) : 0 }; // L: ... args... K_:
int const _top_K{ lua_gettop(K_) };
// if we didn't do anything wrong, the keeper stack should be clean
@@ -751,7 +751,7 @@ KeeperCallResult keeper_call(Universe* U_, KeeperState K_, keeper_api_t func_, l
lua_pushlightuserdata(K_, linda_); // L: ... args... K_: func_ linda
if (
(_args == 0) ||
- (InterCopyContext{ U_, DestState{ K_ }, SourceState{ L_ }, {}, {}, {}, LookupMode::ToKeeper, {} }.inter_copy(_args) == InterCopyResult::Success)
+ (InterCopyContext{ linda_->U, DestState{ K_ }, SourceState{ L_ }, {}, {}, {}, LookupMode::ToKeeper, {} }.inter_copy(_args) == InterCopyResult::Success)
) { // L: ... args... K_: func_ linda args...
lua_call(K_, 1 + _args, LUA_MULTRET); // L: ... args... K_: result...
int const retvals{ lua_gettop(K_) - _top_K };
@@ -761,7 +761,7 @@ KeeperCallResult keeper_call(Universe* U_, KeeperState K_, keeper_api_t func_, l
// when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread)
if (
(retvals == 0) ||
- (InterCopyContext{ U_, DestState{ L_ }, SourceState{ K_ }, {}, {}, {}, LookupMode::FromKeeper, {} }.inter_move(retvals) == InterCopyResult::Success)
+ (InterCopyContext{ linda_->U, DestState{ L_ }, SourceState{ K_ }, {}, {}, {}, LookupMode::FromKeeper, {} }.inter_move(retvals) == InterCopyResult::Success)
) { // L: ... args... result... K_: result...
_result.emplace(retvals);
}
@@ -772,7 +772,7 @@ KeeperCallResult keeper_call(Universe* U_, KeeperState K_, keeper_api_t func_, l
// 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
if (func_ != KEEPER_API(clear)) [[unlikely]] {
// since keeper state GC is stopped, let's run a step once in a while if required
- int const _gc_threshold{ U_->keepers->gc_threshold };
+ int const _gc_threshold{ linda_->U->keepers->gc_threshold };
if (_gc_threshold == 0) [[unlikely]] {
lua_gc(K_, LUA_GCSTEP, 0);
} else if (_gc_threshold > 0) [[likely]] {
diff --git a/src/keeper.h b/src/keeper.h
index 8f30720..ebe2946 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -58,4 +58,4 @@ using keeper_api_t = lua_CFunction;
[[nodiscard]] int keepercall_count(lua_State* L_);
using KeeperCallResult = Unique>;
-[[nodiscard]] KeeperCallResult keeper_call(Universe* U_, KeeperState K_, keeper_api_t func_, lua_State* L_, void* linda_, int starting_index_);
+[[nodiscard]] KeeperCallResult keeper_call(KeeperState K_, keeper_api_t func_, lua_State* L_, Linda* linda_, int starting_index_);
diff --git a/src/lanes.cpp b/src/lanes.cpp
index ee40ffa..f0ec84c 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -116,7 +116,7 @@ THE SOFTWARE.
*/
static void tracking_add(Lane* lane_)
{
- std::lock_guard guard{ lane_->U->trackingMutex };
+ std::lock_guard _guard{ lane_->U->trackingMutex };
assert(lane_->tracking_next == nullptr);
lane_->tracking_next = lane_->U->trackingFirst;
@@ -130,27 +130,27 @@ static void tracking_add(Lane* lane_)
*/
[[nodiscard]] static bool tracking_remove(Lane* lane_)
{
- bool found{ false };
- std::lock_guard guard{ lane_->U->trackingMutex };
+ bool _found{ false };
+ std::lock_guard _guard{ lane_->U->trackingMutex };
// Make sure (within the MUTEX) that we actually are in the chain
// still (at process exit they will remove us from chain and then
// cancel/kill).
//
if (lane_->tracking_next != nullptr) {
- Lane** ref = (Lane**) &lane_->U->trackingFirst;
+ Lane** _ref = (Lane**) &lane_->U->trackingFirst;
- while (*ref != TRACKING_END) {
- if (*ref == lane_) {
- *ref = lane_->tracking_next;
+ while (*_ref != TRACKING_END) {
+ if (*_ref == lane_) {
+ *_ref = lane_->tracking_next;
lane_->tracking_next = nullptr;
- found = true;
+ _found = true;
break;
}
- ref = (Lane**) &((*ref)->tracking_next);
+ _ref = (Lane**) &((*_ref)->tracking_next);
}
- assert(found);
+ assert(_found);
}
- return found;
+ return _found;
}
#endif // HAVE_LANE_TRACKING()
@@ -172,15 +172,15 @@ Lane::Lane(Universe* U_, lua_State* L_)
bool Lane::waitForCompletion(std::chrono::time_point until_)
{
- std::unique_lock lock{ doneMutex };
+ std::unique_lock _guard{ doneMutex };
// std::stop_token token{ thread.get_stop_token() };
// return doneCondVar.wait_until(lock, token, secs_, [this](){ return status >= Lane::Done; });
- return doneCondVar.wait_until(lock, until_, [this]() { return status >= Lane::Done; });
+ return doneCondVar.wait_until(_guard, until_, [this]() { return status >= Lane::Done; });
}
// #################################################################################################
-static void lane_main(Lane* lane);
+static void lane_main(Lane* lane_);
void Lane::startThread(int priority_)
{
thread = std::jthread([this]() { lane_main(this); });
@@ -267,9 +267,9 @@ LUAG_FUNC(set_finalizer)
// Get the current finalizer table (if any), create one if it doesn't exist
std::ignore = kFinalizerRegKey.getSubTable(L_, 1, 0); // L_: finalizer {finalisers}
// must cast to int, not lua_Integer, because LuaJIT signature of lua_rawseti is not the same as PUC-Lua.
- int const idx{ static_cast(lua_rawlen(L_, -1) + 1) };
+ int const _idx{ static_cast(lua_rawlen(L_, -1) + 1) };
lua_pushvalue(L_, 1); // L_: finalizer {finalisers} finalizer
- lua_rawseti(L_, -2, idx); // L_: finalizer {finalisers}
+ lua_rawseti(L_, -2, _idx); // L_: finalizer {finalisers}
// no need to adjust the stack, Lua does this for us
return 0;
}
@@ -337,28 +337,28 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_)
STACK_GROW(L_, 5);
- int const finalizers_index{ lua_gettop(L_) };
- int const err_handler_index{ ERROR_FULL_STACK ? (lua_pushcfunction(L_, lane_error), lua_gettop(L_)) : 0 };
+ int const _finalizers_index{ lua_gettop(L_) };
+ int const _err_handler_index{ ERROR_FULL_STACK ? (lua_pushcfunction(L_, lane_error), lua_gettop(L_)) : 0 };
int rc{ LUA_OK };
- for (int n = static_cast(lua_rawlen(L_, finalizers_index)); n > 0; --n) {
+ for (int n = static_cast(lua_rawlen(L_, _finalizers_index)); n > 0; --n) {
int args = 0;
lua_pushinteger(L_, n); // L_: ... finalizers lane_error n
- lua_rawget(L_, finalizers_index); // L_: ... finalizers lane_error finalizer
+ lua_rawget(L_, _finalizers_index); // L_: ... finalizers lane_error finalizer
LUA_ASSERT(L_, lua_isfunction(L_, -1));
if (lua_rc_ != LUA_OK) { // we have an error message and an optional stack trace at the bottom of the stack
- LUA_ASSERT(L_, finalizers_index == 2 || finalizers_index == 3);
+ LUA_ASSERT(L_, _finalizers_index == 2 || _finalizers_index == 3);
// char const* err_msg = lua_tostring(L_, 1);
lua_pushvalue(L_, 1); // L_: ... finalizers lane_error finalizer err_msg
// 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
- if (finalizers_index == 3) {
+ if (_finalizers_index == 3) {
lua_pushvalue(L_, 2); // L_: ... finalizers lane_error finalizer err_msg stack_trace
}
- args = finalizers_index - 1;
+ args = _finalizers_index - 1;
}
// if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace
- rc = lua_pcall(L_, args, 0, err_handler_index); // L_: ... finalizers lane_error err_msg2?
+ rc = lua_pcall(L_, args, 0, _err_handler_index); // L_: ... finalizers lane_error err_msg2?
if (rc != LUA_OK) {
push_stack_trace(L_, rc, lua_gettop(L_)); // L_: ... finalizers lane_error err_msg2? trace
// If one finalizer fails, don't run the others. Return this
@@ -371,7 +371,7 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_)
if (rc != LUA_OK) {
// ERROR_FULL_STACK accounts for the presence of lane_error on the stack
- int const nb_err_slots{ lua_gettop(L_) - finalizers_index - ERROR_FULL_STACK };
+ int const nb_err_slots{ lua_gettop(L_) - _finalizers_index - ERROR_FULL_STACK };
// a finalizer generated an error, this is what we leave of the stack
for (int n = nb_err_slots; n > 0; --n) {
lua_replace(L_, n);
@@ -379,7 +379,7 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_)
// leave on the stack only the error and optional stack trace produced by the error in the finalizer
lua_settop(L_, nb_err_slots); // L_: ... lane_error trace
} else { // no error from the finalizers, make sure only the original return values from the lane body remain on the stack
- lua_settop(L_, finalizers_index - 1);
+ lua_settop(L_, _finalizers_index - 1);
}
return rc;
@@ -395,7 +395,7 @@ static void push_stack_trace(lua_State* L_, int rc_, int stk_base_)
*/
static void selfdestruct_add(Lane* lane_)
{
- std::lock_guard guard{ lane_->U->selfdestructMutex };
+ std::lock_guard _guard{ lane_->U->selfdestructMutex };
assert(lane_->selfdestruct_next == nullptr);
lane_->selfdestruct_next = lane_->U->selfdestructFirst;
@@ -407,29 +407,29 @@ static void selfdestruct_add(Lane* lane_)
// A free-running lane has ended; remove it from selfdestruct chain
[[nodiscard]] static bool selfdestruct_remove(Lane* lane_)
{
- bool found{ false };
- std::lock_guard guard{ lane_->U->selfdestructMutex };
+ bool _found{ false };
+ std::lock_guard _guard{ lane_->U->selfdestructMutex };
// Make sure (within the MUTEX) that we actually are in the chain
// still (at process exit they will remove us from chain and then
// cancel/kill).
//
if (lane_->selfdestruct_next != nullptr) {
- Lane* volatile* ref = static_cast(&lane_->U->selfdestructFirst);
+ Lane* volatile* _ref = static_cast(&lane_->U->selfdestructFirst);
- while (*ref != SELFDESTRUCT_END) {
- if (*ref == lane_) {
- *ref = lane_->selfdestruct_next;
+ while (*_ref != SELFDESTRUCT_END) {
+ if (*_ref == lane_) {
+ *_ref = lane_->selfdestruct_next;
lane_->selfdestruct_next = nullptr;
// the terminal shutdown should wait until the lane is done with its lua_close()
lane_->U->selfdestructingCount.fetch_add(1, std::memory_order_release);
- found = true;
+ _found = true;
break;
}
- ref = static_cast(&((*ref)->selfdestruct_next));
+ _ref = static_cast(&((*_ref)->selfdestruct_next));
}
- assert(found);
+ assert(_found);
}
- return found;
+ return _found;
}
// #################################################################################################
@@ -442,11 +442,11 @@ static void selfdestruct_add(Lane* lane_)
//
LUAG_FUNC(set_singlethreaded)
{
- [[maybe_unused]] lua_Integer const cores{ luaL_optinteger(L_, 1, 1) };
+ [[maybe_unused]] lua_Integer const _cores{ luaL_optinteger(L_, 1, 1) };
#ifdef PLATFORM_OSX
#ifdef _UTILBINDTHREADTOCPU
- if (cores > 1) {
+ if (_cores > 1) {
raise_luaL_error(L_, "Limiting to N>1 cores not possible");
}
// requires 'chudInitialize()'
@@ -486,15 +486,15 @@ static constexpr RegistryUniqueKey kExtendedStackTraceRegKey{ 0x38147AD48FB426E2
LUAG_FUNC(set_error_reporting)
{
luaL_checktype(L_, 1, LUA_TSTRING);
- char const* mode{ lua_tostring(L_, 1) };
+ char const* _mode{ lua_tostring(L_, 1) };
lua_pushliteral(L_, "extended");
- bool const extended{ strcmp(mode, "extended") == 0 };
- bool const basic{ strcmp(mode, "basic") == 0 };
- if (!extended && !basic) {
- raise_luaL_error(L_, "unsupported error reporting model %s", mode);
+ bool const _extended{ strcmp(_mode, "extended") == 0 };
+ bool const _basic{ strcmp(_mode, "basic") == 0 };
+ if (!_extended && !_basic) {
+ raise_luaL_error(L_, "unsupported error reporting model %s", _mode);
}
- kExtendedStackTraceRegKey.setValue(L_, [extended](lua_State* L_) { lua_pushboolean(L_, extended ? 1 : 0); });
+ kExtendedStackTraceRegKey.setValue(L_, [extended = _extended](lua_State* L_) { lua_pushboolean(L_, extended ? 1 : 0); });
return 0;
}
@@ -512,7 +512,7 @@ LUAG_FUNC(set_error_reporting)
}
STACK_GROW(L_, 3);
- bool const extended{ kExtendedStackTraceRegKey.readBoolValue(L_) };
+ bool const _extended{ kExtendedStackTraceRegKey.readBoolValue(L_) };
STACK_CHECK(L_, 1);
// Place stack trace at 'registry[kStackTraceRegKey]' for the 'lua_pcall()'
@@ -531,32 +531,32 @@ LUAG_FUNC(set_error_reporting)
// and we don't get '.currentline' for that. It's okay - just keep level
// and table index growing separate. --AKa 22-Jan-2009
//
- lua_Debug ar;
- for (int n = 1; lua_getstack(L_, n, &ar); ++n) {
- lua_getinfo(L_, extended ? "Sln" : "Sl", &ar);
- if (extended) {
+ lua_Debug _ar;
+ for (int _n = 1; lua_getstack(L_, _n, &_ar); ++_n) {
+ lua_getinfo(L_, _extended ? "Sln" : "Sl", &_ar);
+ if (_extended) {
lua_newtable(L_); // L_: some_error {} {}
- lua_pushstring(L_, ar.source); // L_: some_error {} {} source
+ lua_pushstring(L_, _ar.source); // L_: some_error {} {} source
lua_setfield(L_, -2, "source"); // L_: some_error {} {}
- lua_pushinteger(L_, ar.currentline); // L_: some_error {} {} currentline
+ lua_pushinteger(L_, _ar.currentline); // L_: some_error {} {} currentline
lua_setfield(L_, -2, "currentline"); // L_: some_error {} {}
- lua_pushstring(L_, ar.name); // L_: some_error {} {} name
+ lua_pushstring(L_, _ar.name); // L_: some_error {} {} name
lua_setfield(L_, -2, "name"); // L_: some_error {} {}
- lua_pushstring(L_, ar.namewhat); // L_: some_error {} {} namewhat
+ lua_pushstring(L_, _ar.namewhat); // L_: some_error {} {} namewhat
lua_setfield(L_, -2, "namewhat"); // L_: some_error {} {}
- lua_pushstring(L_, ar.what); // L_: some_error {} {} what
+ lua_pushstring(L_, _ar.what); // L_: some_error {} {} what
lua_setfield(L_, -2, "what"); // L_: some_error {} {}
- } else if (ar.currentline > 0) {
- lua_pushfstring(L_, "%s:%d", ar.short_src, ar.currentline); // L_: some_error {} "blah:blah"
+ } else if (_ar.currentline > 0) {
+ lua_pushfstring(L_, "%s:%d", _ar.short_src, _ar.currentline); // L_: some_error {} "blah:blah"
} else {
- lua_pushfstring(L_, "%s:?", ar.short_src); // L_: some_error {} "blah"
+ lua_pushfstring(L_, "%s:?", _ar.short_src); // L_: some_error {} "blah"
}
- lua_rawseti(L_, -2, (lua_Integer) n); // L_: some_error {}
+ lua_rawseti(L_, -2, static_cast(_n)); // L_: some_error {}
}
// store the stack trace table in the registry
@@ -572,12 +572,12 @@ LUAG_FUNC(set_error_reporting)
void Lane::changeDebugName(int nameIdx_)
{
// xxh64 of string "debugName" generated at https://www.pelock.com/products/hash-calculator
- static constexpr RegistryUniqueKey hidden_regkey{ 0xA194E2645C57F6DDull };
+ static constexpr RegistryUniqueKey kRegKey{ 0xA194E2645C57F6DDull };
nameIdx_ = lua_absindex(L, nameIdx_);
luaL_checktype(L, nameIdx_, LUA_TSTRING); // L: ... "name" ...
STACK_CHECK_START_REL(L, 0);
// store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global...
- hidden_regkey.setValue(L, [nameIdx = nameIdx_](lua_State* L_) { lua_pushvalue(L_, nameIdx); });// L: ... "name" ...
+ kRegKey.setValue(L, [nameIdx = nameIdx_](lua_State* L_) { lua_pushvalue(L_, nameIdx); }); // L: ... "name" ...
// keep a direct pointer on the string
debugName = lua_tostring(L, nameIdx_);
// to see VM name in Decoda debugger Virtual Machine window
@@ -594,11 +594,11 @@ void Lane::changeDebugName(int nameIdx_)
LUAG_FUNC(set_debug_threadname)
{
// C s_lane structure is a light userdata upvalue
- Lane* const lane{ lua_tolightuserdata(L_, lua_upvalueindex(1)) };
- LUA_ASSERT(L_, L_ == lane->L); // this function is exported in a lane's state, therefore it is callable only from inside the Lane's state
+ Lane* const _lane{ lua_tolightuserdata(L_, lua_upvalueindex(1)) };
+ LUA_ASSERT(L_, L_ == _lane->L); // this function is exported in a lane's state, therefore it is callable only from inside the Lane's state
lua_settop(L_, 1);
STACK_CHECK_START_REL(L_, 0);
- lane->changeDebugName(-1);
+ _lane->changeDebugName(-1);
STACK_CHECK(L_, 0);
return 0;
}
@@ -607,9 +607,9 @@ LUAG_FUNC(set_debug_threadname)
LUAG_FUNC(get_debug_threadname)
{
- Lane* const lane{ ToLane(L_, 1) };
+ Lane* const _lane{ ToLane(L_, 1) };
luaL_argcheck(L_, lua_gettop(L_) == 1, 2, "too many arguments");
- lua_pushstring(L_, lane->debugName);
+ lua_pushstring(L_, _lane->debugName);
return 1;
}
@@ -617,14 +617,14 @@ LUAG_FUNC(get_debug_threadname)
LUAG_FUNC(set_thread_priority)
{
- lua_Integer const prio{ luaL_checkinteger(L_, 1) };
+ lua_Integer const _prio{ luaL_checkinteger(L_, 1) };
// public Lanes API accepts a generic range -3/+3
// that will be remapped into the platform-specific scheduler priority scheme
// On some platforms, -3 is equivalent to -2 and +3 to +2
- if (prio < kThreadPrioMin || prio > kThreadPrioMax) {
- raise_luaL_error(L_, "priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, prio);
+ if (_prio < kThreadPrioMin || _prio > kThreadPrioMax) {
+ raise_luaL_error(L_, "priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _prio);
}
- THREAD_SET_PRIORITY(static_cast(prio), universe_get(L_)->sudo);
+ THREAD_SET_PRIORITY(static_cast(_prio), universe_get(L_)->sudo);
return 0;
}
@@ -632,11 +632,11 @@ LUAG_FUNC(set_thread_priority)
LUAG_FUNC(set_thread_affinity)
{
- lua_Integer const affinity{ luaL_checkinteger(L_, 1) };
- if (affinity <= 0) {
- raise_luaL_error(L_, "invalid affinity (%d)", affinity);
+ lua_Integer const _affinity{ luaL_checkinteger(L_, 1) };
+ if (_affinity <= 0) {
+ raise_luaL_error(L_, "invalid affinity (%d)", _affinity);
}
- THREAD_SET_AFFINITY(static_cast(affinity));
+ THREAD_SET_AFFINITY(static_cast(_affinity));
return 0;
}
@@ -662,9 +662,9 @@ static struct errcode_name s_errcodes[] = {
};
static char const* get_errcode_name(int _code)
{
- for (int i{ 0 }; i < 7; ++i) {
- if (s_errcodes[i].code == _code) {
- return s_errcodes[i].name;
+ for (errcode_name const& _entry : s_errcodes) {
+ if (_entry.code == _code) {
+ return _entry.name;
}
}
return "";
@@ -675,61 +675,61 @@ static char const* get_errcode_name(int _code)
static void lane_main(Lane* lane_)
{
- lua_State* const L{ lane_->L };
+ lua_State* const _L{ lane_->L };
// wait until the launching thread has finished preparing L
lane_->ready.wait();
- int rc{ LUA_ERRRUN };
+ int _rc{ LUA_ERRRUN };
if (lane_->status == Lane::Pending) { // nothing wrong happened during preparation, we can work
// At this point, the lane function and arguments are on the stack
- int const nargs{ lua_gettop(L) - 1 };
- DEBUGSPEW_CODE(Universe* U = universe_get(L));
+ int const nargs{ lua_gettop(_L) - 1 };
+ DEBUGSPEW_CODE(Universe* U = universe_get(_L));
lane_->status = Lane::Running; // Pending -> Running
// Tie "set_finalizer()" to the state
- lua_pushcfunction(L, LG_set_finalizer);
- populate_func_lookup_table(L, -1, "set_finalizer");
- lua_setglobal(L, "set_finalizer");
+ lua_pushcfunction(_L, LG_set_finalizer);
+ populate_func_lookup_table(_L, -1, "set_finalizer");
+ lua_setglobal(_L, "set_finalizer");
// Tie "set_debug_threadname()" to the state
// But don't register it in the lookup database because of the Lane pointer upvalue
- lua_pushlightuserdata(L, lane_);
- lua_pushcclosure(L, LG_set_debug_threadname, 1);
- lua_setglobal(L, "set_debug_threadname");
+ lua_pushlightuserdata(_L, lane_);
+ lua_pushcclosure(_L, LG_set_debug_threadname, 1);
+ lua_setglobal(_L, "set_debug_threadname");
// Tie "cancel_test()" to the state
- lua_pushcfunction(L, LG_cancel_test);
- populate_func_lookup_table(L, -1, "cancel_test");
- lua_setglobal(L, "cancel_test");
+ lua_pushcfunction(_L, LG_cancel_test);
+ populate_func_lookup_table(_L, -1, "cancel_test");
+ lua_setglobal(_L, "cancel_test");
// this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around
#if ERROR_FULL_STACK
// Tie "set_error_reporting()" to the state
- lua_pushcfunction(L, LG_set_error_reporting);
- populate_func_lookup_table(L, -1, "set_error_reporting");
- lua_setglobal(L, "set_error_reporting");
+ lua_pushcfunction(_L, LG_set_error_reporting);
+ populate_func_lookup_table(_L, -1, "set_error_reporting");
+ lua_setglobal(_L, "set_error_reporting");
- STACK_GROW(L, 1);
- lua_pushcfunction(L, lane_error); // L: func args handler
- lua_insert(L, 1); // L: handler func args
+ STACK_GROW(_L, 1);
+ lua_pushcfunction(_L, lane_error); // L: func args handler
+ lua_insert(_L, 1); // L: handler func args
#endif // L: ERROR_FULL_STACK
- rc = lua_pcall(L, nargs, LUA_MULTRET, ERROR_FULL_STACK); // L: retvals|err
+ _rc = lua_pcall(_L, nargs, LUA_MULTRET, ERROR_FULL_STACK); // L: retvals|err
#if ERROR_FULL_STACK
- lua_remove(L, 1); // L: retvals|error
+ lua_remove(_L, 1); // L: retvals|error
#endif // ERROR_FULL_STACK
// in case of error and if it exists, fetch stack trace from registry and push it
- push_stack_trace(L, rc, 1); // L: retvals|error [trace]
+ push_stack_trace(_L, _rc, 1); // L: retvals|error [trace]
- 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))));
+ 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))));
// Call finalizers, if the script has set them up.
//
- int rc2{ run_finalizers(L, rc) };
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END(U), L, get_errcode_name(rc2)));
- if (rc2 != LUA_OK) { // Error within a finalizer!
+ int _rc2{ run_finalizers(_L, _rc) };
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END(U), _L, get_errcode_name(_rc2)));
+ if (_rc2 != LUA_OK) { // Error within a finalizer!
// the finalizer generated an error, and left its own error message [and stack trace] on the stack
- rc = rc2; // we're overruling the earlier script error or normal return
+ _rc = _rc2; // we're overruling the earlier script error or normal return
}
lane_->waiting_on = nullptr; // just in case
if (selfdestruct_remove(lane_)) { // check and remove (under lock!)
@@ -750,12 +750,12 @@ static void lane_main(Lane* lane_)
if (lane_) {
// leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them
- Lane::Status const st = (rc == LUA_OK) ? Lane::Done : kCancelError.equals(L, 1) ? Lane::Cancelled : Lane::Error;
+ Lane::Status const _st = (_rc == LUA_OK) ? Lane::Done : kCancelError.equals(_L, 1) ? Lane::Cancelled : Lane::Error;
{
// 'doneMutex' protects the -> Done|Error|Cancelled state change
std::lock_guard lock{ lane_->doneMutex };
- lane_->status = st;
+ lane_->status = _st;
lane_->doneCondVar.notify_one(); // wake up master (while 'lane_->doneMutex' is on)
}
}
@@ -769,17 +769,17 @@ static void lane_main(Lane* lane_)
// upvalue[1]: _G.require
LUAG_FUNC(require)
{
- char const* name = lua_tostring(L_, 1); // L_: "name" ...
- int const nargs{ lua_gettop(L_) };
- DEBUGSPEW_CODE(Universe* U = universe_get(L_));
+ char const* _name{ lua_tostring(L_, 1) }; // L_: "name" ...
+ int const _nargs{ lua_gettop(L_) };
+ DEBUGSPEW_CODE(Universe * _U{ universe_get(L_) });
STACK_CHECK_START_REL(L_, 0);
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END(U), name));
- DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END(_U), _name));
+ DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U });
lua_pushvalue(L_, lua_upvalueindex(1)); // L_: "name" ... require
lua_insert(L_, 1); // L_: require "name" ...
- lua_call(L_, nargs, 1); // L_: module
- populate_func_lookup_table(L_, -1, name);
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END(U), name));
+ lua_call(L_, _nargs, 1); // L_: module
+ populate_func_lookup_table(L_, -1, _name);
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END(_U), _name));
STACK_CHECK(L_, 0);
return 1;
}
@@ -791,17 +791,17 @@ LUAG_FUNC(require)
// lanes.register( "modname", module)
LUAG_FUNC(register)
{
- char const* name = luaL_checkstring(L_, 1);
- LuaType const mod_type{ lua_type_as_enum(L_, 2) };
+ char const* _name{ luaL_checkstring(L_, 1) };
+ LuaType const _mod_type{ lua_type_as_enum(L_, 2) };
// ignore extra parameters, just in case
lua_settop(L_, 2);
- luaL_argcheck(L_, (mod_type == LuaType::TABLE) || (mod_type == LuaType::FUNCTION), 2, "unexpected module type");
+ luaL_argcheck(L_, (_mod_type == LuaType::TABLE) || (_mod_type == LuaType::FUNCTION), 2, "unexpected module type");
DEBUGSPEW_CODE(Universe* U = universe_get(L_));
STACK_CHECK_START_REL(L_, 0); // "name" mod_table
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END(U), name));
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END(U), _name));
DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
- populate_func_lookup_table(L_, -1, name);
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END(U), name));
+ populate_func_lookup_table(L_, -1, _name);
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END(U), _name));
STACK_CHECK(L_, 0);
return 0;
}
@@ -827,37 +827,37 @@ static constexpr UniqueKey kLaneGC{ 0x5D6122141727F960ull };
LUAG_FUNC(lane_new)
{
// first 8 args: func libs priority globals package required gc_cb name
- char const* const libs_str{ lua_tostring(L_, 2) };
- bool const have_priority{ !lua_isnoneornil(L_, 3) };
- int const priority{ have_priority ? static_cast(lua_tointeger(L_, 3)) : kThreadPrioDefault };
- int const globals_idx{ lua_isnoneornil(L_, 4) ? 0 : 4 };
- int const package_idx{ lua_isnoneornil(L_, 5) ? 0 : 5 };
- int const required_idx{ lua_isnoneornil(L_, 6) ? 0 : 6 };
- int const gc_cb_idx{ lua_isnoneornil(L_, 7) ? 0 : 7 };
- int const name_idx{ lua_isnoneornil(L_, 8) ? 0 : 8 };
+ char const* const _libs_str{ lua_tostring(L_, 2) };
+ bool const _have_priority{ !lua_isnoneornil(L_, 3) };
+ int const _priority{ _have_priority ? static_cast(lua_tointeger(L_, 3)) : kThreadPrioDefault };
+ int const _globals_idx{ lua_isnoneornil(L_, 4) ? 0 : 4 };
+ int const _package_idx{ lua_isnoneornil(L_, 5) ? 0 : 5 };
+ int const _required_idx{ lua_isnoneornil(L_, 6) ? 0 : 6 };
+ int const _gc_cb_idx{ lua_isnoneornil(L_, 7) ? 0 : 7 };
+ int const _name_idx{ lua_isnoneornil(L_, 8) ? 0 : 8 };
static constexpr int kFixedArgsIdx{ 8 };
- int const nargs{ lua_gettop(L_) - kFixedArgsIdx };
- Universe* const U{ universe_get(L_) };
- LUA_ASSERT(L_, nargs >= 0);
+ int const _nargs{ lua_gettop(L_) - kFixedArgsIdx };
+ Universe* const _U{ universe_get(L_) };
+ LUA_ASSERT(L_, _nargs >= 0);
// public Lanes API accepts a generic range -3/+3
// that will be remapped into the platform-specific scheduler priority scheme
// On some platforms, -3 is equivalent to -2 and +3 to +2
- if (have_priority && (priority < kThreadPrioMin || priority > kThreadPrioMax)) {
- raise_luaL_error(L_, "Priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, priority);
+ if (_have_priority && (_priority < kThreadPrioMin || _priority > kThreadPrioMax)) {
+ raise_luaL_error(L_, "Priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _priority);
}
/* --- Create and prepare the sub state --- */
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END(U)));
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: setup\n" INDENT_END(_U)));
// populate with selected libraries at the same time.
- lua_State* const L2{ luaG_newstate(U, SourceState{ L_ }, libs_str) }; // L_: [8 args] ... L2:
- STACK_CHECK_START_REL(L2, 0);
+ lua_State* const _L2{ luaG_newstate(_U, SourceState{ L_ }, _libs_str) }; // L_: [8 args] ... L2:
+ STACK_CHECK_START_REL(_L2, 0);
// 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread)
- Lane* const lane{ new (U) Lane{ U, L2 } };
- if (lane == nullptr) {
+ Lane* const _lane{ new (_U) Lane{ _U, _L2 } };
+ if (_lane == nullptr) {
raise_luaL_error(L_, "could not create lane: out of memory");
}
@@ -959,135 +959,135 @@ LUAG_FUNC(lane_new)
m_lane->ready.count_down();
m_lane = nullptr;
}
- } onExit{ L_, lane, gc_cb_idx, name_idx DEBUGSPEW_COMMA_PARAM(U) };
+ } onExit{ L_, _lane, _gc_cb_idx, _name_idx DEBUGSPEW_COMMA_PARAM(_U) };
// launch the thread early, it will sync with a std::latch to parallelize OS thread warmup and L2 preparation
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END(U)));
- lane->startThread(priority);
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END(_U)));
+ _lane->startThread(_priority);
- STACK_GROW(L2, nargs + 3);
+ STACK_GROW(_L2, _nargs + 3);
STACK_GROW(L_, 3);
STACK_CHECK_START_REL(L_, 0);
// package
- if (package_idx != 0) {
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END(U)));
+ if (_package_idx != 0) {
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END(_U)));
// when copying with mode LookupMode::LaneBody, should raise an error in case of problem, not leave it one the stack
- InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, SourceIndex{ package_idx }, {}, {}, {} };
+ InterCopyContext c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, SourceIndex{ _package_idx }, {}, {}, {} };
[[maybe_unused]] InterCopyResult const ret{ c.inter_copy_package() };
LUA_ASSERT(L_, ret == InterCopyResult::Success); // either all went well, or we should not even get here
}
// modules to require in the target lane *before* the function is transfered!
- if (required_idx != 0) {
- int nbRequired = 1;
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END(U)));
- DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
+ if (_required_idx != 0) {
+ int _nbRequired{ 1 };
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END(_U)));
+ DEBUGSPEW_CODE(DebugSpewIndentScope scope{ _U });
// should not happen, was checked in lanes.lua before calling lane_new()
- if (lua_type(L_, required_idx) != LUA_TTABLE) {
- raise_luaL_error(L_, "expected required module list as a table, got %s", luaL_typename(L_, required_idx));
+ if (lua_type(L_, _required_idx) != LUA_TTABLE) {
+ raise_luaL_error(L_, "expected required module list as a table, got %s", luaL_typename(L_, _required_idx));
}
lua_pushnil(L_); // L_: [8 args] args... nil L2:
- while (lua_next(L_, required_idx) != 0) { // L_: [8 args] args... n "modname" L2:
- if (lua_type(L_, -1) != LUA_TSTRING || lua_type(L_, -2) != LUA_TNUMBER || lua_tonumber(L_, -2) != nbRequired) {
+ while (lua_next(L_, _required_idx) != 0) { // L_: [8 args] args... n "modname" L2:
+ if (lua_type(L_, -1) != LUA_TSTRING || lua_type(L_, -2) != LUA_TNUMBER || lua_tonumber(L_, -2) != _nbRequired) {
raise_luaL_error(L_, "required module list should be a list of strings");
} else {
// require the module in the target state, and populate the lookup table there too
size_t len;
char const* name = lua_tolstring(L_, -1, &len);
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END(U), name));
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END(_U), name));
// require the module in the target lane
- lua_getglobal(L2, "require"); // L_: [8 args] args... n "modname" L2: require()?
- if (lua_isnil(L2, -1)) {
- lua_pop(L2, 1); // L_: [8 args] args... n "modname" L2:
+ lua_getglobal(_L2, "require"); // L_: [8 args] args... n "modname" L2: require()?
+ if (lua_isnil(_L2, -1)) {
+ lua_pop(_L2, 1); // L_: [8 args] args... n "modname" L2:
raise_luaL_error(L_, "cannot pre-require modules without loading 'package' library first");
} else {
- lua_pushlstring(L2, name, len); // L_: [8 args] args... n "modname" L2: require() name
- if (lua_pcall(L2, 1, 1, 0) != LUA_OK) { // L_: [8 args] args... n "modname" L2: ret/errcode
+ lua_pushlstring(_L2, name, len); // L_: [8 args] args... n "modname" L2: require() name
+ if (lua_pcall(_L2, 1, 1, 0) != LUA_OK) { // L_: [8 args] args... n "modname" L2: ret/errcode
// propagate error to main state if any
- InterCopyContext c{ U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} };
- std::ignore = c.inter_move(1); // L_: [8 args] args... n "modname" error L2:
+ InterCopyContext _c{ _U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} };
+ std::ignore = _c.inter_move(1); // L_: [8 args] args... n "modname" error L2:
raise_lua_error(L_);
}
// here the module was successfully required // L_: [8 args] args... n "modname" L2: ret
// after requiring the module, register the functions it exported in our name<->function database
- populate_func_lookup_table(L2, -1, name);
- lua_pop(L2, 1); // L_: [8 args] args... n "modname" L2:
+ populate_func_lookup_table(_L2, -1, name);
+ lua_pop(_L2, 1); // L_: [8 args] args... n "modname" L2:
}
}
lua_pop(L_, 1); // L_: func libs priority globals package required gc_cb [... args ...] n
- ++nbRequired;
+ ++_nbRequired;
} // L_: [8 args] args...
}
STACK_CHECK(L_, 0);
- STACK_CHECK(L2, 0); // L_: [8 args] args... L2:
+ STACK_CHECK(_L2, 0); // L_: [8 args] args... L2:
// Appending the specified globals to the global environment
// *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed...
//
- if (globals_idx != 0) {
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END(U)));
- if (!lua_istable(L_, globals_idx)) {
- raise_luaL_error(L_, "Expected table, got %s", luaL_typename(L_, globals_idx));
+ if (_globals_idx != 0) {
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END(_U)));
+ if (!lua_istable(L_, _globals_idx)) {
+ raise_luaL_error(L_, "Expected table, got %s", luaL_typename(L_, _globals_idx));
}
- DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
+ DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U });
lua_pushnil(L_); // L_: [8 args] args... nil L2:
// Lua 5.2 wants us to push the globals table on the stack
- InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
- lua_pushglobaltable(L2); // L_: [8 args] args... nil L2: _G
- while (lua_next(L_, globals_idx)) { // L_: [8 args] args... k v L2: _G
- std::ignore = c.inter_copy(2); // L_: [8 args] args... k v L2: _G k v
+ InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
+ lua_pushglobaltable(_L2); // L_: [8 args] args... nil L2: _G
+ while (lua_next(L_, _globals_idx)) { // L_: [8 args] args... k v L2: _G
+ std::ignore = _c.inter_copy(2); // L_: [8 args] args... k v L2: _G k v
// assign it in L2's globals table
- lua_rawset(L2, -3); // L_: [8 args] args... k v L2: _G
+ lua_rawset(_L2, -3); // L_: [8 args] args... k v L2: _G
lua_pop(L_, 1); // L_: [8 args] args... k
} // L_: [8 args] args...
- lua_pop(L2, 1); // L_: [8 args] args... L2:
+ lua_pop(_L2, 1); // L_: [8 args] args... L2:
}
STACK_CHECK(L_, 0);
- STACK_CHECK(L2, 0);
+ STACK_CHECK(_L2, 0);
// Lane main function
- LuaType const func_type{ lua_type_as_enum(L_, 1) };
- if (func_type == LuaType::FUNCTION) {
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END(U)));
- DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
+ LuaType const _func_type{ lua_type_as_enum(L_, 1) };
+ if (_func_type == LuaType::FUNCTION) {
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END(_U)));
+ DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U });
lua_pushvalue(L_, 1); // L_: [8 args] args... func L2:
- InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
- InterCopyResult const res{ c.inter_move(1) }; // L_: [8 args] args... L2: func
- if (res != InterCopyResult::Success) {
+ InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
+ InterCopyResult const _res{ _c.inter_move(1) }; // L_: [8 args] args... L2: func
+ if (_res != InterCopyResult::Success) {
raise_luaL_error(L_, "tried to copy unsupported types");
}
- } else if (func_type == LuaType::STRING) {
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: compile lane body\n" INDENT_END(U)));
+ } else if (_func_type == LuaType::STRING) {
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: compile lane body\n" INDENT_END(_U)));
// compile the string
- if (luaL_loadstring(L2, lua_tostring(L_, 1)) != 0) { // L_: [8 args] args... L2: func
+ if (luaL_loadstring(_L2, lua_tostring(L_, 1)) != 0) { // L_: [8 args] args... L2: func
raise_luaL_error(L_, "error when parsing lane function code");
}
} else {
- raise_luaL_error(L_, "Expected function, got %s", lua_typename(L_, func_type));
+ raise_luaL_error(L_, "Expected function, got %s", lua_typename(L_, _func_type));
}
STACK_CHECK(L_, 0);
- STACK_CHECK(L2, 1);
- LUA_ASSERT(L_, lua_isfunction(L2, 1));
+ STACK_CHECK(_L2, 1);
+ LUA_ASSERT(L_, lua_isfunction(_L2, 1));
// revive arguments
- if (nargs > 0) {
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END(U)));
- DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
- InterCopyContext c{ U, DestState{ L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
- InterCopyResult const res{ c.inter_move(nargs) }; // L_: [8 args] L2: func args...
+ if (_nargs > 0) {
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END(_U)));
+ DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U });
+ InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
+ InterCopyResult const res{ _c.inter_move(_nargs) }; // L_: [8 args] L2: func args...
if (res != InterCopyResult::Success) {
raise_luaL_error(L_, "tried to copy unsupported types");
}
}
- STACK_CHECK(L_, -nargs);
+ STACK_CHECK(L_, -_nargs);
LUA_ASSERT(L_, lua_gettop(L_) == kFixedArgsIdx);
// Store 'lane' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive).
- kLanePointerRegKey.setValue(L2, [lane](lua_State* L_) { lua_pushlightuserdata(L_, lane); }); // L_: [8 args] L2: func args...
- STACK_CHECK(L2, 1 + nargs);
+ kLanePointerRegKey.setValue(_L2, [lane = _lane](lua_State* L_) { lua_pushlightuserdata(L_, lane); });// L_: [8 args] L2: func args...
+ STACK_CHECK(_L2, 1 + _nargs);
STACK_CHECK_RESET_REL(L_, 0);
// all went well, the lane's thread can start working
@@ -1112,8 +1112,8 @@ LUAG_FUNC(lane_new)
//
[[nodiscard]] static int lane_gc(lua_State* L_)
{
- bool have_gc_cb{ false };
- Lane* const lane{ ToLane(L_, 1) }; // L_: ud
+ bool _have_gc_cb{ false };
+ Lane* const _lane{ ToLane(L_, 1) }; // L_: ud
// if there a gc callback?
lua_getiuservalue(L_, 1, 1); // L_: ud uservalue
@@ -1121,35 +1121,35 @@ LUAG_FUNC(lane_new)
lua_rawget(L_, -2); // L_: ud uservalue gc_cb|nil
if (!lua_isnil(L_, -1)) {
lua_remove(L_, -2); // L_: ud gc_cb|nil
- lua_pushstring(L_, lane->debugName); // L_: ud gc_cb name
- have_gc_cb = true;
+ lua_pushstring(L_, _lane->debugName); // L_: ud gc_cb name
+ _have_gc_cb = true;
} else {
lua_pop(L_, 2); // L_: ud
}
// We can read 'lane->status' without locks, but not wait for it
- if (lane->status < Lane::Done) {
+ if (_lane->status < Lane::Done) {
// still running: will have to be cleaned up later
- selfdestruct_add(lane);
- assert(lane->selfdestruct_next);
- if (have_gc_cb) {
+ selfdestruct_add(_lane);
+ assert(_lane->selfdestruct_next);
+ if (_have_gc_cb) {
lua_pushliteral(L_, "selfdestruct"); // L_: ud gc_cb name status
lua_call(L_, 2, 0); // L_: ud
}
return 0;
- } else if (lane->L) {
+ } else if (_lane->L) {
// no longer accessing the Lua VM: we can close right now
- lua_close(lane->L);
- lane->L = nullptr;
+ lua_close(_lane->L);
+ _lane->L = nullptr;
// just in case, but s will be freed soon so...
- lane->debugName = "";
+ _lane->debugName = "";
}
// Clean up after a (finished) thread
- delete lane;
+ delete _lane;
// do this after lane cleanup in case the callback triggers an error
- if (have_gc_cb) {
+ if (_have_gc_cb) {
lua_pushliteral(L_, "closed"); // L_: ud gc_cb name status
lua_call(L_, 2, 0); // L_: ud
}
@@ -1170,7 +1170,7 @@ LUAG_FUNC(lane_new)
//
[[nodiscard]] static char const* thread_status_string(Lane::Status status_)
{
- char const* const str{
+ char const* const _str{
(status_ == Lane::Pending) ? "pending" :
(status_ == Lane::Running) ? "running" : // like in 'co.status()'
(status_ == Lane::Waiting) ? "waiting" :
@@ -1179,17 +1179,17 @@ LUAG_FUNC(lane_new)
(status_ == Lane::Cancelled) ? "cancelled" :
nullptr
};
- return str;
+ return _str;
}
// #################################################################################################
void Lane::pushThreadStatus(lua_State* L_)
{
- char const* const str{ thread_status_string(status) };
- LUA_ASSERT(L_, str);
+ char const* const _str{ thread_status_string(status) };
+ LUA_ASSERT(L_, _str);
- lua_pushstring(L_, str);
+ lua_pushstring(L_, _str);
}
// #################################################################################################
@@ -1204,14 +1204,14 @@ void Lane::pushThreadStatus(lua_State* L_)
//
LUAG_FUNC(thread_join)
{
- Lane* const lane{ ToLane(L_, 1) };
- lua_State* const L2{ lane->L };
+ Lane* const _lane{ ToLane(L_, 1) };
+ lua_State* const _L2{ _lane->L };
- std::chrono::time_point until{ std::chrono::time_point::max() };
+ std::chrono::time_point _until{ std::chrono::time_point::max() };
if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion
lua_Duration const duration{ lua_tonumber(L_, 2) };
if (duration.count() >= 0.0) {
- until = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration);
+ _until = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration);
} else {
raise_luaL_argerror(L_, 2, "duration cannot be < 0");
}
@@ -1220,9 +1220,9 @@ LUAG_FUNC(thread_join)
raise_luaL_argerror(L_, 2, "incorrect duration type");
}
- bool const done{ !lane->thread.joinable() || lane->waitForCompletion(until) };
+ bool const done{ !_lane->thread.joinable() || _lane->waitForCompletion(_until) };
lua_settop(L_, 1); // L_: lane
- if (!done || !L2) {
+ if (!done || !_L2) {
lua_pushnil(L_); // L_: lane nil
lua_pushliteral(L_, "timeout"); // L_: lane nil "timeout"
return 2;
@@ -1231,51 +1231,51 @@ LUAG_FUNC(thread_join)
STACK_CHECK_START_REL(L_, 0); // L_: lane
// Thread is Done/Error/Cancelled; all ours now
- int ret{ 0 };
+ int _ret{ 0 };
// debugName is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed
// so store it in the userdata uservalue at a key that can't possibly collide
- lane->securizeDebugName(L_);
- switch (lane->status) {
+ _lane->securizeDebugName(L_);
+ switch (_lane->status) {
case Lane::Done:
{
- int const n{ lua_gettop(L2) }; // whole L2 stack
+ int const _n{ lua_gettop(_L2) }; // whole L2 stack
if (
- (n > 0) &&
- (InterCopyContext{ lane->U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} }.inter_move(n) != InterCopyResult::Success)
+ (_n > 0) &&
+ (InterCopyContext{ _lane->U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} }.inter_move(_n) != InterCopyResult::Success)
) { // L_: lane results L2:
raise_luaL_error(L_, "tried to copy unsupported types");
}
- ret = n;
+ _ret = _n;
}
break;
case Lane::Error:
{
- int const n{ lua_gettop(L2) }; // L_: lane L2: "err" [trace]
+ int const _n{ lua_gettop(_L2) }; // L_: lane L2: "err" [trace]
STACK_GROW(L_, 3);
lua_pushnil(L_); // L_: lane nil
// 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 ...
- InterCopyContext c{ lane->U, DestState{ L_ }, SourceState{ L2 }, {}, {}, {}, {}, {} };
- if (c.inter_move(n) != InterCopyResult::Success) { // L_: lane nil "err" [trace] L2:
- raise_luaL_error(L_, "tried to copy unsupported types: %s", lua_tostring(L_, -n));
+ InterCopyContext _c{ _lane->U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} };
+ if (_c.inter_move(_n) != InterCopyResult::Success) { // L_: lane nil "err" [trace] L2:
+ raise_luaL_error(L_, "tried to copy unsupported types: %s", lua_tostring(L_, -_n));
}
- ret = 1 + n;
+ _ret = 1 + _n;
}
break;
case Lane::Cancelled:
- ret = 0;
+ _ret = 0;
break;
default:
- DEBUGSPEW_CODE(fprintf(stderr, "Status: %d\n", lane->status));
+ DEBUGSPEW_CODE(fprintf(stderr, "Status: %d\n", _lane->status));
LUA_ASSERT(L_, false);
- ret = 0;
+ _ret = 0;
}
- lua_close(L2);
- lane->L = nullptr;
- STACK_CHECK(L_, ret);
- return ret;
+ lua_close(_L2);
+ _lane->L = nullptr;
+ STACK_CHECK(L_, _ret);
+ return _ret;
}
// #################################################################################################
@@ -1291,7 +1291,7 @@ LUAG_FUNC(thread_index)
{
static constexpr int kSelf{ 1 };
static constexpr int kKey{ 2 };
- Lane* const lane{ ToLane(L_, kSelf) };
+ Lane* const _lane{ ToLane(L_, kSelf) };
LUA_ASSERT(L_, lua_gettop(L_) == 2);
STACK_GROW(L_, 8); // up to 8 positions are needed in case of error propagation
@@ -1314,9 +1314,9 @@ LUAG_FUNC(thread_index)
// check if we already fetched the values from the thread or not
lua_pushinteger(L_, 0);
lua_rawget(L_, kUsr);
- bool const fetched{ !lua_isnil(L_, -1) };
+ bool const _fetched{ !lua_isnil(L_, -1) };
lua_pop(L_, 1); // back to our 2 args + uservalue on the stack
- if (!fetched) {
+ if (!_fetched) {
lua_pushinteger(L_, 0);
lua_pushboolean(L_, 1);
lua_rawset(L_, kUsr);
@@ -1324,22 +1324,22 @@ LUAG_FUNC(thread_index)
lua_pushcfunction(L_, LG_thread_join);
lua_pushvalue(L_, kSelf);
lua_call(L_, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+
- switch (lane->status) {
+ switch (_lane->status) {
default:
// this is an internal error, we probably never get here
lua_settop(L_, 0);
lua_pushliteral(L_, "Unexpected status: ");
- lua_pushstring(L_, thread_status_string(lane->status));
+ lua_pushstring(L_, thread_status_string(_lane->status));
lua_concat(L_, 2);
raise_lua_error(L_);
[[fallthrough]]; // fall through if we are killed, as we got nil, "killed" on the stack
case Lane::Done: // got regular return values
{
- int const nvalues{ lua_gettop(L_) - 3 };
- for (int i = nvalues; i > 0; --i) {
+ int const _nvalues{ lua_gettop(L_) - 3 };
+ for (int _i = _nvalues; _i > 0; --_i) {
// pop the last element of the stack, to store it in the uservalue at its proper index
- lua_rawseti(L_, kUsr, i);
+ lua_rawseti(L_, kUsr, _i);
}
}
break;
@@ -1361,8 +1361,8 @@ LUAG_FUNC(thread_index)
}
}
lua_settop(L_, 3); // L_: self KEY ENV
- int const key{ static_cast(lua_tointeger(L_, kKey)) };
- if (key != -1) {
+ int const _key{ static_cast(lua_tointeger(L_, kKey)) };
+ if (_key != -1) {
lua_pushnumber(L_, -1); // L_: self KEY ENV -1
lua_rawget(L_, kUsr); // L_: self KEY ENV "error"|nil
if (!lua_isnil(L_, -1)) { // L_: an error was stored
@@ -1388,15 +1388,15 @@ LUAG_FUNC(thread_index)
lua_pop(L_, 1); // L_: self KEY ENV
}
}
- lua_rawgeti(L_, kUsr, key);
+ lua_rawgeti(L_, kUsr, _key);
}
return 1;
}
if (lua_type(L_, kKey) == LUA_TSTRING) {
- char const* const keystr{ lua_tostring(L_, kKey) };
+ char const* const _keystr{ lua_tostring(L_, kKey) };
lua_settop(L_, 2); // keep only our original arguments on the stack
- if (strcmp(keystr, "status") == 0) {
- lane->pushThreadStatus(L_); // push the string representing the status
+ if (strcmp(_keystr, "status") == 0) {
+ _lane->pushThreadStatus(L_); // push the string representing the status
return 1;
}
// return self.metatable[key]
@@ -1405,7 +1405,7 @@ LUAG_FUNC(thread_index)
lua_rawget(L_, -2); // L_: mt value
// only "cancel" and "join" are registered as functions, any other string will raise an error
if (!lua_iscfunction(L_, -1)) {
- raise_luaL_error(L_, "can't index a lane with '%s'", keystr);
+ raise_luaL_error(L_, "can't index a lane with '%s'", _keystr);
}
return 1;
}
@@ -1428,27 +1428,27 @@ LUAG_FUNC(thread_index)
// Return a list of all known lanes
LUAG_FUNC(threads)
{
- int const top{ lua_gettop(L_) };
- Universe* const U{ universe_get(L_) };
+ int const _top{ lua_gettop(L_) };
+ Universe* const _U{ universe_get(L_) };
// List _all_ still running threads
- std::lock_guard guard{ U->trackingMutex };
- if (U->trackingFirst && U->trackingFirst != TRACKING_END) {
- Lane* lane{ U->trackingFirst };
- int index{ 0 };
+ std::lock_guard _guard{ _U->trackingMutex };
+ if (_U->trackingFirst && _U->trackingFirst != TRACKING_END) {
+ Lane* _lane{ _U->trackingFirst };
+ int _index{ 0 };
lua_newtable(L_); // L_: {}
- while (lane != TRACKING_END) {
+ while (_lane != TRACKING_END) {
// insert a { name='', status='' } tuple, so that several lanes with the same name can't clobber each other
lua_createtable(L_, 0, 2); // L_: {} {}
- lua_pushstring(L_, lane->debugName); // L_: {} {} "name"
+ lua_pushstring(L_, _lane->debugName); // L_: {} {} "name"
lua_setfield(L_, -2, "name"); // L_: {} {}
- lane->pushThreadStatus(L_); // L_: {} {} "status"
+ _lane->pushThreadStatus(L_); // L_: {} {} "status"
lua_setfield(L_, -2, "status"); // L_: {} {}
- lua_rawseti(L_, -2, ++index); // L_: {}
- lane = lane->tracking_next;
+ lua_rawseti(L_, -2, ++_index); // L_: {}
+ _lane = _lane->tracking_next;
}
}
- return lua_gettop(L_) - top; // L_: 0 or 1
+ return lua_gettop(L_) - _top; // L_: 0 or 1
}
#endif // HAVE_LANE_TRACKING()
@@ -1464,8 +1464,8 @@ LUAG_FUNC(threads)
*/
LUAG_FUNC(now_secs)
{
- auto const now{ std::chrono::system_clock::now() };
- lua_Duration duration{ now.time_since_epoch() };
+ auto const _now{ std::chrono::system_clock::now() };
+ lua_Duration duration{ _now.time_since_epoch() };
lua_pushnumber(L_, duration.count());
return 1;
@@ -1487,38 +1487,38 @@ LUAG_FUNC(wakeup_conv)
// .isdst (daylight saving on/off)
STACK_CHECK_START_REL(L_, 0);
- auto readInteger = [L = L_](char const* name_) {
+ auto _readInteger = [L = L_](char const* name_) {
lua_getfield(L, 1, name_);
lua_Integer const val{ lua_tointeger(L, -1) };
lua_pop(L, 1);
return static_cast(val);
};
- int const year{ readInteger("year") };
- int const month{ readInteger("month") };
- int const day{ readInteger("day") };
- int const hour{ readInteger("hour") };
- int const min{ readInteger("min") };
- int const sec{ readInteger("sec") };
+ int const _year{ _readInteger("year") };
+ int const _month{ _readInteger("month") };
+ int const _day{ _readInteger("day") };
+ int const _hour{ _readInteger("hour") };
+ int const _min{ _readInteger("min") };
+ int const _sec{ _readInteger("sec") };
STACK_CHECK(L_, 0);
// If Lua table has '.isdst' we trust that. If it does not, we'll let
// 'mktime' decide on whether the time is within DST or not (value -1).
//
lua_getfield(L_, 1, "isdst");
- int const isdst{ lua_isboolean(L_, -1) ? lua_toboolean(L_, -1) : -1 };
+ int const _isdst{ lua_isboolean(L_, -1) ? lua_toboolean(L_, -1) : -1 };
lua_pop(L_, 1);
STACK_CHECK(L_, 0);
- std::tm t{};
- t.tm_year = year - 1900;
- t.tm_mon = month - 1; // 0..11
- t.tm_mday = day; // 1..31
- t.tm_hour = hour; // 0..23
- t.tm_min = min; // 0..59
- t.tm_sec = sec; // 0..60
- t.tm_isdst = isdst; // 0/1/negative
+ std::tm _t{};
+ _t.tm_year = _year - 1900;
+ _t.tm_mon = _month - 1; // 0..11
+ _t.tm_mday = _day; // 1..31
+ _t.tm_hour = _hour; // 0..23
+ _t.tm_min = _min; // 0..59
+ _t.tm_sec = _sec; // 0..60
+ _t.tm_isdst = _isdst; // 0/1/negative
- lua_pushnumber(L_, static_cast(std::mktime(&t))); // resolution: 1 second
+ lua_pushnumber(L_, static_cast(std::mktime(&_t))); // resolution: 1 second
return 1;
}
@@ -1527,7 +1527,7 @@ LUAG_FUNC(wakeup_conv)
// #################################################################################################
// same as PUC-Lua l_alloc
-extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_)
+extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud_, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_)
{
if (nsize_ == 0) {
free(ptr_);
@@ -1541,9 +1541,9 @@ extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud,
[[nodiscard]] static int luaG_provide_protected_allocator(lua_State* L_)
{
- Universe* const U{ universe_get(L_) };
+ Universe* const _U{ universe_get(L_) };
// push a new full userdata on the stack, giving access to the universe's protected allocator
- [[maybe_unused]] AllocatorDefinition* const def{ new (L_) AllocatorDefinition{ U->protectedAllocator.makeDefinition() } };
+ [[maybe_unused]] AllocatorDefinition* const def{ new (L_) AllocatorDefinition{ _U->protectedAllocator.makeDefinition() } };
return 1;
}
@@ -1585,8 +1585,8 @@ static void initialize_allocator_function(Universe* U_, lua_State* L_)
lua_getfield(L_, -1, "internal_allocator"); // L_: settings "libc"|"allocator"
{
- char const* allocator = lua_tostring(L_, -1);
- if (strcmp(allocator, "libc") == 0) {
+ char const* const _allocator{ lua_tostring(L_, -1) };
+ if (strcmp(_allocator, "libc") == 0) {
U_->internalAllocator = AllocatorDefinition{ libc_lua_Alloc, nullptr };
} else if (U_->provideAllocator == luaG_provide_protected_allocator) {
// user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case.
@@ -1639,20 +1639,20 @@ LUAG_FUNC(configure)
});
}
- Universe* U = universe_get(L_);
- bool const from_master_state{ U == nullptr };
- char const* name = luaL_checkstring(L_, lua_upvalueindex(1));
+ Universe* _U{ universe_get(L_) };
+ bool const _from_master_state{ _U == nullptr };
+ char const* const _name{ luaL_checkstring(L_, lua_upvalueindex(1)) };
LUA_ASSERT(L_, lua_type(L_, 1) == LUA_TTABLE);
STACK_GROW(L_, 4);
STACK_CHECK_START_ABS(L_, 1); // L_: settings
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END(U), L_));
- DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END(_U), L_));
+ DEBUGSPEW_CODE(DebugSpewIndentScope scope{ _U });
- if (U == nullptr) {
- U = universe_create(L_); // L_: settings universe
- DEBUGSPEW_CODE(DebugSpewIndentScope scope2{ U });
+ if (_U == nullptr) {
+ _U = universe_create(L_); // L_: settings universe
+ DEBUGSPEW_CODE(DebugSpewIndentScope _scope2{ _U });
lua_createtable(L_, 0, 1); // L_: settings universe {mt}
lua_getfield(L_, 1, "shutdown_timeout"); // L_: settings universe {mt} shutdown_timeout
lua_getfield(L_, 1, "shutdown_mode"); // L_: settings universe {mt} shutdown_timeout shutdown_mode
@@ -1661,21 +1661,21 @@ LUAG_FUNC(configure)
lua_setmetatable(L_, -2); // L_: settings universe
lua_pop(L_, 1); // L_: settings
lua_getfield(L_, 1, "verbose_errors"); // L_: settings verbose_errors
- U->verboseErrors = lua_toboolean(L_, -1) ? true : false;
+ _U->verboseErrors = lua_toboolean(L_, -1) ? true : false;
lua_pop(L_, 1); // L_: settings
lua_getfield(L_, 1, "demote_full_userdata"); // L_: settings demote_full_userdata
- U->demoteFullUserdata = lua_toboolean(L_, -1) ? true : false;
+ _U->demoteFullUserdata = lua_toboolean(L_, -1) ? true : false;
lua_pop(L_, 1); // L_: settings
#if HAVE_LANE_TRACKING()
lua_getfield(L_, 1, "track_lanes"); // L_: settings track_lanes
- U->trackingFirst = lua_toboolean(L_, -1) ? TRACKING_END : nullptr;
+ _U->trackingFirst = lua_toboolean(L_, -1) ? TRACKING_END : nullptr;
lua_pop(L_, 1); // L_: settings
#endif // HAVE_LANE_TRACKING()
// Linked chains handling
- U->selfdestructFirst = SELFDESTRUCT_END;
- initialize_allocator_function(U, L_);
- initializeOnStateCreate(U, L_);
- init_keepers(U, L_);
+ _U->selfdestructFirst = SELFDESTRUCT_END;
+ initialize_allocator_function(_U, L_);
+ initializeOnStateCreate(_U, L_);
+ init_keepers(_U, L_);
STACK_CHECK(L_, 1);
// Initialize 'timerLinda'; a common Linda object shared by all states
@@ -1685,15 +1685,15 @@ LUAG_FUNC(configure)
STACK_CHECK(L_, 2);
// Proxy userdata contents is only a 'DeepPrelude*' pointer
- U->timerLinda = *lua_tofulluserdata(L_, -1);
+ _U->timerLinda = *lua_tofulluserdata(L_, -1);
// increment refcount so that this linda remains alive as long as the universe exists.
- U->timerLinda->refcount.fetch_add(1, std::memory_order_relaxed);
+ _U->timerLinda->refcount.fetch_add(1, std::memory_order_relaxed);
lua_pop(L_, 1); // L_: settings
}
STACK_CHECK(L_, 1);
// Serialize calls to 'require' from now on, also in the primary state
- serialize_require(DEBUGSPEW_PARAM_COMMA(U) L_);
+ serialize_require(DEBUGSPEW_PARAM_COMMA(_U) L_);
// Retrieve main module interface table
lua_pushvalue(L_, lua_upvalueindex(2)); // L_: settings M
@@ -1704,7 +1704,7 @@ LUAG_FUNC(configure)
luaG_registerlibfuncs(L_, global::sLanesFunctions);
#if HAVE_LANE_TRACKING()
// register core.threads() only if settings say it should be available
- if (U->trackingFirst != nullptr) {
+ if (_U->trackingFirst != nullptr) {
lua_pushcfunction(L_, LG_threads); // L_: settings M LG_threads()
lua_setfield(L_, -2, "threads"); // L_: settings M
}
@@ -1712,11 +1712,11 @@ LUAG_FUNC(configure)
STACK_CHECK(L_, 2);
{
- char const* errmsg{
- DeepFactory::PushDeepProxy(DestState{ L_ }, U->timerLinda, 0, LookupMode::LaneBody)
+ char const* _errmsg{
+ DeepFactory::PushDeepProxy(DestState{ L_ }, _U->timerLinda, 0, LookupMode::LaneBody)
}; // L_: settings M timerLinda
- if (errmsg != nullptr) {
- raise_luaL_error(L_, errmsg);
+ if (_errmsg != nullptr) {
+ raise_luaL_error(L_, _errmsg);
}
lua_setfield(L_, -2, "timer_gateway"); // L_: settings M
}
@@ -1780,12 +1780,12 @@ LUAG_FUNC(configure)
// register all native functions found in that module in the transferable functions database
// we process it before _G because we don't want to find the module when scanning _G (this would generate longer names)
// for example in package.loaded["lanes.core"].*
- populate_func_lookup_table(L_, -1, name);
+ populate_func_lookup_table(L_, -1, _name);
STACK_CHECK(L_, 2);
// record all existing C/JIT-fast functions
// Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack
- if (from_master_state) {
+ if (_from_master_state) {
// don't do this when called during the initialization of a new lane,
// because we will do it after on_state_create() is called,
// and we don't want to skip _G because of caching in case globals are created then
@@ -1798,7 +1798,7 @@ LUAG_FUNC(configure)
// set _R[kConfigRegKey] = settings
kConfigRegKey.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); });
STACK_CHECK(L_, 1);
- DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END(U), L_));
+ DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END(_U), L_));
// Return the settings table
return 1;
}
@@ -1809,9 +1809,9 @@ LUAG_FUNC(configure)
#include
#include
-void signal_handler(int signal)
+void signal_handler(int signal_)
{
- if (signal == SIGABRT) {
+ if (signal_ == SIGABRT) {
_cprintf("caught abnormal termination!");
abort();
}
@@ -1830,18 +1830,18 @@ static void EnableCrashingOnCrashes(void)
typedef BOOL(WINAPI * tSetPolicy)(DWORD dwFlags);
const DWORD EXCEPTION_SWALLOWING = 0x1;
- HMODULE kernel32 = LoadLibraryA("kernel32.dll");
- if (kernel32) {
- tGetPolicy pGetPolicy = (tGetPolicy) GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
- tSetPolicy pSetPolicy = (tSetPolicy) GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
+ HMODULE _kernel32 = LoadLibraryA("kernel32.dll");
+ if (_kernel32) {
+ tGetPolicy pGetPolicy = (tGetPolicy) GetProcAddress(_kernel32, "GetProcessUserModeExceptionPolicy");
+ tSetPolicy pSetPolicy = (tSetPolicy) GetProcAddress(_kernel32, "SetProcessUserModeExceptionPolicy");
if (pGetPolicy && pSetPolicy) {
- DWORD dwFlags;
- if (pGetPolicy(&dwFlags)) {
+ DWORD _dwFlags;
+ if (pGetPolicy(&_dwFlags)) {
// Turn off the filter
- pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
+ pSetPolicy(_dwFlags & ~EXCEPTION_SWALLOWING);
}
}
- FreeLibrary(kernel32);
+ FreeLibrary(_kernel32);
}
// typedef void (* SignalHandlerPointer)( int);
/*SignalHandlerPointer previousHandler =*/signal(SIGABRT, signal_handler);
@@ -1902,8 +1902,8 @@ LANES_API int luaopen_lanes_core(lua_State* L_)
[[nodiscard]] static int default_luaopen_lanes(lua_State* L_)
{
- int const rc{ luaL_loadfile(L_, "lanes.lua") || lua_pcall(L_, 0, 1, 0) };
- if (rc != LUA_OK) {
+ int const _rc{ luaL_loadfile(L_, "lanes.lua") || lua_pcall(L_, 0, 1, 0) };
+ if (_rc != LUA_OK) {
raise_luaL_error(L_, "failed to initialize embedded Lanes");
}
return 1;
diff --git a/src/lanesconf.h b/src/lanesconf.h
index 7b4ff93..aae4f5b 100644
--- a/src/lanesconf.h
+++ b/src/lanesconf.h
@@ -23,7 +23,7 @@
// regular class member/method: no prefix, start with a lowercase letter
// function argument: suffix _
// static function variable: prefix s, followed by an uppercase letter
-// function local variable: prefix l, followed by an uppercase letter
+// function local variable: prefix _, followed by an uppercase letter
// named lambda capture: no prefix, start with a lowercase letter
#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
diff --git a/src/linda.cpp b/src/linda.cpp
index 07911b3..fedcd01 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -59,8 +59,8 @@ Linda::Linda(Universe* U_, LindaGroup group_, char const* name_, size_t len_)
Linda::~Linda()
{
if (std::holds_alternative(nameVariant)) {
- AllocatedName& name = std::get(nameVariant);
- U->internalAllocator.free(name.name, name.len);
+ AllocatedName& _name = std::get(nameVariant);
+ U->internalAllocator.free(_name.name, _name.len);
}
}
@@ -75,13 +75,13 @@ void Linda::setName(char const* name_, size_t len_)
++len_; // don't forget terminating 0
if (len_ < kEmbeddedNameLength) {
nameVariant.emplace();
- char* const name{ std::get(nameVariant).data() };
- memcpy(name, name_, len_);
+ char* const _name{ std::get(nameVariant).data() };
+ memcpy(_name, name_, len_);
} else {
- AllocatedName& name = std::get(nameVariant);
- name.name = static_cast(U->internalAllocator.alloc(len_));
- name.len = len_;
- memcpy(name.name, name_, len_);
+ AllocatedName& _name = std::get(nameVariant);
+ _name.name = static_cast(U->internalAllocator.alloc(len_));
+ _name.len = len_;
+ memcpy(_name.name, name_, len_);
}
}
@@ -90,12 +90,12 @@ void Linda::setName(char const* name_, size_t len_)
char const* Linda::getName() const
{
if (std::holds_alternative(nameVariant)) {
- AllocatedName const& name = std::get(nameVariant);
- return name.name;
+ AllocatedName const& _name = std::get(nameVariant);
+ return _name.name;
}
if (std::holds_alternative(nameVariant)) {
- char const* const name{ std::get(nameVariant).data() };
- return name;
+ char const* const _name{ std::get(nameVariant).data() };
+ return _name;
}
return nullptr;
}
@@ -105,20 +105,20 @@ char const* Linda::getName() const
template
[[nodiscard]] static inline Linda* ToLinda(lua_State* L_, int idx_)
{
- Linda* const linda{ static_cast(LindaFactory::Instance.toDeep(L_, idx_)) };
+ Linda* const _linda{ static_cast(LindaFactory::Instance.toDeep(L_, idx_)) };
if constexpr (!OPT) {
- luaL_argcheck(L_, linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr
- LUA_ASSERT(L_, linda->U == universe_get(L_));
+ luaL_argcheck(L_, _linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr
+ LUA_ASSERT(L_, _linda->U == universe_get(L_));
}
- return linda;
+ return _linda;
}
// #################################################################################################
static void check_key_types(lua_State* L_, int start_, int end_)
{
- for (int i{ start_ }; i <= end_; ++i) {
- LuaType const t{ lua_type_as_enum(L_, i) };
+ for (int _i{ start_ }; _i <= end_; ++_i) {
+ LuaType const t{ lua_type_as_enum(L_, _i) };
switch (t) {
case LuaType::BOOLEAN:
case LuaType::NUMBER:
@@ -128,14 +128,14 @@ static void check_key_types(lua_State* L_, int start_, int end_)
case LuaType::LIGHTUSERDATA:
static constexpr std::array, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel };
for (UniqueKey const& key : kKeysToCheck) {
- if (key.equals(L_, i)) {
- raise_luaL_error(L_, "argument #%d: can't use %s as a key", i, key.debugName);
+ if (key.equals(L_, _i)) {
+ raise_luaL_error(L_, "argument #%d: can't use %s as a key", _i, key.debugName);
break;
}
}
break;
}
- raise_luaL_error(L_, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i);
+ raise_luaL_error(L_, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", _i);
}
}
@@ -144,29 +144,29 @@ static void check_key_types(lua_State* L_, int start_, int end_)
// used to perform all linda operations that access keepers
int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_)
{
- Linda* const linda{ ToLinda(L_, 1) };
+ Linda* const _linda{ ToLinda(L_, 1) };
// acquire the keeper
- Keeper* const K{ linda->acquireKeeper() };
- lua_State* const KL{ K ? K->L : nullptr };
- if (KL == nullptr)
+ Keeper* const _K{ _linda->acquireKeeper() };
+ lua_State* const _KL{ _K ? _K->L : nullptr };
+ if (_KL == nullptr)
return 0;
// if we didn't do anything wrong, the keeper stack should be clean
- LUA_ASSERT(L_, lua_gettop(KL) == 0);
+ LUA_ASSERT(L_, lua_gettop(_KL) == 0);
// push the function to be called and move it before the arguments
lua_pushcfunction(L_, f_);
lua_insert(L_, 1);
// do a protected call
- int const rc{ lua_pcall(L_, lua_gettop(L_) - 1, LUA_MULTRET, 0) };
+ int const _rc{ lua_pcall(L_, lua_gettop(L_) - 1, LUA_MULTRET, 0) };
// whatever happens, the keeper state stack must be empty when we are done
- lua_settop(KL, 0);
+ lua_settop(_KL, 0);
// release the keeper
- linda->releaseKeeper(K);
+ _linda->releaseKeeper(_K);
// if there was an error, forward it
- if (rc != LUA_OK) {
+ if (_rc != LUA_OK) {
raise_lua_error(L_);
}
// return whatever the actual operation provided
@@ -186,108 +186,108 @@ int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_)
*/
LUAG_FUNC(linda_send)
{
- auto send = [](lua_State* L_) {
- Linda* const linda{ ToLinda(L_, 1) };
- int key_i{ 2 }; // index of first key, if timeout not there
+ auto _send = [](lua_State* L_) {
+ Linda* const _linda{ ToLinda(L_, 1) };
+ int _key_i{ 2 }; // index of first key, if timeout not there
- std::chrono::time_point until{ std::chrono::time_point::max() };
+ std::chrono::time_point _until{ std::chrono::time_point::max() };
if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion
- lua_Duration const duration{ lua_tonumber(L_, 2) };
- if (duration.count() >= 0.0) {
- until = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration);
+ lua_Duration const _duration{ lua_tonumber(L_, 2) };
+ if (_duration.count() >= 0.0) {
+ _until = std::chrono::steady_clock::now() + std::chrono::duration_cast(_duration);
} else {
raise_luaL_argerror(L_, 2, "duration cannot be < 0");
}
- ++key_i;
+ ++_key_i;
} else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key
- ++key_i;
+ ++_key_i;
}
// make sure the key is of a valid type
- check_key_types(L_, key_i, key_i);
+ check_key_types(L_, _key_i, _key_i);
STACK_GROW(L_, 1);
// make sure there is something to send
- if (lua_gettop(L_) == key_i) {
+ if (lua_gettop(L_) == _key_i) {
raise_luaL_error(L_, "no data to send");
}
// convert nils to some special non-nil sentinel in sent values
- keeper_toggle_nil_sentinels(L_, key_i + 1, LookupMode::ToKeeper);
- bool ret{ false };
- CancelRequest cancel{ CancelRequest::None };
- KeeperCallResult pushed;
+ keeper_toggle_nil_sentinels(L_, _key_i + 1, LookupMode::ToKeeper);
+ bool _ret{ false };
+ CancelRequest _cancel{ CancelRequest::None };
+ KeeperCallResult _pushed;
{
- Lane* const lane{ kLanePointerRegKey.readLightUserDataValue(L_) };
- Keeper* const K{ linda->whichKeeper() };
- KeeperState const KL{ K ? K->L : nullptr };
- if (KL == nullptr)
+ Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue(L_) };
+ Keeper* const _K{ _linda->whichKeeper() };
+ KeeperState const _KL{ _K ? _K->L : nullptr };
+ if (_KL == nullptr)
return 0;
- STACK_CHECK_START_REL(KL, 0);
- for (bool try_again{ true };;) {
- if (lane != nullptr) {
- cancel = lane->cancelRequest;
+ STACK_CHECK_START_REL(_KL, 0);
+ for (bool _try_again{ true };;) {
+ if (_lane != nullptr) {
+ _cancel = _lane->cancelRequest;
}
- cancel = (cancel != CancelRequest::None) ? cancel : linda->cancelRequest;
+ _cancel = (_cancel != CancelRequest::None) ? _cancel : _linda->cancelRequest;
// if user wants to cancel, or looped because of a timeout, the call returns without sending anything
- if (!try_again || cancel != CancelRequest::None) {
- pushed.emplace(0);
+ if (!_try_again || _cancel != CancelRequest::None) {
+ _pushed.emplace(0);
break;
}
- STACK_CHECK(KL, 0);
- pushed = keeper_call(linda->U, KL, KEEPER_API(send), L_, linda, key_i);
- if (!pushed.has_value()) {
+ STACK_CHECK(_KL, 0);
+ _pushed = keeper_call(_KL, KEEPER_API(send), L_, _linda, _key_i);
+ if (!_pushed.has_value()) {
break;
}
- LUA_ASSERT(L_, pushed.value() == 1);
+ LUA_ASSERT(L_, _pushed.value() == 1);
- ret = lua_toboolean(L_, -1) ? true : false;
+ _ret = lua_toboolean(L_, -1) ? true : false;
lua_pop(L_, 1);
- if (ret) {
+ if (_ret) {
// Wake up ALL waiting threads
- linda->writeHappened.notify_all();
+ _linda->writeHappened.notify_all();
break;
}
// instant timout to bypass the wait syscall
- if (std::chrono::steady_clock::now() >= until) {
+ if (std::chrono::steady_clock::now() >= _until) {
break; /* no wait; instant timeout */
}
// storage limit hit, wait until timeout or signalled that we should try again
{
- Lane::Status prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
- if (lane != nullptr) {
+ Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
+ if (_lane != nullptr) {
// change status of lane to "waiting"
- prev_status = lane->status; // Running, most likely
- LUA_ASSERT(L_, prev_status == Lane::Running); // but check, just in case
- lane->status = Lane::Waiting;
- LUA_ASSERT(L_, lane->waiting_on == nullptr);
- lane->waiting_on = &linda->readHappened;
+ _prev_status = _lane->status; // Running, most likely
+ LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case
+ _lane->status = Lane::Waiting;
+ LUA_ASSERT(L_, _lane->waiting_on == nullptr);
+ _lane->waiting_on = &_linda->readHappened;
}
// could not send because no room: wait until some data was read before trying again, or until timeout is reached
- std::unique_lock keeper_lock{ K->mutex, std::adopt_lock };
- std::cv_status const status{ linda->readHappened.wait_until(keeper_lock, until) };
- keeper_lock.release(); // we don't want to release the lock!
- try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups
- if (lane != nullptr) {
- lane->waiting_on = nullptr;
- lane->status = prev_status;
+ std::unique_lock _keeper_lock{ _K->mutex, std::adopt_lock };
+ std::cv_status const status{ _linda->readHappened.wait_until(_keeper_lock, _until) };
+ _keeper_lock.release(); // we don't want to release the lock!
+ _try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups
+ if (_lane != nullptr) {
+ _lane->waiting_on = nullptr;
+ _lane->status = _prev_status;
}
}
}
- STACK_CHECK(KL, 0);
+ STACK_CHECK(_KL, 0);
}
- if (!pushed.has_value()) {
+ if (!_pushed.has_value()) {
raise_luaL_error(L_, "tried to copy unsupported types");
}
- switch (cancel) {
+ switch (_cancel) {
case CancelRequest::Soft:
// if user wants to soft-cancel, the call returns lanes.cancel_error
kCancelError.pushKey(L_);
@@ -298,11 +298,11 @@ LUAG_FUNC(linda_send)
raise_cancel_error(L_); // raises an error and doesn't return
default:
- lua_pushboolean(L_, ret); // true (success) or false (timeout)
+ lua_pushboolean(L_, _ret); // true (success) or false (timeout)
return 1;
}
};
- return Linda::ProtectedCall(L_, send);
+ return Linda::ProtectedCall(L_, _send);
}
// #################################################################################################
@@ -320,122 +320,122 @@ LUAG_FUNC(linda_send)
*/
LUAG_FUNC(linda_receive)
{
- auto receive = [](lua_State* L_) {
- Linda* const linda{ ToLinda(L_, 1) };
- int key_i{ 2 }; // index of first key, if timeout not there
+ auto _receive = [](lua_State* L_) {
+ Linda* const _linda{ ToLinda(L_, 1) };
+ int _key_i{ 2 }; // index of first key, if timeout not there
- std::chrono::time_point until{ std::chrono::time_point::max() };
+ std::chrono::time_point _until{ std::chrono::time_point::max() };
if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion
- lua_Duration const duration{ lua_tonumber(L_, 2) };
- if (duration.count() >= 0.0) {
- until = std::chrono::steady_clock::now() + std::chrono::duration_cast(duration);
+ lua_Duration const _duration{ lua_tonumber(L_, 2) };
+ if (_duration.count() >= 0.0) {
+ _until = std::chrono::steady_clock::now() + std::chrono::duration_cast(_duration);
} else {
raise_luaL_argerror(L_, 2, "duration cannot be < 0");
}
- ++key_i;
+ ++_key_i;
} else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key
- ++key_i;
+ ++_key_i;
}
- keeper_api_t selected_keeper_receive{ nullptr };
- int expected_pushed_min{ 0 }, expected_pushed_max{ 0 };
+ keeper_api_t _selected_keeper_receive{ nullptr };
+ int _expected_pushed_min{ 0 }, _expected_pushed_max{ 0 };
// are we in batched mode?
kLindaBatched.pushKey(L_);
- int const is_batched{ lua501_equal(L_, key_i, -1) };
+ int const _is_batched{ lua501_equal(L_, _key_i, -1) };
lua_pop(L_, 1);
- if (is_batched) {
+ if (_is_batched) {
// no need to pass linda.batched in the keeper state
- ++key_i;
+ ++_key_i;
// make sure the keys are of a valid type
- check_key_types(L_, key_i, key_i);
+ check_key_types(L_, _key_i, _key_i);
// receive multiple values from a single slot
- selected_keeper_receive = KEEPER_API(receive_batched);
+ _selected_keeper_receive = KEEPER_API(receive_batched);
// we expect a user-defined amount of return value
- expected_pushed_min = (int) luaL_checkinteger(L_, key_i + 1);
- expected_pushed_max = (int) luaL_optinteger(L_, key_i + 2, expected_pushed_min);
+ _expected_pushed_min = (int) luaL_checkinteger(L_, _key_i + 1);
+ _expected_pushed_max = (int) luaL_optinteger(L_, _key_i + 2, _expected_pushed_min);
// don't forget to count the key in addition to the values
- ++expected_pushed_min;
- ++expected_pushed_max;
- if (expected_pushed_min > expected_pushed_max) {
+ ++_expected_pushed_min;
+ ++_expected_pushed_max;
+ if (_expected_pushed_min > _expected_pushed_max) {
raise_luaL_error(L_, "batched min/max error");
}
} else {
// make sure the keys are of a valid type
- check_key_types(L_, key_i, lua_gettop(L_));
+ check_key_types(L_, _key_i, lua_gettop(L_));
// receive a single value, checking multiple slots
- selected_keeper_receive = KEEPER_API(receive);
+ _selected_keeper_receive = KEEPER_API(receive);
// we expect a single (value, key) pair of returned values
- expected_pushed_min = expected_pushed_max = 2;
+ _expected_pushed_min = _expected_pushed_max = 2;
}
- Lane* const lane{ kLanePointerRegKey.readLightUserDataValue(L_) };
- Keeper* const K{ linda->whichKeeper() };
- KeeperState const KL{ K ? K->L : nullptr };
- if (KL == nullptr)
+ Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue(L_) };
+ Keeper* const _K{ _linda->whichKeeper() };
+ KeeperState const _KL{ _K ? _K->L : nullptr };
+ if (_KL == nullptr)
return 0;
- CancelRequest cancel{ CancelRequest::None };
- KeeperCallResult pushed;
- STACK_CHECK_START_REL(KL, 0);
- for (bool try_again{ true };;) {
- if (lane != nullptr) {
- cancel = lane->cancelRequest;
+ CancelRequest _cancel{ CancelRequest::None };
+ KeeperCallResult _pushed;
+ STACK_CHECK_START_REL(_KL, 0);
+ for (bool _try_again{ true };;) {
+ if (_lane != nullptr) {
+ _cancel = _lane->cancelRequest;
}
- cancel = (cancel != CancelRequest::None) ? cancel : linda->cancelRequest;
+ _cancel = (_cancel != CancelRequest::None) ? _cancel : _linda->cancelRequest;
// if user wants to cancel, or looped because of a timeout, the call returns without sending anything
- if (!try_again || cancel != CancelRequest::None) {
- pushed.emplace(0);
+ if (!_try_again || _cancel != CancelRequest::None) {
+ _pushed.emplace(0);
break;
}
// all arguments of receive() but the first are passed to the keeper's receive function
- pushed = keeper_call(linda->U, KL, selected_keeper_receive, L_, linda, key_i);
- if (!pushed.has_value()) {
+ _pushed = keeper_call(_KL, _selected_keeper_receive, L_, _linda, _key_i);
+ if (!_pushed.has_value()) {
break;
}
- if (pushed.value() > 0) {
- LUA_ASSERT(L_, pushed.value() >= expected_pushed_min && pushed.value() <= expected_pushed_max);
+ if (_pushed.value() > 0) {
+ LUA_ASSERT(L_, _pushed.value() >= _expected_pushed_min && _pushed.value() <= _expected_pushed_max);
// replace sentinels with real nils
- keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - pushed.value(), LookupMode::FromKeeper);
+ keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - _pushed.value(), LookupMode::FromKeeper);
// To be done from within the 'K' locking area
//
- linda->readHappened.notify_all();
+ _linda->readHappened.notify_all();
break;
}
- if (std::chrono::steady_clock::now() >= until) {
+ if (std::chrono::steady_clock::now() >= _until) {
break; /* instant timeout */
}
// nothing received, wait until timeout or signalled that we should try again
{
- Lane::Status prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
- if (lane != nullptr) {
+ Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
+ if (_lane != nullptr) {
// change status of lane to "waiting"
- prev_status = lane->status; // Running, most likely
- LUA_ASSERT(L_, prev_status == Lane::Running); // but check, just in case
- lane->status = Lane::Waiting;
- LUA_ASSERT(L_, lane->waiting_on == nullptr);
- lane->waiting_on = &linda->writeHappened;
+ _prev_status = _lane->status; // Running, most likely
+ LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case
+ _lane->status = Lane::Waiting;
+ LUA_ASSERT(L_, _lane->waiting_on == nullptr);
+ _lane->waiting_on = &_linda->writeHappened;
}
// not enough data to read: wakeup when data was sent, or when timeout is reached
- std::unique_lock keeper_lock{ K->mutex, std::adopt_lock };
- std::cv_status const status{ linda->writeHappened.wait_until(keeper_lock, until) };
- keeper_lock.release(); // we don't want to release the lock!
- try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups
- if (lane != nullptr) {
- lane->waiting_on = nullptr;
- lane->status = prev_status;
+ std::unique_lock _keeper_lock{ _K->mutex, std::adopt_lock };
+ std::cv_status const _status{ _linda->writeHappened.wait_until(_keeper_lock, _until) };
+ _keeper_lock.release(); // we don't want to release the lock!
+ _try_again = (_status == std::cv_status::no_timeout); // detect spurious wakeups
+ if (_lane != nullptr) {
+ _lane->waiting_on = nullptr;
+ _lane->status = _prev_status;
}
}
}
- STACK_CHECK(KL, 0);
+ STACK_CHECK(_KL, 0);
- if (!pushed.has_value()) {
+ if (!_pushed.has_value()) {
raise_luaL_error(L_, "tried to copy unsupported types");
}
- switch (cancel) {
+ switch (_cancel) {
case CancelRequest::Soft:
// if user wants to soft-cancel, the call returns kCancelError
kCancelError.pushKey(L_);
@@ -446,10 +446,10 @@ LUAG_FUNC(linda_receive)
raise_cancel_error(L_); // raises an error and doesn't return
default:
- return pushed.value();
+ return _pushed.value();
}
};
- return Linda::ProtectedCall(L_, receive);
+ return Linda::ProtectedCall(L_, _receive);
}
// #################################################################################################
@@ -465,40 +465,40 @@ LUAG_FUNC(linda_receive)
LUAG_FUNC(linda_set)
{
auto set = [](lua_State* L_) {
- Linda* const linda{ ToLinda(L_, 1) };
- bool const has_value{ lua_gettop(L_) > 2 };
+ Linda* const _linda{ ToLinda(L_, 1) };
+ bool const _has_value{ lua_gettop(L_) > 2 };
// make sure the key is of a valid type (throws an error if not the case)
check_key_types(L_, 2, 2);
- Keeper* const K{ linda->whichKeeper() };
- KeeperCallResult pushed;
- if (linda->cancelRequest == CancelRequest::None) {
- if (has_value) {
+ Keeper* const _K{ _linda->whichKeeper() };
+ KeeperCallResult _pushed;
+ if (_linda->cancelRequest == CancelRequest::None) {
+ if (_has_value) {
// convert nils to some special non-nil sentinel in sent values
keeper_toggle_nil_sentinels(L_, 3, LookupMode::ToKeeper);
}
- pushed = keeper_call(linda->U, K->L, KEEPER_API(set), L_, linda, 2);
- if (pushed.has_value()) { // no error?
- LUA_ASSERT(L_, pushed.value() == 0 || pushed.value() == 1);
+ _pushed = keeper_call(_K->L, KEEPER_API(set), L_, _linda, 2);
+ if (_pushed.has_value()) { // no error?
+ LUA_ASSERT(L_, _pushed.value() == 0 || _pushed.value() == 1);
- if (has_value) {
+ if (_has_value) {
// we put some data in the slot, tell readers that they should wake
- linda->writeHappened.notify_all(); // To be done from within the 'K' locking area
+ _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area
}
- if (pushed.value() == 1) {
+ if (_pushed.value() == 1) {
// the key was full, but it is no longer the case, tell writers they should wake
LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TBOOLEAN && lua_toboolean(L_, -1) == 1);
- linda->readHappened.notify_all(); // To be done from within the 'K' locking area
+ _linda->readHappened.notify_all(); // To be done from within the 'K' locking area
}
}
} else { // linda is cancelled
// do nothing and return lanes.cancel_error
kCancelError.pushKey(L_);
- pushed.emplace(1);
+ _pushed.emplace(1);
}
// must trigger any error after keeper state has been released
- return OptionalValue(pushed, L_, "tried to copy unsupported types");
+ return OptionalValue(_pushed, L_, "tried to copy unsupported types");
};
return Linda::ProtectedCall(L_, set);
}
@@ -512,16 +512,16 @@ LUAG_FUNC(linda_set)
*/
LUAG_FUNC(linda_count)
{
- auto count = [](lua_State* L_) {
- Linda* const linda{ ToLinda(L_, 1) };
+ auto _count = [](lua_State* L_) {
+ Linda* const _linda{ ToLinda(L_, 1) };
// make sure the keys are of a valid type
check_key_types(L_, 2, lua_gettop(L_));
- Keeper* const K{ linda->whichKeeper() };
- KeeperCallResult const pushed{ keeper_call(linda->U, K->L, KEEPER_API(count), L_, linda, 2) };
- return OptionalValue(pushed, L_, "tried to count an invalid key");
+ Keeper* const _K{ _linda->whichKeeper() };
+ KeeperCallResult const _pushed{ keeper_call(_K->L, KEEPER_API(count), L_, _linda, 2) };
+ return OptionalValue(_pushed, L_, "tried to count an invalid key");
};
- return Linda::ProtectedCall(L_, count);
+ return Linda::ProtectedCall(L_, _count);
}
// #################################################################################################
@@ -534,27 +534,27 @@ LUAG_FUNC(linda_count)
LUAG_FUNC(linda_get)
{
auto get = [](lua_State* L_) {
- Linda* const linda{ ToLinda(L_, 1) };
- lua_Integer const count{ luaL_optinteger(L_, 3, 1) };
- luaL_argcheck(L_, count >= 1, 3, "count should be >= 1");
+ Linda* const _linda{ ToLinda(L_, 1) };
+ lua_Integer const _count{ luaL_optinteger(L_, 3, 1) };
+ luaL_argcheck(L_, _count >= 1, 3, "count should be >= 1");
luaL_argcheck(L_, lua_gettop(L_) <= 3, 4, "too many arguments");
// make sure the key is of a valid type (throws an error if not the case)
check_key_types(L_, 2, 2);
- KeeperCallResult pushed;
- if (linda->cancelRequest == CancelRequest::None) {
- Keeper* const K{ linda->whichKeeper() };
- pushed = keeper_call(linda->U, K->L, KEEPER_API(get), L_, linda, 2);
- if (pushed.value_or(0) > 0) {
- keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - pushed.value(), LookupMode::FromKeeper);
+ KeeperCallResult _pushed;
+ if (_linda->cancelRequest == CancelRequest::None) {
+ Keeper* const _K{ _linda->whichKeeper() };
+ _pushed = keeper_call(_K->L, KEEPER_API(get), L_, _linda, 2);
+ if (_pushed.value_or(0) > 0) {
+ keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - _pushed.value(), LookupMode::FromKeeper);
}
} else { // linda is cancelled
// do nothing and return lanes.cancel_error
kCancelError.pushKey(L_);
- pushed.emplace(1);
+ _pushed.emplace(1);
}
// an error can be raised if we attempt to read an unregistered function
- return OptionalValue(pushed, L_, "tried to copy unsupported types");
+ return OptionalValue(_pushed, L_, "tried to copy unsupported types");
};
return Linda::ProtectedCall(L_, get);
}
@@ -569,8 +569,8 @@ LUAG_FUNC(linda_get)
*/
LUAG_FUNC(linda_limit)
{
- auto limit = [](lua_State* L_) {
- Linda* const linda{ ToLinda(L_, 1) };
+ auto _limit = [](lua_State* L_) {
+ Linda* const _linda{ ToLinda(L_, 1) };
// make sure we got 3 arguments: the linda, a key and a limit
luaL_argcheck(L_, lua_gettop(L_) == 3, 2, "wrong number of arguments");
// make sure we got a numeric limit
@@ -578,24 +578,24 @@ LUAG_FUNC(linda_limit)
// make sure the key is of a valid type
check_key_types(L_, 2, 2);
- KeeperCallResult pushed;
- if (linda->cancelRequest == CancelRequest::None) {
- Keeper* const K{ linda->whichKeeper() };
- pushed = keeper_call(linda->U, K->L, KEEPER_API(limit), L_, linda, 2);
- 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
- if (pushed.value() == 1) {
+ KeeperCallResult _pushed;
+ if (_linda->cancelRequest == CancelRequest::None) {
+ Keeper* const _K{ _linda->whichKeeper() };
+ _pushed = keeper_call(_K->L, KEEPER_API(limit), L_, _linda, 2);
+ 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
+ if (_pushed.value() == 1) {
LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TBOOLEAN && lua_toboolean(L_, -1) == 1);
- linda->readHappened.notify_all(); // To be done from within the 'K' locking area
+ _linda->readHappened.notify_all(); // To be done from within the 'K' locking area
}
} else { // linda is cancelled
// do nothing and return lanes.cancel_error
kCancelError.pushKey(L_);
- pushed.emplace(1);
+ _pushed.emplace(1);
}
// propagate pushed boolean if any
- return pushed.value();
+ return _pushed.value();
};
- return Linda::ProtectedCall(L_, limit);
+ return Linda::ProtectedCall(L_, _limit);
}
// #################################################################################################
@@ -607,23 +607,23 @@ LUAG_FUNC(linda_limit)
*/
LUAG_FUNC(linda_cancel)
{
- Linda* const linda{ ToLinda(L_, 1) };
- char const* who = luaL_optstring(L_, 2, "both");
+ Linda* const _linda{ ToLinda(L_, 1) };
+ char const* _who{ luaL_optstring(L_, 2, "both") };
// make sure we got 3 arguments: the linda, a key and a limit
luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments");
- linda->cancelRequest = CancelRequest::Soft;
- if (strcmp(who, "both") == 0) { // tell everyone writers to wake up
- linda->writeHappened.notify_all();
- linda->readHappened.notify_all();
- } else if (strcmp(who, "none") == 0) { // reset flag
- linda->cancelRequest = CancelRequest::None;
- } else if (strcmp(who, "read") == 0) { // tell blocked readers to wake up
- linda->writeHappened.notify_all();
- } else if (strcmp(who, "write") == 0) { // tell blocked writers to wake up
- linda->readHappened.notify_all();
+ _linda->cancelRequest = CancelRequest::Soft;
+ if (strcmp(_who, "both") == 0) { // tell everyone writers to wake up
+ _linda->writeHappened.notify_all();
+ _linda->readHappened.notify_all();
+ } else if (strcmp(_who, "none") == 0) { // reset flag
+ _linda->cancelRequest = CancelRequest::None;
+ } else if (strcmp(_who, "read") == 0) { // tell blocked readers to wake up
+ _linda->writeHappened.notify_all();
+ } else if (strcmp(_who, "write") == 0) { // tell blocked writers to wake up
+ _linda->readHappened.notify_all();
} else {
- raise_luaL_error(L_, "unknown wake hint '%s'", who);
+ raise_luaL_error(L_, "unknown wake hint '%s'", _who);
}
return 0;
}
@@ -642,8 +642,8 @@ LUAG_FUNC(linda_cancel)
*/
LUAG_FUNC(linda_deep)
{
- Linda* const linda{ ToLinda(L_, 1) };
- lua_pushlightuserdata(L_, linda); // just the address
+ Linda* const _linda{ ToLinda(L_, 1) };
+ lua_pushlightuserdata(L_, _linda); // just the address
return 1;
}
@@ -660,15 +660,15 @@ LUAG_FUNC(linda_deep)
template
[[nodiscard]] static int LindaToString(lua_State* L_, int idx_)
{
- Linda* const linda{ ToLinda(L_, idx_) };
- if (linda != nullptr) {
- char text[128];
- int len;
- if (linda->getName())
- len = sprintf(text, "Linda: %.*s", (int) sizeof(text) - 8, linda->getName());
+ Linda* const _linda{ ToLinda(L_, idx_) };
+ if (_linda != nullptr) {
+ char _text[128];
+ int _len;
+ if (_linda->getName())
+ _len = sprintf(_text, "Linda: %.*s", (int) sizeof(_text) - 8, _linda->getName());
else
- len = sprintf(text, "Linda: %p", linda);
- lua_pushlstring(L_, text, len);
+ _len = sprintf(_text, "Linda: %p", _linda);
+ lua_pushlstring(L_, _text, _len);
return 1;
}
return 0;
@@ -692,17 +692,17 @@ LUAG_FUNC(linda_tostring)
*/
LUAG_FUNC(linda_concat)
{ // L_: linda1? linda2?
- bool atLeastOneLinda{ false };
+ bool _atLeastOneLinda{ false };
// Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both.
if (LindaToString(L_, 1)) {
- atLeastOneLinda = true;
+ _atLeastOneLinda = true;
lua_replace(L_, 1);
}
if (LindaToString