aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/allocator.cpp2
-rw-r--r--src/allocator.hpp4
-rw-r--r--src/cancel.cpp140
-rw-r--r--src/compat.cpp50
-rw-r--r--src/compat.hpp138
-rw-r--r--src/deep.cpp28
-rw-r--r--src/intercopycontext.cpp189
-rw-r--r--src/keeper.cpp113
-rw-r--r--src/keeper.hpp3
-rw-r--r--src/lane.cpp424
-rw-r--r--src/lane.hpp10
-rw-r--r--src/lanes.cpp126
-rw-r--r--src/lanes.lua42
-rw-r--r--src/lanesconf.h8
-rw-r--r--src/linda.cpp169
-rw-r--r--src/linda.hpp24
-rw-r--r--src/lindafactory.cpp16
-rw-r--r--src/nameof.cpp34
-rw-r--r--src/state.cpp18
-rw-r--r--src/threading.cpp150
-rw-r--r--src/threading.hpp14
-rw-r--r--src/tools.cpp101
-rw-r--r--src/tools.hpp3
-rw-r--r--src/tracker.cpp2
-rw-r--r--src/uniquekey.hpp6
-rw-r--r--src/universe.cpp105
-rw-r--r--src/universe.hpp18
27 files changed, 1104 insertions, 833 deletions
diff --git a/src/allocator.cpp b/src/allocator.cpp
index 84acde5..243583b 100644
--- a/src/allocator.cpp
+++ b/src/allocator.cpp
@@ -35,7 +35,7 @@ namespace lanes
35{ 35{
36 AllocatorDefinition& AllocatorDefinition::Validated(lua_State* const L_, StackIndex const idx_) 36 AllocatorDefinition& AllocatorDefinition::Validated(lua_State* const L_, StackIndex const idx_)
37 { 37 {
38 lanes::AllocatorDefinition* const _def{ luaG_tofulluserdata<lanes::AllocatorDefinition>(L_, idx_) }; 38 lanes::AllocatorDefinition* const _def{ luaW_tofulluserdata<lanes::AllocatorDefinition>(L_, idx_) };
39 // raise an error and don't return if the full userdata at the specified index is not a valid AllocatorDefinition 39 // raise an error and don't return if the full userdata at the specified index is not a valid AllocatorDefinition
40 if (!_def) { 40 if (!_def) {
41 raise_luaL_error(L_, "Bad config.allocator function, provided value is not a userdata"); 41 raise_luaL_error(L_, "Bad config.allocator function, provided value is not a userdata");
diff --git a/src/allocator.hpp b/src/allocator.hpp
index c073391..b578f12 100644
--- a/src/allocator.hpp
+++ b/src/allocator.hpp
@@ -33,6 +33,8 @@ namespace lanes {
33 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception 33 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
34 static void operator delete([[maybe_unused]] void* const p_, [[maybe_unused]] lua_State* const L_) {} 34 static void operator delete([[maybe_unused]] void* const p_, [[maybe_unused]] lua_State* const L_) {}
35 35
36 ~AllocatorDefinition() = default;
37
36 AllocatorDefinition(lua_Alloc const allocF_, void* const allocUD_) noexcept 38 AllocatorDefinition(lua_Alloc const allocF_, void* const allocUD_) noexcept
37 : allocF{ allocF_ } 39 : allocF{ allocF_ }
38 , allocUD{ allocUD_ } 40 , allocUD{ allocUD_ }
@@ -64,7 +66,7 @@ namespace lanes {
64 [[nodiscard]] 66 [[nodiscard]]
65 lua_State* newState() const 67 lua_State* newState() const
66 { 68 {
67 return lua_newstate(allocF, allocUD); 69 return luaW_newstate(allocF, allocUD, luaL_makeseed(nullptr));
68 } 70 }
69 71
70 [[nodiscard]] 72 [[nodiscard]]
diff --git a/src/cancel.cpp b/src/cancel.cpp
index 5bba9fc..6812b0d 100644
--- a/src/cancel.cpp
+++ b/src/cancel.cpp
@@ -38,6 +38,53 @@ THE SOFTWARE.
38#include "debugspew.hpp" 38#include "debugspew.hpp"
39#include "lane.hpp" 39#include "lane.hpp"
40 40
41namespace {
42 namespace local {
43
44 // #########################################################################################
45 // #########################################################################################
46
47 [[nodiscard]]
48 static std::optional<CancelOp> WhichCancelOp(std::string_view const& opString_)
49 {
50 if (opString_ == "soft") {
51 return std::make_optional<CancelOp>(CancelRequest::Soft, LuaHookMask::None);
52 } else if (opString_ == "hard") {
53 return std::make_optional<CancelOp>(CancelRequest::Hard, LuaHookMask::None);
54 } else if (opString_ == "call") {
55 return std::make_optional<CancelOp>(CancelRequest::Hard, LuaHookMask::Call);
56 } else if (opString_ == "ret") {
57 return std::make_optional<CancelOp>(CancelRequest::Hard, LuaHookMask::Ret);
58 } else if (opString_ == "line") {
59 return std::make_optional<CancelOp>(CancelRequest::Hard, LuaHookMask::Line);
60 } else if (opString_ == "count") {
61 return std::make_optional<CancelOp>(CancelRequest::Hard, LuaHookMask::Count);
62 } else if (opString_ == "all") {
63 return std::make_optional<CancelOp>(CancelRequest::Hard, LuaHookMask::All);
64 }
65 return std::nullopt;
66 }
67
68 // #########################################################################################
69
70 [[nodiscard]]
71 static CancelOp WhichCancelOp(lua_State* const L_, StackIndex const idx_)
72 {
73 if (luaW_type(L_, idx_) == LuaType::STRING) {
74 std::string_view const _str{ luaW_tostring(L_, idx_) };
75 auto const _op{ WhichCancelOp(_str) };
76 lua_remove(L_, idx_); // argument is processed, remove it
77 if (!_op.has_value()) {
78 raise_luaL_error(L_, "Invalid cancel operation '%s'", _str.data());
79 }
80 return _op.value();
81 }
82 return CancelOp{ CancelRequest::Hard, LuaHookMask::None };
83 }
84
85 } // namespace local
86} // namespace
87
41// ################################################################################################# 88// #################################################################################################
42// ################################################################################################# 89// #################################################################################################
43 90
@@ -60,66 +107,6 @@ CancelRequest CheckCancelRequest(lua_State* const L_)
60 107
61// ################################################################################################# 108// #################################################################################################
62// ################################################################################################# 109// #################################################################################################
63
64//---
65// = lane_cancel( lane_ud [,timeout_secs=0.0] [,wake_lindas_bool=false] )
66//
67// The originator thread asking us specifically to cancel the other thread.
68//
69// 'timeout': <0: wait forever, until the lane is finished
70// 0.0: just signal it to cancel, no time waited
71// >0: time to wait for the lane to detect cancellation
72//
73// 'wake_lindas_bool': if true, signal any linda the thread is waiting on
74// instead of waiting for its timeout (if any)
75//
76// Returns: true if the lane was already finished (Done/Error/Cancelled) or if we
77// managed to cancel it.
78// false if the cancellation timed out, or a kill was needed.
79//
80
81// #################################################################################################
82// #################################################################################################
83
84static std::optional<CancelOp> WhichCancelOp(std::string_view const& opString_)
85{
86 if (opString_ == "soft") {
87 return std::make_optional<CancelOp>(CancelRequest::Soft, LuaHookMask::None);
88 } else if (opString_ == "hard") {
89 return std::make_optional<CancelOp>(CancelRequest::Hard, LuaHookMask::None);
90 } else if (opString_== "call") {
91 return std::make_optional<CancelOp>(CancelRequest::Hard, LuaHookMask::Call);
92 } else if (opString_ == "ret") {
93 return std::make_optional<CancelOp>(CancelRequest::Hard, LuaHookMask::Ret);
94 } else if (opString_ == "line") {
95 return std::make_optional<CancelOp>(CancelRequest::Hard, LuaHookMask::Line);
96 } else if (opString_ == "count") {
97 return std::make_optional<CancelOp>(CancelRequest::Hard, LuaHookMask::Count);
98 } else if (opString_ == "all") {
99 return std::make_optional<CancelOp>(CancelRequest::Hard, LuaHookMask::All);
100 }
101 return std::nullopt;
102}
103
104// #################################################################################################
105
106[[nodiscard]]
107static CancelOp WhichCancelOp(lua_State* const L_, StackIndex const idx_)
108{
109 if (luaG_type(L_, idx_) == LuaType::STRING) {
110 std::string_view const _str{ luaG_tostring(L_, idx_) };
111 auto const _op{ WhichCancelOp(_str) };
112 lua_remove(L_, idx_); // argument is processed, remove it
113 if (!_op.has_value()) {
114 raise_luaL_error(L_, "Invalid cancel operation '%s'", _str.data());
115 }
116 return _op.value();
117 }
118 return CancelOp{ CancelRequest::Hard, LuaHookMask::None };
119}
120
121// #################################################################################################
122// #################################################################################################
123// ######################################### Lua API ############################################### 110// ######################################### Lua API ###############################################
124// ################################################################################################# 111// #################################################################################################
125// ################################################################################################# 112// #################################################################################################
@@ -133,24 +120,45 @@ static CancelOp WhichCancelOp(lua_State* const L_, StackIndex const idx_)
133LUAG_FUNC(cancel_test) 120LUAG_FUNC(cancel_test)
134{ 121{
135 CancelRequest const _test{ CheckCancelRequest(L_) }; 122 CancelRequest const _test{ CheckCancelRequest(L_) };
136 lua_pushboolean(L_, _test != CancelRequest::None); 123 if (_test == CancelRequest::None) {
124 lua_pushboolean(L_, 0);
125 } else {
126 luaW_pushstring(L_, (_test == CancelRequest::Soft) ? "soft" : "hard");
127 }
137 return 1; 128 return 1;
138} 129}
139 130
140// ################################################################################################# 131// #################################################################################################
141 132
133//---
134// = lane_cancel( lane_ud [,timeout_secs=0.0] [,wake_lindas_bool=false] )
135//
136// The originator thread asking us specifically to cancel the other thread.
137//
138// 'timeout': <0: wait forever, until the lane is finished
139// 0.0: just signal it to cancel, no time waited
140// >0: time to wait for the lane to detect cancellation
141//
142// 'wake_lindas_bool': if true, signal any linda the thread is waiting on
143// instead of waiting for its timeout (if any)
144//
145// Returns: true if the lane was already finished (Done/Error/Cancelled) or if we
146// managed to cancel it.
147// false if the cancellation timed out, or a kill was needed.
148//
149
142// bool[,reason] = lane_h:cancel( [cancel_op, hookcount] [, timeout] [, wake_lane]) 150// bool[,reason] = lane_h:cancel( [cancel_op, hookcount] [, timeout] [, wake_lane])
143LUAG_FUNC(lane_cancel) 151LUAG_FUNC(lane_cancel)
144{ 152{
145 Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) }; // L_: lane [cancel_op, hookcount] [, timeout] [, wake_lane] 153 Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) }; // L_: lane [cancel_op, hookcount] [, timeout] [, wake_lane]
146 CancelOp const _op{ WhichCancelOp(L_, StackIndex{ 2 }) }; // L_: lane [hookcount] [, timeout] [, wake_lane] 154 CancelOp const _op{ local::WhichCancelOp(L_, StackIndex{ 2 }) }; // L_: lane [hookcount] [, timeout] [, wake_lane]
147 155
148 int const _hook_count{ std::invoke([_op, L_]() { 156 int const _hook_count{ std::invoke([_op, L_]() {
149 if (_op.hookMask == LuaHookMask::None) { 157 if (_op.hookMask == LuaHookMask::None) {
150 // the caller shouldn't have provided a hook count in that case 158 // the caller shouldn't have provided a hook count in that case
151 return 0; 159 return 0;
152 } 160 }
153 if (luaG_type(L_, StackIndex{ 2 }) != LuaType::NUMBER) { 161 if (luaW_type(L_, StackIndex{ 2 }) != LuaType::NUMBER) {
154 raise_luaL_error(L_, "Hook count expected"); 162 raise_luaL_error(L_, "Hook count expected");
155 } 163 }
156 auto const _hook_count{ static_cast<int>(lua_tointeger(L_, 2)) }; 164 auto const _hook_count{ static_cast<int>(lua_tointeger(L_, 2)) };
@@ -162,7 +170,7 @@ LUAG_FUNC(lane_cancel)
162 }) }; 170 }) };
163 171
164 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; 172 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
165 if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion 173 if (luaW_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion
166 lua_Duration const duration{ lua_tonumber(L_, 2) }; 174 lua_Duration const duration{ lua_tonumber(L_, 2) };
167 if (duration.count() >= 0.0) { 175 if (duration.count() >= 0.0) {
168 _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); 176 _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration);
@@ -178,7 +186,7 @@ LUAG_FUNC(lane_cancel)
178 WakeLane _wake_lane{ (_op.mode == CancelRequest::Hard) ? WakeLane::Yes : WakeLane::No }; 186 WakeLane _wake_lane{ (_op.mode == CancelRequest::Hard) ? WakeLane::Yes : WakeLane::No };
179 if (lua_gettop(L_) >= 2) { 187 if (lua_gettop(L_) >= 2) {
180 if (!lua_isboolean(L_, 2)) { 188 if (!lua_isboolean(L_, 2)) {
181 raise_luaL_error(L_, "Boolean expected for wake_lane argument, got %s", luaG_typename(L_, StackIndex{ 2 }).data()); 189 raise_luaL_error(L_, "Boolean expected for wake_lane argument, got %s", luaW_typename(L_, StackIndex{ 2 }).data());
182 } 190 }
183 _wake_lane = lua_toboolean(L_, 2) ? WakeLane::Yes : WakeLane::No; 191 _wake_lane = lua_toboolean(L_, 2) ? WakeLane::Yes : WakeLane::No;
184 lua_remove(L_, 2); // argument is processed, remove it // L_: lane 192 lua_remove(L_, 2); // argument is processed, remove it // L_: lane
@@ -198,7 +206,7 @@ LUAG_FUNC(lane_cancel)
198 206
199 case CancelResult::Timeout: 207 case CancelResult::Timeout:
200 lua_pushboolean(L_, 0); // L_: false 208 lua_pushboolean(L_, 0); // L_: false
201 luaG_pushstring(L_, "timeout"); // L_: false "timeout" 209 luaW_pushstring(L_, "timeout"); // L_: false "timeout"
202 break; 210 break;
203 211
204 case CancelResult::Cancelled: 212 case CancelResult::Cancelled:
diff --git a/src/compat.cpp b/src/compat.cpp
index 7e90142..c833480 100644
--- a/src/compat.cpp
+++ b/src/compat.cpp
@@ -5,10 +5,10 @@
5 5
6// ################################################################################################# 6// #################################################################################################
7 7
8UserValueCount luaG_getalluservalues(lua_State* const L_, StackIndex const idx_) 8UserValueCount luaW_getalluservalues(lua_State* const L_, StackIndex const idx_)
9{ 9{
10 STACK_CHECK_START_REL(L_, 0); 10 STACK_CHECK_START_REL(L_, 0);
11 StackIndex const _idx{ luaG_absindex(L_, idx_) }; 11 StackIndex const _idx{ luaW_absindex(L_, idx_) };
12 UserValueIndex _nuv{ 0 }; 12 UserValueIndex _nuv{ 0 };
13 do { 13 do {
14 // we don't know how many uservalues we are going to extract, there might be a lot... 14 // we don't know how many uservalues we are going to extract, there might be a lot...
@@ -24,15 +24,15 @@ UserValueCount luaG_getalluservalues(lua_State* const L_, StackIndex const idx_)
24// ################################################################################################# 24// #################################################################################################
25 25
26// a small helper to obtain a module's table from the registry instead of relying on the presence of _G["<name>"] 26// a small helper to obtain a module's table from the registry instead of relying on the presence of _G["<name>"]
27LuaType luaG_getmodule(lua_State* const L_, std::string_view const& name_) 27LuaType luaW_getmodule(lua_State* const L_, std::string_view const& name_)
28{ 28{
29 STACK_CHECK_START_REL(L_, 0); 29 STACK_CHECK_START_REL(L_, 0);
30 LuaType _type{ luaG_getfield(L_, kIdxRegistry, LUA_LOADED_TABLE) }; // L_: _R._LOADED|nil 30 LuaType _type{ luaW_getfield(L_, kIdxRegistry, LUA_LOADED_TABLE) }; // L_: _R._LOADED|nil
31 if (_type != LuaType::TABLE) { // L_: _R._LOADED|nil 31 if (_type != LuaType::TABLE) { // L_: _R._LOADED|nil
32 STACK_CHECK(L_, 1); 32 STACK_CHECK(L_, 1);
33 return _type; 33 return _type;
34 } 34 }
35 _type = luaG_getfield(L_, kIdxTop, name_); // L_: _R._LOADED {module}|nil 35 _type = luaW_getfield(L_, kIdxTop, name_); // L_: _R._LOADED {module}|nil
36 lua_remove(L_, -2); // L_: {module}|nil 36 lua_remove(L_, -2); // L_: {module}|nil
37 STACK_CHECK(L_, 1); 37 STACK_CHECK(L_, 1);
38 return _type; 38 return _type;
@@ -52,7 +52,7 @@ int luaL_getsubtable(lua_State* const L_, StackIndex const idx_, char const* fna
52 return 1; /* table already there */ 52 return 1; /* table already there */
53 } else { 53 } else {
54 lua_pop(L_, 1); /* remove previous result */ 54 lua_pop(L_, 1); /* remove previous result */
55 StackIndex const _absidx{ luaG_absindex(L_, idx_) }; 55 StackIndex const _absidx{ luaW_absindex(L_, idx_) };
56 lua_newtable(L_); 56 lua_newtable(L_);
57 lua_pushvalue(L_, -1); /* copy to be left at top */ 57 lua_pushvalue(L_, -1); /* copy to be left at top */
58 lua_setfield(L_, _absidx, fname_); /* assign new table to field */ 58 lua_setfield(L_, _absidx, fname_); /* assign new table to field */
@@ -149,3 +149,41 @@ int lua_setiuservalue(lua_State* const L_, StackIndex const idx_, UserValueIndex
149} 149}
150 150
151#endif // LUA_VERSION_NUM 151#endif // LUA_VERSION_NUM
152
153// #################################################################################################
154
155#if LUA_VERSION_NUM < 505
156
157#include <time.h>
158
159/* Size for the buffer, in bytes */
160#define BUFSEEDB (sizeof(void*) + sizeof(time_t))
161
162/* Size for the buffer in int's, rounded up */
163#define BUFSEED ((BUFSEEDB + sizeof(int) - 1) / sizeof(int))
164
165/*
166** Copy the contents of variable 'v' into the buffer pointed by 'b'.
167** (The '&b[0]' disguises 'b' to fix an absurd warning from clang.)
168*/
169#define addbuff(b, v) (memcpy(&b[0], &(v), sizeof(v)), b += sizeof(v))
170
171// Copied from Lua 5.5 lauxlib.c
172unsigned int luaL_makeseed(lua_State*)
173{
174 unsigned int buff[BUFSEED];
175 unsigned int res;
176 unsigned int i;
177 time_t t = time(nullptr);
178 char* b = (char*) buff;
179 addbuff(b, b); /* local variable's address */
180 addbuff(b, t); /* time */
181 /* fill (rare but possible) remain of the buffer with zeros */
182 memset(b, 0, sizeof(buff) - BUFSEEDB);
183 res = buff[0];
184 for (i = 1; i < BUFSEED; i++)
185 res ^= (res >> 3) + (res << 7) + buff[i];
186 return res;
187}
188
189#endif // LUA_VERSION_NUM < 505
diff --git a/src/compat.hpp b/src/compat.hpp
index 9a8dedf..4dc7433 100644
--- a/src/compat.hpp
+++ b/src/compat.hpp
@@ -5,7 +5,6 @@
5 5
6// try to detect if we are building against LuaJIT or MoonJIT 6// try to detect if we are building against LuaJIT or MoonJIT
7#if defined(LUA_JITLIBNAME) 7#if defined(LUA_JITLIBNAME)
8#include "luajit.h"
9#if (defined(__x86_64__) || defined(_M_X64) || defined(__LP64__)) 8#if (defined(__x86_64__) || defined(_M_X64) || defined(__LP64__))
10#define LUAJIT_FLAVOR() 64 9#define LUAJIT_FLAVOR() 64
11#else // 64 bits 10#else // 64 bits
@@ -21,7 +20,7 @@
21#endif // LUA_OK 20#endif // LUA_OK
22 21
23#ifndef LUA_ERRGCMM 22#ifndef LUA_ERRGCMM
24#define LUA_ERRGCMM 666 // doesn't exist in Lua 5.1 and Lua 5.4, we don't care about the actual value 23#define LUA_ERRGCMM 666 // doesn't exist in Lua 5.1 and Lua 5.4/5.5, we don't care about the actual value
25#endif // LUA_ERRGCMM 24#endif // LUA_ERRGCMM
26 25
27 26
@@ -29,7 +28,7 @@
29#define LUA_LOADED_TABLE "_LOADED" // doesn't exist before Lua 5.3 28#define LUA_LOADED_TABLE "_LOADED" // doesn't exist before Lua 5.3
30#endif // LUA_LOADED_TABLE 29#endif // LUA_LOADED_TABLE
31 30
32// code is now preferring Lua 5.4 API 31// code is now preferring Lua 5.5 API
33 32
34// ################################################################################################# 33// #################################################################################################
35 34
@@ -76,18 +75,6 @@ int luaL_getsubtable(lua_State* L_, StackIndex idx_, char const* fname_);
76 75
77// ################################################################################################# 76// #################################################################################################
78 77
79// wrap Lua 5.3 calls under Lua 5.1 API when it is simpler that way
80#if LUA_VERSION_NUM == 503
81
82inline int luaL_optint(lua_State* L_, int n_, lua_Integer d_)
83{
84 return static_cast<int>(luaL_optinteger(L_, n_, d_));
85}
86
87#endif // LUA_VERSION_NUM == 503
88
89// #################################################################################################
90
91#if LUA_VERSION_NUM < 504 78#if LUA_VERSION_NUM < 504
92 79
93void* lua_newuserdatauv(lua_State* L_, size_t sz_, UserValueCount nuvalue_); 80void* lua_newuserdatauv(lua_State* L_, size_t sz_, UserValueCount nuvalue_);
@@ -100,15 +87,11 @@ int lua_setiuservalue(lua_State* L_, StackIndex idx_, UserValueIndex n_);
100 87
101// ################################################################################################# 88// #################################################################################################
102 89
103// wrap Lua 5.4 calls under Lua 5.1 API when it is simpler that way 90#if LUA_VERSION_NUM < 505
104#if LUA_VERSION_NUM == 504
105 91
106inline int luaL_optint(lua_State* L_, StackIndex n_, lua_Integer d_) 92unsigned int luaL_makeseed(lua_State*);
107{
108 return static_cast<int>(luaL_optinteger(L_, n_, d_));
109}
110 93
111#endif // LUA_VERSION_NUM == 504 94#endif // LUA_VERSION_NUM < 505
112 95
113// ################################################################################################# 96// #################################################################################################
114 97
@@ -136,14 +119,14 @@ inline constexpr LuaError ToLuaError(int const rc_)
136 119
137// break lexical order for that one because it's needed below 120// break lexical order for that one because it's needed below
138[[nodiscard]] 121[[nodiscard]]
139inline LuaType luaG_type(lua_State* const L_, StackIndex const idx_) 122inline LuaType luaW_type(lua_State* const L_, StackIndex const idx_)
140{ 123{
141 return static_cast<LuaType>(lua_type(L_, idx_)); 124 return static_cast<LuaType>(lua_type(L_, idx_));
142} 125}
143 126
144// ################################################################################################# 127// #################################################################################################
145// ################################################################################################# 128// #################################################################################################
146// All the compatibility wrappers we expose start with luaG_ 129// All the compatibility wrappers we expose start with luaW_
147// ################################################################################################# 130// #################################################################################################
148// ################################################################################################# 131// #################################################################################################
149 132
@@ -152,7 +135,7 @@ inline LuaType luaG_type(lua_State* const L_, StackIndex const idx_)
152 135
153// a replacement of lua_tolstring 136// a replacement of lua_tolstring
154[[nodiscard]] 137[[nodiscard]]
155inline std::string_view luaG_tostring(lua_State* const L_, StackIndex const idx_) 138inline std::string_view luaW_tostring(lua_State* const L_, StackIndex const idx_)
156{ 139{
157 size_t _len{ 0 }; 140 size_t _len{ 0 };
158 char const* _str{ lua_tolstring(L_, idx_, &_len) }; 141 char const* _str{ lua_tolstring(L_, idx_, &_len) };
@@ -160,7 +143,7 @@ inline std::string_view luaG_tostring(lua_State* const L_, StackIndex const idx_
160} 143}
161 144
162[[nodiscard]] 145[[nodiscard]]
163inline std::string_view luaG_checkstring(lua_State* const L_, StackIndex const idx_) 146inline std::string_view luaW_checkstring(lua_State* const L_, StackIndex const idx_)
164{ 147{
165 size_t _len{ 0 }; 148 size_t _len{ 0 };
166 char const* _str{ luaL_checklstring(L_, idx_, &_len) }; 149 char const* _str{ luaL_checklstring(L_, idx_, &_len) };
@@ -168,7 +151,7 @@ inline std::string_view luaG_checkstring(lua_State* const L_, StackIndex const i
168} 151}
169 152
170[[nodiscard]] 153[[nodiscard]]
171inline std::string_view luaG_optstring(lua_State* const L_, StackIndex const idx_, std::string_view const& default_) 154inline std::string_view luaW_optstring(lua_State* const L_, StackIndex const idx_, std::string_view const& default_)
172{ 155{
173 if (lua_isnoneornil(L_, idx_)) { 156 if (lua_isnoneornil(L_, idx_)) {
174 return default_; 157 return default_;
@@ -179,13 +162,13 @@ inline std::string_view luaG_optstring(lua_State* const L_, StackIndex const idx
179} 162}
180 163
181template <typename... EXTRA> 164template <typename... EXTRA>
182inline std::string_view luaG_pushstring(lua_State* const L_, std::string_view const& str_, EXTRA&&... extra_) 165inline std::string_view luaW_pushstring(lua_State* const L_, std::string_view const& str_, EXTRA&&... extra_)
183{ 166{
184 if constexpr (sizeof...(EXTRA) == 0) { 167 if constexpr (sizeof...(EXTRA) == 0) {
185 if constexpr (LUA_VERSION_NUM == 501) { 168 if constexpr (LUA_VERSION_NUM == 501) {
186 // lua_pushlstring doesn't return a value in Lua 5.1 169 // lua_pushlstring doesn't return a value in Lua 5.1
187 lua_pushlstring(L_, str_.data(), str_.size()); 170 lua_pushlstring(L_, str_.data(), str_.size());
188 return luaG_tostring(L_, kIdxTop); 171 return luaW_tostring(L_, kIdxTop);
189 } else { 172 } else {
190 return std::string_view{ lua_pushlstring(L_, str_.data(), str_.size()), str_.size() }; 173 return std::string_view{ lua_pushlstring(L_, str_.data(), str_.size()), str_.size() };
191 } 174 }
@@ -198,7 +181,7 @@ inline std::string_view luaG_pushstring(lua_State* const L_, std::string_view co
198// ################################################################################################# 181// #################################################################################################
199 182
200// use this in place of lua_absindex to save a function call 183// use this in place of lua_absindex to save a function call
201inline StackIndex luaG_absindex(lua_State* const L_, StackIndex const idx_) 184inline StackIndex luaW_absindex(lua_State* const L_, StackIndex const idx_)
202{ 185{
203 return StackIndex{ (idx_ >= 0 || idx_ <= kIdxRegistry) ? idx_ : StackIndex{ lua_gettop(L_) + idx_ + 1 } }; 186 return StackIndex{ (idx_ >= 0 || idx_ <= kIdxRegistry) ? idx_ : StackIndex{ lua_gettop(L_) + idx_ + 1 } };
204} 187}
@@ -227,14 +210,14 @@ static inline int WrapLuaDump(LUA_DUMP f_, lua_State* const L_, lua_Writer const
227 210
228// ------------------------------------------------------------------------------------------------- 211// -------------------------------------------------------------------------------------------------
229 212
230static inline int luaG_dump(lua_State* const L_, lua_Writer const writer_, void* const data_, int const strip_) 213static inline int luaW_dump(lua_State* const L_, lua_Writer const writer_, void* const data_, int const strip_)
231{ 214{
232 return WrapLuaDump(lua_dump, L_, writer_, data_, strip_); 215 return WrapLuaDump(lua_dump, L_, writer_, data_, strip_);
233} 216}
234 217
235// ################################################################################################# 218// #################################################################################################
236 219
237UserValueCount luaG_getalluservalues(lua_State* L_, StackIndex idx_); 220UserValueCount luaW_getalluservalues(lua_State* L_, StackIndex idx_);
238 221
239// ################################################################################################# 222// #################################################################################################
240 223
@@ -272,7 +255,7 @@ static inline int WrapLuaGetField(LUA_GETFIELD f_, lua_State* const L_, StackInd
272// ------------------------------------------------------------------------------------------------- 255// -------------------------------------------------------------------------------------------------
273 256
274[[nodiscard]] 257[[nodiscard]]
275static inline LuaType luaG_getfield(lua_State* const L_, StackIndex const idx_, std::string_view const& name_) 258static inline LuaType luaW_getfield(lua_State* const L_, StackIndex const idx_, std::string_view const& name_)
276{ 259{
277 return static_cast<LuaType>(WrapLuaGetField(lua_getfield, L_, idx_, name_)); 260 return static_cast<LuaType>(WrapLuaGetField(lua_getfield, L_, idx_, name_));
278} 261}
@@ -280,21 +263,62 @@ static inline LuaType luaG_getfield(lua_State* const L_, StackIndex const idx_,
280// ################################################################################################# 263// #################################################################################################
281 264
282[[nodiscard]] 265[[nodiscard]]
283LuaType luaG_getmodule(lua_State* L_, std::string_view const& name_); 266LuaType luaW_getmodule(lua_State* L_, std::string_view const& name_);
267
268// #################################################################################################
269
270template <typename LUA_NEWSTATE>
271concept RequiresOldLuaNewState = requires(LUA_NEWSTATE f_)
272{
273 {
274 f_(nullptr, 0)
275 } -> std::same_as<lua_State*>;
276};
277
278template <RequiresOldLuaNewState LUA_NEWSTATE>
279static inline lua_State* WrapLuaNewState(LUA_NEWSTATE const lua_newstate_, lua_Alloc const allocf_, void* const ud_, [[maybe_unused]] unsigned int const seed_)
280{
281 // until Lua 5.5, lua_newstate has only 2 parameters
282 return lua_newstate_(allocf_, ud_);
283}
284
285// -------------------------------------------------------------------------------------------------
286
287template <typename LUA_NEWSTATE>
288concept RequiresNewLuaNewState = requires(LUA_NEWSTATE f_)
289{
290 {
291 f_(nullptr, nullptr, 0)
292 } -> std::same_as<lua_State*>;
293};
294
295template <RequiresNewLuaNewState LUA_NEWSTATE>
296static inline lua_State* WrapLuaNewState(LUA_NEWSTATE const lua_newstate_, lua_Alloc const allocf_, void* const ud_, unsigned int const seed_)
297{
298 // starting with Lua 5.5, lua_newstate has 3 parameters
299 return lua_newstate_(allocf_, ud_, seed_);
300}
301
302// -------------------------------------------------------------------------------------------------
303
304static inline lua_State* luaW_newstate(lua_Alloc const allocf_, void* const ud_, unsigned int const seed_)
305{
306 return WrapLuaNewState(lua_newstate, allocf_, ud_, seed_);
307}
284 308
285// ################################################################################################# 309// #################################################################################################
286 310
287template<typename ENUM> 311template<typename ENUM>
288requires std::is_enum_v<ENUM> 312requires std::is_enum_v<ENUM>
289[[nodiscard]] 313[[nodiscard]]
290ENUM luaG_optenum(lua_State* const L_, StackIndex const idx_, ENUM const def_) 314ENUM luaW_optenum(lua_State* const L_, StackIndex const idx_, ENUM const def_)
291{ 315{
292 return static_cast<ENUM>(luaL_optinteger(L_, idx_, static_cast<std::underlying_type_t<ENUM>>(def_))); 316 return static_cast<ENUM>(luaL_optinteger(L_, idx_, static_cast<std::underlying_type_t<ENUM>>(def_)));
293} 317}
294 318
295// ################################################################################################# 319// #################################################################################################
296 320
297inline void luaG_registerlibfuncs(lua_State* const L_, luaL_Reg const* funcs_) 321inline void luaW_registerlibfuncs(lua_State* const L_, luaL_Reg const* funcs_)
298{ 322{
299 // fake externs to make clang happy... 323 // fake externs to make clang happy...
300 extern void luaL_register(lua_State*, char const*, luaL_Reg const*); // Lua 5.1 324 extern void luaL_register(lua_State*, char const*, luaL_Reg const*); // Lua 5.1
@@ -353,7 +377,7 @@ static inline int WrapLuaResume(LUA_RESUME const lua_resume_, lua_State* const L
353// ------------------------------------------------------------------------------------------------- 377// -------------------------------------------------------------------------------------------------
354 378
355[[nodiscard]] 379[[nodiscard]]
356static inline LuaError luaG_resume(lua_State* const L_, lua_State* const from_, int const nargs_, int* const nresults_) 380static inline LuaError luaW_resume(lua_State* const L_, lua_State* const from_, int const nargs_, int* const nresults_)
357{ 381{
358 return ToLuaError(WrapLuaResume(lua_resume, L_, from_, nargs_, nresults_)); 382 return ToLuaError(WrapLuaResume(lua_resume, L_, from_, nargs_, nresults_));
359} 383}
@@ -364,11 +388,11 @@ template <typename LUA_RAWGET>
364concept RequiresOldLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<void>; }; 388concept RequiresOldLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<void>; };
365 389
366template <RequiresOldLuaRawget LUA_RAWGET> 390template <RequiresOldLuaRawget LUA_RAWGET>
367static inline LuaType WrapLuaRawget(LUA_RAWGET lua_rawget_, lua_State* const L_, StackIndex const idx_) 391static inline LuaType WrapLuaRawget(LUA_RAWGET const lua_rawget_, lua_State* const L_, StackIndex const idx_)
368{ 392{
369 // until Lua 5.3, lua_rawget -> void 393 // until Lua 5.3, lua_rawget -> void
370 lua_rawget_(L_, idx_); 394 lua_rawget_(L_, idx_);
371 return luaG_type(L_, kIdxTop); 395 return luaW_type(L_, kIdxTop);
372} 396}
373 397
374// ------------------------------------------------------------------------------------------------- 398// -------------------------------------------------------------------------------------------------
@@ -377,7 +401,7 @@ template <typename LUA_RAWGET>
377concept RequiresNewLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<int>; }; 401concept RequiresNewLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<int>; };
378 402
379template <RequiresNewLuaRawget LUA_RAWGET> 403template <RequiresNewLuaRawget LUA_RAWGET>
380static inline LuaType WrapLuaRawget(LUA_RAWGET lua_rawget_, lua_State* const L_, StackIndex const idx_) 404static inline LuaType WrapLuaRawget(LUA_RAWGET const lua_rawget_, lua_State* const L_, StackIndex const idx_)
381{ 405{
382 // starting with Lua 5.3, lua_rawget -> int (the type of the extracted value) 406 // starting with Lua 5.3, lua_rawget -> int (the type of the extracted value)
383 return static_cast<LuaType>(lua_rawget_(L_, idx_)); 407 return static_cast<LuaType>(lua_rawget_(L_, idx_));
@@ -385,42 +409,42 @@ static inline LuaType WrapLuaRawget(LUA_RAWGET lua_rawget_, lua_State* const L_,
385 409
386// ------------------------------------------------------------------------------------------------- 410// -------------------------------------------------------------------------------------------------
387 411
388static inline LuaType luaG_rawget(lua_State* const L_, StackIndex const idx_) 412static inline LuaType luaW_rawget(lua_State* const L_, StackIndex const idx_)
389{ 413{
390 return WrapLuaRawget(lua_rawget, L_, idx_); 414 return WrapLuaRawget(lua_rawget, L_, idx_);
391} 415}
392 416
393// ################################################################################################# 417// #################################################################################################
394 418
395static inline LuaType luaG_rawgetfield(lua_State* const L_, StackIndex const idx_, std::string_view const& name_) 419static inline LuaType luaW_rawgetfield(lua_State* const L_, StackIndex const idx_, std::string_view const& name_)
396{ 420{
397 auto const _absIdx{ luaG_absindex(L_, idx_) }; 421 auto const _absIdx{ luaW_absindex(L_, idx_) };
398 luaG_pushstring(L_, name_); // L_: ... t ... name_ 422 luaW_pushstring(L_, name_); // L_: ... t ... name_
399 lua_rawget(L_, _absIdx); // L_: ... t ... <field> 423 lua_rawget(L_, _absIdx); // L_: ... t ... <field>
400 return luaG_type(L_, kIdxTop); 424 return luaW_type(L_, kIdxTop);
401} 425}
402 426
403// ################################################################################################# 427// #################################################################################################
404 428
405template <size_t N> 429template <size_t N>
406static inline void luaG_newlib(lua_State* const L_, luaL_Reg const (&funcs_)[N]) 430static inline void luaW_newlib(lua_State* const L_, luaL_Reg const (&funcs_)[N])
407{ 431{
408 lua_createtable(L_, 0, N - 1); 432 lua_createtable(L_, 0, N - 1);
409 luaG_registerlibfuncs(L_, funcs_); 433 luaW_registerlibfuncs(L_, funcs_);
410} 434}
411 435
412// ################################################################################################# 436// #################################################################################################
413 437
414template <typename T> 438template <typename T>
415[[nodiscard]] 439[[nodiscard]]
416T* luaG_newuserdatauv(lua_State* const L_, UserValueCount const nuvalue_) 440T* luaW_newuserdatauv(lua_State* const L_, UserValueCount const nuvalue_)
417{ 441{
418 return static_cast<T*>(lua_newuserdatauv(L_, sizeof(T), nuvalue_)); 442 return static_cast<T*>(lua_newuserdatauv(L_, sizeof(T), nuvalue_));
419} 443}
420 444
421// ################################################################################################# 445// #################################################################################################
422 446
423inline void luaG_pushglobaltable(lua_State* const L_) 447inline void luaW_pushglobaltable(lua_State* const L_)
424{ 448{
425#ifdef LUA_GLOBALSINDEX // All flavors of Lua 5.1 449#ifdef LUA_GLOBALSINDEX // All flavors of Lua 5.1
426 ::lua_pushvalue(L_, LUA_GLOBALSINDEX); 450 ::lua_pushvalue(L_, LUA_GLOBALSINDEX);
@@ -431,15 +455,15 @@ inline void luaG_pushglobaltable(lua_State* const L_)
431 455
432// ################################################################################################# 456// #################################################################################################
433 457
434inline void luaG_setfield(lua_State* const L_, StackIndex const idx_, char const* const k_) = delete; 458inline void luaW_setfield(lua_State* const L_, StackIndex const idx_, char const* const k_) = delete;
435inline void luaG_setfield(lua_State* const L_, StackIndex const idx_, std::string_view const& k_) 459inline void luaW_setfield(lua_State* const L_, StackIndex const idx_, std::string_view const& k_)
436{ 460{
437 lua_setfield(L_, idx_, k_.data()); 461 lua_setfield(L_, idx_, k_.data());
438} 462}
439 463
440// ################################################################################################# 464// #################################################################################################
441 465
442inline void luaG_setmetatable(lua_State* const L_, std::string_view const& tname_) 466inline void luaW_setmetatable(lua_State* const L_, std::string_view const& tname_)
443{ 467{
444 // fake externs to make clang happy... 468 // fake externs to make clang happy...
445 if constexpr (LUA_VERSION_NUM > 501) { 469 if constexpr (LUA_VERSION_NUM > 501) {
@@ -456,7 +480,7 @@ inline void luaG_setmetatable(lua_State* const L_, std::string_view const& tname
456// a small helper to extract a full userdata pointer from the stack in a safe way 480// a small helper to extract a full userdata pointer from the stack in a safe way
457template <typename T> 481template <typename T>
458[[nodiscard]] 482[[nodiscard]]
459T* luaG_tofulluserdata(lua_State* const L_, StackIndex const index_) 483T* luaW_tofulluserdata(lua_State* const L_, StackIndex const index_)
460{ 484{
461 LUA_ASSERT(L_, lua_isnil(L_, index_) || lua_type(L_, index_) == LUA_TUSERDATA); 485 LUA_ASSERT(L_, lua_isnil(L_, index_) || lua_type(L_, index_) == LUA_TUSERDATA);
462 return static_cast<T*>(lua_touserdata(L_, index_)); 486 return static_cast<T*>(lua_touserdata(L_, index_));
@@ -466,7 +490,7 @@ T* luaG_tofulluserdata(lua_State* const L_, StackIndex const index_)
466 490
467template <typename T> 491template <typename T>
468[[nodiscard]] 492[[nodiscard]]
469auto luaG_tolightuserdata(lua_State* const L_, StackIndex const index_) 493auto luaW_tolightuserdata(lua_State* const L_, StackIndex const index_)
470{ 494{
471 LUA_ASSERT(L_, lua_isnil(L_, index_) || lua_islightuserdata(L_, index_)); 495 LUA_ASSERT(L_, lua_isnil(L_, index_) || lua_islightuserdata(L_, index_));
472 if constexpr (std::is_pointer_v<T>) { 496 if constexpr (std::is_pointer_v<T>) {
@@ -479,7 +503,7 @@ auto luaG_tolightuserdata(lua_State* const L_, StackIndex const index_)
479// ------------------------------------------------------------------------------------------------- 503// -------------------------------------------------------------------------------------------------
480 504
481[[nodiscard]] 505[[nodiscard]]
482inline std::string_view luaG_typename(lua_State* const L_, LuaType const t_) 506inline std::string_view luaW_typename(lua_State* const L_, LuaType const t_)
483{ 507{
484 return lua_typename(L_, static_cast<int>(t_)); 508 return lua_typename(L_, static_cast<int>(t_));
485} 509}
@@ -487,7 +511,7 @@ inline std::string_view luaG_typename(lua_State* const L_, LuaType const t_)
487// ------------------------------------------------------------------------------------------------- 511// -------------------------------------------------------------------------------------------------
488 512
489[[nodiscard]] 513[[nodiscard]]
490inline std::string_view luaG_typename(lua_State* const L_, StackIndex const idx_) 514inline std::string_view luaW_typename(lua_State* const L_, StackIndex const idx_)
491{ 515{
492 return luaG_typename(L_, luaG_type(L_, idx_)); 516 return luaW_typename(L_, luaW_type(L_, idx_));
493} 517}
diff --git a/src/deep.cpp b/src/deep.cpp
index 82f1214..acd93a6 100644
--- a/src/deep.cpp
+++ b/src/deep.cpp
@@ -75,7 +75,7 @@ namespace {
75 lua_rawget(L_, -2); // L_: {} b 75 lua_rawget(L_, -2); // L_: {} b
76 } 76 }
77 lua_remove(L_, -2); // L_: a|b 77 lua_remove(L_, -2); // L_: a|b
78 LuaType const _type{ luaG_type(L_, kIdxTop) }; 78 LuaType const _type{ luaW_type(L_, kIdxTop) };
79 if (_type != LuaType::NIL && _type != expectedType_) { 79 if (_type != LuaType::NIL && _type != expectedType_) {
80 raise_luaL_error(L_, "INTERNAL ERROR: Unexpected value."); 80 raise_luaL_error(L_, "INTERNAL ERROR: Unexpected value.");
81 } 81 }
@@ -103,7 +103,7 @@ namespace {
103[[nodiscard]] 103[[nodiscard]]
104int DeepFactory::DeepGC(lua_State* const L_) 104int DeepFactory::DeepGC(lua_State* const L_)
105{ 105{
106 DeepPrelude* const* const _proxy{ luaG_tofulluserdata<DeepPrelude*>(L_, StackIndex{ 1 }) }; 106 DeepPrelude* const* const _proxy{ luaW_tofulluserdata<DeepPrelude*>(L_, StackIndex{ 1 }) };
107 DeepPrelude* const _p{ *_proxy }; 107 DeepPrelude* const _p{ *_proxy };
108 108
109 // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded 109 // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded
@@ -150,7 +150,7 @@ DeepFactory* DeepFactory::LookupFactory(lua_State* const L_, StackIndex const in
150{ 150{
151 // when looking inside a keeper, we are 100% sure the object is a deep userdata 151 // when looking inside a keeper, we are 100% sure the object is a deep userdata
152 if (mode_ == LookupMode::FromKeeper) { 152 if (mode_ == LookupMode::FromKeeper) {
153 DeepPrelude* const _proxy{ *luaG_tofulluserdata<DeepPrelude*>(L_, index_) }; 153 DeepPrelude* const _proxy{ *luaW_tofulluserdata<DeepPrelude*>(L_, index_) };
154 // we can (and must) cast and fetch the internally stored factory 154 // we can (and must) cast and fetch the internally stored factory
155 return &_proxy->factory; 155 return &_proxy->factory;
156 } else { 156 } else {
@@ -167,7 +167,7 @@ DeepFactory* DeepFactory::LookupFactory(lua_State* const L_, StackIndex const in
167 // replace metatable with the factory pointer, if it is actually a deep userdata 167 // replace metatable with the factory pointer, if it is actually a deep userdata
168 LookupDeep(L_, LuaType::LIGHTUSERDATA); // L_: deep ... factory|nil 168 LookupDeep(L_, LuaType::LIGHTUSERDATA); // L_: deep ... factory|nil
169 169
170 DeepFactory* const _ret{ luaG_tolightuserdata<DeepFactory>(L_, kIdxTop) }; // nullptr if not a userdata 170 DeepFactory* const _ret{ luaW_tolightuserdata<DeepFactory>(L_, kIdxTop) }; // nullptr if not a userdata
171 lua_pop(L_, 1); 171 lua_pop(L_, 1);
172 STACK_CHECK(L_, 0); 172 STACK_CHECK(L_, 0);
173 return _ret; 173 return _ret;
@@ -190,7 +190,7 @@ void DeepFactory::PushDeepProxy(DestState const L_, DeepPrelude* const prelude_,
190 190
191 // Check if a proxy already exists 191 // Check if a proxy already exists
192 lua_pushlightuserdata(L_, prelude_); // L_: DPC deep 192 lua_pushlightuserdata(L_, prelude_); // L_: DPC deep
193 if (luaG_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // L_: DPC proxy 193 if (luaW_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // L_: DPC proxy
194 lua_remove(L_, -2); // L_: proxy 194 lua_remove(L_, -2); // L_: proxy
195 STACK_CHECK(L_, 1); 195 STACK_CHECK(L_, 1);
196 return; 196 return;
@@ -202,7 +202,7 @@ void DeepFactory::PushDeepProxy(DestState const L_, DeepPrelude* const prelude_,
202 STACK_GROW(L_, 7); 202 STACK_GROW(L_, 7);
203 203
204 // a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4) 204 // a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4)
205 DeepPrelude** const _proxy{ luaG_newuserdatauv<DeepPrelude*>(L_, nuv_) }; // L_: DPC proxy 205 DeepPrelude** const _proxy{ luaW_newuserdatauv<DeepPrelude*>(L_, nuv_) }; // L_: DPC proxy
206 LUA_ASSERT(L_, _proxy); 206 LUA_ASSERT(L_, _proxy);
207 *_proxy = prelude_; 207 *_proxy = prelude_;
208 prelude_->refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data 208 prelude_->refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data
@@ -223,7 +223,7 @@ void DeepFactory::PushDeepProxy(DestState const L_, DeepPrelude* const prelude_,
223 raise_luaL_error(errL_, "Bad DeepFactory::createMetatable overload: unexpected pushed value"); 223 raise_luaL_error(errL_, "Bad DeepFactory::createMetatable overload: unexpected pushed value");
224 } 224 }
225 // if the metatable contains a __gc, we will call it from our own 225 // if the metatable contains a __gc, we will call it from our own
226 std::ignore = luaG_rawgetfield(L_, kIdxTop, "__gc"); // L_: DPC proxy metatable __gc 226 std::ignore = luaW_rawgetfield(L_, kIdxTop, "__gc"); // L_: DPC proxy metatable __gc
227 } else { 227 } else {
228 // keepers need a minimal metatable that only contains our own __gc 228 // keepers need a minimal metatable that only contains our own __gc
229 lua_createtable(L_, 0, 1); // L_: DPC proxy metatable 229 lua_createtable(L_, 0, 1); // L_: DPC proxy metatable
@@ -251,8 +251,8 @@ void DeepFactory::PushDeepProxy(DestState const L_, DeepPrelude* const prelude_,
251 raise_luaL_error(errL_, "lanes receiving deep userdata should register the 'package' library"); 251 raise_luaL_error(errL_, "lanes receiving deep userdata should register the 'package' library");
252 } 252 }
253 253
254 luaG_pushstring(L_, _modname); // L_: DPC proxy metatable require() "module" 254 luaW_pushstring(L_, _modname); // L_: DPC proxy metatable require() "module"
255 if (luaG_getfield(L_, kIdxRegistry, LUA_LOADED_TABLE) != LuaType::TABLE) { // L_: DPC proxy metatable require() "module" _R._LOADED 255 if (luaW_getfield(L_, kIdxRegistry, LUA_LOADED_TABLE) != LuaType::TABLE) { // L_: DPC proxy metatable require() "module" _R._LOADED
256 // no L.registry._LOADED; can this ever happen? 256 // no L.registry._LOADED; can this ever happen?
257 lua_pop(L_, 6); // L_: 257 lua_pop(L_, 6); // L_:
258 raise_luaL_error(errL_, "unexpected error while requiring a module identified by DeepFactory::moduleName"); 258 raise_luaL_error(errL_, "unexpected error while requiring a module identified by DeepFactory::moduleName");
@@ -270,7 +270,7 @@ void DeepFactory::PushDeepProxy(DestState const L_, DeepPrelude* const prelude_,
270 LuaError const _require_result{ lua_pcall(L_, 1, 0, 0) }; // L_: DPC proxy metatable error? 270 LuaError const _require_result{ lua_pcall(L_, 1, 0, 0) }; // L_: DPC proxy metatable error?
271 if (_require_result != LuaError::OK) { 271 if (_require_result != LuaError::OK) {
272 // failed, raise the error in the proper state 272 // failed, raise the error in the proper state
273 raise_luaL_error(errL_, luaG_tostring(L_, kIdxTop)); 273 raise_luaL_error(errL_, luaW_tostring(L_, kIdxTop));
274 } 274 }
275 } 275 }
276 } else { // already loaded, we are happy 276 } else { // already loaded, we are happy
@@ -279,7 +279,7 @@ void DeepFactory::PushDeepProxy(DestState const L_, DeepPrelude* const prelude_,
279 } 279 }
280 } 280 }
281 STACK_CHECK(L_, 3); // L_: DPC proxy metatable 281 STACK_CHECK(L_, 3); // L_: DPC proxy metatable
282 LUA_ASSERT(L_, luaG_type(L_, StackIndex{ -2 }) == LuaType::USERDATA); 282 LUA_ASSERT(L_, luaW_type(L_, StackIndex{ -2 }) == LuaType::USERDATA);
283 LUA_ASSERT(L_, lua_istable(L_, -1)); 283 LUA_ASSERT(L_, lua_istable(L_, -1));
284 lua_setmetatable(L_, -2); // L_: DPC proxy 284 lua_setmetatable(L_, -2); // L_: DPC proxy
285 285
@@ -288,7 +288,7 @@ void DeepFactory::PushDeepProxy(DestState const L_, DeepPrelude* const prelude_,
288 lua_pushvalue(L_, -2); // L_: DPC proxy deep proxy 288 lua_pushvalue(L_, -2); // L_: DPC proxy deep proxy
289 lua_rawset(L_, -4); // L_: DPC proxy 289 lua_rawset(L_, -4); // L_: DPC proxy
290 lua_remove(L_, -2); // L_: proxy 290 lua_remove(L_, -2); // L_: proxy
291 LUA_ASSERT(L_, luaG_type(L_, kIdxTop) == LuaType::USERDATA); 291 LUA_ASSERT(L_, luaW_type(L_, kIdxTop) == LuaType::USERDATA);
292 STACK_CHECK(L_, 1); 292 STACK_CHECK(L_, 1);
293} 293}
294 294
@@ -305,7 +305,7 @@ void DeepFactory::PushDeepProxy(DestState const L_, DeepPrelude* const prelude_,
305 * Reference counting and true userdata proxying are taken care of for the actual data type. 305 * Reference counting and true userdata proxying are taken care of for the actual data type.
306 * 306 *
307 * Types using the deep userdata system (and only those!) can be passed between 307 * Types using the deep userdata system (and only those!) can be passed between
308 * separate Lua states via 'luaG_inter_move()'. 308 * separate Lua states via 'luaW_inter_move()'.
309 * 309 *
310 * Returns: 'proxy' userdata for accessing the deep data via 'DeepFactory::toDeep()' 310 * Returns: 'proxy' userdata for accessing the deep data via 'DeepFactory::toDeep()'
311 */ 311 */
@@ -382,7 +382,7 @@ DeepPrelude* DeepFactory::toDeep(lua_State* const L_, StackIndex const index_) c
382 } 382 }
383 STACK_CHECK(L_, 0); 383 STACK_CHECK(L_, 0);
384 384
385 DeepPrelude** const _proxy{ luaG_tofulluserdata<DeepPrelude*>(L_, index_) }; 385 DeepPrelude** const _proxy{ luaW_tofulluserdata<DeepPrelude*>(L_, index_) };
386 return *_proxy; 386 return *_proxy;
387} 387}
388 388
diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp
index a93615b..6e9b66c 100644
--- a/src/intercopycontext.cpp
+++ b/src/intercopycontext.cpp
@@ -36,23 +36,6 @@ THE SOFTWARE.
36 36
37// ################################################################################################# 37// #################################################################################################
38 38
39// Lua 5.4.3 style of dumping (see lstrlib.c)
40// we have to do it that way because we can't unbalance the stack between buffer operations
41// namely, this means we can't push a function on top of the stack *after* we initialize the buffer!
42// luckily, this also works with earlier Lua versions
43[[nodiscard]]
44static int buf_writer(lua_State* L_, void const* b_, size_t size_, void* ud_)
45{
46 luaL_Buffer* const _B{ static_cast<luaL_Buffer*>(ud_) };
47 if (!_B->L) {
48 luaL_buffinit(L_, _B);
49 }
50 luaL_addlstring(_B, static_cast<char const*>(b_), size_);
51 return 0;
52}
53
54// #################################################################################################
55
56// function sentinel used to transfer native functions from/to keeper states 39// function sentinel used to transfer native functions from/to keeper states
57[[nodiscard]] 40[[nodiscard]]
58static int func_lookup_sentinel(lua_State* const L_) 41static int func_lookup_sentinel(lua_State* const L_)
@@ -93,7 +76,7 @@ static int userdata_lookup_sentinel(lua_State* const L_)
93[[nodiscard]] 76[[nodiscard]]
94std::string_view InterCopyContext::findLookupName() const 77std::string_view InterCopyContext::findLookupName() const
95{ 78{
96 LUA_ASSERT(L1, lua_isfunction(L1, L1_i) || lua_istable(L1, L1_i) || luaG_type(L1, L1_i) == LuaType::USERDATA); 79 LUA_ASSERT(L1, lua_isfunction(L1, L1_i) || lua_istable(L1, L1_i) || luaW_type(L1, L1_i) == LuaType::USERDATA);
97 STACK_CHECK_START_REL(L1, 0); // L1: ... v ... 80 STACK_CHECK_START_REL(L1, 0); // L1: ... v ...
98 STACK_GROW(L1, 3); // up to 3 slots are necessary on error 81 STACK_GROW(L1, 3); // up to 3 slots are necessary on error
99 if (mode == LookupMode::FromKeeper) { 82 if (mode == LookupMode::FromKeeper) {
@@ -114,7 +97,7 @@ std::string_view InterCopyContext::findLookupName() const
114 lua_pushvalue(L1, L1_i); // L1: ... v ... {} v 97 lua_pushvalue(L1, L1_i); // L1: ... v ... {} v
115 lua_rawget(L1, -2); // L1: ... v ... {} "f.q.n" 98 lua_rawget(L1, -2); // L1: ... v ... {} "f.q.n"
116 } 99 }
117 std::string_view _fqn{ luaG_tostring(L1, kIdxTop) }; 100 std::string_view _fqn{ luaW_tostring(L1, kIdxTop) };
118 DEBUGSPEW_CODE(DebugSpew(U) << "function [C] " << _fqn << std::endl); 101 DEBUGSPEW_CODE(DebugSpew(U) << "function [C] " << _fqn << std::endl);
119 // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database 102 // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database
120 lua_pop(L1, (mode == LookupMode::FromKeeper) ? 1 : 2); // L1: ... v ... 103 lua_pop(L1, (mode == LookupMode::FromKeeper) ? 1 : 2); // L1: ... v ...
@@ -122,12 +105,12 @@ std::string_view InterCopyContext::findLookupName() const
122 if (_fqn.empty() && !lua_istable(L1, L1_i)) { // raise an error if we try to send an unknown function/userdata (but not for tables) 105 if (_fqn.empty() && !lua_istable(L1, L1_i)) { // raise an error if we try to send an unknown function/userdata (but not for tables)
123 // try to discover the name of the function/userdata we want to send 106 // try to discover the name of the function/userdata we want to send
124 kLaneNameRegKey.pushValue(L1); // L1: ... v ... lane_name 107 kLaneNameRegKey.pushValue(L1); // L1: ... v ... lane_name
125 std::string_view const _from{ luaG_tostring(L1, kIdxTop) }; 108 std::string_view const _from{ luaW_tostring(L1, kIdxTop) };
126 lua_pushcfunction(L1, LG_nameof); // L1: ... v ... lane_name LG_nameof 109 lua_pushcfunction(L1, LG_nameof); // L1: ... v ... lane_name LG_nameof
127 lua_pushvalue(L1, L1_i); // L1: ... v ... lane_name LG_nameof t 110 lua_pushvalue(L1, L1_i); // L1: ... v ... lane_name LG_nameof t
128 lua_call(L1, 1, 2); // L1: ... v ... lane_name "type" "name"|nil 111 lua_call(L1, 1, 2); // L1: ... v ... lane_name "type" "name"|nil
129 StackIndex const _indexTypeWhat{ -2 }; 112 StackIndex const _indexTypeWhat{ -2 };
130 std::string_view const _typewhat{ (luaG_type(L1, _indexTypeWhat) == LuaType::STRING) ? luaG_tostring(L1, _indexTypeWhat) : luaG_typename(L1, _indexTypeWhat) }; 113 std::string_view const _typewhat{ (luaW_type(L1, _indexTypeWhat) == LuaType::STRING) ? luaW_tostring(L1, _indexTypeWhat) : luaW_typename(L1, _indexTypeWhat) };
131 // second return value can be nil if the table was not found 114 // second return value can be nil if the table was not found
132 // probable reason: the function was removed from the source Lua state before Lanes was required. 115 // probable reason: the function was removed from the source Lua state before Lanes was required.
133 std::string_view _what, _gotchaA, _gotchaB; 116 std::string_view _what, _gotchaA, _gotchaB;
@@ -139,7 +122,7 @@ std::string_view InterCopyContext::findLookupName() const
139 _gotchaA = ""; 122 _gotchaA = "";
140 _gotchaB = ""; 123 _gotchaB = "";
141 StackIndex const _indexWhat{ kIdxTop }; 124 StackIndex const _indexWhat{ kIdxTop };
142 _what = (luaG_type(L1, _indexWhat) == LuaType::STRING) ? luaG_tostring(L1, _indexWhat) : luaG_typename(L1, _indexWhat); 125 _what = (luaW_type(L1, _indexWhat) == LuaType::STRING) ? luaW_tostring(L1, _indexWhat) : luaW_typename(L1, _indexWhat);
143 } 126 }
144 raise_luaL_error(L1, "%s%s '%s' not found in %s origin transfer database.%s", _typewhat.data(), _gotchaA.data(), _what.data(), _from.empty() ? "main" : _from.data(), _gotchaB.data()); 127 raise_luaL_error(L1, "%s%s '%s' not found in %s origin transfer database.%s", _typewhat.data(), _gotchaA.data(), _what.data(), _from.empty() ? "main" : _from.data(), _gotchaB.data());
145 } 128 }
@@ -158,7 +141,7 @@ static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull };
158[[nodiscard]] 141[[nodiscard]]
159static lua_Integer get_mt_id(Universe* const U_, lua_State* const L_, StackIndex const idx_) 142static lua_Integer get_mt_id(Universe* const U_, lua_State* const L_, StackIndex const idx_)
160{ 143{
161 StackIndex const _absidx{ luaG_absindex(L_, idx_) }; 144 StackIndex const _absidx{ luaW_absindex(L_, idx_) };
162 145
163 STACK_GROW(L_, 3); 146 STACK_GROW(L_, 3);
164 147
@@ -195,11 +178,12 @@ static lua_Integer get_mt_id(Universe* const U_, lua_State* const L_, StackIndex
195// L2 has the cache key for this function at the top of the stack 178// L2 has the cache key for this function at the top of the stack
196void InterCopyContext::copyFunction() const 179void InterCopyContext::copyFunction() const
197{ 180{
198 LUA_ASSERT(L1, L2_cache_i != 0); // L2: ... {cache} ... p 181 LUA_ASSERT(L1, L2_cache_i != 0); // L1: ... f L2: ... {cache} ... p
199 STACK_GROW(L1, 2); 182 STACK_GROW(L1, 2);
200 STACK_CHECK_START_REL(L1, 0); 183 STACK_CHECK_START_REL(L1, 0);
184 STACK_CHECK_START_REL(L2, 0);
201 185
202 // 'luaG_dump()' needs the function at top of stack 186 // 'luaW_dump()' needs the function at top of stack
203 // if already on top of the stack, no need to push again 187 // if already on top of the stack, no need to push again
204 bool const _needToPush{ L1_i != lua_gettop(L1) }; 188 bool const _needToPush{ L1_i != lua_gettop(L1) };
205 if (_needToPush) { 189 if (_needToPush) {
@@ -211,19 +195,17 @@ void InterCopyContext::copyFunction() const
211 // to the writer" (and we only return 0) 195 // to the writer" (and we only return 0)
212 // not sure this could ever fail but for memory shortage reasons 196 // not sure this could ever fail but for memory shortage reasons
213 // last argument is Lua 5.4-specific (no stripping) 197 // last argument is Lua 5.4-specific (no stripping)
214 luaL_Buffer B{}; 198 tools::PushFunctionBytecode(L1, L2, U->stripFunctions); // L1: ... f L2: ... {cache} ... p "<bytecode>"
215 if (luaG_dump(L1, buf_writer, &B, U->stripFunctions) != 0) {
216 raise_luaL_error(getErrL(), "internal error: function dump failed.");
217 }
218 199
219 // pushes dumped string on 'L1' 200 // if pushed, we need to pop
220 luaL_pushresult(&B); // L1: ... f b
221
222 // if not pushed, no need to pop
223 if (_needToPush) { 201 if (_needToPush) {
224 lua_remove(L1, -2); // L1: ... b 202 lua_pop(L1, 1); // L1: ...
225 } 203 }
226 204
205 // When we are done, the stack of L1 should be the original one, with the bytecode string added on top of L2
206 STACK_CHECK(L1, 0);
207 STACK_CHECK(L2, 1);
208
227 // transfer the bytecode, then the upvalues, to create a similar closure 209 // transfer the bytecode, then the upvalues, to create a similar closure
228 { 210 {
229 char const* _fname{}; 211 char const* _fname{};
@@ -231,16 +213,16 @@ void InterCopyContext::copyFunction() const
231 if constexpr (LOG_FUNC_INFO) 213 if constexpr (LOG_FUNC_INFO)
232 { 214 {
233 lua_Debug _ar; 215 lua_Debug _ar;
234 lua_pushvalue(L1, L1_i); // L1: ... b f 216 lua_pushvalue(L1, L1_i); // L1: ... f
235 // "To get information about a function you push it onto the stack and start the what string with the character '>'." 217 // "To get information about a function you push it onto the stack and start the what string with the character '>'."
236 // fills 'fname' 'namewhat' and 'linedefined', pops function 218 // fills 'fname' 'namewhat' and 'linedefined', pops function
237 lua_getinfo(L1, ">nS", &_ar); // L1: ... b 219 lua_getinfo(L1, ">nS", &_ar); // L1: ...
238 _fname = _ar.namewhat; 220 _fname = _ar.namewhat;
239 DEBUGSPEW_CODE(DebugSpew(U) << "FNAME: " << _ar.short_src << " @ " << _ar.linedefined << std::endl); 221 DEBUGSPEW_CODE(DebugSpew(U) << "FNAME: " << _ar.short_src << " @ " << _ar.linedefined << std::endl);
240 } 222 }
241 223
242 { 224 {
243 std::string_view const _bytecode{ luaG_tostring(L1, kIdxTop) }; // L1: ... b 225 std::string_view const _bytecode{ luaW_tostring(L2, kIdxTop) }; // L2: ... {cache} ... p "<bytecode>"
244 LUA_ASSERT(L1, !_bytecode.empty()); 226 LUA_ASSERT(L1, !_bytecode.empty());
245 STACK_GROW(L2, 2); 227 STACK_GROW(L2, 2);
246 // Note: Line numbers seem to be taken precisely from the 228 // Note: Line numbers seem to be taken precisely from the
@@ -249,15 +231,15 @@ void InterCopyContext::copyFunction() const
249 // 231 //
250 // TBD: Can we get the function's original name through, as well? 232 // TBD: Can we get the function's original name through, as well?
251 // 233 //
252 if (luaL_loadbuffer(L2, _bytecode.data(), _bytecode.size(), _fname) != 0) { // L2: ... {cache} ... p function 234 if (luaL_loadbuffer(L2, _bytecode.data(), _bytecode.size(), _fname) != 0) { // L2: ... {cache} ... p "<bytecode>" function
253 // chunk is precompiled so only LUA_ERRMEM can happen 235 // chunk is precompiled so only LUA_ERRMEM can happen
254 // "Otherwise, it pushes an error message" 236 // "Otherwise, it pushes an error message"
255 // 237 //
256 STACK_GROW(L1, 1); 238 STACK_GROW(L1, 1);
257 raise_luaL_error(getErrL(), "%s: %s", _fname, lua_tostring(L2, -1)); 239 raise_luaL_error(getErrL(), "%s: %s", _fname, lua_tostring(L2, kIdxTop));
258 } 240 }
259 // remove the dumped string 241 // remove the dumped string
260 lua_pop(L1, 1); // ... 242 lua_replace(L2, -2); // L2: ... {cache} ... p function
261 // now set the cache as soon as we can. 243 // now set the cache as soon as we can.
262 // this is necessary if one of the function's upvalues references it indirectly 244 // this is necessary if one of the function's upvalues references it indirectly
263 // we need to find it in the cache even if it isn't fully transfered yet 245 // we need to find it in the cache even if it isn't fully transfered yet
@@ -267,6 +249,7 @@ void InterCopyContext::copyFunction() const
267 lua_rawset(L2, L2_cache_i); // L2: ... {cache} ... function 249 lua_rawset(L2, L2_cache_i); // L2: ... {cache} ... function
268 } 250 }
269 STACK_CHECK(L1, 0); 251 STACK_CHECK(L1, 0);
252 STACK_CHECK(L2, 0); // cache key is replaced by the function, so no stack level change
270 253
271 /* push over any upvalues; references to this function will come from 254 /* push over any upvalues; references to this function will come from
272 * cache so we don't end up in eternal loop. 255 * cache so we don't end up in eternal loop.
@@ -278,12 +261,12 @@ void InterCopyContext::copyFunction() const
278 { 261 {
279 InterCopyContext _c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} }; 262 InterCopyContext _c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} };
280 // if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table 263 // if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table
281 luaG_pushglobaltable(L1); // L1: ... _G 264 luaW_pushglobaltable(L1); // L1: ... _G
282 for (char const* _upname{}; (_upname = lua_getupvalue(L1, L1_i, 1 + _n)); ++_n) { // L1: ... _G up[n] 265 for (char const* _upname{}; (_upname = lua_getupvalue(L1, L1_i, 1 + _n)); ++_n) { // L1: ... _G up[n]
283 DEBUGSPEW_CODE(DebugSpew(U) << "UPNAME[" << _n << "]: " << _c.name << " -> "); 266 DEBUGSPEW_CODE(DebugSpew(U) << "UPNAME[" << _n << "]: " << _c.name << " -> ");
284 if (lua_rawequal(L1, -1, -2)) { // is the upvalue equal to the global table? 267 if (lua_rawequal(L1, -1, -2)) { // is the upvalue equal to the global table?
285 DEBUGSPEW_CODE(DebugSpew(nullptr) << "pushing destination global scope" << std::endl); 268 DEBUGSPEW_CODE(DebugSpew(nullptr) << "pushing destination global scope" << std::endl);
286 luaG_pushglobaltable(L2); // L2: ... {cache} ... function <upvalues> 269 luaW_pushglobaltable(L2); // L2: ... {cache} ... function <upvalues>
287 } else { 270 } else {
288 DEBUGSPEW_CODE(DebugSpew(nullptr) << "copying value" << std::endl); 271 DEBUGSPEW_CODE(DebugSpew(nullptr) << "copying value" << std::endl);
289 _c.name = _upname; 272 _c.name = _upname;
@@ -297,6 +280,7 @@ void InterCopyContext::copyFunction() const
297 lua_pop(L1, 1); // L1: ... 280 lua_pop(L1, 1); // L1: ...
298 } // L2: ... {cache} ... function + 'n' upvalues (>=0) 281 } // L2: ... {cache} ... function + 'n' upvalues (>=0)
299 STACK_CHECK(L1, 0); 282 STACK_CHECK(L1, 0);
283 STACK_CHECK(L2, _n);
300 284
301 // Set upvalues (originally set to 'nil' by 'lua_load') 285 // Set upvalues (originally set to 'nil' by 'lua_load')
302 for (StackIndex const _func_index{ lua_gettop(L2) - _n }; _n > 0; --_n) { 286 for (StackIndex const _func_index{ lua_gettop(L2) - _n }; _n > 0; --_n) {
@@ -307,6 +291,7 @@ void InterCopyContext::copyFunction() const
307 // once all upvalues have been set we are left 291 // once all upvalues have been set we are left
308 // with the function at the top of the stack // L2: ... {cache} ... function 292 // with the function at the top of the stack // L2: ... {cache} ... function
309 } 293 }
294 STACK_CHECK(L2, 0);
310 STACK_CHECK(L1, 0); 295 STACK_CHECK(L1, 0);
311} 296}
312 297
@@ -327,7 +312,7 @@ void InterCopyContext::lookupNativeFunction() const
327 312
328 case LookupMode::ToKeeper: 313 case LookupMode::ToKeeper:
329 // push a sentinel closure that holds the lookup name as upvalue 314 // push a sentinel closure that holds the lookup name as upvalue
330 luaG_pushstring(L2, _fqn); // L1: ... f ... L2: "f.q.n" 315 luaW_pushstring(L2, _fqn); // L1: ... f ... L2: "f.q.n"
331 lua_pushcclosure(L2, func_lookup_sentinel, 1); // L1: ... f ... L2: f 316 lua_pushcclosure(L2, func_lookup_sentinel, 1); // L1: ... f ... L2: f
332 break; 317 break;
333 318
@@ -336,16 +321,16 @@ void InterCopyContext::lookupNativeFunction() const
336 kLookupRegKey.pushValue(L2); // L1: ... f ... L2: {} 321 kLookupRegKey.pushValue(L2); // L1: ... f ... L2: {}
337 STACK_CHECK(L2, 1); 322 STACK_CHECK(L2, 1);
338 LUA_ASSERT(L1, lua_istable(L2, -1)); 323 LUA_ASSERT(L1, lua_istable(L2, -1));
339 luaG_pushstring(L2, _fqn); // L1: ... f ... L2: {} "f.q.n" 324 luaW_pushstring(L2, _fqn); // L1: ... f ... L2: {} "f.q.n"
340 LuaType const _objType{ luaG_rawget(L2, StackIndex{ -2 }) }; // L1: ... f ... L2: {} f 325 LuaType const _objType{ luaW_rawget(L2, StackIndex{ -2 }) }; // L1: ... f ... L2: {} f
341 // nil means we don't know how to transfer stuff: user should do something 326 // nil means we don't know how to transfer stuff: user should do something
342 // anything other than function or table should not happen! 327 // anything other than function or table should not happen!
343 if (_objType != LuaType::FUNCTION && _objType != LuaType::TABLE && _objType != LuaType::USERDATA) { 328 if (_objType != LuaType::FUNCTION && _objType != LuaType::TABLE && _objType != LuaType::USERDATA) {
344 kLaneNameRegKey.pushValue(L1); // L1: ... f ... lane_name 329 kLaneNameRegKey.pushValue(L1); // L1: ... f ... lane_name
345 std::string_view const _from{ luaG_tostring(L1, kIdxTop) }; 330 std::string_view const _from{ luaW_tostring(L1, kIdxTop) };
346 lua_pop(L1, 1); // L1: ... f ... 331 lua_pop(L1, 1); // L1: ... f ...
347 kLaneNameRegKey.pushValue(L2); // L1: ... f ... L2: {} f lane_name 332 kLaneNameRegKey.pushValue(L2); // L1: ... f ... L2: {} f lane_name
348 std::string_view const _to{ luaG_tostring(L2, kIdxTop) }; 333 std::string_view const _to{ luaW_tostring(L2, kIdxTop) };
349 lua_pop(L2, 1); // L2: {} f 334 lua_pop(L2, 1); // L2: {} f
350 raise_luaL_error( 335 raise_luaL_error(
351 getErrL(), 336 getErrL(),
@@ -368,7 +353,7 @@ void InterCopyContext::lookupNativeFunction() const
368// Always pushes a function to 'L2'. 353// Always pushes a function to 'L2'.
369void InterCopyContext::copyCachedFunction() const 354void InterCopyContext::copyCachedFunction() const
370{ 355{
371 FuncSubType const _funcSubType{ luaG_getfuncsubtype(L1, L1_i) }; 356 FuncSubType const _funcSubType{ luaW_getfuncsubtype(L1, L1_i) };
372 if (_funcSubType == FuncSubType::Bytecode) { 357 if (_funcSubType == FuncSubType::Bytecode) {
373 void* const _aspointer{ const_cast<void*>(lua_topointer(L1, L1_i)) }; 358 void* const _aspointer{ const_cast<void*>(lua_topointer(L1, L1_i)) };
374 // TODO: Merge this and same code for tables 359 // TODO: Merge this and same code for tables
@@ -386,10 +371,10 @@ void InterCopyContext::copyCachedFunction() const
386 // push a light userdata uniquely representing the function 371 // push a light userdata uniquely representing the function
387 lua_pushlightuserdata(L2, _aspointer); // L2: ... {cache} ... p 372 lua_pushlightuserdata(L2, _aspointer); // L2: ... {cache} ... p
388 373
389 //DEBUGSPEW_CODE(DebugSpew(U) << "<< ID: " << luaG_tostring(L2, -1) << " >>" << std::endl); 374 //DEBUGSPEW_CODE(DebugSpew(U) << "<< ID: " << luaW_tostring(L2, -1) << " >>" << std::endl);
390 375
391 lua_pushvalue(L2, -1); // L2: ... {cache} ... p p 376 lua_pushvalue(L2, -1); // L2: ... {cache} ... p p
392 if (luaG_rawget(L2, L2_cache_i) == LuaType::NIL) { // function is unknown // L2: ... {cache} ... p function|nil|true 377 if (luaW_rawget(L2, L2_cache_i) == LuaType::NIL) { // function is unknown // L2: ... {cache} ... p function|nil|true
393 lua_pop(L2, 1); // L2: ... {cache} ... p 378 lua_pop(L2, 1); // L2: ... {cache} ... p
394 379
395 // Set to 'true' for the duration of creation; need to find self-references 380 // Set to 'true' for the duration of creation; need to find self-references
@@ -405,7 +390,7 @@ void InterCopyContext::copyCachedFunction() const
405 } else { // function is native/LuaJIT: no need to cache 390 } else { // function is native/LuaJIT: no need to cache
406 lookupNativeFunction(); // L2: ... {cache} ... function 391 lookupNativeFunction(); // L2: ... {cache} ... function
407 // if the function was in fact a lookup sentinel, we can either get a function, table or full userdata here 392 // if the function was in fact a lookup sentinel, we can either get a function, table or full userdata here
408 LUA_ASSERT(L1, lua_isfunction(L2, kIdxTop) || lua_istable(L2, kIdxTop) || luaG_type(L2, kIdxTop) == LuaType::USERDATA); 393 LUA_ASSERT(L1, lua_isfunction(L2, kIdxTop) || lua_istable(L2, kIdxTop) || luaW_type(L2, kIdxTop) == LuaType::USERDATA);
409 } 394 }
410} 395}
411 396
@@ -430,7 +415,7 @@ bool InterCopyContext::lookupTable() const
430 415
431 case LookupMode::ToKeeper: 416 case LookupMode::ToKeeper:
432 // push a sentinel closure that holds the lookup name as upvalue 417 // push a sentinel closure that holds the lookup name as upvalue
433 luaG_pushstring(L2, _fqn); // L1: ... t ... L2: "f.q.n" 418 luaW_pushstring(L2, _fqn); // L1: ... t ... L2: "f.q.n"
434 lua_pushcclosure(L2, table_lookup_sentinel, 1); // L1: ... t ... L2: f 419 lua_pushcclosure(L2, table_lookup_sentinel, 1); // L1: ... t ... L2: f
435 break; 420 break;
436 421
@@ -439,26 +424,26 @@ bool InterCopyContext::lookupTable() const
439 kLookupRegKey.pushValue(L2); // L1: ... t ... L2: {} 424 kLookupRegKey.pushValue(L2); // L1: ... t ... L2: {}
440 STACK_CHECK(L2, 1); 425 STACK_CHECK(L2, 1);
441 LUA_ASSERT(L1, lua_istable(L2, -1)); 426 LUA_ASSERT(L1, lua_istable(L2, -1));
442 luaG_pushstring(L2, _fqn); // L2: {} "f.q.n" 427 luaW_pushstring(L2, _fqn); // L2: {} "f.q.n"
443 // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead) 428 // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead)
444 // but not when we extract something out of a keeper, as there is nothing to clone! 429 // but not when we extract something out of a keeper, as there is nothing to clone!
445 if (luaG_rawget(L2, StackIndex{ -2 }) == LuaType::NIL && mode == LookupMode::LaneBody) { // L2: {} t 430 if (luaW_rawget(L2, StackIndex{ -2 }) == LuaType::NIL && mode == LookupMode::LaneBody) { // L2: {} t
446 lua_pop(L2, 2); // L1: ... t ... L2: 431 lua_pop(L2, 2); // L1: ... t ... L2:
447 STACK_CHECK(L2, 0); 432 STACK_CHECK(L2, 0);
448 return false; 433 return false;
449 } 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 434 } 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
450 kLaneNameRegKey.pushValue(L1); // L1: ... t ... lane_name 435 kLaneNameRegKey.pushValue(L1); // L1: ... t ... lane_name
451 std::string_view const _from{ luaG_tostring(L1, kIdxTop) }; 436 std::string_view const _from{ luaW_tostring(L1, kIdxTop) };
452 lua_pop(L1, 1); // L1: ... t ... 437 lua_pop(L1, 1); // L1: ... t ...
453 kLaneNameRegKey.pushValue(L2); // L1: ... t ... L2: {} t lane_name 438 kLaneNameRegKey.pushValue(L2); // L1: ... t ... L2: {} t lane_name
454 std::string_view const _to{ luaG_tostring(L2, kIdxTop) }; 439 std::string_view const _to{ luaW_tostring(L2, kIdxTop) };
455 lua_pop(L2, 1); // L1: ... t ... L2: {} t 440 lua_pop(L2, 1); // L1: ... t ... L2: {} t
456 raise_luaL_error( 441 raise_luaL_error(
457 getErrL(), 442 getErrL(),
458 "%s: source table '%s' found as %s in %s destination transfer database.", 443 "%s: source table '%s' found as %s in %s destination transfer database.",
459 _from.empty() ? "main" : _from.data(), 444 _from.empty() ? "main" : _from.data(),
460 _fqn.data(), 445 _fqn.data(),
461 luaG_typename(L2, kIdxTop).data(), 446 luaW_typename(L2, kIdxTop).data(),
462 _to.empty() ? "main" : _to.data()); 447 _to.empty() ? "main" : _to.data());
463 } 448 }
464 lua_remove(L2, -2); // L1: ... t ... L2: t 449 lua_remove(L2, -2); // L1: ... t ... L2: t
@@ -487,8 +472,8 @@ void InterCopyContext::interCopyKeyValuePair() const
487 char* _valPath{ nullptr }; 472 char* _valPath{ nullptr };
488 if (U->verboseErrors) { 473 if (U->verboseErrors) {
489 // for debug purposes, let's try to build a useful name 474 // for debug purposes, let's try to build a useful name
490 if (luaG_type(L1, _key_i) == LuaType::STRING) { 475 if (luaW_type(L1, _key_i) == LuaType::STRING) {
491 std::string_view const _key{ luaG_tostring(L1, _key_i) }; 476 std::string_view const _key{ luaW_tostring(L1, _key_i) };
492 size_t const _bufLen{ name.size() + _key.size() + 2 }; // +2 for separator dot and terminating 0 477 size_t const _bufLen{ name.size() + _key.size() + 2 }; // +2 for separator dot and terminating 0
493 _valPath = static_cast<char*>(alloca(_bufLen)); 478 _valPath = static_cast<char*>(alloca(_bufLen));
494 sprintf(_valPath, "%s." STRINGVIEW_FMT, name.data(), (int) _key.size(), _key.data()); 479 sprintf(_valPath, "%s." STRINGVIEW_FMT, name.data(), (int) _key.size(), _key.data());
@@ -500,15 +485,15 @@ void InterCopyContext::interCopyKeyValuePair() const
500 sprintf(_valPath, "%s[" LUA_INTEGER_FMT "]", name.data(), key); 485 sprintf(_valPath, "%s[" LUA_INTEGER_FMT "]", name.data(), key);
501 } 486 }
502#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 487#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
503 else if (luaG_type(L1, _key_i) == LuaType::NUMBER) { 488 else if (luaW_type(L1, _key_i) == LuaType::NUMBER) {
504 lua_Number const key{ lua_tonumber(L1, _key_i) }; 489 lua_Number const key{ lua_tonumber(L1, _key_i) };
505 _valPath = (char*) alloca(name.size() + 32 + 3); // +3 for [] and terminating 0 490 _valPath = (char*) alloca(name.size() + 32 + 3); // +3 for [] and terminating 0
506 sprintf(_valPath, "%s[" LUA_NUMBER_FMT "]", name.data(), key); 491 sprintf(_valPath, "%s[" LUA_NUMBER_FMT "]", name.data(), key);
507 } else if (luaG_type(L1, _key_i) == LuaType::LIGHTUSERDATA) { 492 } else if (luaW_type(L1, _key_i) == LuaType::LIGHTUSERDATA) {
508 void* const _key{ lua_touserdata(L1, _key_i) }; 493 void* const _key{ lua_touserdata(L1, _key_i) };
509 _valPath = (char*) alloca(name.size() + 16 + 5); // +5 for [U:] and terminating 0 494 _valPath = (char*) alloca(name.size() + 16 + 5); // +5 for [U:] and terminating 0
510 sprintf(_valPath, "%s[U:%p]", name.data(), _key); 495 sprintf(_valPath, "%s[U:%p]", name.data(), _key);
511 } else if (luaG_type(L1, _key_i) == LuaType::BOOLEAN) { 496 } else if (luaW_type(L1, _key_i) == LuaType::BOOLEAN) {
512 int const _key{ lua_toboolean(L1, _key_i) }; 497 int const _key{ lua_toboolean(L1, _key_i) };
513 _valPath = (char*) alloca(name.size() + 8); // +8 for [], 'false' and terminating 0 498 _valPath = (char*) alloca(name.size() + 8); // +8 for [], 'false' and terminating 0
514 sprintf(_valPath, "%s[%s]", name.data(), _key ? "true" : "false"); 499 sprintf(_valPath, "%s[%s]", name.data(), _key ? "true" : "false");
@@ -532,7 +517,7 @@ LuaType InterCopyContext::processConversion() const
532{ 517{
533 static constexpr int kPODmask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING); 518 static constexpr int kPODmask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING);
534 519
535 LuaType _val_type{ luaG_type(L1, L1_i) }; 520 LuaType _val_type{ luaW_type(L1, L1_i) };
536 521
537 STACK_CHECK_START_REL(L1, 0); 522 STACK_CHECK_START_REL(L1, 0);
538 523
@@ -548,7 +533,7 @@ LuaType InterCopyContext::processConversion() const
548 } 533 }
549 // we have a metatable // L1: ... mt 534 // we have a metatable // L1: ... mt
550 static constexpr std::string_view kConvertField{ "__lanesconvert" }; 535 static constexpr std::string_view kConvertField{ "__lanesconvert" };
551 LuaType const _converterType{ luaG_getfield(L1, kIdxTop, kConvertField) }; // L1: ... mt kConvertField 536 LuaType const _converterType{ luaW_getfield(L1, kIdxTop, kConvertField) }; // L1: ... mt kConvertField
552 switch (_converterType) { 537 switch (_converterType) {
553 case LuaType::NIL: 538 case LuaType::NIL:
554 // no __lanesconvert, nothing to do 539 // no __lanesconvert, nothing to do
@@ -557,18 +542,18 @@ LuaType InterCopyContext::processConversion() const
557 542
558 case LuaType::LIGHTUSERDATA: 543 case LuaType::LIGHTUSERDATA:
559 if (kNilSentinel.equals(L1, kIdxTop)) { 544 if (kNilSentinel.equals(L1, kIdxTop)) {
560 DEBUGSPEW_CODE(DebugSpew(U) << "converted " << luaG_typename(L1, _val_type) << " to nil" << std::endl); 545 DEBUGSPEW_CODE(DebugSpew(U) << "converted " << luaW_typename(L1, _val_type) << " to nil" << std::endl);
561 lua_replace(L1, L1_i); // L1: ... mt 546 lua_replace(L1, L1_i); // L1: ... mt
562 lua_pop(L1, 1); // L1: ... 547 lua_pop(L1, 1); // L1: ...
563 _val_type = _converterType; 548 _val_type = _converterType;
564 } else { 549 } else {
565 raise_luaL_error(getErrL(), "Invalid %s type %s", kConvertField.data(), luaG_typename(L1, _converterType).data()); 550 raise_luaL_error(getErrL(), "Invalid %s type %s", kConvertField.data(), luaW_typename(L1, _converterType).data());
566 } 551 }
567 break; 552 break;
568 553
569 case LuaType::STRING: 554 case LuaType::STRING:
570 // kConvertField == "decay" -> replace source value with it's pointer 555 // kConvertField == "decay" -> replace source value with it's pointer
571 if (std::string_view const _mode{ luaG_tostring(L1, kIdxTop) }; _mode == "decay") { 556 if (std::string_view const _mode{ luaW_tostring(L1, kIdxTop) }; _mode == "decay") {
572 lua_pop(L1, 1); // L1: ... mt 557 lua_pop(L1, 1); // L1: ... mt
573 lua_pushlightuserdata(L1, const_cast<void*>(lua_topointer(L1, L1_i))); // L1: ... mt decayed 558 lua_pushlightuserdata(L1, const_cast<void*>(lua_topointer(L1, L1_i))); // L1: ... mt decayed
574 lua_replace(L1, L1_i); // L1: ... mt 559 lua_replace(L1, L1_i); // L1: ... mt
@@ -581,18 +566,18 @@ LuaType InterCopyContext::processConversion() const
581 566
582 case LuaType::FUNCTION: 567 case LuaType::FUNCTION:
583 lua_pushvalue(L1, L1_i); // L1: ... mt kConvertField val 568 lua_pushvalue(L1, L1_i); // L1: ... mt kConvertField val
584 luaG_pushstring(L1, mode == LookupMode::ToKeeper ? "keeper" : "regular"); // L1: ... mt kConvertField val string 569 luaW_pushstring(L1, mode == LookupMode::ToKeeper ? "keeper" : "regular"); // L1: ... mt kConvertField val string
585 lua_call(L1, 2, 1); // val:kConvertField(str) -> result // L1: ... mt kConvertField converted 570 lua_call(L1, 2, 1); // val:kConvertField(str) -> result // L1: ... mt kConvertField converted
586 lua_replace(L1, L1_i); // L1: ... mt 571 lua_replace(L1, L1_i); // L1: ... mt
587 lua_pop(L1, 1); // L1: ... mt 572 lua_pop(L1, 1); // L1: ... mt
588 _val_type = luaG_type(L1, L1_i); 573 _val_type = luaW_type(L1, L1_i);
589 break; 574 break;
590 575
591 default: 576 default:
592 raise_luaL_error(getErrL(), "Invalid %s type %s", kConvertField.data(), luaG_typename(L1, _converterType).data()); 577 raise_luaL_error(getErrL(), "Invalid %s type %s", kConvertField.data(), luaW_typename(L1, _converterType).data());
593 } 578 }
594 STACK_CHECK(L1, 0); 579 STACK_CHECK(L1, 0);
595 LUA_ASSERT(getErrL(), luaG_type(L1, L1_i) == _val_type); 580 LUA_ASSERT(getErrL(), luaW_type(L1, L1_i) == _val_type);
596 return _val_type; 581 return _val_type;
597} 582}
598 583
@@ -615,7 +600,7 @@ bool InterCopyContext::pushCachedMetatable() const
615 // do we already know this metatable? 600 // do we already know this metatable?
616 std::ignore = kMtIdRegKey.getSubTable(L2, NArr{ 0 }, NRec{ 0 }); // L2: _R[kMtIdRegKey] 601 std::ignore = kMtIdRegKey.getSubTable(L2, NArr{ 0 }, NRec{ 0 }); // L2: _R[kMtIdRegKey]
617 lua_pushinteger(L2, _mt_id); // L2: _R[kMtIdRegKey] id 602 lua_pushinteger(L2, _mt_id); // L2: _R[kMtIdRegKey] id
618 if (luaG_rawget(L2, StackIndex{ -2 }) == LuaType::NIL) { // L2 did not know the metatable // L2: _R[kMtIdRegKey] mt|nil 603 if (luaW_rawget(L2, StackIndex{ -2 }) == LuaType::NIL) { // L2 did not know the metatable // L2: _R[kMtIdRegKey] mt|nil
619 lua_pop(L2, 1); // L2: _R[kMtIdRegKey] 604 lua_pop(L2, 1); // L2: _R[kMtIdRegKey]
620 InterCopyContext const _c{ U, L2, L1, L2_cache_i, SourceIndex{ lua_gettop(L1) }, VT::METATABLE, mode, name }; 605 InterCopyContext const _c{ U, L2, L1, L2_cache_i, SourceIndex{ lua_gettop(L1) }, VT::METATABLE, mode, name };
621 if (_c.interCopyOne() != InterCopyResult::Success) { // L2: _R[kMtIdRegKey] mt? 606 if (_c.interCopyOne() != InterCopyResult::Success) { // L2: _R[kMtIdRegKey] mt?
@@ -662,9 +647,9 @@ bool InterCopyContext::pushCachedTable() const
662 // push a light userdata uniquely representing the table 647 // push a light userdata uniquely representing the table
663 lua_pushlightuserdata(L2, const_cast<void*>(_p)); // L1: ... t ... L2: ... p 648 lua_pushlightuserdata(L2, const_cast<void*>(_p)); // L1: ... t ... L2: ... p
664 649
665 //DEBUGSPEW_CODE(DebugSpew(U) << "<< ID: " << luaG_tostring(L2, -1) << " >>" << std::endl); 650 //DEBUGSPEW_CODE(DebugSpew(U) << "<< ID: " << luaW_tostring(L2, -1) << " >>" << std::endl);
666 651
667 bool const _not_found_in_cache{ luaG_rawget(L2, L2_cache_i) == LuaType::NIL }; // L1: ... t ... L2: ... {cached|nil} 652 bool const _not_found_in_cache{ luaW_rawget(L2, L2_cache_i) == LuaType::NIL }; // L1: ... t ... L2: ... {cached|nil}
668 if (_not_found_in_cache) { 653 if (_not_found_in_cache) {
669 // create a new entry in the cache 654 // create a new entry in the cache
670 lua_pop(L2, 1); // L1: ... t ... L2: ... 655 lua_pop(L2, 1); // L1: ... t ... L2: ...
@@ -696,7 +681,7 @@ bool InterCopyContext::lookupUserdata() const
696 681
697 case LookupMode::ToKeeper: 682 case LookupMode::ToKeeper:
698 // push a sentinel closure that holds the lookup name as upvalue 683 // push a sentinel closure that holds the lookup name as upvalue
699 luaG_pushstring(L2, _fqn); // L1: ... f ... L2: "f.q.n" 684 luaW_pushstring(L2, _fqn); // L1: ... f ... L2: "f.q.n"
700 lua_pushcclosure(L2, userdata_lookup_sentinel, 1); // L1: ... f ... L2: f 685 lua_pushcclosure(L2, userdata_lookup_sentinel, 1); // L1: ... f ... L2: f
701 break; 686 break;
702 687
@@ -705,16 +690,16 @@ bool InterCopyContext::lookupUserdata() const
705 kLookupRegKey.pushValue(L2); // L1: ... f ... L2: {} 690 kLookupRegKey.pushValue(L2); // L1: ... f ... L2: {}
706 STACK_CHECK(L2, 1); 691 STACK_CHECK(L2, 1);
707 LUA_ASSERT(L1, lua_istable(L2, -1)); 692 LUA_ASSERT(L1, lua_istable(L2, -1));
708 luaG_pushstring(L2, _fqn); // L1: ... f ... L2: {} "f.q.n" 693 luaW_pushstring(L2, _fqn); // L1: ... f ... L2: {} "f.q.n"
709 LuaType const _type{ luaG_rawget(L2, StackIndex{ -2 }) }; // L1: ... f ... L2: {} f 694 LuaType const _type{ luaW_rawget(L2, StackIndex{ -2 }) }; // L1: ... f ... L2: {} f
710 // nil means we don't know how to transfer stuff: user should do something 695 // nil means we don't know how to transfer stuff: user should do something
711 // anything other than function or table should not happen! 696 // anything other than function or table should not happen!
712 if (_type != LuaType::FUNCTION && _type != LuaType::TABLE) { 697 if (_type != LuaType::FUNCTION && _type != LuaType::TABLE) {
713 kLaneNameRegKey.pushValue(L1); // L1: ... f ... lane_name 698 kLaneNameRegKey.pushValue(L1); // L1: ... f ... lane_name
714 std::string_view const _from{ luaG_tostring(L1, kIdxTop) }; 699 std::string_view const _from{ luaW_tostring(L1, kIdxTop) };
715 lua_pop(L1, 1); // L1: ... f ... 700 lua_pop(L1, 1); // L1: ... f ...
716 kLaneNameRegKey.pushValue(L2); // L1: ... f ... L2: {} f lane_name 701 kLaneNameRegKey.pushValue(L2); // L1: ... f ... L2: {} f lane_name
717 std::string_view const _to{ luaG_tostring(L2, kIdxTop) }; 702 std::string_view const _to{ luaW_tostring(L2, kIdxTop) };
718 lua_pop(L2, 1); // L2: {} f 703 lua_pop(L2, 1); // L2: {} f
719 raise_luaL_error( 704 raise_luaL_error(
720 getErrL(), 705 getErrL(),
@@ -736,7 +721,7 @@ bool InterCopyContext::lookupUserdata() const
736[[nodiscard]] 721[[nodiscard]]
737bool InterCopyContext::tryCopyClonable() const 722bool InterCopyContext::tryCopyClonable() const
738{ 723{
739 SourceIndex const _L1_i{ luaG_absindex(L1, L1_i).value() }; 724 SourceIndex const _L1_i{ luaW_absindex(L1, L1_i).value() };
740 void* const _source{ lua_touserdata(L1, _L1_i) }; 725 void* const _source{ lua_touserdata(L1, _L1_i) };
741 726
742 STACK_CHECK_START_REL(L1, 0); 727 STACK_CHECK_START_REL(L1, 0);
@@ -744,7 +729,7 @@ bool InterCopyContext::tryCopyClonable() const
744 729
745 // Check if the source was already cloned during this copy 730 // Check if the source was already cloned during this copy
746 lua_pushlightuserdata(L2, _source); // L2: ... source 731 lua_pushlightuserdata(L2, _source); // L2: ... source
747 if (luaG_rawget(L2, L2_cache_i) != LuaType::NIL) { // L2: ... clone? 732 if (luaW_rawget(L2, L2_cache_i) != LuaType::NIL) { // L2: ... clone?
748 STACK_CHECK(L2, 1); 733 STACK_CHECK(L2, 1);
749 return true; 734 return true;
750 } else { 735 } else {
@@ -759,7 +744,7 @@ bool InterCopyContext::tryCopyClonable() const
759 } 744 }
760 745
761 // no __lanesclone? -> not clonable 746 // no __lanesclone? -> not clonable
762 if (luaG_getfield(L1, kIdxTop, "__lanesclone") == LuaType::NIL) { // L1: ... mt nil 747 if (luaW_getfield(L1, kIdxTop, "__lanesclone") == LuaType::NIL) { // L1: ... mt nil
763 lua_pop(L1, 2); // L1: ... 748 lua_pop(L1, 2); // L1: ...
764 STACK_CHECK(L1, 0); 749 STACK_CHECK(L1, 0);
765 return false; 750 return false;
@@ -769,10 +754,10 @@ bool InterCopyContext::tryCopyClonable() const
769 754
770 // we need to copy over the uservalues of the userdata as well 755 // we need to copy over the uservalues of the userdata as well
771 { 756 {
772 StackIndex const _mt{ luaG_absindex(L1, StackIndex{ -2 }) }; // L1: ... mt __lanesclone 757 StackIndex const _mt{ luaW_absindex(L1, StackIndex{ -2 }) }; // L1: ... mt __lanesclone
773 auto const userdata_size{ static_cast<size_t>(lua_rawlen(L1, _L1_i)) }; // make 32-bits builds happy 758 auto const userdata_size{ static_cast<size_t>(lua_rawlen(L1, _L1_i)) }; // make 32-bits builds happy
774 // extract all the uservalues, but don't transfer them yet 759 // extract all the uservalues, but don't transfer them yet
775 UserValueCount const _nuv{ luaG_getalluservalues(L1, _L1_i) }; // L1: ... mt __lanesclone [uv]* 760 UserValueCount const _nuv{ luaW_getalluservalues(L1, _L1_i) }; // L1: ... mt __lanesclone [uv]*
776 // create the clone userdata with the required number of uservalue slots 761 // create the clone userdata with the required number of uservalue slots
777 void* const _clone{ lua_newuserdatauv(L2, userdata_size, _nuv) }; // L2: ... u 762 void* const _clone{ lua_newuserdatauv(L2, userdata_size, _nuv) }; // L2: ... u
778 // copy the metatable in the target state, and give it to the clone we put there 763 // copy the metatable in the target state, and give it to the clone we put there
@@ -804,7 +789,7 @@ bool InterCopyContext::tryCopyClonable() const
804 // assign uservalues 789 // assign uservalues
805 UserValueIndex _uvi{ _nuv.value() }; 790 UserValueIndex _uvi{ _nuv.value() };
806 while (_uvi > 0) { 791 while (_uvi > 0) {
807 _c.L1_i = SourceIndex{ luaG_absindex(L1, kIdxTop).value() }; 792 _c.L1_i = SourceIndex{ luaW_absindex(L1, kIdxTop).value() };
808 if (_c.interCopyOne() != InterCopyResult::Success) { // L2: ... u uv 793 if (_c.interCopyOne() != InterCopyResult::Success) { // L2: ... u uv
809 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); 794 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
810 } 795 }
@@ -850,10 +835,10 @@ bool InterCopyContext::tryCopyDeep() const
850 STACK_CHECK_START_REL(L2, 0); 835 STACK_CHECK_START_REL(L2, 0);
851 836
852 // extract all uservalues of the source. unfortunately, the only way to know their count is to iterate until we fail 837 // extract all uservalues of the source. unfortunately, the only way to know their count is to iterate until we fail
853 UserValueCount const _nuv{ luaG_getalluservalues(L1, L1_i) }; // L1: ... deep ... [uv]* 838 UserValueCount const _nuv{ luaW_getalluservalues(L1, L1_i) }; // L1: ... deep ... [uv]*
854 STACK_CHECK(L1, _nuv); 839 STACK_CHECK(L1, _nuv);
855 840
856 DeepPrelude* const _deep{ *luaG_tofulluserdata<DeepPrelude*>(L1, L1_i) }; 841 DeepPrelude* const _deep{ *luaW_tofulluserdata<DeepPrelude*>(L1, L1_i) };
857 DeepFactory::PushDeepProxy(L2, _deep, _nuv, mode, getErrL()); // L1: ... deep ... [uv]* L2: deep 842 DeepFactory::PushDeepProxy(L2, _deep, _nuv, mode, getErrL()); // L1: ... deep ... [uv]* L2: deep
858 843
859 // transfer all uservalues of the source in the destination 844 // transfer all uservalues of the source in the destination
@@ -863,7 +848,7 @@ bool InterCopyContext::tryCopyDeep() const
863 STACK_GROW(L2, _nuv); 848 STACK_GROW(L2, _nuv);
864 UserValueIndex _uvi{ _nuv.value() }; 849 UserValueIndex _uvi{ _nuv.value() };
865 while (_uvi) { 850 while (_uvi) {
866 _c.L1_i = SourceIndex{ luaG_absindex(L1, kIdxTop).value() }; 851 _c.L1_i = SourceIndex{ luaW_absindex(L1, kIdxTop).value() };
867 if (_c.interCopyOne() != InterCopyResult::Success) { // L1: ... deep ... [uv]* L2: deep uv 852 if (_c.interCopyOne() != InterCopyResult::Success) { // L1: ... deep ... [uv]* L2: deep uv
868 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); 853 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
869 } 854 }
@@ -911,7 +896,7 @@ bool InterCopyContext::interCopyFunction() const
911 lua_getupvalue(L1, L1_i, 2); // L1: ... u 896 lua_getupvalue(L1, L1_i, 2); // L1: ... u
912 void* _source{ lua_touserdata(L1, -1) }; 897 void* _source{ lua_touserdata(L1, -1) };
913 lua_pushlightuserdata(L2, _source); // L2: ... source 898 lua_pushlightuserdata(L2, _source); // L2: ... source
914 if (luaG_rawget(L2, L2_cache_i) != LuaType::NIL) { // L2: ... u? 899 if (luaW_rawget(L2, L2_cache_i) != LuaType::NIL) { // L2: ... u?
915 lua_pop(L1, 1); // L1: ... 900 lua_pop(L1, 1); // L1: ...
916 STACK_CHECK(L1, 0); 901 STACK_CHECK(L1, 0);
917 STACK_CHECK(L2, 1); 902 STACK_CHECK(L2, 1);
@@ -933,7 +918,7 @@ bool InterCopyContext::interCopyFunction() const
933 auto const _userdata_size{ static_cast<size_t>(lua_rawlen(L1, kIdxTop)) }; // make 32-bits builds happy 918 auto const _userdata_size{ static_cast<size_t>(lua_rawlen(L1, kIdxTop)) }; // make 32-bits builds happy
934 { 919 {
935 // extract uservalues (don't transfer them yet) 920 // extract uservalues (don't transfer them yet)
936 UserValueCount const _nuv{ luaG_getalluservalues(L1, source_i) }; // L1: ... u [uv]* 921 UserValueCount const _nuv{ luaW_getalluservalues(L1, source_i) }; // L1: ... u [uv]*
937 STACK_CHECK(L1, _nuv + 1); 922 STACK_CHECK(L1, _nuv + 1);
938 // create the clone userdata with the required number of uservalue slots 923 // create the clone userdata with the required number of uservalue slots
939 _clone = lua_newuserdatauv(L2, _userdata_size, _nuv); // L2: ... mt u 924 _clone = lua_newuserdatauv(L2, _userdata_size, _nuv); // L2: ... mt u
@@ -948,7 +933,7 @@ bool InterCopyContext::interCopyFunction() const
948 InterCopyContext _c{ *this }; 933 InterCopyContext _c{ *this };
949 UserValueIndex _uvi{ _nuv.value() }; 934 UserValueIndex _uvi{ _nuv.value() };
950 while (_uvi > 0) { 935 while (_uvi > 0) {
951 _c.L1_i = SourceIndex{ luaG_absindex(L1, kIdxTop).value() }; 936 _c.L1_i = SourceIndex{ luaW_absindex(L1, kIdxTop).value() };
952 if (_c.interCopyOne() != InterCopyResult::Success) { // L2: ... mt u uv 937 if (_c.interCopyOne() != InterCopyResult::Success) { // L2: ... mt u uv
953 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); 938 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
954 } 939 }
@@ -965,9 +950,9 @@ bool InterCopyContext::interCopyFunction() const
965 // perform the custom cloning part 950 // perform the custom cloning part
966 lua_insert(L2, -2); // L2: ... u mt 951 lua_insert(L2, -2); // L2: ... u mt
967 // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with 952 // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with
968 LuaType const _funcType{ luaG_getfield(L2, kIdxTop, "__lanesclone") }; // L2: ... u mt __lanesclone 953 LuaType const _funcType{ luaW_getfield(L2, kIdxTop, "__lanesclone") }; // L2: ... u mt __lanesclone
969 if (_funcType != LuaType::FUNCTION) { 954 if (_funcType != LuaType::FUNCTION) {
970 raise_luaL_error(getErrL(), "INTERNAL ERROR: __lanesclone is a %s, not a function", luaG_typename(L2, _funcType).data()); 955 raise_luaL_error(getErrL(), "INTERNAL ERROR: __lanesclone is a %s, not a function", luaW_typename(L2, _funcType).data());
971 } 956 }
972 lua_remove(L2, -2); // L2: ... u __lanesclone 957 lua_remove(L2, -2); // L2: ... u __lanesclone
973 lua_pushlightuserdata(L2, _clone); // L2: ... u __lanesclone clone 958 lua_pushlightuserdata(L2, _clone); // L2: ... u __lanesclone clone
@@ -1060,9 +1045,9 @@ bool InterCopyContext::interCopyNumber() const
1060[[nodiscard]] 1045[[nodiscard]]
1061bool InterCopyContext::interCopyString() const 1046bool InterCopyContext::interCopyString() const
1062{ 1047{
1063 std::string_view const _s{ luaG_tostring(L1, L1_i) }; 1048 std::string_view const _s{ luaW_tostring(L1, L1_i) };
1064 DEBUGSPEW_CODE(DebugSpew(nullptr) << "'" << _s << "'" << std::endl); 1049 DEBUGSPEW_CODE(DebugSpew(nullptr) << "'" << _s << "'" << std::endl);
1065 luaG_pushstring(L2, _s); 1050 luaW_pushstring(L2, _s);
1066 return true; 1051 return true;
1067} 1052}
1068 1053
@@ -1154,7 +1139,7 @@ bool InterCopyContext::interCopyUserdata() const
1154 1139
1155 // Last, let's try to see if this userdata is special (aka is it some userdata that we registered in our lookup databases during module registration?) 1140 // Last, let's try to see if this userdata is special (aka is it some userdata that we registered in our lookup databases during module registration?)
1156 if (lookupUserdata()) { 1141 if (lookupUserdata()) {
1157 LUA_ASSERT(L1, luaG_type(L2, kIdxTop) == LuaType::USERDATA || (lua_tocfunction(L2, kIdxTop) == userdata_lookup_sentinel)); // from lookup data. can also be userdata_lookup_sentinel if this is a userdata we know 1142 LUA_ASSERT(L1, luaW_type(L2, kIdxTop) == LuaType::USERDATA || (lua_tocfunction(L2, kIdxTop) == userdata_lookup_sentinel)); // from lookup data. can also be userdata_lookup_sentinel if this is a userdata we know
1158 return true; 1143 return true;
1159 } 1144 }
1160 1145
@@ -1291,8 +1276,8 @@ InterCopyResult InterCopyContext::interCopyPackage() const
1291 } const _onExit{ L2 }; 1276 } const _onExit{ L2 };
1292 1277
1293 STACK_CHECK_START_REL(L1, 0); 1278 STACK_CHECK_START_REL(L1, 0);
1294 if (luaG_type(L1, L1_i) != LuaType::TABLE) { 1279 if (luaW_type(L1, L1_i) != LuaType::TABLE) {
1295 std::string_view const _msg{ luaG_pushstring(L1, "expected package as table, got a %s", luaL_typename(L1, L1_i)) }; 1280 std::string_view const _msg{ luaW_pushstring(L1, "expected package as table, got a %s", luaL_typename(L1, L1_i)) };
1296 STACK_CHECK(L1, 1); 1281 STACK_CHECK(L1, 1);
1297 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later 1282 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
1298 if (mode == LookupMode::LaneBody) { 1283 if (mode == LookupMode::LaneBody) {
@@ -1300,7 +1285,7 @@ InterCopyResult InterCopyContext::interCopyPackage() const
1300 } 1285 }
1301 return InterCopyResult::Error; 1286 return InterCopyResult::Error;
1302 } 1287 }
1303 if (luaG_getmodule(L2, LUA_LOADLIBNAME) == LuaType::NIL) { // package library not loaded: do nothing 1288 if (luaW_getmodule(L2, LUA_LOADLIBNAME) == LuaType::NIL) { // package library not loaded: do nothing
1304 DEBUGSPEW_CODE(DebugSpew(U) << "'package' not loaded, nothing to do" << std::endl); 1289 DEBUGSPEW_CODE(DebugSpew(U) << "'package' not loaded, nothing to do" << std::endl);
1305 STACK_CHECK(L1, 0); 1290 STACK_CHECK(L1, 0);
1306 return InterCopyResult::Success; 1291 return InterCopyResult::Success;
@@ -1317,7 +1302,7 @@ InterCopyResult InterCopyContext::interCopyPackage() const
1317 continue; 1302 continue;
1318 } 1303 }
1319 DEBUGSPEW_CODE(DebugSpew(U) << "package." << _entry << std::endl); 1304 DEBUGSPEW_CODE(DebugSpew(U) << "package." << _entry << std::endl);
1320 if (luaG_getfield(L1, L1_i, _entry) == LuaType::NIL) { 1305 if (luaW_getfield(L1, L1_i, _entry) == LuaType::NIL) {
1321 lua_pop(L1, 1); 1306 lua_pop(L1, 1);
1322 } else { 1307 } else {
1323 { 1308 {
@@ -1328,9 +1313,9 @@ InterCopyResult InterCopyContext::interCopyPackage() const
1328 STACK_CHECK(L1, 0); 1313 STACK_CHECK(L1, 0);
1329 } 1314 }
1330 if (_result == InterCopyResult::Success) { 1315 if (_result == InterCopyResult::Success) {
1331 luaG_setfield(L2, StackIndex{ -2 }, _entry); // set package[entry] 1316 luaW_setfield(L2, StackIndex{ -2 }, _entry); // set package[entry]
1332 } else { 1317 } else {
1333 std::string_view const _msg{ luaG_pushstring(L1, "failed to copy package.%s", _entry.data()) }; 1318 std::string_view const _msg{ luaW_pushstring(L1, "failed to copy package.%s", _entry.data()) };
1334 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later 1319 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
1335 if (mode == LookupMode::LaneBody) { 1320 if (mode == LookupMode::LaneBody) {
1336 raise_luaL_error(getErrL(), _msg); 1321 raise_luaL_error(getErrL(), _msg);
diff --git a/src/keeper.cpp b/src/keeper.cpp
index c8c470f..4af0d86 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -77,7 +77,7 @@ class KeyUD final
77 77
78 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents 78 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents
79 [[nodiscard]] 79 [[nodiscard]]
80 static void* operator new([[maybe_unused]] size_t size_, KeeperState L_) noexcept { return luaG_newuserdatauv<KeyUD>(L_, UserValueCount{ 1 }); } 80 static void* operator new([[maybe_unused]] size_t size_, KeeperState L_) noexcept { return luaW_newuserdatauv<KeyUD>(L_, UserValueCount{ 1 }); }
81 // always embedded somewhere else or "in-place constructed" as a full userdata 81 // always embedded somewhere else or "in-place constructed" as a full userdata
82 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception 82 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
83 static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] KeeperState L_) { LUA_ASSERT(L_, !"should never be called"); } 83 static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] KeeperState L_) { LUA_ASSERT(L_, !"should never be called"); }
@@ -104,6 +104,7 @@ class KeyUD final
104 104
105// ################################################################################################# 105// #################################################################################################
106 106
107[[nodiscard]]
107bool KeyUD::changeLimit(LindaLimit const limit_) 108bool KeyUD::changeLimit(LindaLimit const limit_)
108{ 109{
109 bool const _newSlackAvailable{ 110 bool const _newSlackAvailable{
@@ -127,6 +128,7 @@ LindaRestrict KeyUD::changeRestrict(LindaRestrict const restrict_)
127 128
128// in: nothing 129// in: nothing
129// out: { first = 1, count = 0, limit = -1} 130// out: { first = 1, count = 0, limit = -1}
131[[nodiscard]]
130KeyUD* KeyUD::Create(KeeperState const K_) 132KeyUD* KeyUD::Create(KeeperState const K_)
131{ 133{
132 STACK_GROW(K_, 2); 134 STACK_GROW(K_, 2);
@@ -141,9 +143,10 @@ KeyUD* KeyUD::Create(KeeperState const K_)
141 143
142// ################################################################################################# 144// #################################################################################################
143 145
146[[nodiscard]]
144KeyUD* KeyUD::GetPtr(KeeperState const K_, StackIndex const idx_) 147KeyUD* KeyUD::GetPtr(KeeperState const K_, StackIndex const idx_)
145{ 148{
146 return luaG_tofulluserdata<KeyUD>(K_, idx_); 149 return luaW_tofulluserdata<KeyUD>(K_, idx_);
147} 150}
148 151
149// ################################################################################################# 152// #################################################################################################
@@ -181,6 +184,7 @@ void KeyUD::peek(KeeperState const K_, int const count_) const
181 184
182// in: fifo 185// in: fifo
183// out: remove the fifo table from the stack, push as many items as required on the stack (function assumes they exist in sufficient number) 186// out: remove the fifo table from the stack, push as many items as required on the stack (function assumes they exist in sufficient number)
187[[nodiscard]]
184int KeyUD::pop(KeeperState const K_, int const minCount_, int const maxCount_) 188int KeyUD::pop(KeeperState const K_, int const minCount_, int const maxCount_)
185{ 189{
186 if (count < minCount_) { 190 if (count < minCount_) {
@@ -191,23 +195,33 @@ int KeyUD::pop(KeeperState const K_, int const minCount_, int const maxCount_)
191 int const _popCount{ std::min(count, maxCount_) }; 195 int const _popCount{ std::min(count, maxCount_) };
192 LUA_ASSERT(K_, KeyUD::GetPtr(K_, kIdxTop) == this); // K_: ... this 196 LUA_ASSERT(K_, KeyUD::GetPtr(K_, kIdxTop) == this); // K_: ... this
193 prepareAccess(K_, kIdxTop); // K_: ... fifo 197 prepareAccess(K_, kIdxTop); // K_: ... fifo
198
199 STACK_CHECK_START_REL(K_, 0);
194 StackIndex const _fifo_idx{ lua_gettop(K_) }; 200 StackIndex const _fifo_idx{ lua_gettop(K_) };
195 // each iteration pushes a value on the stack! 201 // each iteration pushes a value on the stack!
196 STACK_GROW(K_, _popCount + 2); 202 STACK_GROW(K_, _popCount + 2);
197 // skip first item, we will push it last 203
198 for (int const _i : std::ranges::iota_view{ 1, _popCount }) { 204 // remove an element from fifo sequence and push it on the stack
205 auto _extractFifoItem = [K = K_, first = first, fifo_idx = lua_gettop(K_)](int const _i)
206 {
207 STACK_CHECK_START_REL(K, 0);
199 int const _at{ first + _i }; 208 int const _at{ first + _i };
200 // push item on the stack 209 // push item on the stack
201 lua_rawgeti(K_, _fifo_idx, _at); // K_: ... fifo val 210 lua_rawgeti(K, fifo_idx, _at); // K_: ... fifo val
202 // remove item from the fifo 211 // remove item from the fifo
203 lua_pushnil(K_); // K_: ... fifo val nil 212 lua_pushnil(K); // K_: ... fifo val nil
204 lua_rawseti(K_, _fifo_idx, _at); // K_: ... fifo val 213 lua_rawseti(K, fifo_idx, _at); // K_: ... fifo val
214 STACK_CHECK(K, 1);
215 };
216
217 // skip first item, we will push it last to avoid shifting the whole stack when removing 'fifo'
218 for (int const _i : std::ranges::iota_view{ 1, _popCount }) {
219 _extractFifoItem(_i); // K_: ... fifo val1...valN
205 } 220 }
206 // now process first item 221 // now process first item
207 lua_rawgeti(K_, _fifo_idx, first); // K_: ... fifo vals val 222 _extractFifoItem(0); // K_: ... fifo val1...valN val0
208 lua_pushnil(K_); // K_: ... fifo vals val nil 223 STACK_CHECK(K_, _popCount);
209 lua_rawseti(K_, _fifo_idx, first); // K_: ... fifo vals val 224 lua_replace(K_, _fifo_idx); // K_: ... val0...valN
210 lua_replace(K_, _fifo_idx); // K_: ... vals
211 225
212 // avoid ever-growing indexes by resetting each time we detect the fifo is empty 226 // avoid ever-growing indexes by resetting each time we detect the fifo is empty
213 int const _new_count{ count - _popCount }; 227 int const _new_count{ count - _popCount };
@@ -222,7 +236,7 @@ int KeyUD::pop(KeeperState const K_, int const minCount_, int const maxCount_)
222// replaces it by its uservalue on the stack (the table holding the fifo values) 236// replaces it by its uservalue on the stack (the table holding the fifo values)
223void KeyUD::prepareAccess(KeeperState const K_, StackIndex const idx_) const 237void KeyUD::prepareAccess(KeeperState const K_, StackIndex const idx_) const
224{ 238{
225 StackIndex const _idx{ luaG_absindex(K_, idx_) }; 239 StackIndex const _idx{ luaW_absindex(K_, idx_) };
226 LUA_ASSERT(K_, KeyUD::GetPtr(K_, idx_) == this); 240 LUA_ASSERT(K_, KeyUD::GetPtr(K_, idx_) == this);
227 // we can replace the key userdata in the stack without fear of it being GCed, there are other references around 241 // we can replace the key userdata in the stack without fear of it being GCed, there are other references around
228 lua_getiuservalue(K_, _idx, kContentsTableIndex); 242 lua_getiuservalue(K_, _idx, kContentsTableIndex);
@@ -233,9 +247,10 @@ void KeyUD::prepareAccess(KeeperState const K_, StackIndex const idx_) const
233 247
234// in: expect this val... on top of the stack 248// in: expect this val... on top of the stack
235// out: nothing, removes all pushed values from the stack 249// out: nothing, removes all pushed values from the stack
250[[nodiscard]]
236bool KeyUD::push(KeeperState const K_, int const count_, bool const enforceLimit_) 251bool KeyUD::push(KeeperState const K_, int const count_, bool const enforceLimit_)
237{ 252{
238 StackIndex const _fifoIdx{ luaG_absindex(K_, StackIndex{ -1 - count_ }) }; 253 StackIndex const _fifoIdx{ luaW_absindex(K_, StackIndex{ -1 - count_ }) };
239 LUA_ASSERT(K_, KeyUD::GetPtr(K_, _fifoIdx) == this); // K_: this val... 254 LUA_ASSERT(K_, KeyUD::GetPtr(K_, _fifoIdx) == this); // K_: this val...
240 if (enforceLimit_ && (limit >= 0) && (count + count_ > limit)) { // not enough room 255 if (enforceLimit_ && (limit >= 0) && (count + count_ > limit)) { // not enough room
241 return false; 256 return false;
@@ -259,16 +274,16 @@ bool KeyUD::push(KeeperState const K_, int const count_, bool const enforceLimit
259void KeyUD::pushFillStatus(KeeperState const K_) const 274void KeyUD::pushFillStatus(KeeperState const K_) const
260{ 275{
261 if (limit < 0) { 276 if (limit < 0) {
262 luaG_pushstring(K_, kUnder); 277 luaW_pushstring(K_, kUnder);
263 return; 278 return;
264 } 279 }
265 int const _delta{limit - count}; 280 int const _delta{ limit - count };
266 if (_delta < 0) { 281 if (_delta < 0) {
267 luaG_pushstring(K_, kOver); 282 luaW_pushstring(K_, kOver);
268 } else if (_delta > 0) { 283 } else if (_delta > 0) {
269 luaG_pushstring(K_, kUnder); 284 luaW_pushstring(K_, kUnder);
270 } else { 285 } else {
271 luaG_pushstring(K_, kExact); 286 luaW_pushstring(K_, kExact);
272 } 287 }
273} 288}
274 289
@@ -279,13 +294,16 @@ void KeyUD::PushFillStatus(KeeperState const K_, KeyUD const* const key_)
279 if (key_) { 294 if (key_) {
280 key_->pushFillStatus(K_); // _K: ... <fill status> 295 key_->pushFillStatus(K_); // _K: ... <fill status>
281 } else { 296 } else {
282 luaG_pushstring(K_, KeyUD::kUnder); // _K: ... "under" 297 luaW_pushstring(K_, KeyUD::kUnder); // _K: ... "under"
283 } 298 }
284} 299}
285 300
286// ################################################################################################# 301// #################################################################################################
287 302
288// expects 'this' on top of the stack 303// in: expects 'this' on top of the stack
304// out: nothing
305// returns true if the channel was full
306[[nodiscard]]
289bool KeyUD::reset(KeeperState const K_) 307bool KeyUD::reset(KeeperState const K_)
290{ 308{
291 LUA_ASSERT(K_, KeyUD::GetPtr(K_, kIdxTop) == this); 309 LUA_ASSERT(K_, KeyUD::GetPtr(K_, kIdxTop) == this);
@@ -312,10 +330,10 @@ static void PushKeysDB(KeeperState const K_, StackIndex const idx_)
312{ 330{
313 STACK_GROW(K_, 5); 331 STACK_GROW(K_, 5);
314 STACK_CHECK_START_REL(K_, 0); 332 STACK_CHECK_START_REL(K_, 0);
315 StackIndex const _absidx{ luaG_absindex(K_, idx_) }; 333 StackIndex const _absidx{ luaW_absindex(K_, idx_) };
316 kLindasRegKey.pushValue(K_); // K_: ... LindasDB 334 kLindasRegKey.pushValue(K_); // K_: ... LindasDB
317 lua_pushvalue(K_, _absidx); // K_: ... LindasDB linda 335 lua_pushvalue(K_, _absidx); // K_: ... LindasDB linda
318 if (luaG_rawget(K_, StackIndex{ -2 }) == LuaType::NIL) { // K_: ... LindasDB KeysDB 336 if (luaW_rawget(K_, StackIndex{ -2 }) == LuaType::NIL) { // K_: ... LindasDB KeysDB
319 lua_pop(K_, 1); // K_: ... LindasDB 337 lua_pop(K_, 1); // K_: ... LindasDB
320 // add a new KeysDB table for this linda 338 // add a new KeysDB table for this linda
321 lua_newtable(K_); // K_: ... LindasDB KeysDB 339 lua_newtable(K_); // K_: ... LindasDB KeysDB
@@ -337,6 +355,7 @@ static void PushKeysDB(KeeperState const K_, StackIndex const idx_)
337 355
338// in: linda 356// in: linda
339// out: nothing 357// out: nothing
358[[nodiscard]]
340int keepercall_collectgarbage(lua_State* const L_) 359int keepercall_collectgarbage(lua_State* const L_)
341{ 360{
342 lua_gc(L_, LUA_GCCOLLECT, 0); 361 lua_gc(L_, LUA_GCCOLLECT, 0);
@@ -346,6 +365,7 @@ int keepercall_collectgarbage(lua_State* const L_)
346// ################################################################################################# 365// #################################################################################################
347 366
348// in: linda [, key [, ...]] 367// in: linda [, key [, ...]]
368[[nodiscard]]
349int keepercall_count(lua_State* const L_) 369int keepercall_count(lua_State* const L_)
350{ 370{
351 KeeperState const _K{ L_ }; 371 KeeperState const _K{ L_ };
@@ -370,7 +390,7 @@ int keepercall_count(lua_State* const L_)
370 case 2: // _K: linda key 390 case 2: // _K: linda key
371 PushKeysDB(_K, StackIndex{ 1 }); // _K: linda key KeysDB 391 PushKeysDB(_K, StackIndex{ 1 }); // _K: linda key KeysDB
372 lua_replace(_K, 1); // _K: KeysDB key 392 lua_replace(_K, 1); // _K: KeysDB key
373 if (luaG_rawget(_K, StackIndex{ -2 }) == LuaType::NIL) { // the key is unknown // _K: KeysDB KeyUD|nil 393 if (luaW_rawget(_K, StackIndex{ -2 }) == LuaType::NIL) { // the key is unknown // _K: KeysDB KeyUD|nil
374 lua_remove(_K, -2); // _K: nil 394 lua_remove(_K, -2); // _K: nil
375 } else { // the key is known // _K: KeysDB KeyUD 395 } else { // the key is known // _K: KeysDB KeyUD
376 KeyUD* const _key{ KeyUD::GetPtr(_K, kIdxTop) }; 396 KeyUD* const _key{ KeyUD::GetPtr(_K, kIdxTop) };
@@ -409,6 +429,7 @@ int keepercall_count(lua_State* const L_)
409 429
410// in: linda 430// in: linda
411// not part of the linda public API, only used for cleanup at linda GC 431// not part of the linda public API, only used for cleanup at linda GC
432[[nodiscard]]
412int keepercall_destruct(lua_State* const L_) 433int keepercall_destruct(lua_State* const L_)
413{ 434{
414 STACK_GROW(L_, 3); 435 STACK_GROW(L_, 3);
@@ -427,6 +448,7 @@ int keepercall_destruct(lua_State* const L_)
427 448
428// in: linda_ud key [count] 449// in: linda_ud key [count]
429// out: N <N values>|kRestrictedChannel 450// out: N <N values>|kRestrictedChannel
451[[nodiscard]]
430int keepercall_get(lua_State* const L_) 452int keepercall_get(lua_State* const L_)
431{ 453{
432 KeeperState const _K{ L_ }; 454 KeeperState const _K{ L_ };
@@ -461,6 +483,7 @@ int keepercall_get(lua_State* const L_)
461 483
462// in: linda key [n|nil] 484// in: linda key [n|nil]
463// out: boolean, <fill status: string> 485// out: boolean, <fill status: string>
486[[nodiscard]]
464int keepercall_limit(lua_State* const L_) 487int keepercall_limit(lua_State* const L_)
465{ 488{
466 KeeperState const _K{ L_ }; 489 KeeperState const _K{ L_ };
@@ -480,7 +503,7 @@ int keepercall_limit(lua_State* const L_)
480 if (_key && _key->limit >= 0) { 503 if (_key && _key->limit >= 0) {
481 lua_pushinteger(_K, _key->limit); // _K: limit 504 lua_pushinteger(_K, _key->limit); // _K: limit
482 } else { // if the key doesn't exist, it is unlimited by default 505 } else { // if the key doesn't exist, it is unlimited by default
483 luaG_pushstring(_K, "unlimited"); // _K: "unlimited" 506 luaW_pushstring(_K, "unlimited"); // _K: "unlimited"
484 } 507 }
485 // return a single value: the limit of the key 508 // return a single value: the limit of the key
486 } else { 509 } else {
@@ -504,6 +527,7 @@ int keepercall_limit(lua_State* const L_)
504 527
505// in: linda, key [, key]? 528// in: linda, key [, key]?
506// out: (key, val) or nothing 529// out: (key, val) or nothing
530[[nodiscard]]
507int keepercall_receive(lua_State* const L_) 531int keepercall_receive(lua_State* const L_)
508{ 532{
509 KeeperState const _K{ L_ }; 533 KeeperState const _K{ L_ };
@@ -548,6 +572,7 @@ int keepercall_receive(lua_State* const L_)
548// ################################################################################################# 572// #################################################################################################
549 573
550// in: linda key mincount [maxcount] 574// in: linda key mincount [maxcount]
575[[nodiscard]]
551int keepercall_receive_batched(lua_State* const L_) 576int keepercall_receive_batched(lua_State* const L_)
552{ 577{
553 KeeperState const _K{ L_ }; 578 KeeperState const _K{ L_ };
@@ -581,6 +606,7 @@ int keepercall_receive_batched(lua_State* const L_)
581 606
582// in: linda key [mode] 607// in: linda key [mode]
583// out: mode 608// out: mode
609[[nodiscard]]
584int keepercall_restrict(lua_State* const L_) 610int keepercall_restrict(lua_State* const L_)
585{ 611{
586 KeeperState const _K{ L_ }; 612 KeeperState const _K{ L_ };
@@ -591,7 +617,7 @@ int keepercall_restrict(lua_State* const L_)
591 if (_reading) { 617 if (_reading) {
592 return LindaRestrict::None; 618 return LindaRestrict::None;
593 } 619 }
594 std::string_view const _val{ luaG_tostring(_K, StackIndex{ 3 }) }; 620 std::string_view const _val{ luaW_tostring(_K, StackIndex{ 3 }) };
595 if (_val == "set/get") { 621 if (_val == "set/get") {
596 return LindaRestrict::SetGet; 622 return LindaRestrict::SetGet;
597 } 623 }
@@ -623,7 +649,7 @@ int keepercall_restrict(lua_State* const L_)
623 lua_settop(_K, 0); // _K: 649 lua_settop(_K, 0); // _K:
624 auto const _prevRstrct{ _key ? _key->restrict : LindaRestrict::None }; 650 auto const _prevRstrct{ _key ? _key->restrict : LindaRestrict::None };
625 // return a single value: the restrict mode of the key 651 // return a single value: the restrict mode of the key
626 luaG_pushstring(_K, _encodeRestrict(_prevRstrct)); // _K: _previous 652 luaW_pushstring(_K, _encodeRestrict(_prevRstrct)); // _K: _previous
627 } else { 653 } else {
628 if (_key == nullptr) { // _K: KeysDB key nil 654 if (_key == nullptr) { // _K: KeysDB key nil
629 lua_pop(_K, 1); // _K: KeysDB key 655 lua_pop(_K, 1); // _K: KeysDB key
@@ -635,7 +661,7 @@ int keepercall_restrict(lua_State* const L_)
635 // return true if we decide that blocked threads waiting to write on that key should be awakened 661 // return true if we decide that blocked threads waiting to write on that key should be awakened
636 // this is the case if we detect the key was full but it is no longer the case 662 // this is the case if we detect the key was full but it is no longer the case
637 LindaRestrict const _previous{ _key->changeRestrict(_rstrct) }; 663 LindaRestrict const _previous{ _key->changeRestrict(_rstrct) };
638 luaG_pushstring(_K, _encodeRestrict(_previous)); // _K: _previous 664 luaW_pushstring(_K, _encodeRestrict(_previous)); // _K: _previous
639 } 665 }
640 STACK_CHECK(_K, 1); 666 STACK_CHECK(_K, 1);
641 return 1; 667 return 1;
@@ -645,6 +671,7 @@ int keepercall_restrict(lua_State* const L_)
645 671
646// in: linda, key, ... 672// in: linda, key, ...
647// out: true|false|kRestrictedChannel 673// out: true|false|kRestrictedChannel
674[[nodiscard]]
648int keepercall_send(lua_State* const L_) 675int keepercall_send(lua_State* const L_)
649{ 676{
650 KeeperState const _K{ L_ }; 677 KeeperState const _K{ L_ };
@@ -653,7 +680,7 @@ int keepercall_send(lua_State* const L_)
653 PushKeysDB(_K, StackIndex{ 1 }); // _K: linda key val... KeysDB 680 PushKeysDB(_K, StackIndex{ 1 }); // _K: linda key val... KeysDB
654 // get the fifo associated to this key in this linda, create it if it doesn't exist 681 // get the fifo associated to this key in this linda, create it if it doesn't exist
655 lua_pushvalue(_K, 2); // _K: linda key val... KeysDB key 682 lua_pushvalue(_K, 2); // _K: linda key val... KeysDB key
656 if (luaG_rawget(_K, StackIndex{ -2 }) == LuaType::NIL) { // _K: linda key val... KeysDB KeyUD|nil 683 if (luaW_rawget(_K, StackIndex{ -2 }) == LuaType::NIL) { // _K: linda key val... KeysDB KeyUD|nil
657 lua_pop(_K, 1); // _K: linda key val... KeysDB 684 lua_pop(_K, 1); // _K: linda key val... KeysDB
658 std::ignore = KeyUD::Create(KeeperState{ _K }); // _K: linda key val... KeysDB KeyUD 685 std::ignore = KeyUD::Create(KeeperState{ _K }); // _K: linda key val... KeysDB KeyUD
659 // KeysDB[key] = KeyUD 686 // KeysDB[key] = KeyUD
@@ -684,6 +711,7 @@ int keepercall_send(lua_State* const L_)
684 711
685// in: linda key [val...] 712// in: linda key [val...]
686// out: true if the linda was full but it's no longer the case, else false, or kRestrictedChannel if the key is restricted 713// out: true if the linda was full but it's no longer the case, else false, or kRestrictedChannel if the key is restricted
714[[nodiscard]]
687int keepercall_set(lua_State* const L_) 715int keepercall_set(lua_State* const L_)
688{ 716{
689 KeeperState const _K{ L_ }; 717 KeeperState const _K{ L_ };
@@ -753,6 +781,7 @@ int keepercall_set(lua_State* const L_)
753 * 781 *
754 * Returns: number of return values (pushed to 'L'), unset in case of error 782 * Returns: number of return values (pushed to 'L'), unset in case of error
755 */ 783 */
784[[nodiscard]]
756KeeperCallResult keeper_call(KeeperState const K_, keeper_api_t const func_, lua_State* const L_, Linda* const linda_, StackIndex const starting_index_) 785KeeperCallResult keeper_call(KeeperState const K_, keeper_api_t const func_, lua_State* const L_, Linda* const linda_, StackIndex const starting_index_)
757{ 786{
758 KeeperCallResult _result; 787 KeeperCallResult _result;
@@ -822,6 +851,7 @@ KeeperCallResult keeper_call(KeeperState const K_, keeper_api_t const func_, lua
822// } 851// }
823// ... 852// ...
824// } 853// }
854[[nodiscard]]
825int Keeper::PushLindaStorage(Linda& linda_, DestState const L_) 855int Keeper::PushLindaStorage(Linda& linda_, DestState const L_)
826{ 856{
827 Keeper* const _keeper{ linda_.whichKeeper() }; 857 Keeper* const _keeper{ linda_.whichKeeper() };
@@ -833,7 +863,7 @@ int Keeper::PushLindaStorage(Linda& linda_, DestState const L_)
833 STACK_CHECK_START_REL(_K, 0); 863 STACK_CHECK_START_REL(_K, 0);
834 kLindasRegKey.pushValue(_K); // _K: LindasDB L_: 864 kLindasRegKey.pushValue(_K); // _K: LindasDB L_:
835 lua_pushlightuserdata(_K, &linda_); // _K: LindasDB linda L_: 865 lua_pushlightuserdata(_K, &linda_); // _K: LindasDB linda L_:
836 LuaType const _type{ luaG_rawget(_K, StackIndex{ -2 }) }; // _K: LindasDB KeysDB L_: 866 LuaType const _type{ luaW_rawget(_K, StackIndex{ -2 }) }; // _K: LindasDB KeysDB L_:
837 lua_remove(_K, -2); // _K: KeysDB L_: 867 lua_remove(_K, -2); // _K: KeysDB L_:
838 if (_type != LuaType::TABLE) { // possible if we didn't send anything through that linda 868 if (_type != LuaType::TABLE) { // possible if we didn't send anything through that linda
839 lua_pop(_K, 1); // _K: L_: 869 lua_pop(_K, 1); // _K: L_:
@@ -870,20 +900,20 @@ int Keeper::PushLindaStorage(Linda& linda_, DestState const L_)
870 if (_key->limit >= 0) { 900 if (_key->limit >= 0) {
871 lua_pushinteger(L_, _key->limit); // _K: KeysDB key L_: out key keyout fifo limit 901 lua_pushinteger(L_, _key->limit); // _K: KeysDB key L_: out key keyout fifo limit
872 } else { 902 } else {
873 luaG_pushstring(L_, "unlimited"); // _K: KeysDB key L_: out key keyout fifo limit 903 luaW_pushstring(L_, "unlimited"); // _K: KeysDB key L_: out key keyout fifo limit
874 } 904 }
875 STACK_CHECK(L_, 5); 905 STACK_CHECK(L_, 5);
876 lua_setfield(L_, -3, "limit"); // _K: KeysDB key L_: out key keyout fifo 906 lua_setfield(L_, -3, "limit"); // _K: KeysDB key L_: out key keyout fifo
877 // keyout.restrict 907 // keyout.restrict
878 switch (_key->restrict) { 908 switch (_key->restrict) {
879 case LindaRestrict::None: 909 case LindaRestrict::None:
880 luaG_pushstring(L_, "none"); // _K: KeysDB key L_: out key keyout fifo restrict 910 luaW_pushstring(L_, "none"); // _K: KeysDB key L_: out key keyout fifo restrict
881 break; 911 break;
882 case LindaRestrict::SetGet: 912 case LindaRestrict::SetGet:
883 luaG_pushstring(L_, "set/get"); // _K: KeysDB key L_: out key keyout fifo restrict 913 luaW_pushstring(L_, "set/get"); // _K: KeysDB key L_: out key keyout fifo restrict
884 break; 914 break;
885 case LindaRestrict::SendReceive: 915 case LindaRestrict::SendReceive:
886 luaG_pushstring(L_, "send/receive"); // _K: KeysDB key L_: out key keyout fifo restrict 916 luaW_pushstring(L_, "send/receive"); // _K: KeysDB key L_: out key keyout fifo restrict
887 break; 917 break;
888 } 918 }
889 STACK_CHECK(L_, 5); 919 STACK_CHECK(L_, 5);
@@ -951,16 +981,16 @@ void Keepers::collectGarbage()
951 981
952// ################################################################################################# 982// #################################################################################################
953 983
954 984[[nodiscard]]
955void Keepers::close() 985bool Keepers::close()
956{ 986{
957 if (isClosing.test_and_set(std::memory_order_release)) { 987 if (isClosing.test_and_set(std::memory_order_release)) {
958 assert(false); // should never close more than once in practice 988 return false; // should never close more than once in practice
959 return;
960 } 989 }
961 990
991 // We may have not initialized the keepers if an error was raised in Universe::Create because of bad settings
962 if (std::holds_alternative<std::monostate>(keeper_array)) { 992 if (std::holds_alternative<std::monostate>(keeper_array)) {
963 return; 993 return true;
964 } 994 }
965 995
966 auto _closeOneKeeper = [](Keeper& keeper_) { 996 auto _closeOneKeeper = [](Keeper& keeper_) {
@@ -989,6 +1019,7 @@ void Keepers::close()
989 } 1019 }
990 1020
991 keeper_array.emplace<std::monostate>(); 1021 keeper_array.emplace<std::monostate>();
1022 return true;
992} 1023}
993 1024
994// ################################################################################################# 1025// #################################################################################################
@@ -1059,7 +1090,7 @@ void Keepers::initialize(Universe& U_, lua_State* L_, size_t const nbKeepers_, i
1059 keeper_.K = _K; 1090 keeper_.K = _K;
1060 1091
1061 // Give a name to the state 1092 // Give a name to the state
1062 luaG_pushstring(_K, "Keeper #%d", i_ + 1); // L_: settings _K: "Keeper #n" 1093 luaW_pushstring(_K, "Keeper #%d", i_ + 1); // L_: settings _K: "Keeper #n"
1063 if constexpr (HAVE_DECODA_SUPPORT()) { 1094 if constexpr (HAVE_DECODA_SUPPORT()) {
1064 lua_pushvalue(_K, -1); // _K: "Keeper #n" Keeper #n" 1095 lua_pushvalue(_K, -1); // _K: "Keeper #n" Keeper #n"
1065 lua_setglobal(_K, "decoda_name"); // L_: settings _K: "Keeper #n" 1096 lua_setglobal(_K, "decoda_name"); // L_: settings _K: "Keeper #n"
@@ -1081,9 +1112,9 @@ void Keepers::initialize(Universe& U_, lua_State* L_, size_t const nbKeepers_, i
1081 STACK_CHECK(_K, 0); 1112 STACK_CHECK(_K, 0);
1082 1113
1083 // copy package.path and package.cpath from the source state 1114 // copy package.path and package.cpath from the source state
1084 if (luaG_getmodule(L, LUA_LOADLIBNAME) != LuaType::NIL) { // L_: settings package _K: 1115 if (luaW_getmodule(L, LUA_LOADLIBNAME) != LuaType::NIL) { // L_: settings package _K:
1085 // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately 1116 // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately
1086 InterCopyContext _c{ U, DestState{ _K.value() }, SourceState{ L }, {}, SourceIndex{ luaG_absindex(L, kIdxTop).value() }, {}, LookupMode::ToKeeper, {} }; 1117 InterCopyContext _c{ U, DestState{ _K.value() }, SourceState{ L }, {}, SourceIndex{ luaW_absindex(L, kIdxTop).value() }, {}, LookupMode::ToKeeper, {} };
1087 if (_c.interCopyPackage() != InterCopyResult::Success) { // L_: settings ... error_msg _K: 1118 if (_c.interCopyPackage() != InterCopyResult::Success) { // L_: settings ... error_msg _K:
1088 // if something went wrong, the error message is at the top of the stack 1119 // if something went wrong, the error message is at the top of the stack
1089 lua_remove(L, -2); // L_: settings error_msg 1120 lua_remove(L, -2); // L_: settings error_msg
diff --git a/src/keeper.hpp b/src/keeper.hpp
index f1083b3..955577c 100644
--- a/src/keeper.hpp
+++ b/src/keeper.hpp
@@ -68,7 +68,8 @@ struct Keepers
68 68
69 Keepers() = default; 69 Keepers() = default;
70 void collectGarbage(); 70 void collectGarbage();
71 void close(); 71 [[nodiscard]]
72 bool close();
72 [[nodiscard]] 73 [[nodiscard]]
73 Keeper* getKeeper(KeeperIndex idx_); 74 Keeper* getKeeper(KeeperIndex idx_);
74 [[nodiscard]] 75 [[nodiscard]]
diff --git a/src/lane.cpp b/src/lane.cpp
index 5cebdfa..b23ff78 100644
--- a/src/lane.cpp
+++ b/src/lane.cpp
@@ -49,7 +49,7 @@ static LUAG_FUNC(lane_get_threadname)
49{ 49{
50 Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) }; 50 Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) };
51 luaL_argcheck(L_, lua_gettop(L_) == 1, 2, "too many arguments"); 51 luaL_argcheck(L_, lua_gettop(L_) == 1, 2, "too many arguments");
52 luaG_pushstring(L_, _lane->getDebugName()); 52 luaW_pushstring(L_, _lane->getDebugName());
53 return 1; 53 return 1;
54} 54}
55 55
@@ -85,17 +85,17 @@ static LUAG_FUNC(set_finalizer)
85static LUAG_FUNC(lane_threadname) 85static LUAG_FUNC(lane_threadname)
86{ 86{
87 // C s_lane structure is a light userdata upvalue 87 // C s_lane structure is a light userdata upvalue
88 Lane* const _lane{ luaG_tolightuserdata<Lane>(L_, StackIndex{ lua_upvalueindex(1) }) }; 88 Lane* const _lane{ luaW_tolightuserdata<Lane>(L_, StackIndex{ lua_upvalueindex(1) }) };
89 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 89 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
90 if (lua_gettop(L_) == 1) { 90 if (lua_gettop(L_) == 1) {
91 lua_settop(L_, 1); 91 lua_settop(L_, 1);
92 STACK_CHECK_START_REL(L_, 0); 92 STACK_CHECK_START_REL(L_, 0);
93 _lane->storeDebugName(luaG_tostring(L_, kIdxTop)); 93 _lane->storeDebugName(luaW_tostring(L_, kIdxTop));
94 _lane->applyDebugName(); 94 _lane->applyDebugName();
95 STACK_CHECK(L_, 0); 95 STACK_CHECK(L_, 0);
96 return 0; 96 return 0;
97 } else if (lua_gettop(L_) == 0) { 97 } else if (lua_gettop(L_) == 0) {
98 luaG_pushstring(L_, _lane->getDebugName()); 98 luaW_pushstring(L_, _lane->getDebugName());
99 return 1; 99 return 1;
100 } else { 100 } else {
101 raise_luaL_error(L_, "Wrong number of arguments"); 101 raise_luaL_error(L_, "Wrong number of arguments");
@@ -105,7 +105,7 @@ static LUAG_FUNC(lane_threadname)
105// ################################################################################################# 105// #################################################################################################
106 106
107//--- 107//---
108// [...] | [nil, err_any, stack_tbl]= lane:join([wait_secs]) 108// [true, ...] | [nil, err_any, stack_tbl]= lane:join([wait_secs])
109// 109//
110// timeout: returns nil 110// timeout: returns nil
111// done: returns return values (0..N) 111// done: returns return values (0..N)
@@ -117,7 +117,7 @@ static LUAG_FUNC(lane_join)
117 Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) }; 117 Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) };
118 118
119 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; 119 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
120 if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion 120 if (luaW_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion
121 lua_Duration const duration{ lua_tonumber(L_, 2) }; 121 lua_Duration const duration{ lua_tonumber(L_, 2) };
122 if (duration.count() >= 0.0) { 122 if (duration.count() >= 0.0) {
123 _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); 123 _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration);
@@ -128,71 +128,20 @@ static LUAG_FUNC(lane_join)
128 } else if (!lua_isnoneornil(L_, 2)) { 128 } else if (!lua_isnoneornil(L_, 2)) {
129 raise_luaL_argerror(L_, StackIndex{ 2 }, "incorrect duration type"); 129 raise_luaL_argerror(L_, StackIndex{ 2 }, "incorrect duration type");
130 } 130 }
131
132 lua_settop(L_, 1); // L_: lane 131 lua_settop(L_, 1); // L_: lane
133 bool const _done{ !_lane->thread.joinable() || _lane->waitForCompletion(_until) };
134 132
135 if (!_done) { 133 // wait until suspended or done
136 lua_pushnil(L_); // L_: lane nil 134 STACK_CHECK_START_REL(L_, 0); // L_: lane
137 luaG_pushstring(L_, "timeout"); // L_: lane nil "timeout" 135 if (!_lane->waitForJoin(L_, _until)) {
136 // in that case, should have pushed nil, "timeout"
137 STACK_CHECK(L_, 2);
138 return 2; 138 return 2;
139 } 139 }
140 STACK_CHECK(L_, 0); // L_: lane
141 // Thread is Done/Error/Cancelled; the Lane thread isn't working with it, therefore we can.
140 142
141 STACK_CHECK_START_REL(L_, 0); // L_: lane 143 std::ignore = _lane->storeResults(L_);
142 // Thread is Suspended or Done/Error/Cancelled; the Lane thread isn't working with it, therefore we can. 144 int const _ret{ _lane->pushStoredResults(L_) };
143
144 int _ret{ 0 };
145 int const _stored{ _lane->storeResults(L_) };
146 STACK_GROW(L_, std::max(3, _stored + 1));
147 switch (_lane->status.load(std::memory_order_acquire)) {
148 case Lane::Suspended: // got yielded values
149 case Lane::Done: // got regular return values
150 {
151 if (_stored == 0) {
152 raise_luaL_error(L_, _lane->L ? "First return value must be non-nil when using join()" : "Can't join() more than once or after indexing");
153 }
154 lua_getiuservalue(L_, StackIndex{ 1 }, UserValueIndex{ 1 }); // L_: lane {uv}
155 for (int _i = 2; _i <= _stored; ++_i) {
156 lua_rawgeti(L_, 2, _i); // L_: lane {uv} results2...N
157 }
158 lua_rawgeti(L_, 2, 1); // L_: lane {uv} results2...N result1
159 lua_replace(L_, 2); // L_: lane results
160 _ret = _stored;
161 }
162 break;
163
164 case Lane::Error:
165 {
166 LUA_ASSERT(L_, _stored == 2 || _stored == 3);
167 lua_getiuservalue(L_, StackIndex{ 1 }, UserValueIndex{ 1 }); // L_: lane {uv}
168 lua_rawgeti(L_, 2, 2); // L_: lane {uv} <error>
169 lua_rawgeti(L_, 2, 3); // L_: lane {uv} <error> <trace>|nil
170 if (lua_isnil(L_, -1)) {
171 lua_replace(L_, 2); // L_: lane nil <error>
172 } else {
173 lua_rawgeti(L_, 2, 1); // L_: lane {uv} <error> <trace> nil
174 lua_replace(L_, 2); // L_: lane nil <error> <trace>
175 }
176 _ret = lua_gettop(L_) - 1; // 2 or 3
177 }
178 break;
179
180 case Lane::Cancelled:
181 LUA_ASSERT(L_, _stored == 2);
182 lua_getiuservalue(L_, StackIndex{ 1 }, UserValueIndex{ 1 }); // L_: lane {uv}
183 lua_rawgeti(L_, 2, 2); // L_: lane {uv} cancel_error
184 lua_rawgeti(L_, 2, 1); // L_: lane {uv} cancel_error nil
185 lua_replace(L_, -3); // L_: lane nil cancel_error
186 LUA_ASSERT(L_, lua_isnil(L_, -2) && kCancelError.equals(L_, kIdxTop));
187 _ret = 2;
188 break;
189
190 default:
191 DEBUGSPEW_CODE(DebugSpew(nullptr) << "Unknown Lane status: " << static_cast<int>(_lane->status.load(std::memory_order_relaxed)) << std::endl);
192 LUA_ASSERT(L_, false);
193 _ret = 0;
194 }
195 STACK_CHECK(L_, _ret);
196 return _ret; 145 return _ret;
197} 146}
198 147
@@ -204,23 +153,13 @@ LUAG_FUNC(lane_resume)
204 Lane* const _lane{ ToLane(L_, kIdxSelf) }; 153 Lane* const _lane{ ToLane(L_, kIdxSelf) };
205 lua_State* const _L2{ _lane->L }; 154 lua_State* const _L2{ _lane->L };
206 155
207 // wait until the lane yields 156 // wait until the lane yields or returns
208 std::optional<Lane::Status> _hadToWait{}; // for debugging, if we ever raise the error just below 157 std::ignore = _lane->waitForCompletion(std::chrono::time_point<std::chrono::steady_clock>::max(), true);
209 { 158
210 std::unique_lock _guard{ _lane->doneMutex };
211 Lane::Status const _status{ _lane->status.load(std::memory_order_acquire) };
212 if (_status == Lane::Pending || _status == Lane::Running || _status == Lane::Resuming) {
213 _hadToWait = _status;
214 _lane->doneCondVar.wait(_guard, [_lane]() { return _lane->status.load(std::memory_order_acquire) == Lane::Suspended; });
215 }
216 }
217 if (_lane->status.load(std::memory_order_acquire) != Lane::Suspended) { 159 if (_lane->status.load(std::memory_order_acquire) != Lane::Suspended) {
218 if (_hadToWait) { 160 raise_luaL_error(L_, "cannot resume non-suspended coroutine Lane");
219 raise_luaL_error(L_, "INTERNAL ERROR: Lane status is %s instead of 'suspended'", _lane->threadStatusString().data());
220 } else {
221 raise_luaL_error(L_, "Can't resume a non-suspended coroutine-type Lane");
222 }
223 } 161 }
162
224 int const _nargs{ lua_gettop(L_) - 1 }; 163 int const _nargs{ lua_gettop(L_) - 1 };
225 int const _nresults{ lua_gettop(_L2) }; 164 int const _nresults{ lua_gettop(_L2) };
226 STACK_CHECK_START_ABS(L_, 1 + _nargs); // L_: self args... _L2: results... 165 STACK_CHECK_START_ABS(L_, 1 + _nargs); // L_: self args... _L2: results...
@@ -263,12 +202,17 @@ static int lane_index_number(lua_State* L_)
263 int const _key{ static_cast<int>(lua_tointeger(L_, 2)) }; 202 int const _key{ static_cast<int>(lua_tointeger(L_, 2)) };
264 lua_pop(L_, 1); // L_: lane 203 lua_pop(L_, 1); // L_: lane
265 204
205 // wait until suspended or done
206 STACK_CHECK_START_REL(L_, 0); // L_: lane
266 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; 207 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
267 if (!_lane->waitForCompletion(_until)) { 208 if (!_lane->waitForJoin(L_, _until)) {
268 raise_luaL_error(L_, "INTERNAL ERROR: Failed to join"); 209 // in that case, should have pushed nil, "timeout"
210 STACK_CHECK(L_, 2);
211 return 2;
269 } 212 }
213 STACK_CHECK(L_, 0); // L_: lane
214 // Thread is Done/Error/Cancelled; the Lane thread isn't working with it, therefore we can.
270 215
271 // make sure results are stored
272 int const _stored{ _lane->storeResults(L_) }; 216 int const _stored{ _lane->storeResults(L_) };
273 if (_key > _stored) { 217 if (_key > _stored) {
274 // get nil if indexing beyond the actual returned value count 218 // get nil if indexing beyond the actual returned value count
@@ -276,6 +220,7 @@ static int lane_index_number(lua_State* L_)
276 } else { 220 } else {
277 _lane->pushIndexedResult(L_, _key); // L_: lane result 221 _lane->pushIndexedResult(L_, _key); // L_: lane result
278 } 222 }
223
279 return 1; 224 return 1;
280} 225}
281 226
@@ -292,13 +237,13 @@ static int lane_index_string(lua_State* L_)
292 Lane* const _lane{ ToLane(L_, kIdxSelf) }; 237 Lane* const _lane{ ToLane(L_, kIdxSelf) };
293 LUA_ASSERT(L_, lua_gettop(L_) == 2); // L_: lane "key" 238 LUA_ASSERT(L_, lua_gettop(L_) == 2); // L_: lane "key"
294 239
295 std::string_view const _keystr{ luaG_tostring(L_, kIdxKey) }; 240 std::string_view const _keystr{ luaW_tostring(L_, kIdxKey) };
296 lua_settop(L_, 2); // keep only our original arguments on the stack 241 lua_settop(L_, 2); // keep only our original arguments on the stack
297 242
298 // look in metatable first 243 // look in metatable first
299 lua_getmetatable(L_, kIdxSelf); // L_: lane "key" mt 244 lua_getmetatable(L_, kIdxSelf); // L_: lane "key" mt
300 lua_replace(L_, -3); // L_: mt "key" 245 lua_replace(L_, -3); // L_: mt "key"
301 if (luaG_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // found something? // L_: mt value 246 if (luaW_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // found something? // L_: mt value
302 return 1; // done 247 return 1; // done
303 } 248 }
304 249
@@ -324,7 +269,7 @@ static LUAG_FUNC(lane_index)
324 Lane* const _lane{ ToLane(L_, kIdxSelf) }; 269 Lane* const _lane{ ToLane(L_, kIdxSelf) };
325 LUA_ASSERT(L_, lua_gettop(L_) == 2); 270 LUA_ASSERT(L_, lua_gettop(L_) == 2);
326 271
327 switch (luaG_type(L_, kKey)) { 272 switch (luaW_type(L_, kKey)) {
328 case LuaType::NUMBER: 273 case LuaType::NUMBER:
329 return lane_index_number(L_); // stack modification is undefined, returned value is at the top 274 return lane_index_number(L_); // stack modification is undefined, returned value is at the top
330 275
@@ -334,19 +279,19 @@ static LUAG_FUNC(lane_index)
334 default: // unknown key 279 default: // unknown key
335 lua_getmetatable(L_, kIdxSelf); // L_: mt 280 lua_getmetatable(L_, kIdxSelf); // L_: mt
336 kCachedError.pushKey(L_); // L_: mt kCachedError 281 kCachedError.pushKey(L_); // L_: mt kCachedError
337 if (luaG_rawget(L_, StackIndex{ -2 }) != LuaType::FUNCTION) { // L_: mt error() 282 if (luaW_rawget(L_, StackIndex{ -2 }) != LuaType::FUNCTION) { // L_: mt error()
338 raise_luaL_error(L_, "INTERNAL ERROR: cached error() is a %s, not a function", luaG_typename(L_, kIdxTop).data()); 283 raise_luaL_error(L_, "INTERNAL ERROR: cached error() is a %s, not a function", luaW_typename(L_, kIdxTop).data());
339 } 284 }
340 luaG_pushstring(L_, "Unknown key: "); // L_: mt error() "Unknown key: " 285 luaW_pushstring(L_, "Unknown key: "); // L_: mt error() "Unknown key: "
341 kCachedTostring.pushKey(L_); // L_: mt error() "Unknown key: " kCachedTostring 286 kCachedTostring.pushKey(L_); // L_: mt error() "Unknown key: " kCachedTostring
342 if (luaG_rawget(L_, StackIndex{ -4 }) != LuaType::FUNCTION) { // L_: mt error() "Unknown key: " tostring() 287 if (luaW_rawget(L_, StackIndex{ -4 }) != LuaType::FUNCTION) { // L_: mt error() "Unknown key: " tostring()
343 raise_luaL_error(L_, "INTERNAL ERROR: cached tostring() is a %s, not a function", luaG_typename(L_, kIdxTop).data()); 288 raise_luaL_error(L_, "INTERNAL ERROR: cached tostring() is a %s, not a function", luaW_typename(L_, kIdxTop).data());
344 } 289 }
345 lua_pushvalue(L_, kKey); // L_: mt error() "Unknown key: " tostring() k 290 lua_pushvalue(L_, kKey); // L_: mt error() "Unknown key: " tostring() k
346 lua_call(L_, 1, 1); // L_: mt error() "Unknown key: " "k" 291 lua_call(L_, 1, 1); // L_: mt error() "Unknown key: " "k"
347 lua_concat(L_, 2); // L_: mt error() "Unknown key: <k>" 292 lua_concat(L_, 2); // L_: mt error() "Unknown key: <k>"
348 lua_call(L_, 1, 0); // error( "Unknown key: " .. key) -> doesn't return // L_: mt 293 lua_call(L_, 1, 0); // error( "Unknown key: " .. key) -> doesn't return // L_: mt
349 raise_luaL_error(L_, "%s[%s]: should not get here!", _lane->getDebugName().data(), luaG_typename(L_, kKey).data()); 294 raise_luaL_error(L_, "%s[%s]: should not get here!", _lane->getDebugName().data(), luaW_typename(L_, kKey).data());
350 } 295 }
351} 296}
352 297
@@ -437,7 +382,7 @@ int Lane::LuaErrorHandler(lua_State* L_)
437 // table of { "sourcefile.lua:<line>", ... } 382 // table of { "sourcefile.lua:<line>", ... }
438 // 383 //
439 lua_newtable(L_); // L_: some_error {} 384 lua_newtable(L_); // L_: some_error {}
440 StackIndex const kIdxTraceTbl{ luaG_absindex(L_, kIdxTop) }; 385 StackIndex const kIdxTraceTbl{ luaW_absindex(L_, kIdxTop) };
441 386
442 // Best to start from level 1, but in some cases it might be a C function 387 // Best to start from level 1, but in some cases it might be a C function
443 // and we don't get '.currentline' for that. It's okay - just keep level 388 // and we don't get '.currentline' for that. It's okay - just keep level
@@ -448,25 +393,25 @@ int Lane::LuaErrorHandler(lua_State* L_)
448 lua_getinfo(L_, _extended ? "Sln" : "Sl", &_ar); 393 lua_getinfo(L_, _extended ? "Sln" : "Sl", &_ar);
449 if (_extended) { 394 if (_extended) {
450 lua_newtable(L_); // L_: some_error {} {} 395 lua_newtable(L_); // L_: some_error {} {}
451 StackIndex const kIdxFrameTbl{ luaG_absindex(L_, kIdxTop) }; 396 StackIndex const kIdxFrameTbl{ luaW_absindex(L_, kIdxTop) };
452 lua_pushstring(L_, _ar.source); // L_: some_error {} {} source 397 lua_pushstring(L_, _ar.source); // L_: some_error {} {} source
453 luaG_setfield(L_, kIdxFrameTbl, std::string_view{ "source" }); // L_: some_error {} {} 398 luaW_setfield(L_, kIdxFrameTbl, std::string_view{ "source" }); // L_: some_error {} {}
454 399
455 lua_pushinteger(L_, _ar.currentline); // L_: some_error {} {} currentline 400 lua_pushinteger(L_, _ar.currentline); // L_: some_error {} {} currentline
456 luaG_setfield(L_, kIdxFrameTbl, std::string_view{ "currentline" }); // L_: some_error {} {} 401 luaW_setfield(L_, kIdxFrameTbl, std::string_view{ "currentline" }); // L_: some_error {} {}
457 402
458 lua_pushstring(L_, _ar.name ? _ar.name : "<?>"); // L_: some_error {} {} name 403 lua_pushstring(L_, _ar.name ? _ar.name : "<?>"); // L_: some_error {} {} name
459 luaG_setfield(L_, kIdxFrameTbl, std::string_view{ "name" }); // L_: some_error {} {} 404 luaW_setfield(L_, kIdxFrameTbl, std::string_view{ "name" }); // L_: some_error {} {}
460 405
461 lua_pushstring(L_, _ar.namewhat); // L_: some_error {} {} namewhat 406 lua_pushstring(L_, _ar.namewhat); // L_: some_error {} {} namewhat
462 luaG_setfield(L_, kIdxFrameTbl, std::string_view{ "namewhat" }); // L_: some_error {} {} 407 luaW_setfield(L_, kIdxFrameTbl, std::string_view{ "namewhat" }); // L_: some_error {} {}
463 408
464 lua_pushstring(L_, _ar.what); // L_: some_error {} {} what 409 lua_pushstring(L_, _ar.what); // L_: some_error {} {} what
465 luaG_setfield(L_, kIdxFrameTbl, std::string_view{ "what" }); // L_: some_error {} {} 410 luaW_setfield(L_, kIdxFrameTbl, std::string_view{ "what" }); // L_: some_error {} {}
466 } else if (_ar.currentline > 0) { 411 } else if (_ar.currentline > 0) {
467 luaG_pushstring(L_, "%s:%d", _ar.short_src, _ar.currentline); // L_: some_error {} "blah:blah" 412 luaW_pushstring(L_, "%s:%d", _ar.short_src, _ar.currentline); // L_: some_error {} "blah:blah"
468 } else { 413 } else {
469 luaG_pushstring(L_, "%s:?", _ar.short_src); // L_: some_error {} "blah" 414 luaW_pushstring(L_, "%s:?", _ar.short_src); // L_: some_error {} "blah"
470 } 415 }
471 lua_rawseti(L_, kIdxTraceTbl, static_cast<lua_Integer>(_n)); // L_: some_error {} 416 lua_rawseti(L_, kIdxTraceTbl, static_cast<lua_Integer>(_n)); // L_: some_error {}
472 } 417 }
@@ -489,6 +434,7 @@ static int PushStackTrace(lua_State* const L_, Lane::ErrorTraceLevel const error
489 StackIndex const _top{ lua_gettop(L_) }; 434 StackIndex const _top{ lua_gettop(L_) };
490 switch (rc_) { 435 switch (rc_) {
491 case LuaError::OK: // no error, body return values are on the stack 436 case LuaError::OK: // no error, body return values are on the stack
437 case LuaError::YIELD:
492 break; 438 break;
493 439
494 case LuaError::ERRRUN: // cancellation or a runtime error 440 case LuaError::ERRRUN: // cancellation or a runtime error
@@ -502,7 +448,7 @@ static int PushStackTrace(lua_State* const L_, Lane::ErrorTraceLevel const error
502 448
503 // For cancellation the error message is kCancelError, and a stack trace isn't placed 449 // For cancellation the error message is kCancelError, and a stack trace isn't placed
504 // For other errors, the message can be whatever was thrown, and we should have a stack trace table 450 // For other errors, the message can be whatever was thrown, and we should have a stack trace table
505 LUA_ASSERT(L_, luaG_type(L_, StackIndex{ 1 + stk_base_ }) == (kCancelError.equals(L_, stk_base_) ? LuaType::NIL : LuaType::TABLE)); 451 LUA_ASSERT(L_, luaW_type(L_, StackIndex{ 1 + stk_base_ }) == (kCancelError.equals(L_, stk_base_) ? LuaType::NIL : LuaType::TABLE));
506 // Just leaving the stack trace table on the stack is enough to get it through to the master. 452 // Just leaving the stack trace table on the stack is enough to get it through to the master.
507 } else { 453 } else {
508 // any kind of error can be thrown with error(), or through a lane/linda cancellation 454 // any kind of error can be thrown with error(), or through a lane/linda cancellation
@@ -514,7 +460,7 @@ static int PushStackTrace(lua_State* const L_, Lane::ErrorTraceLevel const error
514 case LuaError::ERRERR: // error while running the error handler (if any, for example an out-of-memory condition) 460 case LuaError::ERRERR: // error while running the error handler (if any, for example an out-of-memory condition)
515 default: 461 default:
516 // the Lua core provides a string error message in those situations 462 // the Lua core provides a string error message in those situations
517 LUA_ASSERT(L_, (lua_gettop(L_) == stk_base_) && (luaG_type(L_, stk_base_) == LuaType::STRING)); 463 LUA_ASSERT(L_, (lua_gettop(L_) == stk_base_) && (luaW_type(L_, stk_base_) == LuaType::STRING));
518 break; 464 break;
519 } 465 }
520 return lua_gettop(L_) - _top; // either 0 or 1 466 return lua_gettop(L_) - _top; // either 0 or 1
@@ -563,7 +509,7 @@ static LuaError run_finalizers(Lane* const lane_, Lane::ErrorTraceLevel const er
563 LUA_ASSERT(_L, lua_isfunction(_L, -1)); 509 LUA_ASSERT(_L, lua_isfunction(_L, -1));
564 if (lua_rc_ != LuaError::OK) { // we have <error>, [trace] on the thread stack 510 if (lua_rc_ != LuaError::OK) { // we have <error>, [trace] on the thread stack
565 LUA_ASSERT(_L, lane_->nresults == 1 || lane_->nresults == 2); 511 LUA_ASSERT(_L, lane_->nresults == 1 || lane_->nresults == 2);
566 //std::string_view const _err_msg{ luaG_tostring(_L, 1) }; 512 //std::string_view const _err_msg{ luaW_tostring(_L, 1) };
567 if (lane_->isCoroutine()) { 513 if (lane_->isCoroutine()) {
568 // transfer them on the main state 514 // transfer them on the main state
569 lua_pushvalue(lane_->L, 1); 515 lua_pushvalue(lane_->L, 1);
@@ -615,7 +561,7 @@ static LuaError run_finalizers(Lane* const lane_, Lane::ErrorTraceLevel const er
615 561
616 if (lane_->isCoroutine()) { 562 if (lane_->isCoroutine()) {
617 // only the coroutine thread should remain on the master state when we are done 563 // only the coroutine thread should remain on the master state when we are done
618 LUA_ASSERT(_L, lua_gettop(_L) == 1 && luaG_type(_L, StackIndex{ 1 }) == LuaType::THREAD); 564 LUA_ASSERT(_L, lua_gettop(_L) == 1 && luaW_type(_L, StackIndex{ 1 }) == LuaType::THREAD);
619 } 565 }
620 566
621 return _rc; 567 return _rc;
@@ -711,7 +657,7 @@ static void lane_main(Lane* const lane_)
711 LuaError _rc{ LuaError::ERRRUN }; 657 LuaError _rc{ LuaError::ERRRUN };
712 if (lane_->status.load(std::memory_order_acquire) == Lane::Pending) { // nothing wrong happened during preparation, we can work 658 if (lane_->status.load(std::memory_order_acquire) == Lane::Pending) { // nothing wrong happened during preparation, we can work
713 // At this point, the lane function and arguments are on the stack, possibly preceded by the error handler 659 // At this point, the lane function and arguments are on the stack, possibly preceded by the error handler
714 int const _errorHandlerCount{ lane_->errorHandlerCount() }; 660 int const _errorHandlerCount{ lane_->errorHandlerCount() }; // no error handler for coroutines, ever.
715 int _nargs{ lua_gettop(_L) - 1 - _errorHandlerCount }; 661 int _nargs{ lua_gettop(_L) - 1 - _errorHandlerCount };
716 { 662 {
717 std::unique_lock _guard{ lane_->doneMutex }; 663 std::unique_lock _guard{ lane_->doneMutex };
@@ -724,27 +670,60 @@ static void lane_main(Lane* const lane_)
724 lane_->nresults = lua_gettop(_L) - _errorHandlerCount; 670 lane_->nresults = lua_gettop(_L) - _errorHandlerCount;
725 } else { 671 } else {
726 // S and L are different: we run as a coroutine in Lua thread L created in state S 672 // S and L are different: we run as a coroutine in Lua thread L created in state S
673 bool _shouldClose{ false };
727 do { 674 do {
728 // starting with Lua 5.4, lua_resume can leave more stuff on the stack below the actual yielded values. 675 // starting with Lua 5.4, lua_resume can leave more stuff on the stack below the actual yielded values.
729 // that's why we have lane_->nresults 676 // that's why we have lane_->nresults
730 _rc = luaG_resume(_L, nullptr, _nargs, &lane_->nresults); // L: eh? ... retvals|err... 677 _rc = luaW_resume(_L, nullptr, _nargs, &lane_->nresults); // L: ... retvals|err...
731 if (_rc == LuaError::YIELD) { 678 if (_rc == LuaError::YIELD) {
732 // change our status to suspended, and wait until someone wants us to resume
733 std::unique_lock _guard{ lane_->doneMutex };
734 lane_->status.store(Lane::Suspended, std::memory_order_release); // Running -> Suspended
735 lane_->doneCondVar.notify_one();
736 // wait until the user wants us to resume
737 // TODO: do I update waiting_on or not, so that the lane can be woken by cancellation requests here?
738 // lane_->waiting_on = &lane_->doneCondVar;
739 lane_->doneCondVar.wait(_guard, [lane_]() { return lane_->status.load(std::memory_order_acquire) == Lane::Resuming; });
740 // here lane_->doneMutex is locked again
741 // lane_->waiting_on = nullptr;
742 lane_->status.store(Lane::Running, std::memory_order_release); // Resuming -> Running
743 // on the stack we find the values pushed by lane:resume() 679 // on the stack we find the values pushed by lane:resume()
744 _nargs = lua_gettop(_L); 680 _nargs = lua_gettop(_L);
681 if (std::unique_lock _guard{ lane_->doneMutex }; true) {
682 // change our status to suspended, and wait until someone wants us to resume
683 lane_->status.store(Lane::Suspended, std::memory_order_release); // Running -> Suspended
684 lane_->doneCondVar.notify_one();
685 // wait until the user wants us to resume
686 // update waiting_on, so that the lane can be woken by cancellation requests here
687 lane_->waiting_on = &lane_->doneCondVar;
688 lane_->doneCondVar.wait(_guard,
689 [lane_,&_shouldClose]()
690 {
691 auto const _status{ lane_->status.load(std::memory_order_acquire) };
692 // wait interrupted because of a cancellation or join request means we have to abort the resume loop
693 _shouldClose = (_status == Lane::Closing);
694 return _shouldClose || (_status == Lane::Resuming) || (lane_->cancelRequest.load(std::memory_order_relaxed) != CancelRequest::None);
695 }
696 );
697 // here lane_->doneMutex is locked again
698 lane_->waiting_on = nullptr;
699 lane_->status.store(Lane::Running, std::memory_order_release); // Resuming -> Running
700 }
701 } else {
702 _shouldClose = true;
745 } 703 }
746 } while (_rc == LuaError::YIELD); 704 } while (!_shouldClose);
747 if (_rc != LuaError::OK) { // : err... 705 if (_rc == LuaError::YIELD) {
706#if LUA_VERSION_NUM >= 504
707 lua_State* const _S{ lane_->S };
708 STACK_CHECK_START_REL(_S, 0);
709 // lua_closethread cleans the stack, meaning we lose the yielded values! -> store
710 lua_xmove(_L, _S, lane_->nresults);
711 // lane is cancelled before completion (for example at Lanes shutdown), close everything
712 _rc = static_cast<LuaError>(lua_closethread(_L, nullptr)); // L: ... retvals|err <close_err>
713 // then restore the yielded values
714 if (_rc == LuaError::OK) {
715 lua_xmove(_S, _L, lane_->nresults);
716 } else {
717 lua_pop(_S, lane_->nresults);
718 }
719 STACK_CHECK(_S, 0);
720
721#else // LUA_VERSION_NUM
722 // Lua prior to 5.4 do not have lua_closethread.
723 _rc = LuaError::OK;
724#endif // LUA_VERSION_NUM
725 }
726 if (_rc != LuaError::OK) { // an error occurred // L: err...
748 // for some reason, in my tests with Lua 5.4, when the coroutine raises an error, I have 3 copies of it on the stack 727 // for some reason, in my tests with Lua 5.4, when the coroutine raises an error, I have 3 copies of it on the stack
749 // or false + the error message when running Lua 5.1 728 // or false + the error message when running Lua 5.1
750 // since the rest of our code wants only the error message, let us keep only the latter. 729 // since the rest of our code wants only the error message, let us keep only the latter.
@@ -769,7 +748,7 @@ static void lane_main(Lane* const lane_)
769 // in case of error and if it exists, fetch stack trace from registry and push it 748 // in case of error and if it exists, fetch stack trace from registry and push it
770 lane_->nresults += PushStackTrace(_L, lane_->errorTraceLevel, _rc, StackIndex{ 1 }); // L: retvals|error [trace] 749 lane_->nresults += PushStackTrace(_L, lane_->errorTraceLevel, _rc, StackIndex{ 1 }); // L: retvals|error [trace]
771 750
772 DEBUGSPEW_CODE(DebugSpew(lane_->U) << "Lane " << _L << " body: " << GetErrcodeName(_rc) << " (" << (kCancelError.equals(_L, StackIndex{ 1 }) ? "cancelled" : luaG_typename(_L, StackIndex{ 1 })) << ")" << std::endl); 751 DEBUGSPEW_CODE(DebugSpew(lane_->U) << "Lane " << _L << " body: " << GetErrcodeName(_rc) << " (" << (kCancelError.equals(_L, StackIndex{ 1 }) ? "cancelled" : luaW_typename(_L, StackIndex{ 1 })) << ")" << std::endl);
773 // Call finalizers, if the script has set them up. 752 // Call finalizers, if the script has set them up.
774 // If the lane is not a coroutine, there is only a regular state, so everything is the same whether we use S or L. 753 // If the lane is not a coroutine, there is only a regular state, so everything is the same whether we use S or L.
775 // If the lane is a coroutine, this has to be done from the master state (S), not the thread (L), because we can't lua_pcall in a thread state 754 // If the lane is a coroutine, this has to be done from the master state (S), not the thread (L), because we can't lua_pcall in a thread state
@@ -783,10 +762,12 @@ static void lane_main(Lane* const lane_)
783 if (lane_->selfdestructRemove()) { // check and remove (under lock!) 762 if (lane_->selfdestructRemove()) { // check and remove (under lock!)
784 // We're a free-running thread and no-one is there to clean us up. 763 // We're a free-running thread and no-one is there to clean us up.
785 lane_->closeState(); 764 lane_->closeState();
786 lane_->U->selfdestructMutex.lock(); 765
787 // done with lua_close(), terminal shutdown sequence may proceed 766 // let's try not to crash if the lane didn't terminate gracefully and the Universe met its end
788 lane_->U->selfdestructingCount.fetch_sub(1, std::memory_order_release); 767 if (!lane_->flaggedAfterUniverseGC.load(std::memory_order_relaxed)) {
789 lane_->U->selfdestructMutex.unlock(); 768 // done with lua_close(), terminal shutdown sequence may proceed
769 lane_->U->selfdestructingCount.fetch_sub(1, std::memory_order_release);
770 }
790 771
791 // we destroy ourselves, therefore our thread member too, from inside the thread body 772 // we destroy ourselves, therefore our thread member too, from inside the thread body
792 // detach so that we don't try to join, as this doesn't seem a good idea 773 // detach so that we don't try to join, as this doesn't seem a good idea
@@ -807,6 +788,8 @@ static void lane_main(Lane* const lane_)
807// ################################################################################################# 788// #################################################################################################
808 789
809#if LUA_VERSION_NUM >= 504 790#if LUA_VERSION_NUM >= 504
791
792// __close(lane_ud, <err>)
810static LUAG_FUNC(lane_close) 793static LUAG_FUNC(lane_close)
811{ 794{
812 [[maybe_unused]] Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) }; // L_: lane err|nil 795 [[maybe_unused]] Lane* const _lane{ ToLane(L_, StackIndex{ 1 }) }; // L_: lane err|nil
@@ -814,12 +797,13 @@ static LUAG_FUNC(lane_close)
814 lua_settop(L_, 1); // L_: lane 797 lua_settop(L_, 1); // L_: lane
815 798
816 // no error if the lane body doesn't return a non-nil first value 799 // no error if the lane body doesn't return a non-nil first value
817 luaG_pushstring(L_, "close"); // L_: lane "close" 800 luaW_pushstring(L_, "close"); // L_: lane "close"
818 lua_pushcclosure(L_, LG_lane_join, 1); // L_: lane join() 801 lua_pushcclosure(L_, LG_lane_join, 1); // L_: lane join()
819 lua_insert(L_, 1); // L_: join() lane 802 lua_insert(L_, 1); // L_: join() lane
820 lua_call(L_, 1, LUA_MULTRET); // L_: join() results 803 lua_call(L_, 1, LUA_MULTRET); // L_: join() results
821 return lua_gettop(L_); 804 return lua_gettop(L_);
822} 805}
806
823#endif // LUA_VERSION_NUM >= 504 807#endif // LUA_VERSION_NUM >= 504
824 808
825// ################################################################################################# 809// #################################################################################################
@@ -843,9 +827,9 @@ static LUAG_FUNC(lane_gc)
843 // if there a gc callback? 827 // if there a gc callback?
844 lua_getiuservalue(L_, StackIndex{ 1 }, UserValueIndex{ 1 }); // L_: ud uservalue 828 lua_getiuservalue(L_, StackIndex{ 1 }, UserValueIndex{ 1 }); // L_: ud uservalue
845 kLaneGC.pushKey(L_); // L_: ud uservalue __gc 829 kLaneGC.pushKey(L_); // L_: ud uservalue __gc
846 if (luaG_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // L_: ud uservalue gc_cb|nil 830 if (luaW_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // L_: ud uservalue gc_cb|nil
847 lua_remove(L_, -2); // L_: ud gc_cb|nil 831 lua_remove(L_, -2); // L_: ud gc_cb|nil
848 luaG_pushstring(L_, _lane->getDebugName()); // L_: ud gc_cb name 832 luaW_pushstring(L_, _lane->getDebugName()); // L_: ud gc_cb name
849 _have_gc_cb = true; 833 _have_gc_cb = true;
850 } else { 834 } else {
851 lua_pop(L_, 2); // L_: ud 835 lua_pop(L_, 2); // L_: ud
@@ -856,7 +840,7 @@ static LUAG_FUNC(lane_gc)
856 // still running: will have to be cleaned up later 840 // still running: will have to be cleaned up later
857 _lane->selfdestructAdd(); 841 _lane->selfdestructAdd();
858 if (_have_gc_cb) { 842 if (_have_gc_cb) {
859 luaG_pushstring(L_, "selfdestruct"); // L_: ud gc_cb name status 843 luaW_pushstring(L_, "selfdestruct"); // L_: ud gc_cb name status
860 lua_call(L_, 2, 0); // L_: ud 844 lua_call(L_, 2, 0); // L_: ud
861 } 845 }
862 return 0; 846 return 0;
@@ -871,7 +855,7 @@ static LUAG_FUNC(lane_gc)
871 855
872 // do this after lane cleanup in case the callback triggers an error 856 // do this after lane cleanup in case the callback triggers an error
873 if (_have_gc_cb) { 857 if (_have_gc_cb) {
874 luaG_pushstring(L_, "closed"); // L_: ud gc_cb name status 858 luaW_pushstring(L_, "closed"); // L_: ud gc_cb name status
875 lua_call(L_, 2, 0); // L_: ud 859 lua_call(L_, 2, 0); // L_: ud
876 } 860 }
877 return 0; 861 return 0;
@@ -916,7 +900,7 @@ void Lane::applyDebugName() const
916{ 900{
917 if constexpr (HAVE_DECODA_SUPPORT()) { 901 if constexpr (HAVE_DECODA_SUPPORT()) {
918 // to see VM name in Decoda debugger Virtual Machine window 902 // to see VM name in Decoda debugger Virtual Machine window
919 luaG_pushstring(L, debugName); // L: ... "name" 903 luaW_pushstring(L, debugName); // L: ... "name"
920 lua_setglobal(L, "decoda_name"); // L: ... 904 lua_setglobal(L, "decoda_name"); // L: ...
921 } 905 }
922 // and finally set the OS thread name 906 // and finally set the OS thread name
@@ -941,7 +925,8 @@ CancelResult Lane::cancel(CancelOp const op_, std::chrono::time_point<std::chron
941 925
942 // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here 926 // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here
943 // We can read status without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) 927 // We can read status without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN)
944 if (status.load(std::memory_order_acquire) >= Lane::Done) { 928 auto const _status{ status.load(std::memory_order_acquire) };
929 if (_status == Lane::Done || _status == Lane::Error || _status == Lane::Cancelled) {
945 // say "ok" by default, including when lane is already done 930 // say "ok" by default, including when lane is already done
946 return CancelResult::Cancelled; 931 return CancelResult::Cancelled;
947 } 932 }
@@ -968,14 +953,15 @@ CancelResult Lane::internalCancel(CancelRequest const rq_, std::chrono::time_poi
968 // lane_->thread.get_stop_source().request_stop(); 953 // lane_->thread.get_stop_source().request_stop();
969 } 954 }
970 if (wakeLane_ == WakeLane::Yes) { // wake the thread so that execution returns from any pending linda operation if desired 955 if (wakeLane_ == WakeLane::Yes) { // wake the thread so that execution returns from any pending linda operation if desired
971 if (status.load(std::memory_order_acquire) == Lane::Waiting) { // waiting_on is updated under control of status acquire/release semantics 956 auto const _status{ status.load(std::memory_order_acquire) };
957 if (_status == Lane::Waiting || _status == Lane::Suspended) { // waiting_on is updated under control of status acquire/release semantics
972 if (std::condition_variable* const _waiting_on{ waiting_on }) { 958 if (std::condition_variable* const _waiting_on{ waiting_on }) {
973 _waiting_on->notify_all(); 959 _waiting_on->notify_all();
974 } 960 }
975 } 961 }
976 } 962 }
977 // wait until the lane stops working with its state (either Suspended or Done+) 963 // wait until the lane stops working with its state (either Suspended or Done+)
978 CancelResult const result{ waitForCompletion(until_) ? CancelResult::Cancelled : CancelResult::Timeout }; 964 CancelResult const result{ waitForCompletion(until_, false) ? CancelResult::Cancelled : CancelResult::Timeout };
979 return result; 965 return result;
980} 966}
981 967
@@ -1027,7 +1013,7 @@ void Lane::PushMetatable(lua_State* const L_)
1027{ 1013{
1028 STACK_CHECK_START_REL(L_, 0); 1014 STACK_CHECK_START_REL(L_, 0);
1029 if (luaL_newmetatable(L_, kLaneMetatableName.data())) { // L_: mt 1015 if (luaL_newmetatable(L_, kLaneMetatableName.data())) { // L_: mt
1030 luaG_registerlibfuncs(L_, local::sLaneFunctions); 1016 luaW_registerlibfuncs(L_, local::sLaneFunctions);
1031 // cache error() and tostring() 1017 // cache error() and tostring()
1032 kCachedError.pushKey(L_); // L_: mt kCachedError 1018 kCachedError.pushKey(L_); // L_: mt kCachedError
1033 lua_getglobal(L_, "error"); // L_: mt kCachedError error() 1019 lua_getglobal(L_, "error"); // L_: mt kCachedError error()
@@ -1036,7 +1022,7 @@ void Lane::PushMetatable(lua_State* const L_)
1036 lua_getglobal(L_, "tostring"); // L_: mt kCachedTostring tostring() 1022 lua_getglobal(L_, "tostring"); // L_: mt kCachedTostring tostring()
1037 lua_rawset(L_, -3); // L_: mt 1023 lua_rawset(L_, -3); // L_: mt
1038 // hide the actual metatable from getmetatable() 1024 // hide the actual metatable from getmetatable()
1039 luaG_pushstring(L_, kLaneMetatableName); // L_: mt "Lane" 1025 luaW_pushstring(L_, kLaneMetatableName); // L_: mt "Lane"
1040 lua_setfield(L_, -2, "__metatable"); // L_: mt 1026 lua_setfield(L_, -2, "__metatable"); // L_: mt
1041 } 1027 }
1042 STACK_CHECK(L_, 1); 1028 STACK_CHECK(L_, 1);
@@ -1049,7 +1035,7 @@ void Lane::pushStatusString(lua_State* const L_) const
1049 std::string_view const _str{ threadStatusString() }; 1035 std::string_view const _str{ threadStatusString() };
1050 LUA_ASSERT(L_, !_str.empty()); 1036 LUA_ASSERT(L_, !_str.empty());
1051 1037
1052 luaG_pushstring(L_, _str); 1038 luaW_pushstring(L_, _str);
1053} 1039}
1054 1040
1055// ################################################################################################# 1041// #################################################################################################
@@ -1108,12 +1094,88 @@ void Lane::pushIndexedResult(lua_State* const L_, int const key_) const
1108// ################################################################################################# 1094// #################################################################################################
1109 1095
1110[[nodiscard]] 1096[[nodiscard]]
1097int Lane::pushStoredResults(lua_State* const L_) const
1098{
1099 STACK_CHECK_START_ABS(L_, 1); // should only have the lane UD on the stack
1100 static constexpr StackIndex kIdxSelf{ 1 };
1101 static constexpr UserValueIndex kUvResults{ 1 };
1102 LUA_ASSERT(L_, ToLane(L_, kIdxSelf) == this); // L_: lane
1103 lua_getiuservalue(L_, kIdxSelf, kUvResults); // L_: lane {uv}
1104 lua_rawgeti(L_, kIdxTop, 0); // L_: lane {uv} stored
1105 int const _stored{ static_cast<int>(lua_tointeger(L_, kIdxTop)) };
1106 lua_pop(L_, 1); // L_: lane {uv}
1107
1108 int _ret{};
1109 STACK_GROW(L_, std::max(3, _stored + 1));
1110 switch (status.load(std::memory_order_acquire)) {
1111 case Lane::Suspended:
1112 raise_luaL_error(L_, "INTERNAL ERROR: SHOULD NEVER BE SUSPENDED HERE");
1113 break;
1114
1115 case Lane::Done: // got regular return values
1116 if (_stored > 0) {
1117 for (int _i = 2; _i <= _stored; ++_i) {
1118 lua_rawgeti(L_, 2, _i); // L_: lane {uv} results2...N
1119 }
1120 lua_rawgeti(L_, 2, 1); // L_: lane {uv} results2...N result1
1121 lua_replace(L_, 2); // L_: lane results
1122 } else {
1123 lua_pop(L_, 1); // L_: lane
1124 }
1125 // we precede the lane body returned values with boolean true
1126 lua_pushboolean(L_, 1); // L_: lane results true
1127 lua_replace(L_, 1); // L_: true results
1128 _ret = _stored + 1;
1129 STACK_CHECK(L_, _ret);
1130 break;
1131
1132 case Lane::Error:
1133 {
1134 LUA_ASSERT(L_, _stored == 2 || _stored == 3); // contains nil error [trace]
1135 lua_rawgeti(L_, 2, 2); // L_: lane {uv} <error>
1136 lua_rawgeti(L_, 2, 3); // L_: lane {uv} <error> <trace>|nil
1137 if (lua_isnil(L_, -1)) {
1138 lua_replace(L_, 2); // L_: lane nil <error>
1139 } else {
1140 lua_rawgeti(L_, 2, 1); // L_: lane {uv} <error> <trace> nil
1141 lua_replace(L_, 2); // L_: lane nil <error> <trace>
1142 }
1143 _ret = _stored; // 2 or 3
1144 STACK_CHECK(L_, _ret + 1); // stack still contains the lane UD below
1145 }
1146 break;
1147
1148 case Lane::Cancelled:
1149 {
1150 LUA_ASSERT(L_, _stored == 2);
1151 lua_rawgeti(L_, 2, 2); // L_: lane {uv} cancel_error
1152 lua_rawgeti(L_, 2, 1); // L_: lane {uv} cancel_error nil
1153 lua_replace(L_, -3); // L_: lane nil cancel_error
1154 LUA_ASSERT(L_, lua_isnil(L_, -2) && kCancelError.equals(L_, kIdxTop));
1155 _ret = 2;
1156 STACK_CHECK(L_, _ret + 1); // stack still contains the lane UD below
1157 }
1158 break;
1159
1160 default:
1161 DEBUGSPEW_CODE(DebugSpew(nullptr) << "Unknown Lane status: " << static_cast<int>(_lane->status.load(std::memory_order_relaxed)) << std::endl);
1162 LUA_ASSERT(L_, false);
1163 _ret = 0;
1164 STACK_CHECK(L_, _ret);
1165 }
1166 LUA_ASSERT(L_, lua_gettop(L_) >= _ret);
1167 return _ret;
1168}
1169
1170// #################################################################################################
1171
1172[[nodiscard]]
1111std::string_view Lane::pushErrorTraceLevel(lua_State* L_) const 1173std::string_view Lane::pushErrorTraceLevel(lua_State* L_) const
1112{ 1174{
1113 std::string_view const _str{ errorTraceLevelString() }; 1175 std::string_view const _str{ errorTraceLevelString() };
1114 LUA_ASSERT(L_, !_str.empty()); 1176 LUA_ASSERT(L_, !_str.empty());
1115 1177
1116 return luaG_pushstring(L_, _str); 1178 return luaW_pushstring(L_, _str);
1117} 1179}
1118 1180
1119// ################################################################################################# 1181// #################################################################################################
@@ -1124,7 +1186,7 @@ void Lane::resetResultsStorage(lua_State* const L_, StackIndex const self_idx_)
1124{ 1186{
1125 STACK_GROW(L_, 4); 1187 STACK_GROW(L_, 4);
1126 STACK_CHECK_START_REL(L_, 0); 1188 STACK_CHECK_START_REL(L_, 0);
1127 StackIndex const _self_idx{ luaG_absindex(L_, self_idx_) }; 1189 StackIndex const _self_idx{ luaW_absindex(L_, self_idx_) };
1128 LUA_ASSERT(L_, ToLane(L_, _self_idx) == this); // L_: ... self ... 1190 LUA_ASSERT(L_, ToLane(L_, _self_idx) == this); // L_: ... self ...
1129 // create the new table 1191 // create the new table
1130 lua_newtable(L_); // L_: ... self ... {} 1192 lua_newtable(L_); // L_: ... self ... {}
@@ -1158,7 +1220,7 @@ void Lane::securizeDebugName(lua_State* const L_)
1158 lua_newtable(L_); // L_: lane ... {uv} {} 1220 lua_newtable(L_); // L_: lane ... {uv} {}
1159 { 1221 {
1160 std::lock_guard<std::mutex> _guard{ debugNameMutex }; 1222 std::lock_guard<std::mutex> _guard{ debugNameMutex };
1161 debugName = luaG_pushstring(L_, debugName); // L_: lane ... {uv} {} name 1223 debugName = luaW_pushstring(L_, debugName); // L_: lane ... {uv} {} name
1162 } 1224 }
1163 lua_rawset(L_, -3); // L_: lane ... {uv} 1225 lua_rawset(L_, -3); // L_: lane ... {uv}
1164 lua_pop(L_, 1); // L_: lane 1226 lua_pop(L_, 1); // L_: lane
@@ -1167,11 +1229,11 @@ void Lane::securizeDebugName(lua_State* const L_)
1167 1229
1168// ################################################################################################# 1230// #################################################################################################
1169 1231
1170void Lane::startThread(int const priority_) 1232void Lane::startThread(lua_State* const L_, int const priority_, NativePrioFlag native_)
1171{ 1233{
1172 thread = std::thread([this]() { lane_main(this); }); 1234 thread = std::thread([this]() { lane_main(this); });
1173 if (priority_ != kThreadPrioDefault) { 1235 if (priority_ != kThreadPrioDefault) {
1174 THREAD_SET_PRIORITY(thread, priority_, U->sudo); 1236 THREAD_SET_PRIORITY(L_, thread, priority_, native_, U->sudo);
1175 } 1237 }
1176} 1238}
1177 1239
@@ -1181,13 +1243,13 @@ void Lane::storeDebugName(std::string_view const& name_)
1181{ 1243{
1182 STACK_CHECK_START_REL(L, 0); 1244 STACK_CHECK_START_REL(L, 0);
1183 // 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... 1245 // 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...
1184 kLaneNameRegKey.setValue(L, [name = name_](lua_State* L_) { luaG_pushstring(L_, name); }); 1246 kLaneNameRegKey.setValue(L, [name = name_](lua_State* L_) { luaW_pushstring(L_, name); });
1185 STACK_CHECK(L, 0); 1247 STACK_CHECK(L, 0);
1186 kLaneNameRegKey.pushValue(L); // L: ... "name" ... 1248 kLaneNameRegKey.pushValue(L); // L: ... "name" ...
1187 // keep a direct view on the stored string 1249 // keep a direct view on the stored string
1188 { 1250 {
1189 std::lock_guard<std::mutex> _guard{ debugNameMutex }; 1251 std::lock_guard<std::mutex> _guard{ debugNameMutex };
1190 debugName = luaG_tostring(L, kIdxTop); 1252 debugName = luaW_tostring(L, kIdxTop);
1191 } 1253 }
1192 lua_pop(L, 1); 1254 lua_pop(L, 1);
1193 STACK_CHECK(L, 0); 1255 STACK_CHECK(L, 0);
@@ -1207,20 +1269,21 @@ int Lane::storeResults(lua_State* const L_)
1207 lua_getiuservalue(L_, kIdxSelf, UserValueIndex{ 1 }); // L_: lane ... {uv} 1269 lua_getiuservalue(L_, kIdxSelf, UserValueIndex{ 1 }); // L_: lane ... {uv}
1208 StackIndex const _tidx{ lua_gettop(L_) }; 1270 StackIndex const _tidx{ lua_gettop(L_) };
1209 1271
1210 int _stored{}; 1272 // if the results were already stored from a previous indexing, just say how many values we have in store
1211 if (nresults == 0) { 1273 if (!L) {
1212 lua_rawgeti(L_, -1, 0); // L_: lane ... {uv} nresults 1274 lua_rawgeti(L_, -1, 0); // L_: lane ... {uv} nresults
1213 _stored = static_cast<int>(lua_tointeger(L_, -1)); 1275 auto const _stored{ static_cast<int>(lua_tointeger(L_, -1)) };
1214 lua_pop(L_, 2); 1276 lua_pop(L_, 2);
1215 STACK_CHECK(L_, 0); 1277 STACK_CHECK(L_, 0);
1216 return _stored; 1278 return _stored;
1217 } 1279 }
1218 1280
1281 int _stored{};
1219 switch (status.load(std::memory_order_acquire)) { 1282 switch (status.load(std::memory_order_acquire)) {
1220 default: 1283 default:
1221 // this is an internal error, we probably never get here 1284 // this is an internal error, we probably never get here
1222 lua_settop(L_, 0); // L_: 1285 lua_settop(L_, 0); // L_:
1223 luaG_pushstring(L_, "Unexpected status: "); // L_: "Unexpected status: " 1286 luaW_pushstring(L_, "Unexpected status: "); // L_: "Unexpected status: "
1224 pushStatusString(L_); // L_: "Unexpected status: " "<status>" 1287 pushStatusString(L_); // L_: "Unexpected status: " "<status>"
1225 lua_concat(L_, 2); // L_: "Unexpected status: <status>" 1288 lua_concat(L_, 2); // L_: "Unexpected status: <status>"
1226 raise_lua_error(L_); 1289 raise_lua_error(L_);
@@ -1295,12 +1358,13 @@ int Lane::storeResults(lua_State* const L_)
1295//--- 1358//---
1296// str= thread_status( lane ) 1359// str= thread_status( lane )
1297// 1360//
1298// "pending" -> | ("running" <-> "waiting") <-> "suspended" <-> "resuming" | -> "done"/"error"/"cancelled" 1361// "pending" -> | ("running" <-> "waiting") <-> "suspended" <-> "resuming/closing" | -> "done"/"error"/"cancelled"
1299 1362
1300// "pending" not started yet 1363// "pending" not started yet
1301// "running" started, doing its work.. 1364// "running" started, doing its work..
1302// "suspended" returned from a lua_resume 1365// "suspended" returned from a lua_resume
1303// "resuming" told by its parent state to resume 1366// "resuming" told by its parent state to resume
1367// "closing" not observable from the outside: happens only inside a join()/indexation call to unblock a suspended coroutine Lane so that it can join properly
1304// "waiting" blocked in a send()/receive() 1368// "waiting" blocked in a send()/receive()
1305// "done" finished, results are there 1369// "done" finished, results are there
1306// "error" finished at an error, error value is there 1370// "error" finished at an error, error value is there
@@ -1311,7 +1375,7 @@ std::string_view Lane::threadStatusString() const
1311{ 1375{
1312 static constexpr std::string_view kStrs[] = { 1376 static constexpr std::string_view kStrs[] = {
1313 "pending", 1377 "pending",
1314 "running", "suspended", "resuming", 1378 "running", "suspended", "resuming", "closing",
1315 "waiting", 1379 "waiting",
1316 "done", "error", "cancelled" 1380 "done", "error", "cancelled"
1317 }; 1381 };
@@ -1319,12 +1383,13 @@ std::string_view Lane::threadStatusString() const
1319 static_assert(1 == static_cast<std::underlying_type_t<Lane::Status>>(Running)); 1383 static_assert(1 == static_cast<std::underlying_type_t<Lane::Status>>(Running));
1320 static_assert(2 == static_cast<std::underlying_type_t<Lane::Status>>(Suspended)); 1384 static_assert(2 == static_cast<std::underlying_type_t<Lane::Status>>(Suspended));
1321 static_assert(3 == static_cast<std::underlying_type_t<Lane::Status>>(Resuming)); 1385 static_assert(3 == static_cast<std::underlying_type_t<Lane::Status>>(Resuming));
1322 static_assert(4 == static_cast<std::underlying_type_t<Lane::Status>>(Waiting)); 1386 static_assert(4 == static_cast<std::underlying_type_t<Lane::Status>>(Closing));
1323 static_assert(5 == static_cast<std::underlying_type_t<Lane::Status>>(Done)); 1387 static_assert(5 == static_cast<std::underlying_type_t<Lane::Status>>(Waiting));
1324 static_assert(6 == static_cast<std::underlying_type_t<Lane::Status>>(Error)); 1388 static_assert(6 == static_cast<std::underlying_type_t<Lane::Status>>(Done));
1325 static_assert(7 == static_cast<std::underlying_type_t<Lane::Status>>(Cancelled)); 1389 static_assert(7 == static_cast<std::underlying_type_t<Lane::Status>>(Error));
1390 static_assert(8 == static_cast<std::underlying_type_t<Lane::Status>>(Cancelled));
1326 auto const _status{ static_cast<std::underlying_type_t<Lane::Status>>(status.load(std::memory_order_acquire)) }; 1391 auto const _status{ static_cast<std::underlying_type_t<Lane::Status>>(status.load(std::memory_order_acquire)) };
1327 if (_status < 0 || _status > 7) { // should never happen, but better safe than sorry 1392 if (_status < 0 || _status > 8) { // should never happen, but better safe than sorry
1328 return ""; 1393 return "";
1329 } 1394 }
1330 return kStrs[_status]; 1395 return kStrs[_status];
@@ -1332,17 +1397,52 @@ std::string_view Lane::threadStatusString() const
1332 1397
1333// ################################################################################################# 1398// #################################################################################################
1334 1399
1335bool Lane::waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_) 1400bool Lane::waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_, bool const _acceptSuspended)
1336{ 1401{
1337 std::unique_lock _guard{ doneMutex }; 1402 std::unique_lock _guard{ doneMutex };
1338 // std::stop_token token{ thread.get_stop_token() }; 1403 // std::stop_token token{ thread.get_stop_token() };
1339 // return doneCondVar.wait_until(lock, token, secs_, [this](){ return status >= Lane::Done; }); 1404 // return doneCondVar.wait_until(lock, token, secs_, [this](){ return status >= Lane::Done; });
1340 1405
1341 // wait until the lane stops working with its state (either Suspended or Done+) 1406 // wait until the lane exits lane_main (which is the only place where status can become one of the 3 tested values)
1342 return doneCondVar.wait_until(_guard, until_, [this]() 1407 return doneCondVar.wait_until(_guard, until_, [this, suspended = _acceptSuspended ? Lane::Suspended : Lane::Done]() {
1408 auto const _status{ status.load(std::memory_order_acquire) };
1409 return _status == Lane::Done || _status == Lane::Error || _status == Lane::Cancelled || _status == suspended;
1410 });
1411}
1412
1413// #################################################################################################
1414
1415[[nodiscard]]
1416bool Lane::waitForJoin(lua_State* const L_, std::chrono::time_point<std::chrono::steady_clock> until_)
1417{
1418 // wait until suspended or done
1419 {
1420 bool const _done{ !thread.joinable() || waitForCompletion(until_, true) };
1421
1422 if (!_done) {
1423 lua_pushnil(L_); // L_: lane nil
1424 luaW_pushstring(L_, "timeout"); // L_: lane nil "timeout"
1425 return false;
1426 }
1427 }
1428
1429 // if lane is suspended, force the yield loop to break, and the termination of the thread
1430 if (status.load(std::memory_order_acquire) == Lane::Suspended) {
1431 LUA_ASSERT(L_, waiting_on == &doneCondVar);
1432 status.store(Lane::Closing, std::memory_order_release);
1433 doneCondVar.notify_all();
1434 // wait until done
1343 { 1435 {
1344 auto const _status{ status.load(std::memory_order_acquire) }; 1436 bool const _done{ !thread.joinable() || waitForCompletion(until_, true) };
1345 return _status == Lane::Suspended || _status >= Lane::Done; 1437
1438 if (!_done) {
1439 lua_pushnil(L_); // L_: lane nil
1440 luaW_pushstring(L_, "timeout"); // L_: lane nil "timeout"
1441 return false;
1442 }
1346 } 1443 }
1347 ); 1444 LUA_ASSERT(L_, status.load(std::memory_order_acquire) != Lane::Closing);
1445 }
1446 LUA_ASSERT(L_, status.load(std::memory_order_acquire) != Lane::Suspended);
1447 return true;
1348} 1448}
diff --git a/src/lane.hpp b/src/lane.hpp
index 5fe36b6..bd328b1 100644
--- a/src/lane.hpp
+++ b/src/lane.hpp
@@ -56,6 +56,7 @@ class Lane final
56 /* 56 /*
57 Pending: The Lua VM hasn't done anything yet. 57 Pending: The Lua VM hasn't done anything yet.
58 Resuming: The user requested the lane to resume execution from Suspended state. 58 Resuming: The user requested the lane to resume execution from Suspended state.
59 Closing: The user is joining the lane, specifically interrupting a suspended Lane.
59 Suspended: returned from lua_resume, waiting for the client to request a lua_resume. 60 Suspended: returned from lua_resume, waiting for the client to request a lua_resume.
60 Running, Suspended, Waiting: Thread is inside the Lua VM. 61 Running, Suspended, Waiting: Thread is inside the Lua VM.
61 Done, Error, Cancelled: Thread execution is outside the Lua VM. It can be lua_close()d. 62 Done, Error, Cancelled: Thread execution is outside the Lua VM. It can be lua_close()d.
@@ -66,6 +67,7 @@ class Lane final
66 Running, 67 Running,
67 Suspended, 68 Suspended,
68 Resuming, 69 Resuming,
70 Closing,
69 Waiting, 71 Waiting,
70 Done, 72 Done,
71 Error, 73 Error,
@@ -199,12 +201,14 @@ class Lane final
199 static void PushMetatable(lua_State* L_); 201 static void PushMetatable(lua_State* L_);
200 void pushStatusString(lua_State* L_) const; 202 void pushStatusString(lua_State* L_) const;
201 void pushIndexedResult(lua_State* L_, int key_) const; 203 void pushIndexedResult(lua_State* L_, int key_) const;
204 [[nodiscard]]
205 int pushStoredResults(lua_State* L_) const;
202 void resetResultsStorage(lua_State* L_, StackIndex self_idx_); 206 void resetResultsStorage(lua_State* L_, StackIndex self_idx_);
203 void selfdestructAdd(); 207 void selfdestructAdd();
204 [[nodiscard]] 208 [[nodiscard]]
205 bool selfdestructRemove(); 209 bool selfdestructRemove();
206 void securizeDebugName(lua_State* L_); 210 void securizeDebugName(lua_State* L_);
207 void startThread(int priority_); 211 void startThread(lua_State* L_, int priority_, NativePrioFlag native_);
208 void storeDebugName( std::string_view const& name_); 212 void storeDebugName( std::string_view const& name_);
209 [[nodiscard]] 213 [[nodiscard]]
210 int storeResults(lua_State* L_); 214 int storeResults(lua_State* L_);
@@ -212,7 +216,9 @@ class Lane final
212 std::string_view threadStatusString() const; 216 std::string_view threadStatusString() const;
213 // wait until the lane stops working with its state (either Suspended or Done+) 217 // wait until the lane stops working with its state (either Suspended or Done+)
214 [[nodiscard]] 218 [[nodiscard]]
215 bool waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_); 219 bool waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_, bool const _acceptSuspended);
220 [[nodiscard]]
221 bool waitForJoin(lua_State* _L, std::chrono::time_point<std::chrono::steady_clock> until_);
216}; 222};
217 223
218// ################################################################################################# 224// #################################################################################################
diff --git a/src/lanes.cpp b/src/lanes.cpp
index d1a353b..4373aee 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -137,13 +137,15 @@ LUAG_FUNC(set_singlethreaded)
137LUAG_FUNC(set_thread_priority) 137LUAG_FUNC(set_thread_priority)
138{ 138{
139 lua_Integer const _prio{ luaL_checkinteger(L_, 1) }; 139 lua_Integer const _prio{ luaL_checkinteger(L_, 1) };
140 NativePrioFlag const _native{ std::string_view{ "native" } == luaL_optstring(L_, 2, "mapped") };
140 // public Lanes API accepts a generic range -3/+3 141 // public Lanes API accepts a generic range -3/+3
141 // that will be remapped into the platform-specific scheduler priority scheme 142 // that will be remapped into the platform-specific scheduler priority scheme
142 // On some platforms, -3 is equivalent to -2 and +3 to +2 143 // On some platforms, -3 is equivalent to -2 and +3 to +2
143 if (_prio < kThreadPrioMin || _prio > kThreadPrioMax) { 144 if (!_native && (_prio < kThreadPrioMin || _prio > kThreadPrioMax)) {
144 raise_luaL_error(L_, "priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _prio); 145 raise_luaL_error(L_, "priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _prio);
145 } 146 }
146 THREAD_SET_PRIORITY(static_cast<int>(_prio), Universe::Get(L_)->sudo); 147
148 THREAD_SET_PRIORITY(L_, static_cast<int>(_prio), _native, Universe::Get(L_)->sudo);
147 return 0; 149 return 0;
148} 150}
149 151
@@ -155,7 +157,8 @@ LUAG_FUNC(set_thread_affinity)
155 if (_affinity <= 0) { 157 if (_affinity <= 0) {
156 raise_luaL_error(L_, "invalid affinity (%d)", _affinity); 158 raise_luaL_error(L_, "invalid affinity (%d)", _affinity);
157 } 159 }
158 THREAD_SET_AFFINITY(static_cast<unsigned int>(_affinity)); 160
161 THREAD_SET_AFFINITY(L_, static_cast<unsigned int>(_affinity));
159 return 0; 162 return 0;
160} 163}
161 164
@@ -170,17 +173,21 @@ LUAG_FUNC(sleep)
170 lua_pushcfunction(L_, LG_linda_receive); // L_: duration|nil receive() 173 lua_pushcfunction(L_, LG_linda_receive); // L_: duration|nil receive()
171 STACK_CHECK_START_REL(L_, 0); // we pushed the function we intend to call, now prepare the arguments 174 STACK_CHECK_START_REL(L_, 0); // we pushed the function we intend to call, now prepare the arguments
172 _U->timerLinda->push(L_); // L_: duration|nil receive() timerLinda 175 _U->timerLinda->push(L_); // L_: duration|nil receive() timerLinda
173 if (luaG_tostring(L_, StackIndex{ 1 }) == "indefinitely") { 176 if (luaW_tostring(L_, StackIndex{ 1 }) == "indefinitely") {
174 lua_pushnil(L_); // L_: duration? receive() timerLinda nil 177 lua_pushnil(L_); // L_: duration? receive() timerLinda nil
175 } else if (lua_isnoneornil(L_, 1)) { 178 } else if (lua_isnoneornil(L_, 1)) {
176 lua_pushnumber(L_, 0); // L_: duration? receive() timerLinda 0 179 lua_pushnumber(L_, 0); // L_: duration? receive() timerLinda 0
177 } else if (!lua_isnumber(L_, 1)) { 180 } else if (!lua_isnumber(L_, 1)) {
178 raise_luaL_argerror(L_, StackIndex{ 1 }, "invalid duration"); 181 raise_luaL_argerror(L_, StackIndex{ 1 }, "duration must be a number");
179 } 182 }
180 else { 183 else {
184 auto const _n{ lua_tonumber(L_, 1) };
185 if (_n < 0) {
186 raise_luaL_argerror(L_, StackIndex{ 1 }, "duration must be >= 0");
187 }
181 lua_pushnumber(L_, lua_tonumber(L_, 1)); // L_: duration? receive() timerLinda duration 188 lua_pushnumber(L_, lua_tonumber(L_, 1)); // L_: duration? receive() timerLinda duration
182 } 189 }
183 luaG_pushstring(L_, "ac100de1-a696-4619-b2f0-a26de9d58ab8"); // L_: duration? receive() timerLinda duration key 190 luaW_pushstring(L_, "ac100de1-a696-4619-b2f0-a26de9d58ab8"); // L_: duration? receive() timerLinda duration key
184 STACK_CHECK(L_, 3); // 3 arguments ready 191 STACK_CHECK(L_, 3); // 3 arguments ready
185 lua_call(L_, 3, LUA_MULTRET); // timerLinda:receive(duration,key) // L_: duration? result... 192 lua_call(L_, 3, LUA_MULTRET); // timerLinda:receive(duration,key) // L_: duration? result...
186 return lua_gettop(L_) - 1; 193 return lua_gettop(L_) - 1;
@@ -194,7 +201,7 @@ LUAG_FUNC(sleep)
194// upvalue[1]: _G.require 201// upvalue[1]: _G.require
195LUAG_FUNC(require) 202LUAG_FUNC(require)
196{ 203{
197 std::string_view const _name{ luaG_tostring(L_, StackIndex{ 1 }) }; // L_: "name" ... 204 std::string_view const _name{ luaW_tostring(L_, StackIndex{ 1 }) }; // L_: "name" ...
198 int const _nargs{ lua_gettop(L_) }; 205 int const _nargs{ lua_gettop(L_) };
199 DEBUGSPEW_CODE(Universe * _U{ Universe::Get(L_) }); 206 DEBUGSPEW_CODE(Universe * _U{ Universe::Get(L_) });
200 STACK_CHECK_START_REL(L_, 0); 207 STACK_CHECK_START_REL(L_, 0);
@@ -220,8 +227,8 @@ int lanes_register(lua_State* const L_)
220 if (!_U) { 227 if (!_U) {
221 raise_luaL_error(L_, "Lanes is not ready"); 228 raise_luaL_error(L_, "Lanes is not ready");
222 } 229 }
223 std::string_view const _name{ luaG_checkstring(L_, StackIndex{ 1 }) }; 230 std::string_view const _name{ luaW_checkstring(L_, StackIndex{ 1 }) };
224 LuaType const _mod_type{ luaG_type(L_, StackIndex{ 2 }) }; 231 LuaType const _mod_type{ luaW_type(L_, StackIndex{ 2 }) };
225 // ignore extra arguments, just in case 232 // ignore extra arguments, just in case
226 lua_settop(L_, 2); 233 lua_settop(L_, 2);
227 luaL_argcheck(L_, (_mod_type == LuaType::TABLE) || (_mod_type == LuaType::FUNCTION), 2, "unexpected module type"); 234 luaL_argcheck(L_, (_mod_type == LuaType::TABLE) || (_mod_type == LuaType::FUNCTION), 2, "unexpected module type");
@@ -236,9 +243,26 @@ int lanes_register(lua_State* const L_)
236 243
237// ################################################################################################# 244// #################################################################################################
238 245
246LUAG_FUNC(thread_priority_range)
247{
248 NativePrioFlag const _native{ std::string_view{ "native" } == luaL_optstring(L_, 1, "mapped") };
249 if (_native) {
250 auto const [_prio_min, _prio_max] = THREAD_NATIVE_PRIOS();
251 lua_pushinteger(L_, _prio_min);
252 lua_pushinteger(L_, _prio_max);
253 } else {
254 lua_pushinteger(L_, kThreadPrioMin);
255 lua_pushinteger(L_, kThreadPrioMax);
256 }
257 return 2;
258}
259
260// #################################################################################################
261
239//--- [] means can be nil 262//--- [] means can be nil
240// lane_ud = lane_new( function 263// lane_ud = lane_new( function
241// , [libs_str] 264// , [libs_str]
265// , [prio_is_native_bool]
242// , [priority_int] 266// , [priority_int]
243// , [globals_tbl] 267// , [globals_tbl]
244// , [package_tbl] 268// , [package_tbl]
@@ -255,15 +279,16 @@ LUAG_FUNC(lane_new)
255{ 279{
256 static constexpr StackIndex kFuncIdx{ 1 }; 280 static constexpr StackIndex kFuncIdx{ 1 };
257 static constexpr StackIndex kLibsIdx{ 2 }; 281 static constexpr StackIndex kLibsIdx{ 2 };
258 static constexpr StackIndex kPrioIdx{ 3 }; 282 static constexpr StackIndex kPrinIdx{ 3 };
259 static constexpr StackIndex kGlobIdx{ 4 }; 283 static constexpr StackIndex kPrioIdx{ 4 };
260 static constexpr StackIndex kPackIdx{ 5 }; 284 static constexpr StackIndex kGlobIdx{ 5 };
261 static constexpr StackIndex kRequIdx{ 6 }; 285 static constexpr StackIndex kPackIdx{ 6 };
262 static constexpr StackIndex kGcCbIdx{ 7 }; 286 static constexpr StackIndex kRequIdx{ 7 };
263 static constexpr StackIndex kNameIdx{ 8 }; 287 static constexpr StackIndex kGcCbIdx{ 8 };
264 static constexpr StackIndex kErTlIdx{ 9 }; 288 static constexpr StackIndex kNameIdx{ 9 };
265 static constexpr StackIndex kAsCoro{ 10 }; 289 static constexpr StackIndex kErTlIdx{ 10 };
266 static constexpr StackIndex kFixedArgsIdx{ 10 }; 290 static constexpr StackIndex kAsCoro{ 11 };
291 static constexpr StackIndex kFixedArgsIdx{ 11 };
267 292
268 int const _nargs{ lua_gettop(L_) - kFixedArgsIdx }; 293 int const _nargs{ lua_gettop(L_) - kFixedArgsIdx };
269 LUA_ASSERT(L_, _nargs >= 0); 294 LUA_ASSERT(L_, _nargs >= 0);
@@ -271,7 +296,7 @@ LUAG_FUNC(lane_new)
271 Universe* const _U{ Universe::Get(L_) }; 296 Universe* const _U{ Universe::Get(L_) };
272 DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: setup" << std::endl); 297 DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: setup" << std::endl);
273 298
274 std::optional<std::string_view> _libs_str{ lua_isnil(L_, kLibsIdx) ? std::nullopt : std::make_optional(luaG_tostring(L_, kLibsIdx)) }; 299 std::optional<std::string_view> _libs_str{ lua_isnil(L_, kLibsIdx) ? std::nullopt : std::make_optional(luaW_tostring(L_, kLibsIdx)) };
275 lua_State* const _S{ state::NewLaneState(_U, SourceState{ L_ }, _libs_str) }; // L_: [fixed] ... L2: 300 lua_State* const _S{ state::NewLaneState(_U, SourceState{ L_ }, _libs_str) }; // L_: [fixed] ... L2:
276 STACK_CHECK_START_REL(_S, 0); 301 STACK_CHECK_START_REL(_S, 0);
277 302
@@ -333,7 +358,7 @@ LUAG_FUNC(lane_new)
333 DEBUGSPEW_CODE(DebugSpew(lane->U) << "lane_new: preparing lane userdata" << std::endl); 358 DEBUGSPEW_CODE(DebugSpew(lane->U) << "lane_new: preparing lane userdata" << std::endl);
334 STACK_CHECK_START_REL(L, 0); 359 STACK_CHECK_START_REL(L, 0);
335 // a Lane full userdata needs a single uservalue 360 // a Lane full userdata needs a single uservalue
336 Lane** const _ud{ luaG_newuserdatauv<Lane*>(L, UserValueCount{ 1 }) }; // L: ... lane 361 Lane** const _ud{ luaW_newuserdatauv<Lane*>(L, UserValueCount{ 1 }) }; // L: ... lane
337 *_ud = lane; // don't forget to store the pointer in the userdata! 362 *_ud = lane; // don't forget to store the pointer in the userdata!
338 363
339 // Set metatable for the userdata 364 // Set metatable for the userdata
@@ -357,25 +382,25 @@ LUAG_FUNC(lane_new)
357 lua_setiuservalue(L, StackIndex{ -2 }, UserValueIndex{ 1 }); // L: ... lane 382 lua_setiuservalue(L, StackIndex{ -2 }, UserValueIndex{ 1 }); // L: ... lane
358 383
359 StackIndex const _name_idx{ lua_isnoneornil(L, kNameIdx) ? kIdxNone : kNameIdx }; 384 StackIndex const _name_idx{ lua_isnoneornil(L, kNameIdx) ? kIdxNone : kNameIdx };
360 std::string_view _debugName{ (_name_idx > 0) ? luaG_tostring(L, _name_idx) : std::string_view{} }; 385 std::string_view _debugName{ (_name_idx > 0) ? luaW_tostring(L, _name_idx) : std::string_view{} };
361 if (!_debugName.empty()) 386 if (!_debugName.empty())
362 { 387 {
363 if (_debugName == "auto") { 388 if (_debugName == "auto") {
364 if (luaG_type(L, kFuncIdx) == LuaType::STRING) { 389 if (luaW_type(L, kFuncIdx) == LuaType::STRING) {
365 lua_Debug _ar; 390 lua_Debug _ar;
366 if (lua_getstack(L, 2, &_ar) == 0) { // 0 is here, 1 is lanes.gen, 2 is its caller 391 if (lua_getstack(L, 2, &_ar) == 0) { // 0 is here, 1 is lanes.gen, 2 is its caller
367 lua_getstack(L, 1, &_ar); // level 2 may not exist with LuaJIT, try again with level 1 392 lua_getstack(L, 1, &_ar); // level 2 may not exist with LuaJIT, try again with level 1
368 } 393 }
369 lua_getinfo(L, "Sl", &_ar); 394 lua_getinfo(L, "Sl", &_ar);
370 luaG_pushstring(L, "%s:%d", _ar.short_src, _ar.currentline); // L: ... lane "<name>" 395 luaW_pushstring(L, "%s:%d", _ar.short_src, _ar.currentline); // L: ... lane "<name>"
371 } else { 396 } else {
372 lua_Debug _ar; 397 lua_Debug _ar;
373 lua_pushvalue(L, kFuncIdx); // L: ... lane func 398 lua_pushvalue(L, kFuncIdx); // L: ... lane func
374 lua_getinfo(L, ">S", &_ar); // L: ... lane 399 lua_getinfo(L, ">S", &_ar); // L: ... lane
375 luaG_pushstring(L, "%s:%d", _ar.short_src, _ar.linedefined); // L: ... lane "<name>" 400 luaW_pushstring(L, "%s:%d", _ar.short_src, _ar.linedefined); // L: ... lane "<name>"
376 } 401 }
377 lua_replace(L, _name_idx); // L: ... lane 402 lua_replace(L, _name_idx); // L: ... lane
378 _debugName = luaG_tostring(L, _name_idx); 403 _debugName = luaW_tostring(L, _name_idx);
379 } 404 }
380 lane->storeDebugName(_debugName); 405 lane->storeDebugName(_debugName);
381 } 406 }
@@ -400,21 +425,22 @@ LUAG_FUNC(lane_new)
400 // public Lanes API accepts a generic range -3/+3 425 // public Lanes API accepts a generic range -3/+3
401 // that will be remapped into the platform-specific scheduler priority scheme 426 // that will be remapped into the platform-specific scheduler priority scheme
402 // On some platforms, -3 is equivalent to -2 and +3 to +2 427 // On some platforms, -3 is equivalent to -2 and +3 to +2
403 int const _priority{ 428 auto const [_priority, _native] {
404 std::invoke([L = L_]() { 429 std::invoke([L = L_]() {
430 NativePrioFlag const _native{ static_cast<bool>(lua_toboolean(L, kPrinIdx)) };
405 StackIndex const _prio_idx{ lua_isnoneornil(L, kPrioIdx) ? kIdxNone : kPrioIdx }; 431 StackIndex const _prio_idx{ lua_isnoneornil(L, kPrioIdx) ? kIdxNone : kPrioIdx };
406 if (_prio_idx == 0) { 432 if (_prio_idx == kIdxNone) {
407 return kThreadPrioDefault; 433 return std::make_pair(kThreadPrioDefault, _native);
408 } 434 }
409 int const _priority{ static_cast<int>(lua_tointeger(L, _prio_idx)) }; 435 int const _priority{ static_cast<int>(lua_tointeger(L, _prio_idx)) };
410 if ((_priority < kThreadPrioMin || _priority > kThreadPrioMax)) { 436 if (!_native && (_priority < kThreadPrioMin || _priority > kThreadPrioMax)) {
411 raise_luaL_error(L, "Priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _priority); 437 raise_luaL_error(L, "Priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _priority);
412 } 438 }
413 return _priority; 439 return std::make_pair(_priority, _native);
414 }) 440 })
415 }; 441 };
416 442
417 _lane->startThread(_priority); 443 _lane->startThread(L_, _priority, _native);
418 444
419 STACK_GROW(_L2, _nargs + 3); 445 STACK_GROW(_L2, _nargs + 3);
420 STACK_GROW(L_, 3); 446 STACK_GROW(L_, 3);
@@ -439,17 +465,17 @@ LUAG_FUNC(lane_new)
439 DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: process 'required' list" << std::endl); 465 DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: process 'required' list" << std::endl);
440 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); 466 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U });
441 // should not happen, was checked in lanes.lua before calling lane_new() 467 // should not happen, was checked in lanes.lua before calling lane_new()
442 if (luaG_type(L_, _required_idx) != LuaType::TABLE) { 468 if (luaW_type(L_, _required_idx) != LuaType::TABLE) {
443 raise_luaL_error(L_, "expected required module list as a table, got %s", luaL_typename(L_, _required_idx)); 469 raise_luaL_error(L_, "expected required module list as a table, got %s", luaL_typename(L_, _required_idx));
444 } 470 }
445 471
446 lua_pushnil(L_); // L_: [fixed] args... nil L2: 472 lua_pushnil(L_); // L_: [fixed] args... nil L2:
447 while (lua_next(L_, _required_idx) != 0) { // L_: [fixed] args... n "modname" L2: 473 while (lua_next(L_, _required_idx) != 0) { // L_: [fixed] args... n "modname" L2:
448 if (luaG_type(L_, kIdxTop) != LuaType::STRING || luaG_type(L_, StackIndex{ -2 }) != LuaType::NUMBER || lua_tonumber(L_, -2) != _nbRequired) { 474 if (luaW_type(L_, kIdxTop) != LuaType::STRING || luaW_type(L_, StackIndex{ -2 }) != LuaType::NUMBER || lua_tonumber(L_, -2) != _nbRequired) {
449 raise_luaL_error(L_, "required module list should be a list of strings"); 475 raise_luaL_error(L_, "required module list should be a list of strings");
450 } else { 476 } else {
451 // require the module in the target state, and populate the lookup table there too 477 // require the module in the target state, and populate the lookup table there too
452 std::string_view const _name{ luaG_tostring(L_, kIdxTop) }; 478 std::string_view const _name{ luaW_tostring(L_, kIdxTop) };
453 DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: require '" << _name << "'" << std::endl); 479 DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: require '" << _name << "'" << std::endl);
454 480
455 // require the module in the target lane 481 // require the module in the target lane
@@ -458,7 +484,7 @@ LUAG_FUNC(lane_new)
458 lua_pop(_L2, 1); // L_: [fixed] args... n "modname" L2: 484 lua_pop(_L2, 1); // L_: [fixed] args... n "modname" L2:
459 raise_luaL_error(L_, "cannot pre-require modules without loading 'package' library first"); 485 raise_luaL_error(L_, "cannot pre-require modules without loading 'package' library first");
460 } else { 486 } else {
461 luaG_pushstring(_L2, _name); // L_: [fixed] args... n "modname" L2: require() name 487 luaW_pushstring(_L2, _name); // L_: [fixed] args... n "modname" L2: require() name
462 LuaError const _rc{ lua_pcall(_L2, 1, 1, 0) }; // L_: [fixed] args... n "modname" L2: ret/errcode 488 LuaError const _rc{ lua_pcall(_L2, 1, 1, 0) }; // L_: [fixed] args... n "modname" L2: ret/errcode
463 if (_rc != LuaError::OK) { 489 if (_rc != LuaError::OK) {
464 // propagate error to main state if any 490 // propagate error to main state if any
@@ -493,7 +519,7 @@ LUAG_FUNC(lane_new)
493 lua_pushnil(L_); // L_: [fixed] args... nil L2: 519 lua_pushnil(L_); // L_: [fixed] args... nil L2:
494 // Lua 5.2 wants us to push the globals table on the stack 520 // Lua 5.2 wants us to push the globals table on the stack
495 InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} }; 521 InterCopyContext _c{ _U, DestState{ _L2 }, SourceState{ L_ }, {}, {}, {}, {}, {} };
496 luaG_pushglobaltable(_L2); // L_: [fixed] args... nil L2: _G 522 luaW_pushglobaltable(_L2); // L_: [fixed] args... nil L2: _G
497 while (lua_next(L_, _globals_idx)) { // L_: [fixed] args... k v L2: _G 523 while (lua_next(L_, _globals_idx)) { // L_: [fixed] args... k v L2: _G
498 std::ignore = _c.interCopy(2); // L_: [fixed] args... k v L2: _G k v 524 std::ignore = _c.interCopy(2); // L_: [fixed] args... k v L2: _G k v
499 // assign it in L2's globals table 525 // assign it in L2's globals table
@@ -507,7 +533,7 @@ LUAG_FUNC(lane_new)
507 533
508 // Lane main function 534 // Lane main function
509 [[maybe_unused]] int const _errorHandlerCount{ _lane->pushErrorHandler() }; // L_: [fixed] args... L2: eh? 535 [[maybe_unused]] int const _errorHandlerCount{ _lane->pushErrorHandler() }; // L_: [fixed] args... L2: eh?
510 LuaType const _func_type{ luaG_type(L_, kFuncIdx) }; 536 LuaType const _func_type{ luaW_type(L_, kFuncIdx) };
511 if (_func_type == LuaType::FUNCTION) { 537 if (_func_type == LuaType::FUNCTION) {
512 DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: transfer lane body" << std::endl); 538 DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: transfer lane body" << std::endl);
513 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); 539 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U });
@@ -524,7 +550,7 @@ LUAG_FUNC(lane_new)
524 raise_luaL_error(L_, "error when parsing lane function code"); 550 raise_luaL_error(L_, "error when parsing lane function code");
525 } 551 }
526 } else { 552 } else {
527 raise_luaL_error(L_, "Expected function, got %s", luaG_typename(L_, _func_type).data()); 553 raise_luaL_error(L_, "Expected function, got %s", luaW_typename(L_, _func_type).data());
528 } 554 }
529 STACK_CHECK(L_, 0); 555 STACK_CHECK(L_, 0);
530 STACK_CHECK(_L2, _errorHandlerCount + 1); 556 STACK_CHECK(_L2, _errorHandlerCount + 1);
@@ -612,7 +638,7 @@ LUAG_FUNC(wakeup_conv)
612 638
613 STACK_CHECK_START_REL(L_, 0); 639 STACK_CHECK_START_REL(L_, 0);
614 auto _readInteger = [L = L_](std::string_view const& name_) { 640 auto _readInteger = [L = L_](std::string_view const& name_) {
615 std::ignore = luaG_getfield(L, StackIndex{ 1 }, name_); 641 std::ignore = luaW_getfield(L, StackIndex{ 1 }, name_);
616 lua_Integer const val{ lua_tointeger(L, -1) }; 642 lua_Integer const val{ lua_tointeger(L, -1) };
617 lua_pop(L, 1); 643 lua_pop(L, 1);
618 return static_cast<int>(val); 644 return static_cast<int>(val);
@@ -628,7 +654,7 @@ LUAG_FUNC(wakeup_conv)
628 // If Lua table has '.isdst' we trust that. If it does not, we'll let 654 // If Lua table has '.isdst' we trust that. If it does not, we'll let
629 // 'mktime' decide on whether the time is within DST or not (value -1). 655 // 'mktime' decide on whether the time is within DST or not (value -1).
630 // 656 //
631 int const _isdst{ (luaG_getfield(L_, StackIndex{ 1 }, "isdst") == LuaType::BOOLEAN) ? lua_toboolean(L_, -1) : -1 }; 657 int const _isdst{ (luaW_getfield(L_, StackIndex{ 1 }, "isdst") == LuaType::BOOLEAN) ? lua_toboolean(L_, -1) : -1 };
632 lua_pop(L_, 1); 658 lua_pop(L_, 1);
633 STACK_CHECK(L_, 0); 659 STACK_CHECK(L_, 0);
634 660
@@ -658,6 +684,7 @@ namespace {
658 { Universe::kFinally, Universe::InitializeFinalizer }, 684 { Universe::kFinally, Universe::InitializeFinalizer },
659 { "linda", LG_linda }, 685 { "linda", LG_linda },
660 { "nameof", LG_nameof }, 686 { "nameof", LG_nameof },
687 { "thread_priority_range", LG_thread_priority_range },
661 { "now_secs", LG_now_secs }, 688 { "now_secs", LG_now_secs },
662 { "register", lanes_register }, 689 { "register", lanes_register },
663 { "set_singlethreaded", LG_set_singlethreaded }, 690 { "set_singlethreaded", LG_set_singlethreaded },
@@ -692,8 +719,8 @@ LUAG_FUNC(configure)
692 719
693 Universe* _U{ Universe::Get(L_) }; 720 Universe* _U{ Universe::Get(L_) };
694 bool const _from_master_state{ _U == nullptr }; 721 bool const _from_master_state{ _U == nullptr };
695 std::string_view const _name{ luaG_checkstring(L_, StackIndex{ lua_upvalueindex(1) }) }; 722 std::string_view const _name{ luaW_checkstring(L_, StackIndex{ lua_upvalueindex(1) }) };
696 LUA_ASSERT(L_, luaG_type(L_, StackIndex{ 1 }) == LuaType::TABLE); 723 LUA_ASSERT(L_, luaW_type(L_, StackIndex{ 1 }) == LuaType::TABLE);
697 724
698 STACK_GROW(L_, 4); 725 STACK_GROW(L_, 4);
699 STACK_CHECK_START_ABS(L_, 1); // L_: settings 726 STACK_CHECK_START_ABS(L_, 1); // L_: settings
@@ -703,7 +730,7 @@ LUAG_FUNC(configure)
703 730
704 if (_U == nullptr) { 731 if (_U == nullptr) {
705 // 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... 732 // 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...
706 kLaneNameRegKey.setValue(L_, [](lua_State* L_) { luaG_pushstring(L_, "main"); }); 733 kLaneNameRegKey.setValue(L_, [](lua_State* L_) { luaW_pushstring(L_, "main"); });
707 734
708 // create the universe 735 // create the universe
709 _U = Universe::Create(L_); // L_: settings universe 736 _U = Universe::Create(L_); // L_: settings universe
@@ -719,7 +746,7 @@ LUAG_FUNC(configure)
719 lua_pushnil(L_); // L_: settings M nil 746 lua_pushnil(L_); // L_: settings M nil
720 lua_setfield(L_, -2, "configure"); // L_: settings M 747 lua_setfield(L_, -2, "configure"); // L_: settings M
721 // add functions to the module's table 748 // add functions to the module's table
722 luaG_registerlibfuncs(L_, local::sLanesFunctions); 749 luaW_registerlibfuncs(L_, local::sLanesFunctions);
723 750
724 // register core.threads() only if settings say it should be available 751 // register core.threads() only if settings say it should be available
725 if (_U->tracker.isActive()) { 752 if (_U->tracker.isActive()) {
@@ -744,7 +771,7 @@ LUAG_FUNC(configure)
744 lua_pushcclosure(L_, LG_require, 1); // L_: settings M lanes.require 771 lua_pushcclosure(L_, LG_require, 1); // L_: settings M lanes.require
745 lua_setfield(L_, -2, "require"); // L_: settings M 772 lua_setfield(L_, -2, "require"); // L_: settings M
746 773
747 luaG_pushstring( 774 luaW_pushstring(
748 L_, 775 L_,
749 "%d.%d.%d", 776 "%d.%d.%d",
750 LANES_VERSION_MAJOR, 777 LANES_VERSION_MAJOR,
@@ -753,9 +780,6 @@ LUAG_FUNC(configure)
753 ); // L_: settings M VERSION 780 ); // L_: settings M VERSION
754 lua_setfield(L_, -2, "version"); // L_: settings M 781 lua_setfield(L_, -2, "version"); // L_: settings M
755 782
756 lua_pushinteger(L_, kThreadPrioMax); // L_: settings M kThreadPrioMax
757 lua_setfield(L_, -2, "max_prio"); // L_: settings M
758
759 kCancelError.pushKey(L_); // L_: settings M kCancelError 783 kCancelError.pushKey(L_); // L_: settings M kCancelError
760 lua_setfield(L_, -2, "cancel_error"); // L_: settings M 784 lua_setfield(L_, -2, "cancel_error"); // L_: settings M
761 785
@@ -779,7 +803,7 @@ LUAG_FUNC(configure)
779 // don't do this when called during the initialization of a new lane, 803 // don't do this when called during the initialization of a new lane,
780 // because we will do it after on_state_create() is called, 804 // because we will do it after on_state_create() is called,
781 // and we don't want to skip _G because of caching in case globals are created then 805 // and we don't want to skip _G because of caching in case globals are created then
782 luaG_pushglobaltable(L_); // L_: settings M _G 806 luaW_pushglobaltable(L_); // L_: settings M _G
783 tools::PopulateFuncLookupTable(L_, kIdxTop, {}); 807 tools::PopulateFuncLookupTable(L_, kIdxTop, {});
784 lua_pop(L_, 1); // L_: settings M 808 lua_pop(L_, 1); // L_: settings M
785 } 809 }
@@ -859,10 +883,10 @@ LANES_API int luaopen_lanes_core(lua_State* const L_)
859 883
860 // Prevent PUC-Lua/LuaJIT mismatch. Hopefully this works for MoonJIT too 884 // Prevent PUC-Lua/LuaJIT mismatch. Hopefully this works for MoonJIT too
861 if constexpr (LUAJIT_FLAVOR() == 0) { 885 if constexpr (LUAJIT_FLAVOR() == 0) {
862 if (luaG_getmodule(L_, LUA_JITLIBNAME) != LuaType::NIL) 886 if (luaW_getmodule(L_, LUA_JITLIBNAME) != LuaType::NIL)
863 raise_luaL_error(L_, "Lanes is built for PUC-Lua, don't run from LuaJIT"); 887 raise_luaL_error(L_, "Lanes is built for PUC-Lua, don't run from LuaJIT");
864 } else { 888 } else {
865 if (luaG_getmodule(L_, LUA_JITLIBNAME) == LuaType::NIL) 889 if (luaW_getmodule(L_, LUA_JITLIBNAME) == LuaType::NIL)
866 raise_luaL_error(L_, "Lanes is built for LuaJIT, don't run from PUC-Lua"); 890 raise_luaL_error(L_, "Lanes is built for LuaJIT, don't run from PUC-Lua");
867 } 891 }
868 lua_pop(L_, 1); // L_: 892 lua_pop(L_, 1); // L_:
diff --git a/src/lanes.lua b/src/lanes.lua
index 3ee959c..c5b3315 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -280,6 +280,10 @@ local opt_validators =
280 local tv = type(v_) 280 local tv = type(v_)
281 return (tv == "string") and v_ or raise_option_error("name", tv, v_) 281 return (tv == "string") and v_ or raise_option_error("name", tv, v_)
282 end, 282 end,
283 native_priority = function(v_)
284 local tv = type(v_)
285 return (tv == "number") and v_ or raise_option_error("native_priority", tv, v_)
286 end,
283 package = function(v_) 287 package = function(v_)
284 local tv = type(v_) 288 local tv = type(v_)
285 return (tv == "table") and v_ or raise_option_error("package", tv, v_) 289 return (tv == "table") and v_ or raise_option_error("package", tv, v_)
@@ -295,7 +299,7 @@ local opt_validators =
295} 299}
296 300
297-- ############################################################################################# 301-- #############################################################################################
298-- ##################################### lanes.gen() ########################################### 302-- ################################### lanes.gen/coro() ########################################
299-- ############################################################################################# 303-- #############################################################################################
300 304
301local process_gen_opt = function(...) 305local process_gen_opt = function(...)
@@ -367,9 +371,16 @@ local process_gen_opt = function(...)
367 opt[k] = validator(v) 371 opt[k] = validator(v)
368 end 372 end
369 end 373 end
374
375 -- special case: can't have priority and native_priority at the same time
376 if opt.priority and opt.native_priority then
377 error "priority and native_priority cannot be specified together"
378 end
370 return func, libs, opt 379 return func, libs, opt
371end -- process_gen_opt 380end -- process_gen_opt
372 381
382-- #################################################################################################
383
373-- lane_h[1..n]: lane results, same as via 'lane_h:join()' 384-- lane_h[1..n]: lane results, same as via 'lane_h:join()'
374-- lane_h[0]: can be read to make sure a thread has finished (gives the number of available results) 385-- lane_h[0]: can be read to make sure a thread has finished (gives the number of available results)
375-- lane_h[negative]: error message, without propagating the error 386-- lane_h[negative]: error message, without propagating the error
@@ -408,25 +419,28 @@ end -- process_gen_opt
408-- Calling with a function argument ('lane_func') ends the string/table 419-- Calling with a function argument ('lane_func') ends the string/table
409-- modifiers, and prepares a lane generator. 420-- modifiers, and prepares a lane generator.
410 421
411-- receives a sequence of strings and tables, plus a function 422local make_generator = function(is_coro_, ...)
412local gen = function(...)
413 local func, libs, opt = process_gen_opt(...) 423 local func, libs, opt = process_gen_opt(...)
414 local core_lane_new = assert(core.lane_new) 424 local core_lane_new = assert(core.lane_new)
415 local priority, globals, package, required, gc_cb, name, error_trace_level = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb, opt.name, error_trace_levels[opt.error_trace_level] 425 local prio_is_native = opt.native_priority and true or false
426 local priority, globals, package, required, gc_cb, name, error_trace_level = opt.priority or opt.native_priority, opt.globals, opt.package or package, opt.required, opt.gc_cb, opt.name, error_trace_levels[opt.error_trace_level]
416 return function(...) 427 return function(...)
417 -- must pass functions args last else they will be truncated to the first one 428 -- must pass functions args last else they will be truncated to the first one
418 return core_lane_new(func, libs, priority, globals, package, required, gc_cb, name, error_trace_level, false, ...) 429 return core_lane_new(func, libs, prio_is_native, priority, globals, package, required, gc_cb, name, error_trace_level, is_coro_, ...)
419 end 430 end
431end -- make_generator
432
433-- #################################################################################################
434
435-- receives a sequence of strings and tables, plus a function
436local gen = function(...)
437 return make_generator(false, ...)
420end -- gen() 438end -- gen()
421 439
440-- #################################################################################################
441
422local coro = function(...) 442local coro = function(...)
423 local func, libs, opt = process_gen_opt(...) 443 return make_generator(true, ...)
424 local core_lane_new = assert(core.lane_new)
425 local priority, globals, package, required, gc_cb, name, error_trace_level = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb, opt.name, error_trace_levels[opt.error_trace_level]
426 return function(...)
427 -- must pass functions args last else they will be truncated to the first one
428 return core_lane_new(func, libs, priority, globals, package, required, gc_cb, name, error_trace_level, true, ...)
429 end
430end -- coro() 444end -- coro()
431 445
432-- ################################################################################################# 446-- #################################################################################################
@@ -656,7 +670,8 @@ local configure_timers = function()
656 end 670 end
657 end 671 end
658 end -- timer_body() 672 end -- timer_body()
659 timer_lane = gen("lanes_core,table", { name = "LanesTimer", package = {}, priority = core.max_prio }, timer_body)() 673 local min_prio, max_prio = core.thread_priority_range()
674 timer_lane = gen("lanes_core,table", { name = "LanesTimer", package = {}, priority = max_prio }, timer_body)()
660 end -- first_time 675 end -- first_time
661 676
662 ----- 677 -----
@@ -876,6 +891,7 @@ local configure = function(settings_)
876 lanes.set_thread_affinity = core.set_thread_affinity 891 lanes.set_thread_affinity = core.set_thread_affinity
877 lanes.set_thread_priority = core.set_thread_priority 892 lanes.set_thread_priority = core.set_thread_priority
878 lanes.sleep = core.sleep 893 lanes.sleep = core.sleep
894 lanes.thread_priority_range = core.thread_priority_range
879 lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false 895 lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false
880 896
881 lanes.gen = gen 897 lanes.gen = gen
diff --git a/src/lanesconf.h b/src/lanesconf.h
index 0afd493..07f1d52 100644
--- a/src/lanesconf.h
+++ b/src/lanesconf.h
@@ -42,12 +42,12 @@
42#endif // __cplusplus 42#endif // __cplusplus
43#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 43#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
44 44
45// kind of MSVC-specific 45// LANES_DEBUG has to be provided externally (makefile, vcproj, whatever)
46#ifdef _DEBUG 46#ifdef LANES_DEBUG
47#define HAVE_LUA_ASSERT() 1 47#define HAVE_LUA_ASSERT() 1
48#else // NDEBUG 48#else // LANES_DEBUG
49#define HAVE_LUA_ASSERT() 0 49#define HAVE_LUA_ASSERT() 0
50#endif // NDEBUG 50#endif // LANES_DEBUG
51 51
52#define USE_DEBUG_SPEW() 0 52#define USE_DEBUG_SPEW() 0
53#define HAVE_DECODA_SUPPORT() 0 53#define HAVE_DECODA_SUPPORT() 0
diff --git a/src/linda.cpp b/src/linda.cpp
index fa28385..1f4b19d 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -47,7 +47,7 @@ namespace {
47 { 47 {
48 STACK_CHECK_START_REL(L_, 0); 48 STACK_CHECK_START_REL(L_, 0);
49 for (StackIndex const _i : std::ranges::iota_view{ start_, StackIndex{ end_ + 1 } }) { 49 for (StackIndex const _i : std::ranges::iota_view{ start_, StackIndex{ end_ + 1 } }) {
50 LuaType const _t{ luaG_type(L_, _i) }; 50 LuaType const _t{ luaW_type(L_, _i) };
51 switch (_t) { 51 switch (_t) {
52 case LuaType::BOOLEAN: 52 case LuaType::BOOLEAN:
53 case LuaType::NUMBER: 53 case LuaType::NUMBER:
@@ -109,13 +109,13 @@ namespace {
109 { 109 {
110 Linda* const _linda{ ToLinda<OPT>(L_, idx_) }; 110 Linda* const _linda{ ToLinda<OPT>(L_, idx_) };
111 if (_linda != nullptr) { 111 if (_linda != nullptr) {
112 luaG_pushstring(L_, "Linda: "); 112 luaW_pushstring(L_, "Linda: ");
113 std::string_view const _lindaName{ _linda->getName() }; 113 std::string_view const _lindaName{ _linda->getName() };
114 if (!_lindaName.empty()) { 114 if (!_lindaName.empty()) {
115 luaG_pushstring(L_, _lindaName); 115 luaW_pushstring(L_, _lindaName);
116 } else { 116 } else {
117 // obfuscate the pointer so that we can't read the value with our eyes out of a script 117 // obfuscate the pointer so that we can't read the value with our eyes out of a script
118 luaG_pushstring(L_, "%p", _linda->obfuscated()); 118 luaW_pushstring(L_, "%p", _linda->obfuscated());
119 } 119 }
120 lua_concat(L_, 2); 120 lua_concat(L_, 2);
121 return 1; 121 return 1;
@@ -132,7 +132,7 @@ namespace {
132 StackIndex _key_i{ 2 }; // index of first slot, if timeout not there 132 StackIndex _key_i{ 2 }; // index of first slot, if timeout not there
133 133
134 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; 134 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
135 if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion 135 if (luaW_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion
136 lua_Duration const _duration{ lua_tonumber(L_, 2) }; 136 lua_Duration const _duration{ lua_tonumber(L_, 2) };
137 if (_duration.count() >= 0.0) { 137 if (_duration.count() >= 0.0) {
138 _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(_duration); 138 _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(_duration);
@@ -147,6 +147,42 @@ namespace {
147 } 147 }
148 148
149 // ############################################################################################# 149 // #############################################################################################
150 static bool WaitInternal([[maybe_unused]] lua_State* const L_, Lane* const lane_, Linda* const linda_, Keeper* const keeper_, std::condition_variable& waitingOn_, std::chrono::time_point<std::chrono::steady_clock> until_)
151 {
152 Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
153 if (lane_ != nullptr) {
154 // change status of lane to "waiting"
155 _prev_status = lane_->status.load(std::memory_order_acquire); // Running, most likely
156 LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case
157 LUA_ASSERT(L_, lane_->waiting_on == nullptr);
158 lane_->waiting_on = &waitingOn_;
159 lane_->status.store(Lane::Waiting, std::memory_order_release);
160 }
161
162 // wait until the final target date by small increments, interrupting regularly so that we can check for cancel requests,
163 // in case some timing issue caused a cancel request to be issued, and the condvar signalled, before we actually wait for it
164 auto const [_forceTryAgain, _until_check_cancel] = std::invoke([until_, wakePeriod = linda_->getWakePeriod()] {
165 auto _until_check_cancel{ std::chrono::time_point<std::chrono::steady_clock>::max() };
166 if (wakePeriod.count() > 0.0f) {
167 _until_check_cancel = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(wakePeriod);
168 }
169 bool const _forceTryAgain{ _until_check_cancel < until_ };
170 return std::make_tuple(_forceTryAgain, _forceTryAgain ? _until_check_cancel : until_);
171 });
172
173 // operation can't complete: wake when it is signalled to be possible, or when timeout is reached
174 std::unique_lock<std::mutex> _guard{ keeper_->mutex, std::adopt_lock };
175 std::cv_status const _status{ waitingOn_.wait_until(_guard, _until_check_cancel) };
176 _guard.release(); // we don't want to unlock the mutex on exit!
177 bool const _try_again{ _forceTryAgain || (_status == std::cv_status::no_timeout) }; // detect spurious wakeups
178 if (lane_ != nullptr) {
179 lane_->waiting_on = nullptr;
180 lane_->status.store(_prev_status, std::memory_order_release);
181 }
182 return _try_again;
183 }
184
185 // #############################################################################################
150 186
151 // the implementation for linda:receive() and linda:receive_batched() 187 // the implementation for linda:receive() and linda:receive_batched()
152 static int ReceiveInternal(lua_State* const L_, bool const batched_) 188 static int ReceiveInternal(lua_State* const L_, bool const batched_)
@@ -201,6 +237,7 @@ namespace {
201 _cancel = (_cancel != CancelRequest::None) 237 _cancel = (_cancel != CancelRequest::None)
202 ? _cancel 238 ? _cancel
203 : ((_linda->cancelStatus == Linda::Cancelled) ? CancelRequest::Soft : CancelRequest::None); 239 : ((_linda->cancelStatus == Linda::Cancelled) ? CancelRequest::Soft : CancelRequest::None);
240
204 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything 241 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
205 if (!_try_again || _cancel != CancelRequest::None) { 242 if (!_try_again || _cancel != CancelRequest::None) {
206 _pushed.emplace(0); 243 _pushed.emplace(0);
@@ -227,38 +264,7 @@ namespace {
227 } 264 }
228 265
229 // nothing received, wait until timeout or signalled that we should try again 266 // nothing received, wait until timeout or signalled that we should try again
230 { 267 _try_again = WaitInternal(L_, _lane, _linda, _keeper, _linda->writeHappened, _until);
231 Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
232 if (_lane != nullptr) {
233 // change status of lane to "waiting"
234 _prev_status = _lane->status.load(std::memory_order_acquire); // Running, most likely
235 LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case
236 LUA_ASSERT(L_, _lane->waiting_on == nullptr);
237 _lane->waiting_on = &_linda->writeHappened;
238 _lane->status.store(Lane::Waiting, std::memory_order_release);
239 }
240
241 // wait until the final target date by small increments, interrupting regularly so that we can check for cancel requests,
242 // in case some timing issue caused a cancel request to be issued, and the condvar signalled, before we actually wait for it
243 auto const [_forceTryAgain, _until_check_cancel] = std::invoke([_until, wakePeriod = _linda->getWakePeriod()] {
244 auto _until_check_cancel{ std::chrono::time_point<std::chrono::steady_clock>::max() };
245 if (wakePeriod.count() > 0.0f) {
246 _until_check_cancel = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(wakePeriod);
247 }
248 bool const _forceTryAgain{ _until_check_cancel < _until };
249 return std::make_tuple(_forceTryAgain, _forceTryAgain ? _until_check_cancel : _until);
250 });
251
252 // not enough data to read: wakeup when data was sent, or when timeout is reached
253 std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock };
254 std::cv_status const _status{ _linda->writeHappened.wait_until(_guard, _until_check_cancel) };
255 _guard.release(); // we don't want to unlock the mutex on exit!
256 _try_again = _forceTryAgain || (_status == std::cv_status::no_timeout); // detect spurious wakeups
257 if (_lane != nullptr) {
258 _lane->waiting_on = nullptr;
259 _lane->status.store(_prev_status, std::memory_order_release);
260 }
261 }
262 } 268 }
263 STACK_CHECK(_K, 0); 269 STACK_CHECK(_K, 0);
264 270
@@ -273,7 +279,7 @@ namespace {
273 if (_nbPushed == 0) { 279 if (_nbPushed == 0) {
274 // not enough data in the linda slot to fulfill the request, return nil, "timeout" 280 // not enough data in the linda slot to fulfill the request, return nil, "timeout"
275 lua_pushnil(L_); 281 lua_pushnil(L_);
276 luaG_pushstring(L_, "timeout"); 282 luaW_pushstring(L_, "timeout");
277 return 2; 283 return 2;
278 } 284 }
279 return _nbPushed; 285 return _nbPushed;
@@ -332,6 +338,7 @@ Keeper* Linda::acquireKeeper() const
332 Keeper* const _keeper{ whichKeeper() }; 338 Keeper* const _keeper{ whichKeeper() };
333 if (_keeper) { 339 if (_keeper) {
334 _keeper->mutex.lock(); 340 _keeper->mutex.lock();
341 keeperOperationCount.fetch_add(1, std::memory_order_seq_cst);
335 } 342 }
336 return _keeper; 343 return _keeper;
337} 344}
@@ -344,16 +351,16 @@ Linda* Linda::CreateTimerLinda(lua_State* const L_)
344 // Initialize 'timerLinda'; a common Linda object shared by all states 351 // Initialize 'timerLinda'; a common Linda object shared by all states
345 lua_pushcfunction(L_, LG_linda); // L_: lanes.linda 352 lua_pushcfunction(L_, LG_linda); // L_: lanes.linda
346 lua_createtable(L_, 0, 3); // L_: lanes.linda {} 353 lua_createtable(L_, 0, 3); // L_: lanes.linda {}
347 luaG_pushstring(L_, "lanes-timer"); // L_: lanes.linda {} "lanes-timer" 354 luaW_pushstring(L_, "lanes-timer"); // L_: lanes.linda {} "lanes-timer"
348 luaG_setfield(L_, StackIndex{ -2 }, std::string_view{ "name" }); // L_: lanes.linda { .name="lanes-timer" } 355 luaW_setfield(L_, StackIndex{ -2 }, std::string_view{ "name" }); // L_: lanes.linda { .name="lanes-timer" }
349 lua_pushinteger(L_, 0); // L_: lanes.linda { .name="lanes-timer" } 0 356 lua_pushinteger(L_, 0); // L_: lanes.linda { .name="lanes-timer" } 0
350 luaG_setfield(L_, StackIndex{ -2 }, std::string_view{ "group" }); // L_: lanes.linda { .name="lanes-timer" .group = 0 } 357 luaW_setfield(L_, StackIndex{ -2 }, std::string_view{ "group" }); // L_: lanes.linda { .name="lanes-timer" .group = 0 }
351 // note that wake_period is not set (will default to the value in the universe) 358 // note that wake_period is not set (will default to the value in the universe)
352 lua_call(L_, 1, 1); // L_: linda 359 lua_call(L_, 1, 1); // L_: linda
353 STACK_CHECK(L_, 1); 360 STACK_CHECK(L_, 1);
354 361
355 // Proxy userdata contents is only a 'DeepPrelude*' pointer 362 // Proxy userdata contents is only a 'DeepPrelude*' pointer
356 auto const _timerLinda{ *luaG_tofulluserdata<Linda*>(L_, kIdxTop) }; 363 auto const _timerLinda{ *luaW_tofulluserdata<Linda*>(L_, kIdxTop) };
357 // increment refcount so that this linda remains alive as long as the universe exists. 364 // increment refcount so that this linda remains alive as long as the universe exists.
358 _timerLinda->refcount.fetch_add(1, std::memory_order_relaxed); 365 _timerLinda->refcount.fetch_add(1, std::memory_order_relaxed);
359 lua_pop(L_, 1); // L_: 366 lua_pop(L_, 1); // L_:
@@ -416,7 +423,6 @@ int Linda::ProtectedCall(lua_State* const L_, lua_CFunction const f_)
416 // doing LindaFactory::deleteDeepObjectInternal -> keeper_call(clear) 423 // doing LindaFactory::deleteDeepObjectInternal -> keeper_call(clear)
417 lua_gc(L_, LUA_GCSTOP, 0); 424 lua_gc(L_, LUA_GCSTOP, 0);
418 425
419 LUA_ASSERT_CODE(auto const _koip{ _linda->startKeeperOperation(L_) });
420 // if we didn't do anything wrong, the keeper stack should be clean 426 // if we didn't do anything wrong, the keeper stack should be clean
421 LUA_ASSERT(L_, lua_gettop(_K) == 0); 427 LUA_ASSERT(L_, lua_gettop(_K) == 0);
422 428
@@ -424,7 +430,7 @@ int Linda::ProtectedCall(lua_State* const L_, lua_CFunction const f_)
424 lua_pushcfunction(L_, f_); 430 lua_pushcfunction(L_, f_);
425 lua_insert(L_, 1); 431 lua_insert(L_, 1);
426 // do a protected call 432 // do a protected call
427 LuaError const _rc{ lua_pcall(L_, lua_gettop(L_) - 1, LUA_MULTRET, 0) }; 433 LuaError const _rc{ ToLuaError(lua_pcall(L_, lua_gettop(L_) - 1, LUA_MULTRET, 0)) };
428 // whatever happens, the keeper state stack must be empty when we are done 434 // whatever happens, the keeper state stack must be empty when we are done
429 lua_settop(_K, 0); 435 lua_settop(_K, 0);
430 436
@@ -446,7 +452,7 @@ int Linda::ProtectedCall(lua_State* const L_, lua_CFunction const f_)
446 452
447void Linda::pushCancelString(lua_State* L_) const 453void Linda::pushCancelString(lua_State* L_) const
448{ 454{
449 luaG_pushstring(L_, cancelStatus == Status::Cancelled ? "cancelled" : "active"); 455 luaW_pushstring(L_, cancelStatus == Status::Cancelled ? "cancelled" : "active");
450} 456}
451 457
452// ################################################################################################# 458// #################################################################################################
@@ -455,6 +461,7 @@ void Linda::releaseKeeper(Keeper* const keeper_) const
455{ 461{
456 if (keeper_) { // can be nullptr if we tried to acquire during shutdown 462 if (keeper_) { // can be nullptr if we tried to acquire during shutdown
457 assert(keeper_ == whichKeeper()); 463 assert(keeper_ == whichKeeper());
464 keeperOperationCount.fetch_sub(1, std::memory_order_seq_cst);
458 keeper_->mutex.unlock(); 465 keeper_->mutex.unlock();
459 } 466 }
460} 467}
@@ -498,7 +505,7 @@ void Linda::setName(std::string_view const& name_)
498LUAG_FUNC(linda_cancel) 505LUAG_FUNC(linda_cancel)
499{ 506{
500 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; 507 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
501 std::string_view const _who{ luaG_optstring(L_, StackIndex{ 2 }, "both") }; 508 std::string_view const _who{ luaW_optstring(L_, StackIndex{ 2 }, "both") };
502 // make sure we got 2 arguments: the linda and the cancellation mode 509 // make sure we got 2 arguments: the linda and the cancellation mode
503 luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments"); 510 luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments");
504 511
@@ -586,13 +593,13 @@ static int linda_index_string(lua_State* L_)
586 Linda* const _linda{ ToLinda<false>(L_, kIdxSelf) }; 593 Linda* const _linda{ ToLinda<false>(L_, kIdxSelf) };
587 LUA_ASSERT(L_, lua_gettop(L_) == 2); // L_: linda "key" 594 LUA_ASSERT(L_, lua_gettop(L_) == 2); // L_: linda "key"
588 595
589 std::string_view const _keystr{ luaG_tostring(L_, kIdxKey) }; 596 std::string_view const _keystr{ luaW_tostring(L_, kIdxKey) };
590 lua_settop(L_, 2); // keep only our original arguments on the stack 597 lua_settop(L_, 2); // keep only our original arguments on the stack
591 598
592 // look in metatable first 599 // look in metatable first
593 lua_getmetatable(L_, kIdxSelf); // L_: linda "key" mt 600 lua_getmetatable(L_, kIdxSelf); // L_: linda "key" mt
594 lua_replace(L_, -3); // L_: mt "key" 601 lua_replace(L_, -3); // L_: mt "key"
595 if (luaG_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // found something? // L_: mt value 602 if (luaW_rawget(L_, StackIndex{ -2 }) != LuaType::NIL) { // found something? // L_: mt value
596 return 1; // done 603 return 1; // done
597 } 604 }
598 605
@@ -612,12 +619,12 @@ static LUAG_FUNC(linda_index)
612 static constexpr StackIndex kIdxKey{ 2 }; 619 static constexpr StackIndex kIdxKey{ 2 };
613 LUA_ASSERT(L_, lua_gettop(L_) == 2); 620 LUA_ASSERT(L_, lua_gettop(L_) == 2);
614 621
615 switch (luaG_type(L_, kIdxKey)) { 622 switch (luaW_type(L_, kIdxKey)) {
616 case LuaType::STRING: 623 case LuaType::STRING:
617 return linda_index_string(L_); // stack modification is undefined, returned value is at the top 624 return linda_index_string(L_); // stack modification is undefined, returned value is at the top
618 625
619 default: // unknown key 626 default: // unknown key
620 raise_luaL_error(L_, "Unsupported linda indexing key type %s", luaG_typename(L_, kIdxKey).data()); 627 raise_luaL_error(L_, "Unsupported linda indexing key type %s", luaW_typename(L_, kIdxKey).data());
621 } 628 }
622} 629}
623 630
@@ -760,7 +767,7 @@ LUAG_FUNC(linda_limit)
760 int const _nargs{ lua_gettop(L_) }; 767 int const _nargs{ lua_gettop(L_) };
761 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments"); 768 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments");
762 // make sure we got a numeric limit, or "unlimited", (or nothing) 769 // make sure we got a numeric limit, or "unlimited", (or nothing)
763 bool const _unlimited{ luaG_tostring(L_, StackIndex{ 3 }) == "unlimited" }; 770 bool const _unlimited{ luaW_tostring(L_, StackIndex{ 3 }) == "unlimited" };
764 LindaLimit const _val{ _unlimited ? std::numeric_limits<LindaLimit::type>::max() : static_cast<LindaLimit::type>(luaL_optinteger(L_, 3, 0)) }; 771 LindaLimit const _val{ _unlimited ? std::numeric_limits<LindaLimit::type>::max() : static_cast<LindaLimit::type>(luaL_optinteger(L_, 3, 0)) };
765 if (_val < 0) { 772 if (_val < 0) {
766 raise_luaL_argerror(L_, StackIndex{ 3 }, "limit must be >= 0"); 773 raise_luaL_argerror(L_, StackIndex{ 3 }, "limit must be >= 0");
@@ -771,23 +778,23 @@ LUAG_FUNC(linda_limit)
771 KeeperCallResult _pushed; 778 KeeperCallResult _pushed;
772 if (_linda->cancelStatus == Linda::Active) { 779 if (_linda->cancelStatus == Linda::Active) {
773 if (_unlimited) { 780 if (_unlimited) {
774 LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaG_tostring(L_, StackIndex{ 3 }) == "unlimited"); 781 LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaW_tostring(L_, StackIndex{ 3 }) == "unlimited");
775 // inside the Keeper, unlimited is signified with a -1 limit (can't use nil because of nil kNilSentinel conversions!) 782 // inside the Keeper, unlimited is signified with a -1 limit (can't use nil because of nil kNilSentinel conversions!)
776 lua_pop(L_, 1); // L_: linda slot 783 lua_pop(L_, 1); // L_: linda slot
777 lua_pushinteger(L_, -1); // L_: linda slot nil 784 lua_pushinteger(L_, -1); // L_: linda slot nil
778 } 785 }
779 Keeper* const _keeper{ _linda->whichKeeper() }; 786 Keeper* const _keeper{ _linda->whichKeeper() };
780 _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, StackIndex{ 2 }); 787 _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, StackIndex{ 2 });
781 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 2) && luaG_type(L_, kIdxTop) == LuaType::STRING); 788 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 2) && luaW_type(L_, kIdxTop) == LuaType::STRING);
782 if (_nargs == 3) { // 3 args: setting the limit 789 if (_nargs == 3) { // 3 args: setting the limit
783 // changing the limit: no error, boolean value saying if we should wake blocked writer threads 790 // changing the limit: no error, boolean value saying if we should wake blocked writer threads
784 LUA_ASSERT(L_, luaG_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN); // L_: bool string 791 LUA_ASSERT(L_, luaW_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN); // L_: bool string
785 if (lua_toboolean(L_, -2)) { 792 if (lua_toboolean(L_, -2)) {
786 _linda->readHappened.notify_all(); // To be done from within the 'K' locking area 793 _linda->readHappened.notify_all(); // To be done from within the 'K' locking area
787 } 794 }
788 } else { // 2 args: reading the limit 795 } else { // 2 args: reading the limit
789 // reading the limit: a number >=0 or "unlimited" 796 // reading the limit: a number >=0 or "unlimited"
790 LUA_ASSERT(L_, luaG_type(L_, StackIndex{ -2 }) == LuaType::NUMBER || luaG_tostring(L_, StackIndex{ -2 }) == "unlimited"); 797 LUA_ASSERT(L_, luaW_type(L_, StackIndex{ -2 }) == LuaType::NUMBER || luaW_tostring(L_, StackIndex{ -2 }) == "unlimited");
791 } 798 }
792 } else { // linda is cancelled 799 } else { // linda is cancelled
793 // do nothing and return nil,lanes.cancel_error 800 // do nothing and return nil,lanes.cancel_error
@@ -843,7 +850,7 @@ LUAG_FUNC(linda_restrict)
843 int const _nargs{ lua_gettop(L_) }; 850 int const _nargs{ lua_gettop(L_) };
844 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments"); 851 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments");
845 // make sure we got a known restrict mode, (or nothing) 852 // make sure we got a known restrict mode, (or nothing)
846 std::string_view const _mode{ luaG_tostring(L_, StackIndex{ 3 }) }; 853 std::string_view const _mode{ luaW_tostring(L_, StackIndex{ 3 }) };
847 if (!_mode.empty() && (_mode != "none" && _mode != "set/get" && _mode != "send/receive")) { 854 if (!_mode.empty() && (_mode != "none" && _mode != "set/get" && _mode != "send/receive")) {
848 raise_luaL_argerror(L_, StackIndex{ 3 }, "unknown restrict mode"); 855 raise_luaL_argerror(L_, StackIndex{ 3 }, "unknown restrict mode");
849 } 856 }
@@ -855,7 +862,7 @@ LUAG_FUNC(linda_restrict)
855 Keeper* const _keeper{ _linda->whichKeeper() }; 862 Keeper* const _keeper{ _linda->whichKeeper() };
856 _pushed = keeper_call(_keeper->K, KEEPER_API(restrict), L_, _linda, StackIndex{ 2 }); 863 _pushed = keeper_call(_keeper->K, KEEPER_API(restrict), L_, _linda, StackIndex{ 2 });
857 // we should get a single return value: the string describing the previous restrict mode 864 // we should get a single return value: the string describing the previous restrict mode
858 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 1) && luaG_type(L_, kIdxTop) == LuaType::STRING); 865 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 1) && luaW_type(L_, kIdxTop) == LuaType::STRING);
859 } else { // linda is cancelled 866 } else { // linda is cancelled
860 // do nothing and return nil,lanes.cancel_error 867 // do nothing and return nil,lanes.cancel_error
861 lua_pushnil(L_); 868 lua_pushnil(L_);
@@ -916,6 +923,7 @@ LUAG_FUNC(linda_send)
916 _cancel = (_cancel != CancelRequest::None) 923 _cancel = (_cancel != CancelRequest::None)
917 ? _cancel 924 ? _cancel
918 : ((_linda->cancelStatus == Linda::Cancelled) ? CancelRequest::Soft : CancelRequest::None); 925 : ((_linda->cancelStatus == Linda::Cancelled) ? CancelRequest::Soft : CancelRequest::None);
926
919 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything 927 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
920 if (!_try_again || _cancel != CancelRequest::None) { 928 if (!_try_again || _cancel != CancelRequest::None) {
921 _pushed.emplace(0); 929 _pushed.emplace(0);
@@ -948,38 +956,7 @@ LUAG_FUNC(linda_send)
948 } 956 }
949 957
950 // storage limit hit, wait until timeout or signalled that we should try again 958 // storage limit hit, wait until timeout or signalled that we should try again
951 { 959 _try_again = WaitInternal(L_, _lane, _linda, _keeper, _linda->readHappened, _until);
952 Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
953 if (_lane != nullptr) {
954 // change status of lane to "waiting"
955 _prev_status = _lane->status.load(std::memory_order_acquire); // Running, most likely
956 LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case
957 LUA_ASSERT(L_, _lane->waiting_on == nullptr);
958 _lane->waiting_on = &_linda->readHappened;
959 _lane->status.store(Lane::Waiting, std::memory_order_release);
960 }
961
962 // wait until the final target date by small increments, interrupting regularly so that we can check for cancel requests,
963 // in case some timing issue caused a cancel request to be issued, and the condvar signalled, before we actually wait for it
964 auto const [_forceTryAgain, _until_check_cancel] = std::invoke([_until, wakePeriod = _linda->getWakePeriod()] {
965 auto _until_check_cancel{ std::chrono::time_point<std::chrono::steady_clock>::max() };
966 if (wakePeriod.count() > 0.0f) {
967 _until_check_cancel = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(wakePeriod);
968 }
969 bool const _forceTryAgain{ _until_check_cancel < _until };
970 return std::make_tuple(_forceTryAgain, _forceTryAgain ? _until_check_cancel : _until);
971 });
972
973 // could not send because no room: wait until some data was read before trying again, or until timeout is reached
974 std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock };
975 std::cv_status const status{ _linda->readHappened.wait_until(_guard, _until_check_cancel) };
976 _guard.release(); // we don't want to unlock the mutex on exit!
977 _try_again = _forceTryAgain || (status == std::cv_status::no_timeout); // detect spurious wakeups
978 if (_lane != nullptr) {
979 _lane->waiting_on = nullptr;
980 _lane->status.store(_prev_status, std::memory_order_release);
981 }
982 }
983 } 960 }
984 STACK_CHECK(_K, 0); 961 STACK_CHECK(_K, 0);
985 962
@@ -1005,7 +982,7 @@ LUAG_FUNC(linda_send)
1005 } else { 982 } else {
1006 // not enough room in the Linda slot to fulfill the request, return nil, "timeout" 983 // not enough room in the Linda slot to fulfill the request, return nil, "timeout"
1007 lua_pushnil(L_); 984 lua_pushnil(L_);
1008 luaG_pushstring(L_, "timeout"); 985 luaW_pushstring(L_, "timeout");
1009 return 2; 986 return 2;
1010 } 987 }
1011 } 988 }
@@ -1040,7 +1017,7 @@ LUAG_FUNC(linda_set)
1040 if (kRestrictedChannel.equals(L_, kIdxTop)) { 1017 if (kRestrictedChannel.equals(L_, kIdxTop)) {
1041 raise_luaL_error(L_, "Key is restricted"); 1018 raise_luaL_error(L_, "Key is restricted");
1042 } 1019 }
1043 LUA_ASSERT(L_, _pushed.value() == 2 && luaG_type(L_, kIdxTop) == LuaType::STRING && luaG_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN); 1020 LUA_ASSERT(L_, _pushed.value() == 2 && luaW_type(L_, kIdxTop) == LuaType::STRING && luaW_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN);
1044 1021
1045 if (_has_data) { 1022 if (_has_data) {
1046 // we put some data in the slot, tell readers that they should wake 1023 // we put some data in the slot, tell readers that they should wake
@@ -1102,7 +1079,7 @@ LUAG_FUNC(linda_towatch)
1102LUAG_FUNC(linda_wake) 1079LUAG_FUNC(linda_wake)
1103{ 1080{
1104 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; 1081 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
1105 std::string_view const _who{ luaG_optstring(L_, StackIndex{ 2 }, "both") }; 1082 std::string_view const _who{ luaW_optstring(L_, StackIndex{ 2 }, "both") };
1106 // make sure we got 2 arguments: the linda and the wake targets 1083 // make sure we got 2 arguments: the linda and the wake targets
1107 luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments"); 1084 luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments");
1108 1085
@@ -1178,8 +1155,8 @@ LUAG_FUNC(linda)
1178 if (lua_isnil(L_, kIdxTop)) { 1155 if (lua_isnil(L_, kIdxTop)) {
1179 lua_pop(L_, 1); 1156 lua_pop(L_, 1);
1180 lua_pushnumber(L_, _U->lindaWakePeriod.count()); 1157 lua_pushnumber(L_, _U->lindaWakePeriod.count());
1181 } else if (luaG_type(L_, kIdxTop) == LuaType::STRING) { 1158 } else if (luaW_type(L_, kIdxTop) == LuaType::STRING) {
1182 if (luaG_tostring(L_, kIdxTop) != "never") { 1159 if (luaW_tostring(L_, kIdxTop) != "never") {
1183 luaL_argerror(L_, 1, "invalid wake_period"); 1160 luaL_argerror(L_, 1, "invalid wake_period");
1184 } else { 1161 } else {
1185 lua_pop(L_, 1); 1162 lua_pop(L_, 1);
@@ -1201,7 +1178,7 @@ LUAG_FUNC(linda)
1201 1178
1202#if LUA_VERSION_NUM >= 504 // to-be-closed support starts with Lua 5.4 1179#if LUA_VERSION_NUM >= 504 // to-be-closed support starts with Lua 5.4
1203 lua_getfield(L_, 1, "close_handler"); // L_: {} wake_period group close_handler 1180 lua_getfield(L_, 1, "close_handler"); // L_: {} wake_period group close_handler
1204 LuaType const _handlerType{ luaG_type(L_, kIdxTop) }; 1181 LuaType const _handlerType{ luaW_type(L_, kIdxTop) };
1205 if (_handlerType == LuaType::NIL) { 1182 if (_handlerType == LuaType::NIL) {
1206 lua_pop(L_, 1); // L_: {} wake_period group 1183 lua_pop(L_, 1); // L_: {} wake_period group
1207 } else if (_handlerType == LuaType::USERDATA || _handlerType == LuaType::TABLE) { 1184 } else if (_handlerType == LuaType::USERDATA || _handlerType == LuaType::TABLE) {
@@ -1212,7 +1189,7 @@ LUAG_FUNC(linda)
1212 } 1189 }
1213#endif // LUA_VERSION_NUM >= 504 1190#endif // LUA_VERSION_NUM >= 504
1214 1191
1215 auto const _nameType{ luaG_getfield(L_, StackIndex{ 1 }, "name") }; // L_: {} wake_period group [close_handler] name 1192 auto const _nameType{ luaW_getfield(L_, StackIndex{ 1 }, "name") }; // L_: {} wake_period group [close_handler] name
1216 luaL_argcheck(L_, _nameType == LuaType::NIL || _nameType == LuaType::STRING, 1, "name is not a string"); 1193 luaL_argcheck(L_, _nameType == LuaType::NIL || _nameType == LuaType::STRING, 1, "name is not a string");
1217 lua_replace(L_, 1); // L_: name wake_period group [close_handler] 1194 lua_replace(L_, 1); // L_: name wake_period group [close_handler]
1218 } 1195 }
diff --git a/src/linda.hpp b/src/linda.hpp
index cff30e5..f02c46e 100644
--- a/src/linda.hpp
+++ b/src/linda.hpp
@@ -14,26 +14,6 @@ class Linda final
14: public DeepPrelude // Deep userdata MUST start with this header 14: public DeepPrelude // Deep userdata MUST start with this header
15{ 15{
16 public: 16 public:
17 class [[nodiscard]] KeeperOperationInProgress final
18 {
19 private:
20 Linda& linda;
21 lua_State* const L{}; // just here for inspection while debugging
22
23 public:
24 KeeperOperationInProgress(Linda& linda_, lua_State* const L_)
25 : linda{ linda_ }
26 , L{ L_ }
27 {
28 [[maybe_unused]] UnusedInt const _prev{ linda.keeperOperationCount.fetch_add(1, std::memory_order_seq_cst) };
29 }
30
31 public:
32 ~KeeperOperationInProgress()
33 {
34 [[maybe_unused]] UnusedInt const _prev{ linda.keeperOperationCount.fetch_sub(1, std::memory_order_seq_cst) };
35 }
36 };
37 17
38 enum class [[nodiscard]] Status 18 enum class [[nodiscard]] Status
39 { 19 {
@@ -51,7 +31,7 @@ class Linda final
51 // depending on the name length, it is either embedded inside the Linda, or allocated separately 31 // depending on the name length, it is either embedded inside the Linda, or allocated separately
52 std::variant<std::string_view, EmbeddedName> nameVariant{}; 32 std::variant<std::string_view, EmbeddedName> nameVariant{};
53 // counts the keeper operations in progress 33 // counts the keeper operations in progress
54 std::atomic<int> keeperOperationCount{}; 34 mutable std::atomic<int> keeperOperationCount{};
55 lua_Duration wakePeriod{}; 35 lua_Duration wakePeriod{};
56 36
57 public: 37 public:
@@ -110,7 +90,5 @@ class Linda final
110 static int ProtectedCall(lua_State* L_, lua_CFunction f_); 90 static int ProtectedCall(lua_State* L_, lua_CFunction f_);
111 void pushCancelString(lua_State* L_) const; 91 void pushCancelString(lua_State* L_) const;
112 [[nodiscard]] 92 [[nodiscard]]
113 KeeperOperationInProgress startKeeperOperation(lua_State* const L_) { return KeeperOperationInProgress{ *this, L_ }; };
114 [[nodiscard]]
115 Keeper* whichKeeper() const { return U->keepers.getKeeper(keeperIndex); } 93 Keeper* whichKeeper() const { return U->keepers.getKeeper(keeperIndex); }
116}; 94};
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp
index 4eab0c1..483037f 100644
--- a/src/lindafactory.cpp
+++ b/src/lindafactory.cpp
@@ -47,22 +47,22 @@ void LindaFactory::createMetatable(lua_State* L_) const
47 lua_newtable(L_); // L_: mt 47 lua_newtable(L_); // L_: mt
48 48
49 // protect metatable from external access 49 // protect metatable from external access
50 luaG_pushstring(L_, kLindaMetatableName); // L_: mt "<name>" 50 luaW_pushstring(L_, kLindaMetatableName); // L_: mt "<name>"
51 lua_setfield(L_, -2, "__metatable"); // L_: mt 51 lua_setfield(L_, -2, "__metatable"); // L_: mt
52 52
53 // the linda functions 53 // the linda functions
54 luaG_registerlibfuncs(L_, mLindaMT); 54 luaW_registerlibfuncs(L_, mLindaMT);
55 55
56 kNilSentinel.pushKey(L_); // L_: mt kNilSentinel 56 kNilSentinel.pushKey(L_); // L_: mt kNilSentinel
57 lua_setfield(L_, -2, "null"); // L_: mt 57 lua_setfield(L_, -2, "null"); // L_: mt
58 58
59 // if the metatable contains __index, leave it as is 59 // if the metatable contains __index, leave it as is
60 if (luaG_getfield(L_, kIdxTop, kIndex) != LuaType::NIL) { // L_: mt __index 60 if (luaW_getfield(L_, kIdxTop, kIndex) != LuaType::NIL) { // L_: mt __index
61 lua_pop(L_, 1); // L_: mt __index 61 lua_pop(L_, 1); // L_: mt __index
62 } else { 62 } else {
63 // metatable is its own index 63 // metatable is its own index
64 lua_pushvalue(L_, kIdxTop); // L_: mt mt 64 lua_pushvalue(L_, kIdxTop); // L_: mt mt
65 luaG_setfield(L_, StackIndex{ -2 }, kIndex); // L_: mt 65 luaW_setfield(L_, StackIndex{ -2 }, kIndex); // L_: mt
66 } 66 }
67 67
68 STACK_CHECK(L_, 1); 68 STACK_CHECK(L_, 1);
@@ -110,7 +110,7 @@ DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const
110{ 110{
111 STACK_CHECK_START_REL(L_, 0); 111 STACK_CHECK_START_REL(L_, 0);
112 // we always expect name, wake_period, group at the bottom of the stack (either can be nil). any extra stuff we ignore and keep unmodified 112 // we always expect name, wake_period, group at the bottom of the stack (either can be nil). any extra stuff we ignore and keep unmodified
113 std::string_view _linda_name{ luaG_tostring(L_, StackIndex{ 1 }) }; 113 std::string_view _linda_name{ luaW_tostring(L_, StackIndex{ 1 }) };
114 auto const _wake_period{ static_cast<lua_Duration>(lua_tonumber(L_, 2)) }; 114 auto const _wake_period{ static_cast<lua_Duration>(lua_tonumber(L_, 2)) };
115 LindaGroup const _linda_group{ static_cast<int>(lua_tointeger(L_, 3)) }; 115 LindaGroup const _linda_group{ static_cast<int>(lua_tointeger(L_, 3)) };
116 116
@@ -119,12 +119,12 @@ DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const
119 lua_Debug _ar; 119 lua_Debug _ar;
120 if (lua_getstack(L_, 1, &_ar) == 1) { // 1 because we want the name of the function that called lanes.linda (where we currently are) 120 if (lua_getstack(L_, 1, &_ar) == 1) { // 1 because we want the name of the function that called lanes.linda (where we currently are)
121 lua_getinfo(L_, "Sln", &_ar); 121 lua_getinfo(L_, "Sln", &_ar);
122 _linda_name = luaG_pushstring(L_, "%s:%d", _ar.short_src, _ar.currentline); 122 _linda_name = luaW_pushstring(L_, "%s:%d", _ar.short_src, _ar.currentline);
123 } else { 123 } else {
124 _linda_name = luaG_pushstring(L_, "<unresolved>"); 124 _linda_name = luaW_pushstring(L_, "<unresolved>");
125 } 125 }
126 // since the name is not empty, it is at slot 1, and we can replace "auto" with the result, just in case 126 // since the name is not empty, it is at slot 1, and we can replace "auto" with the result, just in case
127 LUA_ASSERT(L_, luaG_tostring(L_, StackIndex{ 1 }) == "auto"); 127 LUA_ASSERT(L_, luaW_tostring(L_, StackIndex{ 1 }) == "auto");
128 lua_replace(L_, 1); 128 lua_replace(L_, 1);
129 } 129 }
130 130
diff --git a/src/nameof.cpp b/src/nameof.cpp
index f236f73..992569b 100644
--- a/src/nameof.cpp
+++ b/src/nameof.cpp
@@ -52,25 +52,25 @@ FqnLength DiscoverObjectNameRecur(lua_State* const L_, FqnLength const shortest_
52 static constexpr auto _pushNameOnFQN = [](lua_State* const L_) { 52 static constexpr auto _pushNameOnFQN = [](lua_State* const L_) {
53 STACK_CHECK_START_REL(L_, 0); 53 STACK_CHECK_START_REL(L_, 0);
54 lua_pushvalue(L_, -2); // L_: o "r" {c} {fqn} ... k v k 54 lua_pushvalue(L_, -2); // L_: o "r" {c} {fqn} ... k v k
55 auto const _keyType{ luaG_type(L_, kIdxTop) }; 55 auto const _keyType{ luaW_type(L_, kIdxTop) };
56 if (_keyType != LuaType::STRING) { 56 if (_keyType != LuaType::STRING) {
57 lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... k v 57 lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... k v
58 luaG_pushstring(L_, "<%s>", luaG_typename(L_, _keyType).data()); // L_: o "r" {c} {fqn} ... k v "<type of k>" 58 luaW_pushstring(L_, "<%s>", luaW_typename(L_, _keyType).data()); // L_: o "r" {c} {fqn} ... k v "<type of k>"
59 } else { 59 } else {
60 // decorate the key string with something that tells us the type of the value 60 // decorate the key string with something that tells us the type of the value
61 switch (luaG_type(L_, StackIndex{ -2 })) { 61 switch (luaW_type(L_, StackIndex{ -2 })) {
62 default: 62 default:
63 LUA_ASSERT(L_, false); // there is something wrong if we end up here 63 LUA_ASSERT(L_, false); // there is something wrong if we end up here
64 luaG_pushstring(L_, "??"); // L_: o "r" {c} {fqn} ... k v "k" "??" 64 luaW_pushstring(L_, "??"); // L_: o "r" {c} {fqn} ... k v "k" "??"
65 break; 65 break;
66 case LuaType::FUNCTION: 66 case LuaType::FUNCTION:
67 luaG_pushstring(L_, "()"); // L_: o "r" {c} {fqn} ... k v "k" "()" 67 luaW_pushstring(L_, "()"); // L_: o "r" {c} {fqn} ... k v "k" "()"
68 break; 68 break;
69 case LuaType::TABLE: 69 case LuaType::TABLE:
70 luaG_pushstring(L_, "[]"); // L_: o "r" {c} {fqn} ... k v "k" "[]" 70 luaW_pushstring(L_, "[]"); // L_: o "r" {c} {fqn} ... k v "k" "[]"
71 break; 71 break;
72 case LuaType::USERDATA: 72 case LuaType::USERDATA:
73 luaG_pushstring(L_, "<>"); // L_: o "r" {c} {fqn} ... k v "k" "<>" 73 luaW_pushstring(L_, "<>"); // L_: o "r" {c} {fqn} ... k v "k" "<>"
74 break; 74 break;
75 } 75 }
76 lua_concat(L_, 2); // L_: o "r" {c} {fqn} ... k v "k??" 76 lua_concat(L_, 2); // L_: o "r" {c} {fqn} ... k v "k??"
@@ -92,7 +92,7 @@ FqnLength DiscoverObjectNameRecur(lua_State* const L_, FqnLength const shortest_
92 static constexpr auto _recurseThenPop = [](lua_State* const L_, FqnLength const shortest_) -> FqnLength { 92 static constexpr auto _recurseThenPop = [](lua_State* const L_, FqnLength const shortest_) -> FqnLength {
93 STACK_CHECK_START_REL(L_, 0); // L_: o "r" {c} {fqn} ... <> 93 STACK_CHECK_START_REL(L_, 0); // L_: o "r" {c} {fqn} ... <>
94 FqnLength r_{ shortest_ }; 94 FqnLength r_{ shortest_ };
95 auto const _type{ luaG_type(L_, kIdxTop) }; 95 auto const _type{ luaW_type(L_, kIdxTop) };
96 if (_type == LuaType::TABLE || _type == LuaType::USERDATA || _type == LuaType::FUNCTION) { 96 if (_type == LuaType::TABLE || _type == LuaType::USERDATA || _type == LuaType::FUNCTION) {
97 r_ = DiscoverObjectNameRecur(L_, shortest_); 97 r_ = DiscoverObjectNameRecur(L_, shortest_);
98 STACK_CHECK(L_, 0); 98 STACK_CHECK(L_, 0);
@@ -113,7 +113,7 @@ FqnLength DiscoverObjectNameRecur(lua_State* const L_, FqnLength const shortest_
113 STACK_CHECK_START_REL(L_, 0); // L_: o "r" {c} {fqn} ... k v 113 STACK_CHECK_START_REL(L_, 0); // L_: o "r" {c} {fqn} ... k v
114 114
115 // filter out uninteresting values 115 // filter out uninteresting values
116 auto const _valType{ luaG_type(L_, kIdxTop) }; 116 auto const _valType{ luaW_type(L_, kIdxTop) };
117 if (_valType == LuaType::NIL || _valType == LuaType::BOOLEAN || _valType == LuaType::LIGHTUSERDATA || _valType == LuaType::NUMBER || _valType == LuaType::STRING) { 117 if (_valType == LuaType::NIL || _valType == LuaType::BOOLEAN || _valType == LuaType::LIGHTUSERDATA || _valType == LuaType::NUMBER || _valType == LuaType::STRING) {
118 lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... k 118 lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... k
119 return _r; 119 return _r;
@@ -177,7 +177,7 @@ FqnLength DiscoverObjectNameRecur(lua_State* const L_, FqnLength const shortest_
177 177
178 UserValueIndex _uvi{ 0 }; 178 UserValueIndex _uvi{ 0 };
179 while (lua_getiuservalue(L_, kIdxTop, ++_uvi) != LUA_TNONE) { // L_: o "r" {c} {fqn} ... U uv 179 while (lua_getiuservalue(L_, kIdxTop, ++_uvi) != LUA_TNONE) { // L_: o "r" {c} {fqn} ... U uv
180 luaG_pushstring(L_, "<uv:%d>", _uvi); // L_: o "r" {c} {fqn} ... U uv name 180 luaW_pushstring(L_, "<uv:%d>", _uvi); // L_: o "r" {c} {fqn} ... U uv name
181 lua_insert(L_, -2); // L_: o "r" {c} {fqn} ... U name uv 181 lua_insert(L_, -2); // L_: o "r" {c} {fqn} ... U name uv
182 r_ = _processKeyValue(L_, r_); // L_: o "r" {c} {fqn} ... U name 182 r_ = _processKeyValue(L_, r_); // L_: o "r" {c} {fqn} ... U name
183 lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... U 183 lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... U
@@ -200,7 +200,7 @@ FqnLength DiscoverObjectNameRecur(lua_State* const L_, FqnLength const shortest_
200 _upname = "<C>"; 200 _upname = "<C>";
201 } 201 }
202 202
203 luaG_pushstring(L_, "upvalue:%s", _upname); // L_: o "r" {c} {fqn} ... F up name 203 luaW_pushstring(L_, "upvalue:%s", _upname); // L_: o "r" {c} {fqn} ... F up name
204 lua_insert(L_, -2); // L_: o "r" {c} {fqn} ... F name up 204 lua_insert(L_, -2); // L_: o "r" {c} {fqn} ... F name up
205 r_ = _processKeyValue(L_, r_); // L_: o "r" {c} {fqn} ... F name 205 r_ = _processKeyValue(L_, r_); // L_: o "r" {c} {fqn} ... F name
206 lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... F 206 lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... F
@@ -213,7 +213,7 @@ FqnLength DiscoverObjectNameRecur(lua_State* const L_, FqnLength const shortest_
213 STACK_GROW(L_, 2); 213 STACK_GROW(L_, 2);
214 STACK_CHECK_START_REL(L_, 0); 214 STACK_CHECK_START_REL(L_, 0);
215 // stack top contains the location to search in (table, function, userdata) 215 // stack top contains the location to search in (table, function, userdata)
216 [[maybe_unused]] auto const _typeWhere{ luaG_type(L_, kIdxTop) }; 216 [[maybe_unused]] auto const _typeWhere{ luaW_type(L_, kIdxTop) };
217 LUA_ASSERT(L_, _typeWhere == LuaType::TABLE || _typeWhere == LuaType::USERDATA || _typeWhere == LuaType::FUNCTION); 217 LUA_ASSERT(L_, _typeWhere == LuaType::TABLE || _typeWhere == LuaType::USERDATA || _typeWhere == LuaType::FUNCTION);
218 lua_pushvalue(L_, kIdxTop); // L_: o "r" {c} {fqn} ... <> <> 218 lua_pushvalue(L_, kIdxTop); // L_: o "r" {c} {fqn} ... <> <>
219 lua_rawget(L_, kCache); // L_: o "r" {c} {fqn} ... <> nil/N 219 lua_rawget(L_, kCache); // L_: o "r" {c} {fqn} ... <> nil/N
@@ -263,12 +263,12 @@ LUAG_FUNC(nameof)
263 263
264 // nil, boolean, light userdata, number and string aren't identifiable 264 // nil, boolean, light userdata, number and string aren't identifiable
265 static constexpr auto _isIdentifiable = [](lua_State* const L_) { 265 static constexpr auto _isIdentifiable = [](lua_State* const L_) {
266 auto const _valType{ luaG_type(L_, kIdxTop) }; 266 auto const _valType{ luaW_type(L_, kIdxTop) };
267 return _valType == LuaType::TABLE || _valType == LuaType::FUNCTION || _valType == LuaType::USERDATA || _valType == LuaType::THREAD; 267 return _valType == LuaType::TABLE || _valType == LuaType::FUNCTION || _valType == LuaType::USERDATA || _valType == LuaType::THREAD;
268 }; 268 };
269 269
270 if (!_isIdentifiable(L_)) { 270 if (!_isIdentifiable(L_)) {
271 luaG_pushstring(L_, luaG_typename(L_, kIdxTop)); // L_: o "type" 271 luaW_pushstring(L_, luaW_typename(L_, kIdxTop)); // L_: o "type"
272 lua_insert(L_, -2); // L_: "type" o 272 lua_insert(L_, -2); // L_: "type" o
273 return 2; 273 return 2;
274 } 274 }
@@ -282,15 +282,15 @@ LUAG_FUNC(nameof)
282 // push a table whose contents are strings that, when concatenated, produce unique name 282 // push a table whose contents are strings that, when concatenated, produce unique name
283 lua_newtable(L_); // L_: o nil {c} {fqn} 283 lua_newtable(L_); // L_: o nil {c} {fqn}
284 // {fqn}[1] = "_G" 284 // {fqn}[1] = "_G"
285 luaG_pushstring(L_, LUA_GNAME); // L_: o nil {c} {fqn} "_G" 285 luaW_pushstring(L_, LUA_GNAME); // L_: o nil {c} {fqn} "_G"
286 lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} 286 lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn}
287 // this is where we start the search 287 // this is where we start the search
288 luaG_pushglobaltable(L_); // L_: o nil {c} {fqn} _G 288 luaW_pushglobaltable(L_); // L_: o nil {c} {fqn} _G
289 auto const _foundInG{ DiscoverObjectNameRecur(L_, FqnLength{ std::numeric_limits<FqnLength::type>::max() }) }; 289 auto const _foundInG{ DiscoverObjectNameRecur(L_, FqnLength{ std::numeric_limits<FqnLength::type>::max() }) };
290 if (lua_isnil(L_, 2)) { // try again with registry, just in case... 290 if (lua_isnil(L_, 2)) { // try again with registry, just in case...
291 LUA_ASSERT(L_, _foundInG == std::numeric_limits<FqnLength::type>::max()); 291 LUA_ASSERT(L_, _foundInG == std::numeric_limits<FqnLength::type>::max());
292 lua_pop(L_, 1); // L_: o nil {c} {fqn} 292 lua_pop(L_, 1); // L_: o nil {c} {fqn}
293 luaG_pushstring(L_, "_R"); // L_: o nil {c} {fqn} "_R" 293 luaW_pushstring(L_, "_R"); // L_: o nil {c} {fqn} "_R"
294 lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} 294 lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn}
295 lua_pushvalue(L_, kIdxRegistry); // L_: o nil {c} {fqn} _R 295 lua_pushvalue(L_, kIdxRegistry); // L_: o nil {c} {fqn} _R
296 [[maybe_unused]] auto const _foundInR{ DiscoverObjectNameRecur(L_, FqnLength{ std::numeric_limits<FqnLength::type>::max() }) }; 296 [[maybe_unused]] auto const _foundInR{ DiscoverObjectNameRecur(L_, FqnLength{ std::numeric_limits<FqnLength::type>::max() }) };
diff --git a/src/state.cpp b/src/state.cpp
index fc7f5ef..2f64194 100644
--- a/src/state.cpp
+++ b/src/state.cpp
@@ -166,7 +166,7 @@ namespace state {
166 }; 166 };
167 167
168 if (_L == nullptr) { 168 if (_L == nullptr) {
169 raise_luaL_error(from_, "luaG_newstate() failed while creating state; out of memory"); 169 raise_luaL_error(from_, "luaW_newstate() failed while creating state; out of memory");
170 } 170 }
171 return _L; 171 return _L;
172 } 172 }
@@ -202,11 +202,11 @@ namespace state {
202 202
203 // neither libs (not even 'base') nor special init func: we are done 203 // neither libs (not even 'base') nor special init func: we are done
204 if (!libs_.has_value() && std::holds_alternative<std::nullptr_t>(U_->onStateCreateFunc)) { 204 if (!libs_.has_value() && std::holds_alternative<std::nullptr_t>(U_->onStateCreateFunc)) {
205 DEBUGSPEW_CODE(DebugSpew(U_) << "luaG_newstate(nullptr)" << std::endl); 205 DEBUGSPEW_CODE(DebugSpew(U_) << "luaW_newstate(nullptr)" << std::endl);
206 return _L; 206 return _L;
207 } 207 }
208 208
209 DEBUGSPEW_CODE(DebugSpew(U_) << "luaG_newstate()" << std::endl); 209 DEBUGSPEW_CODE(DebugSpew(U_) << "luaW_newstate()" << std::endl);
210 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ U_ }); 210 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ U_ });
211 211
212 // copy settings (for example because it may contain a Lua on_state_create function) 212 // copy settings (for example because it may contain a Lua on_state_create function)
@@ -239,7 +239,7 @@ namespace state {
239 lua_pop(_L, 1); 239 lua_pop(_L, 1);
240 } else { 240 } else {
241 lua_pushcfunction(_L, luaopen_base); 241 lua_pushcfunction(_L, luaopen_base);
242 luaG_pushstring(_L, ""); 242 luaW_pushstring(_L, "");
243 lua_call(_L, 1, 0); 243 lua_call(_L, 1, 0);
244 } 244 }
245 } 245 }
@@ -274,7 +274,7 @@ namespace state {
274 274
275 STACK_CHECK(_L, 0); 275 STACK_CHECK(_L, 0);
276 // after all this, register everything we find in our name<->function database 276 // after all this, register everything we find in our name<->function database
277 luaG_pushglobaltable(_L); // L: _G 277 luaW_pushglobaltable(_L); // L: _G
278 tools::PopulateFuncLookupTable(_L, kIdxTop, {}); 278 tools::PopulateFuncLookupTable(_L, kIdxTop, {});
279 lua_pop(_L, 1); // L: 279 lua_pop(_L, 1); // L:
280 STACK_CHECK(_L, 0); 280 STACK_CHECK(_L, 0);
@@ -286,19 +286,19 @@ namespace state {
286 kLookupRegKey.pushValue(_L); // L: {} 286 kLookupRegKey.pushValue(_L); // L: {}
287 lua_pushnil(_L); // L: {} nil 287 lua_pushnil(_L); // L: {} nil
288 while (lua_next(_L, -2)) { // L: {} k v 288 while (lua_next(_L, -2)) { // L: {} k v
289 luaG_pushstring(_L, "["); // L: {} k v "[" 289 luaW_pushstring(_L, "["); // L: {} k v "["
290 290
291 lua_getglobal(_L, "tostring"); // L: {} k v "[" tostring 291 lua_getglobal(_L, "tostring"); // L: {} k v "[" tostring
292 lua_pushvalue(_L, -4); // L: {} k v "[" tostring k 292 lua_pushvalue(_L, -4); // L: {} k v "[" tostring k
293 lua_call(_L, 1, 1); // L: {} k v "[" 'k' 293 lua_call(_L, 1, 1); // L: {} k v "[" 'k'
294 294
295 luaG_pushstring(_L, "] = "); // L: {} k v "[" 'k' "] = " 295 luaW_pushstring(_L, "] = "); // L: {} k v "[" 'k' "] = "
296 296
297 lua_getglobal(_L, "tostring"); // L: {} k v "[" 'k' "] = " tostring 297 lua_getglobal(_L, "tostring"); // L: {} k v "[" 'k' "] = " tostring
298 lua_pushvalue(_L, -5); // L: {} k v "[" 'k' "] = " tostring v 298 lua_pushvalue(_L, -5); // L: {} k v "[" 'k' "] = " tostring v
299 lua_call(_L, 1, 1); // L: {} k v "[" 'k' "] = " 'v' 299 lua_call(_L, 1, 1); // L: {} k v "[" 'k' "] = " 'v'
300 lua_concat(_L, 4); // L: {} k v "[k] = v" 300 lua_concat(_L, 4); // L: {} k v "[k] = v"
301 DEBUGSPEW_CODE(DebugSpew(U_) << luaG_tostring(_L, kIdxTop) << std::endl); 301 DEBUGSPEW_CODE(DebugSpew(U_) << luaW_tostring(_L, kIdxTop) << std::endl);
302 lua_pop(_L, 2); // L: {} k 302 lua_pop(_L, 2); // L: {} k
303 } // lua_next() // L: {} 303 } // lua_next() // L: {}
304 lua_pop(_L, 1); // L: 304 lua_pop(_L, 1); // L:
@@ -317,7 +317,7 @@ namespace state {
317 lua_newtable(L_); // L_: out 317 lua_newtable(L_); // L_: out
318 for (luaL_Reg const& _entry : local::sLibs) { 318 for (luaL_Reg const& _entry : local::sLibs) {
319 lua_pushboolean(L_, 1); // L_: out true 319 lua_pushboolean(L_, 1); // L_: out true
320 luaG_setfield(L_, StackIndex{ -2 }, std::string_view{ _entry.name }); // out[name] = true // L_: out 320 luaW_setfield(L_, StackIndex{ -2 }, std::string_view{ _entry.name }); // out[name] = true // L_: out
321 } 321 }
322 STACK_CHECK(L_, 1); 322 STACK_CHECK(L_, 1);
323 return 1; 323 return 1;
diff --git a/src/threading.cpp b/src/threading.cpp
index 3435075..4b27bed 100644
--- a/src/threading.cpp
+++ b/src/threading.cpp
@@ -49,6 +49,7 @@ THE SOFTWARE.
49 49
50#endif // __linux__ 50#endif // __linux__
51 51
52#include "compat.hpp"
52#include "threading.hpp" 53#include "threading.hpp"
53 54
54#if !defined(PLATFORM_XBOX) && !defined(PLATFORM_WIN32) && !defined(PLATFORM_POCKETPC) 55#if !defined(PLATFORM_XBOX) && !defined(PLATFORM_WIN32) && !defined(PLATFORM_POCKETPC)
@@ -82,25 +83,42 @@ THE SOFTWARE.
82#pragma warning(disable : 4054) 83#pragma warning(disable : 4054)
83#endif 84#endif
84 85
86static constexpr std::string_view StripFuncName(std::string_view const& where_)
87{
88 std::string_view funcname_{ where_ };
89
90 auto _args_pos{ funcname_.find_first_of('(') };
91 funcname_ = funcname_.substr(0, _args_pos);
92 auto _name_pos{ funcname_.find_last_of(' ') };
93 funcname_.remove_prefix(_name_pos + 1);
94 return funcname_;
95}
96
85/* 97/*
86 * FAIL is for unexpected API return values - essentially programming 98 * FAIL is for unexpected API return values - essentially programming
87 * error in _this_ code. 99 * error in _this_ code.
88 */ 100 */
89#if HAVE_WIN32 101#if HAVE_WIN32
90static void FAIL(char const* funcname_, DWORD const rc_) 102
103template <typename F, typename... ARGS>
104void Win32Invoke(lua_State* const L_, std::string_view const& where_, F& f_, ARGS... args_)
91{ 105{
106 auto const _ret{ std::invoke(f_, std::forward<ARGS>(args_)...) };
107 if (!_ret) {
108 auto const _rc{ GetLastError() };
109 std::string_view const _funcname{ StripFuncName(where_) };
110
92#if defined(PLATFORM_XBOX) 111#if defined(PLATFORM_XBOX)
93 fprintf(stderr, "%s() failed! (%d)\n", funcname_, rc_); 112 luaW_pushstring(L_, "%s() failed with code %d", _funcname.data(), _rc);
94#else // PLATFORM_XBOX 113#else // PLATFORM_XBOX
95 char buf[256]; 114 char _buf[256];
96 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, rc_, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, nullptr); 115 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, _rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), _buf, 256, nullptr);
97 fprintf(stderr, "%s() failed! [GetLastError() -> %lu] '%s'", funcname_, rc_, buf); 116 luaW_pushstring(L_, "%s() failed with code %d '%s'", _funcname.data(), _rc, _buf);
98#endif // PLATFORM_XBOX 117#endif // PLATFORM_XBOX
99#ifdef _MSC_VER 118 raise_lua_error(L_);
100 __debugbreak(); // give a chance to the debugger! 119 }
101#endif // _MSC_VER
102 abort();
103} 120}
121
104#endif // HAVE_WIN32 122#endif // HAVE_WIN32
105 123
106/*---=== Threading ===---*/ 124/*---=== Threading ===---*/
@@ -121,33 +139,35 @@ static int const gs_prio_remap[] = {
121 139
122// ################################################################################################# 140// #################################################################################################
123 141
124void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) 142std::pair<int, int> THREAD_NATIVE_PRIOS()
125{ 143{
126 // prio range [-3,+3] was checked by the caller 144 return std::make_pair(THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL);
127 if (!SetThreadPriority(GetCurrentThread(), gs_prio_remap[prio_ + 3])) {
128 FAIL("THREAD_SET_PRIORITY", GetLastError());
129 }
130} 145}
131 146
132// ################################################################################################# 147// #################################################################################################
133 148
134void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, [[maybe_unused]] bool sudo_) 149[[nodiscard]]
150void THREAD_SET_PRIORITY(lua_State* const L_, int const prio_, NativePrioFlag const native_, [[maybe_unused]] SudoFlag const sudo_)
135{ 151{
136 // prio range [-3,+3] was checked by the caller 152 // mapped prio range [-3,+3] was checked by the caller
137 // for some reason when building for mingw, native_handle() is an unsigned long long, but HANDLE is a void* 153 return Win32Invoke(L_, std::source_location::current().function_name(), SetThreadPriority, GetCurrentThread(), native_ ? prio_ : gs_prio_remap[prio_ + 3]);
138 // -> need a strong cast to make g++ happy
139 if (!SetThreadPriority(thread_.native_handle(), gs_prio_remap[prio_ + 3])) {
140 FAIL("THREAD_SET_PRIORITY", GetLastError());
141 }
142} 154}
143 155
144// ################################################################################################# 156// #################################################################################################
145 157
146void THREAD_SET_AFFINITY(unsigned int aff_) 158[[nodiscard]]
159void THREAD_SET_PRIORITY(lua_State* const L_, std::thread& thread_, int const prio_, NativePrioFlag const native_, [[maybe_unused]] SudoFlag const sudo_)
147{ 160{
148 if (!SetThreadAffinityMask(GetCurrentThread(), aff_)) { 161 // mapped prio range [-3,+3] was checked by the caller
149 FAIL("THREAD_SET_AFFINITY", GetLastError()); 162 return Win32Invoke(L_, std::source_location::current().function_name(), SetThreadPriority, thread_.native_handle(), native_ ? prio_ : gs_prio_remap[prio_ + 3]);
150 } 163}
164
165// #################################################################################################
166
167[[nodiscard]]
168void THREAD_SET_AFFINITY(lua_State* const L_, unsigned int aff_)
169{
170 return Win32Invoke(L_, std::source_location::current().function_name(), SetThreadAffinityMask, GetCurrentThread(), aff_);
151} 171}
152 172
153// ################################################################################################# 173// #################################################################################################
@@ -215,24 +235,24 @@ static int pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy)
215#endif // pthread_attr_setschedpolicy() 235#endif // pthread_attr_setschedpolicy()
216#endif // defined(__MINGW32__) || defined(__MINGW64__) 236#endif // defined(__MINGW32__) || defined(__MINGW64__)
217 237
218static void _PT_FAIL(int rc, const char* name, const char* file, int line) 238template <typename F, typename... ARGS>
239void PthreadInvoke(lua_State* const L_, std::string_view const& where_, F& f_, ARGS... args_)
219{ 240{
220 const char* why = (rc == EINVAL) ? "EINVAL" 241 auto const _rc{ std::invoke(f_, std::forward<ARGS>(args_)...) };
221 : (rc == EBUSY) ? "EBUSY" 242 if (_rc) {
222 : (rc == EPERM) ? "EPERM" 243 std::string_view const _funcname{ StripFuncName(where_) };
223 : (rc == ENOMEM) ? "ENOMEM" 244
224 : (rc == ESRCH) ? "ESRCH" 245 char const* _why = (_rc == EINVAL) ? "EINVAL"
225 : (rc == ENOTSUP) ? "ENOTSUP" 246 : (_rc == EBUSY) ? "EBUSY"
226 : "<UNKNOWN>"; 247 : (_rc == EPERM) ? "EPERM"
227 fprintf(stderr, "%s %d: %s failed, %d %s\n", file, line, name, rc, why); 248 : (_rc == ENOMEM) ? "ENOMEM"
228 abort(); 249 : (_rc == ESRCH) ? "ESRCH"
229} 250 : (_rc == ENOTSUP) ? "ENOTSUP"
230#define PT_CALL(call) \ 251 : "<UNKNOWN>";
231 { \ 252
232 int rc = call; \ 253 raise_luaL_error(L_, "%s() failed with code %s", _funcname.data(), _why);
233 if (rc != 0) \
234 _PT_FAIL(rc, #call, __FILE__, __LINE__); \
235 } 254 }
255}
236 256
237// array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range 257// array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range
238static int const gs_prio_remap[] = { 258static int const gs_prio_remap[] = {
@@ -336,8 +356,8 @@ static int const gs_prio_remap[] = {
336// 356//
337#define _PRIO_MODE SCHED_OTHER 357#define _PRIO_MODE SCHED_OTHER
338#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS 358#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
339#define _PRIO_HI 31 359#define _PRIO_HI 15
340#define _PRIO_0 15 360#define _PRIO_0 7
341#define _PRIO_LO 1 361#define _PRIO_LO 1
342#else 362#else
343#error "Unknown OS: not implemented!" 363#error "Unknown OS: not implemented!"
@@ -357,7 +377,18 @@ static int const gs_prio_remap[] = {
357#endif // _PRIO_0 377#endif // _PRIO_0
358}; 378};
359 379
360void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) 380// #################################################################################################
381
382std::pair<int, int> THREAD_NATIVE_PRIOS()
383{
384 int const _prio_min{ sched_get_priority_min(_PRIO_MODE) };
385 int const _prio_max{ sched_get_priority_max(_PRIO_MODE) };
386 return std::make_pair(_prio_min, _prio_max);
387}
388
389// #################################################################################################
390
391void THREAD_SET_PRIORITY(lua_State* const L_, int const prio_, NativePrioFlag const native_, [[maybe_unused]] SudoFlag const sudo_)
361{ 392{
362#ifdef PLATFORM_LINUX 393#ifdef PLATFORM_LINUX
363 if (!sudo_) // only root-privileged process can change priorities 394 if (!sudo_) // only root-privileged process can change priorities
@@ -366,13 +397,13 @@ void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_)
366 397
367 struct sched_param sp; 398 struct sched_param sp;
368 // prio range [-3,+3] was checked by the caller 399 // prio range [-3,+3] was checked by the caller
369 sp.sched_priority = gs_prio_remap[prio_ + 3]; 400 sp.sched_priority = native_ ? prio_ : gs_prio_remap[prio_ + 3];
370 PT_CALL(pthread_setschedparam(pthread_self(), _PRIO_MODE, &sp)); 401 PthreadInvoke(L_, std::source_location::current().function_name(), pthread_setschedparam, pthread_self(), _PRIO_MODE, &sp);
371} 402}
372 403
373// ################################################################################################# 404// #################################################################################################
374 405
375void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, [[maybe_unused]] bool sudo_) 406void THREAD_SET_PRIORITY(lua_State* const L_, std::thread& thread_, int const prio_, NativePrioFlag const native_, [[maybe_unused]] SudoFlag const sudo_)
376{ 407{
377#ifdef PLATFORM_LINUX 408#ifdef PLATFORM_LINUX
378 if (!sudo_) // only root-privileged process can change priorities 409 if (!sudo_) // only root-privileged process can change priorities
@@ -381,28 +412,26 @@ void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, [[maybe_unused]] bool
381 412
382 struct sched_param sp; 413 struct sched_param sp;
383 // prio range [-3,+3] was checked by the caller 414 // prio range [-3,+3] was checked by the caller
384 sp.sched_priority = gs_prio_remap[prio_ + 3]; 415 sp.sched_priority = native_ ? prio_ : gs_prio_remap[prio_ + 3];
385 PT_CALL(pthread_setschedparam(thread_.native_handle(), _PRIO_MODE, &sp)); 416 PthreadInvoke(L_, std::source_location::current().function_name(), pthread_setschedparam, thread_.native_handle(), _PRIO_MODE, &sp);
386} 417}
387 418
388// ################################################################################################# 419// #################################################################################################
389 420
390#ifdef __PROSPERO__ 421#ifdef __PROSPERO__
391 422
392void THREAD_SET_AFFINITY(unsigned int aff_) 423void THREAD_SET_AFFINITY(lua_State* const L_, unsigned int aff_)
393{ 424{
394 scePthreadSetaffinity(scePthreadSelf(), aff_); 425 PthreadInvoke(L_, std::source_location::current().function_name(), scePthreadSetaffinity, scePthreadSelf(), aff_);
395} 426}
396 427
397#else // __PROSPERO__ 428#else // __PROSPERO__
398 429
399void THREAD_SET_AFFINITY(unsigned int aff_) 430void THREAD_SET_AFFINITY(lua_State* const L_, unsigned int aff_)
400{ 431{
401#if HAVE_WIN32 // "hybrid": Win32 API is available, and pthread too 432#if HAVE_WIN32 // "hybrid": Win32 API is available, and pthread too
402 // since pthread_setaffinity_np can be missing (for example mingw), use win32 api instead 433 // since pthread_setaffinity_np can be missing (for example mingw), use win32 api instead
403 if (!SetThreadAffinityMask(GetCurrentThread(), aff_)) { 434 Win32Invoke(L_, std::source_location::current().function_name(), SetThreadAffinityMask, GetCurrentThread(), aff_);
404 FAIL("THREAD_SET_AFFINITY", GetLastError());
405 }
406#else // pure pthread 435#else // pure pthread
407 int bit = 0; 436 int bit = 0;
408#ifdef __NetBSD__ 437#ifdef __NetBSD__
@@ -422,12 +451,13 @@ void THREAD_SET_AFFINITY(unsigned int aff_)
422 aff_ >>= 1; 451 aff_ >>= 1;
423 } 452 }
424#ifdef __ANDROID__ 453#ifdef __ANDROID__
425 PT_CALL(sched_setaffinity(pthread_self(), sizeof(cpu_set_t), &cpuset)); 454
455 PthreadInvoke(L_, std::source_location::current().function_name(), sched_setaffinity, pthread_self(), sizeof(cpu_set_t), &cpuset);
426#elif defined(__NetBSD__) 456#elif defined(__NetBSD__)
427 PT_CALL(pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset), cpuset)); 457 PthreadInvoke(L_, std::source_location::current().function_name(), pthread_setaffinity_np, pthread_self(), cpuset_size(cpuset), cpuset);
428 cpuset_destroy(cpuset); 458 cpuset_destroy(cpuset);
429#else 459#else
430 PT_CALL(pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)); 460 PthreadInvoke(L_, std::source_location::current().function_name(), pthread_setaffinity_np, pthread_self(), sizeof(cpu_set_t), &cpuset);
431#endif 461#endif
432#endif // PLATFORM_MINGW 462#endif // PLATFORM_MINGW
433} 463}
@@ -447,7 +477,7 @@ void THREAD_SETNAME(std::string_view const& name_)
447 477
448void THREAD_SETNAME(std::string_view const& name_) 478void THREAD_SETNAME(std::string_view const& name_)
449{ 479{
450 // exact API to set the thread name is platform-dependant 480 // exact API to set the thread name is platform-dependent
451 // if you need to fix the build, or if you know how to fill a hole, tell me (bnt.germain@gmail.com) so that I can submit the fix in github. 481 // if you need to fix the build, or if you know how to fill a hole, tell me (bnt.germain@gmail.com) so that I can submit the fix in github.
452#if defined PLATFORM_MINGW 482#if defined PLATFORM_MINGW
453 pthread_setname_np(pthread_self(), name_.data()); 483 pthread_setname_np(pthread_self(), name_.data());
diff --git a/src/threading.hpp b/src/threading.hpp
index 912c28f..07c1ab3 100644
--- a/src/threading.hpp
+++ b/src/threading.hpp
@@ -1,6 +1,7 @@
1#pragma once 1#pragma once
2 2
3#include "platform.h" 3#include "platform.h"
4#include "unique.hpp"
4 5
5#define THREADAPI_WINDOWS 1 6#define THREADAPI_WINDOWS 1
6#define THREADAPI_PTHREAD 2 7#define THREADAPI_PTHREAD 2
@@ -73,8 +74,15 @@ static constexpr int kThreadPrioMax{ +3 };
73// ################################################################################################# 74// #################################################################################################
74// ################################################################################################# 75// #################################################################################################
75 76
77DECLARE_UNIQUE_TYPE(SudoFlag, bool);
78DECLARE_UNIQUE_TYPE(NativePrioFlag, bool);
79
80std::pair<int, int> THREAD_NATIVE_PRIOS();
81
76void THREAD_SETNAME(std::string_view const& name_); 82void THREAD_SETNAME(std::string_view const& name_);
77void THREAD_SET_PRIORITY(int prio_, bool sudo_);
78void THREAD_SET_AFFINITY(unsigned int aff_);
79 83
80void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, bool sudo_); 84void THREAD_SET_PRIORITY(lua_State* L_, int prio_, NativePrioFlag native_, SudoFlag sudo_);
85
86void THREAD_SET_AFFINITY(lua_State* L_, unsigned int aff_);
87
88void THREAD_SET_PRIORITY(lua_State* L_, std::thread& thread_, int prio_, NativePrioFlag native_, SudoFlag sudo_);
diff --git a/src/tools.cpp b/src/tools.cpp
index cbfefb0..cd1c593 100644
--- a/src/tools.cpp
+++ b/src/tools.cpp
@@ -45,13 +45,29 @@ static constexpr RegistryUniqueKey kLookupCacheRegKey{ 0x9BF75F84E54B691Bull };
45 45
46// ################################################################################################# 46// #################################################################################################
47 47
48static constexpr int kWriterReturnCode{ 666 }; 48namespace {
49[[nodiscard]] 49 namespace local {
50static int dummy_writer([[maybe_unused]] lua_State* const L_, [[maybe_unused]] void const* p_, [[maybe_unused]] size_t sz_, [[maybe_unused]] void* ud_) 50 static int buf_writer([[maybe_unused]] lua_State* const L_, void const* b_, size_t size_, void* ud_)
51{ 51 {
52 // always fail with this code 52 auto* const _B{ static_cast<luaL_Buffer*>(ud_) };
53 return kWriterReturnCode; 53 if (b_ && size_) {
54} 54 luaL_addlstring(_B, static_cast<char const*>(b_), size_);
55 }
56 return 0;
57 }
58
59 static constexpr int kWriterReturnCode{ 666 };
60 [[nodiscard]]
61 static int dummy_writer([[maybe_unused]] lua_State* const L_, [[maybe_unused]] void const* p_, [[maybe_unused]] size_t sz_, [[maybe_unused]] void* ud_)
62 {
63 // always fail with this code
64 return kWriterReturnCode;
65 }
66
67 } // namespace local
68} // namespace
69
70// #################################################################################################
55 71
56/* 72/*
57 * differentiation between C, bytecode and JIT-fast functions 73 * differentiation between C, bytecode and JIT-fast functions
@@ -59,30 +75,28 @@ static int dummy_writer([[maybe_unused]] lua_State* const L_, [[maybe_unused]] v
59 * +-------------------+------------+----------+ 75 * +-------------------+------------+----------+
60 * | bytecode | C function | JIT-fast | 76 * | bytecode | C function | JIT-fast |
61 * +-----------------+-------------------+------------+----------+ 77 * +-----------------+-------------------+------------+----------+
62 * | lua_topointer | | | | 78 * | lua_tocfunction | nullptr | <some p> | nullptr |
63 * +-----------------+-------------------+------------+----------+ 79 * +-----------------+-------------------+------------+----------+
64 * | lua_tocfunction | nullptr | | nullptr | 80 * | luaW_dump | kWriterReturnCode | 1 | 1 |
65 * +-----------------+-------------------+------------+----------+
66 * | luaG_dump | kWriterReturnCode | 1 | 1 |
67 * +-----------------+-------------------+------------+----------+ 81 * +-----------------+-------------------+------------+----------+
68 */ 82 */
69 83
70[[nodiscard]] 84[[nodiscard]]
71FuncSubType luaG_getfuncsubtype(lua_State* const L_, StackIndex const i_) 85FuncSubType luaW_getfuncsubtype(lua_State* const L_, StackIndex const i_)
72{ 86{
73 if (lua_tocfunction(L_, i_)) { // nullptr for LuaJIT-fast && bytecode functions 87 if (lua_tocfunction(L_, i_)) { // nullptr for LuaJIT-fast && bytecode functions
74 return FuncSubType::Native; 88 return FuncSubType::Native;
75 } 89 }
76 90
77 // luaG_dump expects the function at the top of the stack 91 // luaW_dump expects the function at the top of the stack
78 int const _popCount{ (luaG_absindex(L_, i_) == lua_gettop(L_)) ? 0 : (lua_pushvalue(L_, i_), 1) }; 92 int const _popCount{ (luaW_absindex(L_, i_) == lua_gettop(L_)) ? 0 : (lua_pushvalue(L_, i_), 1) };
79 // here we either have a Lua bytecode or a LuaJIT-compiled function 93 // here we either have a Lua bytecode or a LuaJIT-compiled function
80 int const _dumpres{ luaG_dump(L_, dummy_writer, nullptr, 0) }; 94 int const _dumpres{ luaW_dump(L_, local::dummy_writer, nullptr, 0) };
81 if (_popCount > 0) { 95 if (_popCount > 0) {
82 lua_pop(L_, _popCount); 96 lua_pop(L_, _popCount);
83 } 97 }
84 if (_dumpres == kWriterReturnCode) { 98 if (_dumpres == local::kWriterReturnCode) {
85 // anytime we get kWriterReturnCode, this means that luaG_dump() attempted a dump 99 // anytime we get kWriterReturnCode, this means that luaW_dump() attempted a dump
86 return FuncSubType::Bytecode; 100 return FuncSubType::Bytecode;
87 } 101 }
88 // we didn't try to dump, therefore this is a LuaJIT-fast function 102 // we didn't try to dump, therefore this is a LuaJIT-fast function
@@ -92,7 +106,6 @@ FuncSubType luaG_getfuncsubtype(lua_State* const L_, StackIndex const i_)
92// ################################################################################################# 106// #################################################################################################
93 107
94namespace tools { 108namespace tools {
95
96 // inspired from tconcat() in ltablib.c 109 // inspired from tconcat() in ltablib.c
97 [[nodiscard]] 110 [[nodiscard]]
98 std::string_view PushFQN(lua_State* const L_, StackIndex const t_) 111 std::string_view PushFQN(lua_State* const L_, StackIndex const t_)
@@ -115,9 +128,23 @@ namespace tools {
115 // &b is popped at that point (-> replaced by the result) 128 // &b is popped at that point (-> replaced by the result)
116 luaL_pushresult(&_b); // L_: ... {} ... "<result>" 129 luaL_pushresult(&_b); // L_: ... {} ... "<result>"
117 STACK_CHECK(L_, 1); 130 STACK_CHECK(L_, 1);
118 return luaG_tostring(L_, kIdxTop); 131 return luaW_tostring(L_, kIdxTop);
119 } 132 }
120 133
134 // #############################################################################################
135
136 void PushFunctionBytecode(SourceState const L1_, DestState const L2_, int const strip_)
137 {
138 luaL_Buffer B{};
139 STACK_CHECK_START_REL(L1_, 0);
140 STACK_CHECK_START_REL(L2_, 0);
141 STACK_GROW(L2_, 2);
142 luaL_buffinit(L2_, &B); // L1_: ... f L2_: ... <B stuff>
143 luaW_dump(L1_, local::buf_writer, &B, strip_);
144 luaL_pushresult(&B); // L2_: ... "<bytecode>"
145 STACK_CHECK(L2_, 1);
146 STACK_CHECK(L1_, 0);
147 }
121} // namespace tools 148} // namespace tools
122 149
123// ################################################################################################# 150// #################################################################################################
@@ -145,10 +172,10 @@ static void update_lookup_entry(lua_State* const L_, StackIndex const ctxBase_,
145 // first, raise an error if the function is already known 172 // first, raise an error if the function is already known
146 lua_pushvalue(L_, -1); // L_: ... {bfc} k o o 173 lua_pushvalue(L_, -1); // L_: ... {bfc} k o o
147 lua_rawget(L_, _dest); // L_: ... {bfc} k o name? 174 lua_rawget(L_, _dest); // L_: ... {bfc} k o name?
148 std::string_view const _prevName{ luaG_tostring(L_, kIdxTop) }; // nullptr if we got nil (first encounter of this object) 175 std::string_view const _prevName{ luaW_tostring(L_, kIdxTop) }; // nullptr if we got nil (first encounter of this object)
149 // push name in fqn stack (note that concatenation will crash if name is a not string or a number) 176 // push name in fqn stack (note that concatenation will crash if name is a not string or a number)
150 lua_pushvalue(L_, -3); // L_: ... {bfc} k o name? k 177 lua_pushvalue(L_, -3); // L_: ... {bfc} k o name? k
151 LUA_ASSERT(L_, luaG_type(L_, kIdxTop) == LuaType::NUMBER || luaG_type(L_, kIdxTop) == LuaType::STRING); 178 LUA_ASSERT(L_, luaW_type(L_, kIdxTop) == LuaType::NUMBER || luaW_type(L_, kIdxTop) == LuaType::STRING);
152 TableIndex const _deeper{ depth_ + 1 }; 179 TableIndex const _deeper{ depth_ + 1 };
153 lua_rawseti(L_, _fqn, _deeper); // L_: ... {bfc} k o name? 180 lua_rawseti(L_, _fqn, _deeper); // L_: ... {bfc} k o name?
154 // generate name 181 // generate name
@@ -161,7 +188,7 @@ static void update_lookup_entry(lua_State* const L_, StackIndex const ctxBase_,
161 // Therefore, when we encounter an object for which a name was previously registered, we need to select a single name 188 // Therefore, when we encounter an object for which a name was previously registered, we need to select a single name
162 // based on some sorting order so that we end up with the same name in all databases whatever order the table walk yielded 189 // based on some sorting order so that we end up with the same name in all databases whatever order the table walk yielded
163 if (!_prevName.empty() && ((_prevName.size() < _newName.size()) || (_prevName <= _newName))) { 190 if (!_prevName.empty() && ((_prevName.size() < _newName.size()) || (_prevName <= _newName))) {
164 DEBUGSPEW_CODE(DebugSpew(_U) << luaG_typename(L_, StackIndex{ -3 }) << " '" << _newName << "' remains named '" << _prevName << "'" << std::endl); 191 DEBUGSPEW_CODE(DebugSpew(_U) << luaW_typename(L_, StackIndex{ -3 }) << " '" << _newName << "' remains named '" << _prevName << "'" << std::endl);
165 // the previous name is 'smaller' than the one we just generated: keep it! 192 // the previous name is 'smaller' than the one we just generated: keep it!
166 lua_pop(L_, 3); // L_: ... {bfc} k 193 lua_pop(L_, 3); // L_: ... {bfc} k
167 } else { 194 } else {
@@ -175,7 +202,7 @@ static void update_lookup_entry(lua_State* const L_, StackIndex const ctxBase_,
175 } else { 202 } else {
176 lua_remove(L_, -2); // L_: ... {bfc} k o "f.q.n" 203 lua_remove(L_, -2); // L_: ... {bfc} k o "f.q.n"
177 } 204 }
178 DEBUGSPEW_CODE(DebugSpew(_U) << luaG_typename(L_, StackIndex{ -2 }) << " '" << _newName << "'" << std::endl); 205 DEBUGSPEW_CODE(DebugSpew(_U) << luaW_typename(L_, StackIndex{ -2 }) << " '" << _newName << "'" << std::endl);
179 // prepare the stack for database feed 206 // prepare the stack for database feed
180 lua_pushvalue(L_, -1); // L_: ... {bfc} k o "f.q.n" "f.q.n" 207 lua_pushvalue(L_, -1); // L_: ... {bfc} k o "f.q.n" "f.q.n"
181 lua_pushvalue(L_, -3); // L_: ... {bfc} k o "f.q.n" "f.q.n" o 208 lua_pushvalue(L_, -3); // L_: ... {bfc} k o "f.q.n" "f.q.n" o
@@ -210,7 +237,7 @@ static void populate_lookup_table_recur(lua_State* const L_, StackIndex const db
210 STACK_CHECK_START_REL(L_, 0); // L_: ... {i_} 237 STACK_CHECK_START_REL(L_, 0); // L_: ... {i_}
211 238
212 // if object is a userdata, replace it by its metatable 239 // if object is a userdata, replace it by its metatable
213 if (luaG_type(L_, i_) == LuaType::USERDATA) { 240 if (luaW_type(L_, i_) == LuaType::USERDATA) {
214 lua_getmetatable(L_, i_); // L_: ... {i_} mt 241 lua_getmetatable(L_, i_); // L_: ... {i_} mt
215 lua_replace(L_, i_); // L_: ... {i_} 242 lua_replace(L_, i_); // L_: ... {i_}
216 } 243 }
@@ -239,7 +266,7 @@ static void populate_lookup_table_recur(lua_State* const L_, StackIndex const db
239 lua_pushnil(L_); // L_: ... {i_} {bfc} nil 266 lua_pushnil(L_); // L_: ... {i_} {bfc} nil
240 while (lua_next(L_, i_) != 0) { // L_: ... {i_} {bfc} k v 267 while (lua_next(L_, i_) != 0) { // L_: ... {i_} {bfc} k v
241 // just for debug, not actually needed 268 // just for debug, not actually needed
242 // std::string_view const _key{ (luaG_type(L_, -2) == LuaType::STRING) ? luaG_tostring(L_, -2) : "not a string" }; 269 // std::string_view const _key{ (luaW_type(L_, -2) == LuaType::STRING) ? luaW_tostring(L_, -2) : "not a string" };
243 // subtable: process it recursively 270 // subtable: process it recursively
244 if (lua_istable(L_, kIdxTop)) { // L_: ... {i_} {bfc} k {} 271 if (lua_istable(L_, kIdxTop)) { // L_: ... {i_} {bfc} k {}
245 // increment visit count to make sure we will actually scan it at this recursive level 272 // increment visit count to make sure we will actually scan it at this recursive level
@@ -256,11 +283,11 @@ static void populate_lookup_table_recur(lua_State* const L_, StackIndex const db
256 lua_rawset(L_, _breadthFirstCache); // L_: ... {i_} {bfc} k {} 283 lua_rawset(L_, _breadthFirstCache); // L_: ... {i_} {bfc} k {}
257 // generate a name, and if we already had one name, keep whichever is the shorter 284 // generate a name, and if we already had one name, keep whichever is the shorter
258 update_lookup_entry(L_, dbIdx_, depth_); // L_: ... {i_} {bfc} k 285 update_lookup_entry(L_, dbIdx_, depth_); // L_: ... {i_} {bfc} k
259 } else if (lua_isfunction(L_, kIdxTop) && (luaG_getfuncsubtype(L_, kIdxTop) != FuncSubType::Bytecode)) { 286 } else if (lua_isfunction(L_, kIdxTop) && (luaW_getfuncsubtype(L_, kIdxTop) != FuncSubType::Bytecode)) {
260 // generate a name, and if we already had one name, keep whichever is the shorter 287 // generate a name, and if we already had one name, keep whichever is the shorter
261 // this pops the function from the stack 288 // this pops the function from the stack
262 update_lookup_entry(L_, dbIdx_, depth_); // L_: ... {i_} {bfc} k 289 update_lookup_entry(L_, dbIdx_, depth_); // L_: ... {i_} {bfc} k
263 } else if (luaG_type(L_, kIdxTop) == LuaType::USERDATA) { 290 } else if (luaW_type(L_, kIdxTop) == LuaType::USERDATA) {
264 // generate a name, and if we already had one name, keep whichever is the shorter 291 // generate a name, and if we already had one name, keep whichever is the shorter
265 // this pops the userdata from the stack 292 // this pops the userdata from the stack
266 update_lookup_entry(L_, dbIdx_, depth_); // L_: ... {i_} {bfc} k 293 update_lookup_entry(L_, dbIdx_, depth_); // L_: ... {i_} {bfc} k
@@ -273,13 +300,13 @@ static void populate_lookup_table_recur(lua_State* const L_, StackIndex const db
273 TableIndex const _deeper{ depth_ + 1 }; 300 TableIndex const _deeper{ depth_ + 1 };
274 lua_pushnil(L_); // L_: ... {i_} {bfc} nil 301 lua_pushnil(L_); // L_: ... {i_} {bfc} nil
275 while (lua_next(L_, _breadthFirstCache) != 0) { // L_: ... {i_} {bfc} k {} 302 while (lua_next(L_, _breadthFirstCache) != 0) { // L_: ... {i_} {bfc} k {}
276 DEBUGSPEW_CODE(std::string_view const _key{ (luaG_type(L_, StackIndex{ -2 }) == LuaType::STRING) ? luaG_tostring(L_, StackIndex{ -2 }) : std::string_view{ "<not a string>" } }); 303 DEBUGSPEW_CODE(std::string_view const _key{ (luaW_type(L_, StackIndex{ -2 }) == LuaType::STRING) ? luaW_tostring(L_, StackIndex{ -2 }) : std::string_view{ "<not a string>" } });
277 DEBUGSPEW_CODE(DebugSpew(_U) << "table '"<< _key <<"'" << std::endl); 304 DEBUGSPEW_CODE(DebugSpew(_U) << "table '"<< _key <<"'" << std::endl);
278 DEBUGSPEW_CODE(DebugSpewIndentScope _scope2{ _U }); 305 DEBUGSPEW_CODE(DebugSpewIndentScope _scope2{ _U });
279 // un-visit this table in case we do need to process it 306 // un-visit this table in case we do need to process it
280 lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {} 307 lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {}
281 lua_rawget(L_, _cache); // L_: ... {i_} {bfc} k {} n 308 lua_rawget(L_, _cache); // L_: ... {i_} {bfc} k {} n
282 LUA_ASSERT(L_, luaG_type(L_, kIdxTop) == LuaType::NUMBER); 309 LUA_ASSERT(L_, luaW_type(L_, kIdxTop) == LuaType::NUMBER);
283 _visit_count = lua_tointeger(L_, -1) - 1; 310 _visit_count = lua_tointeger(L_, -1) - 1;
284 lua_pop(L_, 1); // L_: ... {i_} {bfc} k {} 311 lua_pop(L_, 1); // L_: ... {i_} {bfc} k {}
285 lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {} 312 lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {}
@@ -312,7 +339,7 @@ namespace tools {
312 // create a "fully.qualified.name" <-> function equivalence database 339 // create a "fully.qualified.name" <-> function equivalence database
313 void PopulateFuncLookupTable(lua_State* const L_, StackIndex const i_, std::string_view const& name_) 340 void PopulateFuncLookupTable(lua_State* const L_, StackIndex const i_, std::string_view const& name_)
314 { 341 {
315 StackIndex const _in_base{ luaG_absindex(L_, i_) }; 342 StackIndex const _in_base{ luaW_absindex(L_, i_) };
316 DEBUGSPEW_CODE(Universe* _U = Universe::Get(L_)); 343 DEBUGSPEW_CODE(Universe* _U = Universe::Get(L_));
317 std::string_view _name{ name_.empty() ? std::string_view{} : name_ }; 344 std::string_view _name{ name_.empty() ? std::string_view{} : name_ };
318 DEBUGSPEW_CODE(DebugSpew(_U) << L_ << ": PopulateFuncLookupTable('" << _name << "')" << std::endl); 345 DEBUGSPEW_CODE(DebugSpew(_U) << L_ << ": PopulateFuncLookupTable('" << _name << "')" << std::endl);
@@ -323,24 +350,24 @@ namespace tools {
323 StackIndex const _dbIdx{ lua_gettop(L_) }; 350 StackIndex const _dbIdx{ lua_gettop(L_) };
324 STACK_CHECK(L_, 1); 351 STACK_CHECK(L_, 1);
325 LUA_ASSERT(L_, lua_istable(L_, -1)); 352 LUA_ASSERT(L_, lua_istable(L_, -1));
326 LuaType const _moduleType{ luaG_type(L_, _in_base) }; 353 LuaType const _moduleType{ luaW_type(L_, _in_base) };
327 if ((_moduleType == LuaType::FUNCTION) || (_moduleType == LuaType::USERDATA)) { // for example when a module is a simple function 354 if ((_moduleType == LuaType::FUNCTION) || (_moduleType == LuaType::USERDATA)) { // for example when a module is a simple function
328 if (_name.empty()) { 355 if (_name.empty()) {
329 _name = "nullptr"; 356 _name = "nullptr";
330 } 357 }
331 lua_pushvalue(L_, _in_base); // L_: {} f 358 lua_pushvalue(L_, _in_base); // L_: {} f
332 luaG_pushstring(L_, _name); // L_: {} f name_ 359 luaW_pushstring(L_, _name); // L_: {} f name_
333 lua_rawset(L_, -3); // L_: {} 360 lua_rawset(L_, -3); // L_: {}
334 luaG_pushstring(L_, _name); // L_: {} name_ 361 luaW_pushstring(L_, _name); // L_: {} name_
335 lua_pushvalue(L_, _in_base); // L_: {} name_ f 362 lua_pushvalue(L_, _in_base); // L_: {} name_ f
336 lua_rawset(L_, -3); // L_: {} 363 lua_rawset(L_, -3); // L_: {}
337 lua_pop(L_, 1); // L_: 364 lua_pop(L_, 1); // L_:
338 } else if (luaG_type(L_, _in_base) == LuaType::TABLE) { 365 } else if (luaW_type(L_, _in_base) == LuaType::TABLE) {
339 lua_newtable(L_); // L_: {} {fqn} 366 lua_newtable(L_); // L_: {} {fqn}
340 TableIndex _startDepth{ 0 }; 367 TableIndex _startDepth{ 0 };
341 if (!_name.empty()) { 368 if (!_name.empty()) {
342 STACK_CHECK(L_, 2); 369 STACK_CHECK(L_, 2);
343 luaG_pushstring(L_, _name); // L_: {} {fqn} "name" 370 luaW_pushstring(L_, _name); // L_: {} {fqn} "name"
344 // generate a name, and if we already had one name, keep whichever is the shorter 371 // generate a name, and if we already had one name, keep whichever is the shorter
345 lua_pushvalue(L_, _in_base); // L_: {} {fqn} "name" t 372 lua_pushvalue(L_, _in_base); // L_: {} {fqn} "name" t
346 update_lookup_entry(L_, _dbIdx, _startDepth); // L_: {} {fqn} "name" 373 update_lookup_entry(L_, _dbIdx, _startDepth); // L_: {} {fqn} "name"
@@ -355,7 +382,7 @@ namespace tools {
355 lua_pop(L_, 3); // L_: 382 lua_pop(L_, 3); // L_:
356 } else { 383 } else {
357 lua_pop(L_, 1); // L_: 384 lua_pop(L_, 1); // L_:
358 raise_luaL_error(L_, "unsupported module type %s", luaG_typename(L_, _in_base).data()); 385 raise_luaL_error(L_, "unsupported module type %s", luaW_typename(L_, _in_base).data());
359 } 386 }
360 STACK_CHECK(L_, 0); 387 STACK_CHECK(L_, 0);
361 } 388 }
@@ -373,7 +400,7 @@ namespace tools {
373 +[](lua_State* L_) 400 +[](lua_State* L_)
374 { 401 {
375 int const _args{ lua_gettop(L_) }; // L_: args... 402 int const _args{ lua_gettop(L_) }; // L_: args...
376 //[[maybe_unused]] std::string_view const _modname{ luaG_checkstring(L_, 1) }; 403 //[[maybe_unused]] std::string_view const _modname{ luaW_checkstring(L_, 1) };
377 404
378 STACK_GROW(L_, 1); 405 STACK_GROW(L_, 1);
379 406
diff --git a/src/tools.hpp b/src/tools.hpp
index 420b5f8..c555344 100644
--- a/src/tools.hpp
+++ b/src/tools.hpp
@@ -21,7 +21,7 @@ enum class [[nodiscard]] FuncSubType
21}; 21};
22 22
23[[nodiscard]] 23[[nodiscard]]
24FuncSubType luaG_getfuncsubtype(lua_State* L_, StackIndex i_); 24FuncSubType luaW_getfuncsubtype(lua_State* L_, StackIndex i_);
25 25
26// ################################################################################################# 26// #################################################################################################
27 27
@@ -37,5 +37,6 @@ namespace tools {
37 void PopulateFuncLookupTable(lua_State* L_, StackIndex i_, std::string_view const& name_); 37 void PopulateFuncLookupTable(lua_State* L_, StackIndex i_, std::string_view const& name_);
38 [[nodiscard]] 38 [[nodiscard]]
39 std::string_view PushFQN(lua_State* L_, StackIndex t_); 39 std::string_view PushFQN(lua_State* L_, StackIndex t_);
40 void PushFunctionBytecode(SourceState L1_, DestState L2_, int strip_);
40 void SerializeRequire(lua_State* L_); 41 void SerializeRequire(lua_State* L_);
41} // namespace tools 42} // namespace tools
diff --git a/src/tracker.cpp b/src/tracker.cpp
index 8b06522..34866dd 100644
--- a/src/tracker.cpp
+++ b/src/tracker.cpp
@@ -96,7 +96,7 @@ int LaneTracker::pushThreadsTable(lua_State* L_) const
96 while (_lane != TRACKING_END) { 96 while (_lane != TRACKING_END) {
97 // insert a { name='<name>', status='<status>' } tuple, so that several lanes with the same name can't clobber each other 97 // insert a { name='<name>', status='<status>' } tuple, so that several lanes with the same name can't clobber each other
98 lua_createtable(L_, 0, 2); // L_: {} {} 98 lua_createtable(L_, 0, 2); // L_: {} {}
99 luaG_pushstring(L_, _lane->getDebugName()); // L_: {} {} "name" 99 luaW_pushstring(L_, _lane->getDebugName()); // L_: {} {} "name"
100 lua_setfield(L_, -2, "name"); // L_: {} {} 100 lua_setfield(L_, -2, "name"); // L_: {} {}
101 _lane->pushStatusString(L_); // L_: {} {} "<status>" 101 _lane->pushStatusString(L_); // L_: {} {} "<status>"
102 lua_setfield(L_, -2, "status"); // L_: {} {} 102 lua_setfield(L_, -2, "status"); // L_: {} {}
diff --git a/src/uniquekey.hpp b/src/uniquekey.hpp
index 4c9eb58..ace21c4 100644
--- a/src/uniquekey.hpp
+++ b/src/uniquekey.hpp
@@ -80,7 +80,7 @@ class RegistryUniqueKey final
80 STACK_GROW(L_, 1); 80 STACK_GROW(L_, 1);
81 STACK_CHECK_START_REL(L_, 0); 81 STACK_CHECK_START_REL(L_, 0);
82 pushValue(L_); // L_: ... {}|nil 82 pushValue(L_); // L_: ... {}|nil
83 T* const value{ luaG_tolightuserdata<T>(L_, kIdxTop) }; 83 T* const value{ luaW_tolightuserdata<T>(L_, kIdxTop) };
84 lua_pop(L_, 1); // L_: ... 84 lua_pop(L_, 1); // L_: ...
85 STACK_CHECK(L_, 0); 85 STACK_CHECK(L_, 0);
86 return value; 86 return value;
@@ -125,8 +125,8 @@ class RegistryUniqueKey final
125 if (!mode_.empty()) { 125 if (!mode_.empty()) {
126 STACK_GROW(L_, 3); 126 STACK_GROW(L_, 3);
127 lua_createtable(L_, 0, 1); // L_: {} mt 127 lua_createtable(L_, 0, 1); // L_: {} mt
128 luaG_pushstring(L_, "__mode"); // L_: {} mt "__mode" 128 luaW_pushstring(L_, "__mode"); // L_: {} mt "__mode"
129 luaG_pushstring(L_, mode_); // L_: {} mt "__mode" mode 129 luaW_pushstring(L_, mode_); // L_: {} mt "__mode" mode
130 lua_rawset(L_, -3); // L_: {} mt 130 lua_rawset(L_, -3); // L_: {} mt
131 lua_setmetatable(L_, -2); // L_: {} 131 lua_setmetatable(L_, -2); // L_: {}
132 } 132 }
diff --git a/src/universe.cpp b/src/universe.cpp
index 335f056..4db036b 100644
--- a/src/universe.cpp
+++ b/src/universe.cpp
@@ -51,6 +51,16 @@ static constexpr RegistryUniqueKey kUniverseFullRegKey{ 0x1C2D76870DD9DD9Full };
51 51
52// ################################################################################################# 52// #################################################################################################
53 53
54[[nodiscard]]
55void* ProtectedAllocator::Protected_lua_Alloc(void* const ud_, void* const ptr_, size_t const osize_, size_t const nsize_)
56{
57 ProtectedAllocator* const _allocator{ static_cast<ProtectedAllocator*>(ud_) };
58 std::lock_guard<std::mutex> _guard{ _allocator->mutex };
59 return _allocator->alloc(ptr_, osize_, nsize_);
60}
61
62// #################################################################################################
63
54Universe::Universe() 64Universe::Universe()
55{ 65{
56 //--- 66 //---
@@ -101,18 +111,18 @@ void Universe::callOnStateCreate(lua_State* const L_, lua_State* const from_, Lo
101 } 111 }
102 kConfigRegKey.pushValue(L_); // L_: config 112 kConfigRegKey.pushValue(L_); // L_: config
103 STACK_CHECK(L_, 1); 113 STACK_CHECK(L_, 1);
104 LuaType const _funcType{ luaG_getfield(L_, kIdxTop, kOnStateCreate) }; // L_: config on_state_create() 114 LuaType const _funcType{ luaW_getfield(L_, kIdxTop, kOnStateCreate) }; // L_: config on_state_create()
105 if (_funcType != LuaType::FUNCTION) { 115 if (_funcType != LuaType::FUNCTION) {
106 raise_luaL_error(L_, "INTERNAL ERROR: %s is a %s, not a function", kOnStateCreate.data(), luaG_typename(L_, _funcType).data()); 116 raise_luaL_error(L_, "INTERNAL ERROR: %s is a %s, not a function", kOnStateCreate.data(), luaW_typename(L_, _funcType).data());
107 } 117 }
108 lua_remove(L_, -2); // L_: on_state_create() 118 lua_remove(L_, -2); // L_: on_state_create()
109 } 119 }
110 STACK_CHECK(L_, 1); 120 STACK_CHECK(L_, 1);
111 // capture error and raise it in caller state 121 // capture error and raise it in caller state
112 std::string_view const _stateType{ mode_ == LookupMode::LaneBody ? "lane" : "keeper" }; 122 std::string_view const _stateType{ mode_ == LookupMode::LaneBody ? "lane" : "keeper" };
113 luaG_pushstring(L_, _stateType); // L_: on_state_create() "<type>" 123 luaW_pushstring(L_, _stateType); // L_: on_state_create() "<type>"
114 if (lua_pcall(L_, 1, 0, 0) != LUA_OK) { 124 if (lua_pcall(L_, 1, 0, 0) != LUA_OK) {
115 raise_luaL_error(from_, "%s failed in %s: \"%s\"", kOnStateCreate.data(), _stateType.data(), lua_isstring(L_, -1) ? luaG_tostring(L_, kIdxTop).data() : luaG_typename(L_, kIdxTop).data()); 125 raise_luaL_error(from_, "%s failed in %s: \"%s\"", kOnStateCreate.data(), _stateType.data(), lua_isstring(L_, -1) ? luaW_tostring(L_, kIdxTop).data() : luaW_typename(L_, kIdxTop).data());
116 } 126 }
117 STACK_CHECK(L_, 0); 127 STACK_CHECK(L_, 0);
118} 128}
@@ -127,14 +137,14 @@ Universe* Universe::Create(lua_State* const L_)
127 static constexpr StackIndex kIdxSettings{ 1 }; 137 static constexpr StackIndex kIdxSettings{ 1 };
128 LUA_ASSERT(L_, lua_gettop(L_) == 1 && lua_istable(L_, 1)); 138 LUA_ASSERT(L_, lua_gettop(L_) == 1 && lua_istable(L_, 1));
129 STACK_CHECK_START_REL(L_, 0); // L_: settings 139 STACK_CHECK_START_REL(L_, 0); // L_: settings
130 std::ignore = luaG_getfield(L_, kIdxSettings, "nb_user_keepers"); // L_: settings nb_user_keepers 140 std::ignore = luaW_getfield(L_, kIdxSettings, "nb_user_keepers"); // L_: settings nb_user_keepers
131 int const _nbUserKeepers{ static_cast<int>(lua_tointeger(L_, -1)) + 1}; 141 int const _nbUserKeepers{ static_cast<int>(lua_tointeger(L_, -1)) + 1};
132 lua_pop(L_, 1); // L_: settings 142 lua_pop(L_, 1); // L_: settings
133 if (_nbUserKeepers < 1) { 143 if (_nbUserKeepers < 1) {
134 raise_luaL_error(L_, "Bad number of additional keepers (%d)", _nbUserKeepers); 144 raise_luaL_error(L_, "Bad number of additional keepers (%d)", _nbUserKeepers);
135 } 145 }
136 STACK_CHECK(L_, 0); 146 STACK_CHECK(L_, 0);
137 std::ignore = luaG_getfield(L_, kIdxSettings, "keepers_gc_threshold"); // L_: settings keepers_gc_threshold 147 std::ignore = luaW_getfield(L_, kIdxSettings, "keepers_gc_threshold"); // L_: settings keepers_gc_threshold
138 int const _keepers_gc_threshold{ static_cast<int>(lua_tointeger(L_, -1)) }; 148 int const _keepers_gc_threshold{ static_cast<int>(lua_tointeger(L_, -1)) };
139 lua_pop(L_, 1); // L_: settings 149 lua_pop(L_, 1); // L_: settings
140 STACK_CHECK(L_, 0); 150 STACK_CHECK(L_, 0);
@@ -147,30 +157,30 @@ Universe* Universe::Create(lua_State* const L_)
147 157
148 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); 158 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U });
149 lua_createtable(L_, 0, 1); // L_: settings universe {mt} 159 lua_createtable(L_, 0, 1); // L_: settings universe {mt}
150 std::ignore = luaG_getfield(L_, kIdxSettings, "shutdown_timeout"); // L_: settings universe {mt} shutdown_timeout 160 std::ignore = luaW_getfield(L_, kIdxSettings, "shutdown_timeout"); // L_: settings universe {mt} shutdown_timeout
151 lua_pushcclosure(L_, UniverseGC, 1); // L_: settings universe {mt} UniverseGC 161 lua_pushcclosure(L_, UniverseGC, 1); // L_: settings universe {mt} UniverseGC
152 lua_setfield(L_, -2, "__gc"); // L_: settings universe {mt} 162 lua_setfield(L_, -2, "__gc"); // L_: settings universe {mt}
153 lua_setmetatable(L_, -2); // L_: settings universe 163 lua_setmetatable(L_, -2); // L_: settings universe
154 lua_pop(L_, 1); // L_: settings 164 lua_pop(L_, 1); // L_: settings
155 165
156 std::ignore = luaG_getfield(L_, kIdxSettings, "linda_wake_period"); // L_: settings linda_wake_period 166 std::ignore = luaW_getfield(L_, kIdxSettings, "linda_wake_period"); // L_: settings linda_wake_period
157 if (luaG_type(L_, kIdxTop) == LuaType::NUMBER) { 167 if (luaW_type(L_, kIdxTop) == LuaType::NUMBER) {
158 _U->lindaWakePeriod = lua_Duration{ lua_tonumber(L_, kIdxTop) }; 168 _U->lindaWakePeriod = lua_Duration{ lua_tonumber(L_, kIdxTop) };
159 } else { 169 } else {
160 LUA_ASSERT(L_, luaG_tostring(L_, kIdxTop) == "never"); 170 LUA_ASSERT(L_, luaW_tostring(L_, kIdxTop) == "never");
161 } 171 }
162 lua_pop(L_, 1); // L_: settings 172 lua_pop(L_, 1); // L_: settings
163 173
164 std::ignore = luaG_getfield(L_, kIdxSettings, "strip_functions"); // L_: settings strip_functions 174 std::ignore = luaW_getfield(L_, kIdxSettings, "strip_functions"); // L_: settings strip_functions
165 _U->stripFunctions = lua_toboolean(L_, -1) ? true : false; 175 _U->stripFunctions = lua_toboolean(L_, -1) ? true : false;
166 lua_pop(L_, 1); // L_: settings 176 lua_pop(L_, 1); // L_: settings
167 177
168 std::ignore = luaG_getfield(L_, kIdxSettings, "verbose_errors"); // L_: settings verbose_errors 178 std::ignore = luaW_getfield(L_, kIdxSettings, "verbose_errors"); // L_: settings verbose_errors
169 _U->verboseErrors = lua_toboolean(L_, -1) ? true : false; 179 _U->verboseErrors = lua_toboolean(L_, -1) ? true : false;
170 lua_pop(L_, 1); // L_: settings 180 lua_pop(L_, 1); // L_: settings
171 181
172 // tracking 182 // tracking
173 std::ignore = luaG_getfield(L_, kIdxSettings, "track_lanes"); // L_: settings track_lanes 183 std::ignore = luaW_getfield(L_, kIdxSettings, "track_lanes"); // L_: settings track_lanes
174 if (lua_toboolean(L_, -1)) { 184 if (lua_toboolean(L_, -1)) {
175 _U->tracker.activate(); 185 _U->tracker.activate();
176 } 186 }
@@ -178,8 +188,8 @@ Universe* Universe::Create(lua_State* const L_)
178 188
179 // Linked chains handling 189 // Linked chains handling
180 _U->selfdestructFirst = SELFDESTRUCT_END; 190 _U->selfdestructFirst = SELFDESTRUCT_END;
181 _U->initializeAllocatorFunction(L_); 191 _U->initializeAllocatorFunction(L_); // this can raise an error
182 _U->initializeOnStateCreate(L_); 192 _U->initializeOnStateCreate(L_); // this can raise an error
183 _U->keepers.initialize(*_U, L_, static_cast<size_t>(_nbUserKeepers), _keepers_gc_threshold); 193 _U->keepers.initialize(*_U, L_, static_cast<size_t>(_nbUserKeepers), _keepers_gc_threshold);
184 STACK_CHECK(L_, 0); 194 STACK_CHECK(L_, 0);
185 195
@@ -207,7 +217,7 @@ static void* libc_lua_Alloc([[maybe_unused]] void* const ud_, [[maybe_unused]] v
207// ################################################################################################# 217// #################################################################################################
208 218
209[[nodiscard]] 219[[nodiscard]]
210static int luaG_provide_protected_allocator(lua_State* const L_) 220static int luaW_provide_protected_allocator(lua_State* const L_)
211{ 221{
212 Universe* const _U{ Universe::Get(L_) }; 222 Universe* const _U{ Universe::Get(L_) };
213 // push a new full userdata on the stack, giving access to the universe's protected allocator 223 // push a new full userdata on the stack, giving access to the universe's protected allocator
@@ -217,9 +227,9 @@ static int luaG_provide_protected_allocator(lua_State* const L_)
217 227
218// ################################################################################################# 228// #################################################################################################
219 229
230// already called under protection of selfdestructMutex
220void Universe::flagDanglingLanes() const 231void Universe::flagDanglingLanes() const
221{ 232{
222 std::lock_guard<std::mutex> _guard{ selfdestructMutex };
223 Lane* _lane{ selfdestructFirst }; 233 Lane* _lane{ selfdestructFirst };
224 while (_lane != SELFDESTRUCT_END) { 234 while (_lane != SELFDESTRUCT_END) {
225 _lane->flaggedAfterUniverseGC.store(true, std::memory_order_relaxed); 235 _lane->flaggedAfterUniverseGC.store(true, std::memory_order_relaxed);
@@ -235,17 +245,17 @@ void Universe::initializeAllocatorFunction(lua_State* const L_)
235 // start by just grabbing whatever allocator was provided to the master state 245 // start by just grabbing whatever allocator was provided to the master state
236 protectedAllocator.initFrom(L_); 246 protectedAllocator.initFrom(L_);
237 STACK_CHECK_START_REL(L_, 1); // L_: settings 247 STACK_CHECK_START_REL(L_, 1); // L_: settings
238 switch (luaG_getfield(L_, kIdxTop, "allocator")) { // L_: settings allocator|nil|"protected" 248 switch (luaW_getfield(L_, kIdxTop, "allocator")) { // L_: settings allocator|nil|"protected"
239 case LuaType::NIL: 249 case LuaType::NIL:
240 // nothing else to do 250 // nothing else to do
241 break; 251 break;
242 252
243 case LuaType::STRING: 253 case LuaType::STRING:
244 LUA_ASSERT(L_, luaG_tostring(L_, kIdxTop) == "protected"); 254 LUA_ASSERT(L_, luaW_tostring(L_, kIdxTop) == "protected");
245 // set the original allocator to call from inside protection by the mutex 255 // set the original allocator to call from inside protection by the mutex
246 protectedAllocator.installIn(L_); 256 protectedAllocator.installIn(L_);
247 // before a state is created, this function will be called to obtain the allocator 257 // before a state is created, this function will be called to obtain the allocator
248 provideAllocator = luaG_provide_protected_allocator; 258 provideAllocator = luaW_provide_protected_allocator;
249 break; 259 break;
250 260
251 case LuaType::FUNCTION: 261 case LuaType::FUNCTION:
@@ -266,14 +276,14 @@ void Universe::initializeAllocatorFunction(lua_State* const L_)
266 break; 276 break;
267 277
268 default: // should be filtered out in lanes.lua 278 default: // should be filtered out in lanes.lua
269 raise_luaL_error(L_, "Bad config.allocator type %s", luaG_typename(L_, kIdxTop).data()); 279 raise_luaL_error(L_, "Bad config.allocator type %s", luaW_typename(L_, kIdxTop).data());
270 } 280 }
271 lua_pop(L_, 1); // L_: settings 281 lua_pop(L_, 1); // L_: settings
272 STACK_CHECK(L_, 1); 282 STACK_CHECK(L_, 1);
273 283
274 std::ignore = luaG_getfield(L_, kIdxTop, "internal_allocator"); // L_: settings "libc"|"allocator" 284 std::ignore = luaW_getfield(L_, kIdxTop, "internal_allocator"); // L_: settings "libc"|"allocator"
275 LUA_ASSERT(L_, lua_isstring(L_, kIdxTop)); // should be the case due to lanes.lua parameter validation 285 LUA_ASSERT(L_, lua_isstring(L_, kIdxTop)); // should be the case due to lanes.lua parameter validation
276 std::string_view const _allocator{ luaG_tostring(L_, kIdxTop) }; 286 std::string_view const _allocator{ luaW_tostring(L_, kIdxTop) };
277 // use whatever the provider provides. This performs validation of what provideAllocator is giving 287 // use whatever the provider provides. This performs validation of what provideAllocator is giving
278 // we do this even if _allocator == "libc", to have the validation part 288 // we do this even if _allocator == "libc", to have the validation part
279 internalAllocator = resolveAndValidateAllocator(L_, "internal"); 289 internalAllocator = resolveAndValidateAllocator(L_, "internal");
@@ -295,7 +305,7 @@ int Universe::InitializeFinalizer(lua_State* const L_)
295 305
296 // make sure we are only called from the Master Lua State! 306 // make sure we are only called from the Master Lua State!
297 kUniverseFullRegKey.pushValue(L_); // L_: f U 307 kUniverseFullRegKey.pushValue(L_); // L_: f U
298 if (luaG_type(L_, kIdxTop) != LuaType::USERDATA) { 308 if (luaW_type(L_, kIdxTop) != LuaType::USERDATA) {
299 raise_luaL_error(L_, "lanes.%s called from inside a lane", kFinally); 309 raise_luaL_error(L_, "lanes.%s called from inside a lane", kFinally);
300 } 310 }
301 lua_pop(L_, 1); // L_: f 311 lua_pop(L_, 1); // L_: f
@@ -311,8 +321,8 @@ int Universe::InitializeFinalizer(lua_State* const L_)
311void Universe::initializeOnStateCreate(lua_State* const L_) 321void Universe::initializeOnStateCreate(lua_State* const L_)
312{ 322{
313 STACK_CHECK_START_REL(L_, 0); // L_: settings 323 STACK_CHECK_START_REL(L_, 0); // L_: settings
314 if (luaG_getfield(L_, kIdxTop, kOnStateCreate) != LuaType::NIL) { // L_: settings on_state_create|nil 324 if (luaW_getfield(L_, kIdxTop, kOnStateCreate) != LuaType::NIL) { // L_: settings on_state_create|nil
315 LUA_ASSERT(L_, luaG_type(L_, kIdxTop) == LuaType::FUNCTION); // ensured by lanes.lua parameter validation 325 LUA_ASSERT(L_, luaW_type(L_, kIdxTop) == LuaType::FUNCTION); // ensured by lanes.lua parameter validation
316 // store C function pointer in an internal variable 326 // store C function pointer in an internal variable
317 lua_CFunction const _func{ lua_tocfunction(L_, -1) }; // L_: settings on_state_create 327 lua_CFunction const _func{ lua_tocfunction(L_, -1) }; // L_: settings on_state_create
318 if (_func) { 328 if (_func) {
@@ -325,7 +335,7 @@ void Universe::initializeOnStateCreate(lua_State* const L_)
325 // remove this C function from the config table so that it doesn't cause problems 335 // remove this C function from the config table so that it doesn't cause problems
326 // when we transfer the config table in newly created Lua states 336 // when we transfer the config table in newly created Lua states
327 lua_pushnil(L_); // L_: settings on_state_create nil 337 lua_pushnil(L_); // L_: settings on_state_create nil
328 luaG_setfield(L_, StackIndex{ -3 }, kOnStateCreate); // L_: settings on_state_create 338 luaW_setfield(L_, StackIndex{ -3 }, kOnStateCreate); // L_: settings on_state_create
329 } else { 339 } else {
330 // the function is still in the config table. we indicate this with the uintptr_t alternative (actual value is irrelevant) 340 // the function is still in the config table. we indicate this with the uintptr_t alternative (actual value is irrelevant)
331 onStateCreateFunc.emplace<uintptr_t>(std::bit_cast<uintptr_t>(kOnStateCreate.data())); 341 onStateCreateFunc.emplace<uintptr_t>(std::bit_cast<uintptr_t>(kOnStateCreate.data()));
@@ -348,7 +358,7 @@ lanes::AllocatorDefinition Universe::resolveAndValidateAllocator(lua_State* cons
348 358
349 STACK_CHECK_START_REL(L_, 0); // here, we have a function we can call to obtain an allocator 359 STACK_CHECK_START_REL(L_, 0); // here, we have a function we can call to obtain an allocator
350 lua_pushcclosure(L_, provideAllocator, 0); // L_: provideAllocator() 360 lua_pushcclosure(L_, provideAllocator, 0); // L_: provideAllocator()
351 luaG_pushstring(L_, hint_); // L_: provideAllocator() "<hint>" 361 luaW_pushstring(L_, hint_); // L_: provideAllocator() "<hint>"
352 lua_call(L_, 1, 1); // L_: result 362 lua_call(L_, 1, 1); // L_: result
353 // make sure we have a valid AllocatorDefinition on the stack (an error is raised instead if it is not the case) 363 // make sure we have a valid AllocatorDefinition on the stack (an error is raised instead if it is not the case)
354 _ret = lanes::AllocatorDefinition::Validated(L_, kIdxTop); 364 _ret = lanes::AllocatorDefinition::Validated(L_, kIdxTop);
@@ -423,7 +433,7 @@ int Universe::UniverseGC(lua_State* const L_)
423{ 433{
424 lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) }; 434 lua_Duration const _shutdown_timeout{ lua_tonumber(L_, lua_upvalueindex(1)) };
425 STACK_CHECK_START_ABS(L_, 1); 435 STACK_CHECK_START_ABS(L_, 1);
426 Universe* const _U{ luaG_tofulluserdata<Universe>(L_, StackIndex{ 1 }) }; // L_: U 436 Universe* const _U{ luaW_tofulluserdata<Universe>(L_, StackIndex{ 1 }) }; // L_: U
427 437
428 // attempt to terminate all lanes with increasingly stronger cancel methods 438 // attempt to terminate all lanes with increasingly stronger cancel methods
429 bool const _allLanesTerminated{ 439 bool const _allLanesTerminated{
@@ -437,7 +447,7 @@ int Universe::UniverseGC(lua_State* const L_)
437 if (!lua_isnil(L_, -1)) { 447 if (!lua_isnil(L_, -1)) {
438 lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool 448 lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool
439 // no protection. Lua rules for errors in finalizers apply normally: 449 // no protection. Lua rules for errors in finalizers apply normally:
440 // Lua 5.4: error is propagated in the warn system 450 // Lua 5.4+: error is propagated in the warn system
441 // older: error is swallowed 451 // older: error is swallowed
442 lua_call(L_, 1, 1); // L_: U msg? 452 lua_call(L_, 1, 1); // L_: U msg?
443 // phew, no error in finalizer, since we reached that point 453 // phew, no error in finalizer, since we reached that point
@@ -446,24 +456,31 @@ int Universe::UniverseGC(lua_State* const L_)
446 if (lua_isnil(L_, kIdxTop)) { 456 if (lua_isnil(L_, kIdxTop)) {
447 lua_pop(L_, 1); // L_: U 457 lua_pop(L_, 1); // L_: U
448 // no finalizer, or it returned no value: push some default message on the stack, in case it is necessary 458 // no finalizer, or it returned no value: push some default message on the stack, in case it is necessary
449 luaG_pushstring(L_, "uncooperative lanes detected at shutdown"); // L_: U "msg" 459 luaW_pushstring(L_, "uncooperative lanes detected at shutdown"); // L_: U "msg"
450 } 460 }
451 STACK_CHECK(L_, 2); 461 STACK_CHECK(L_, 2);
452 462
453 // now, all remaining lanes are flagged. if they crash because we remove keepers and the Universe from under them, it is their fault 463 {
454 bool const _detectedUncooperativeLanes{ _U->selfdestructFirst != SELFDESTRUCT_END }; 464 std::lock_guard<std::mutex> _guard{ _U->selfdestructMutex };
455 if (_detectedUncooperativeLanes) { 465 // now, all remaining lanes are flagged. if they crash because we remove keepers and the Universe from under them, it is their fault
456 _U->flagDanglingLanes(); 466 bool const _detectedUncooperativeLanes{ _U->selfdestructFirst != SELFDESTRUCT_END };
457 if (luaG_tostring(L_, kIdxTop) == "freeze") { 467 if (_detectedUncooperativeLanes) {
458 std::this_thread::sleep_until(std::chrono::time_point<std::chrono::steady_clock>::max()); 468 _U->flagDanglingLanes();
469 if (luaW_tostring(L_, kIdxTop) == "freeze") {
470 std::this_thread::sleep_until(std::chrono::time_point<std::chrono::steady_clock>::max());
471 } else {
472 // take the value returned by the finalizer (or our default message) and throw it as an error
473 // since we are inside Lua's GCTM, it will be propagated through the warning system (Lua 5.4) or swallowed silently
474 // IMPORTANT: lua_error() is used here instead of the wrapper raise_lua_error() to circumvent what looks like a MSVC compiler bug
475 // that manifests as a crash inside ntdll!longjmp() function, in optimized builds only
476 lua_error(L_);
477 }
459 } else { 478 } else {
460 // take the value returned by the finalizer (or our default message) and throw it as an error 479 // we didn't use the error message, let's keep a clean stack
461 // since we are inside Lua's GCTM, it will be propagated through the warning system (Lua 5.4) or swallowed silently 480 lua_pop(L_, 1); // L_: U
462 // IMPORTANT: lua_error() is used here instead of the wrapper raise_lua_error() to circumvent what looks like a MSVC compiler bug
463 // that manifests as a crash inside ntdll!longjmp() function, in optimized builds only
464 lua_error(L_);
465 } 481 }
466 } 482 }
483 STACK_CHECK(L_, 1);
467 484
468 // --------------------------------------------------------- 485 // ---------------------------------------------------------
469 // we don't reach that point if some lanes are still running 486 // we don't reach that point if some lanes are still running
@@ -472,7 +489,9 @@ int Universe::UniverseGC(lua_State* const L_)
472 // no need to mutex-protect this as all lanes in the universe are gone at that point 489 // no need to mutex-protect this as all lanes in the universe are gone at that point
473 Linda::DeleteTimerLinda(L_, std::exchange(_U->timerLinda, nullptr), PK); 490 Linda::DeleteTimerLinda(L_, std::exchange(_U->timerLinda, nullptr), PK);
474 491
475 _U->keepers.close(); 492 if (!_U->keepers.close()) {
493 raise_luaL_error(L_, "INTERNAL ERROR: Keepers closed more than once");
494 }
476 495
477 // remove the protected allocator, if any 496 // remove the protected allocator, if any
478 _U->protectedAllocator.removeFrom(L_); 497 _U->protectedAllocator.removeFrom(L_);
diff --git a/src/universe.hpp b/src/universe.hpp
index 0c5e659..f781e92 100644
--- a/src/universe.hpp
+++ b/src/universe.hpp
@@ -4,6 +4,7 @@
4#include "cancel.hpp" 4#include "cancel.hpp"
5#include "keeper.hpp" 5#include "keeper.hpp"
6#include "lanesconf.h" 6#include "lanesconf.h"
7#include "threading.hpp"
7#include "tracker.hpp" 8#include "tracker.hpp"
8#include "uniquekey.hpp" 9#include "uniquekey.hpp"
9 10
@@ -27,12 +28,7 @@ class ProtectedAllocator final
27 std::mutex mutex; 28 std::mutex mutex;
28 29
29 [[nodiscard]] 30 [[nodiscard]]
30 static void* protected_lua_Alloc(void* const ud_, void* const ptr_, size_t const osize_, size_t const nsize_) 31 static void* Protected_lua_Alloc(void* const ud_, void* const ptr_, size_t const osize_, size_t const nsize_);
31 {
32 ProtectedAllocator* const allocator{ static_cast<ProtectedAllocator*>(ud_) };
33 std::lock_guard<std::mutex> guard{ allocator->mutex };
34 return allocator->alloc(ptr_, osize_, nsize_);
35 }
36 32
37 public: 33 public:
38 // we are not like our base class: we can't be created inside a full userdata (or we would have to install a metatable and __gc handler to destroy ourselves properly) 34 // we are not like our base class: we can't be created inside a full userdata (or we would have to install a metatable and __gc handler to destroy ourselves properly)
@@ -42,13 +38,13 @@ class ProtectedAllocator final
42 38
43 AllocatorDefinition makeDefinition() 39 AllocatorDefinition makeDefinition()
44 { 40 {
45 return AllocatorDefinition{ protected_lua_Alloc, this }; 41 return AllocatorDefinition{ Protected_lua_Alloc, this };
46 } 42 }
47 43
48 void installIn(lua_State* const L_) const 44 void installIn(lua_State* const L_) const
49 { 45 {
50 // install our replacement allocator function (this is a C function, we need to deconst ourselves) 46 // install our replacement allocator function (this is a C function, we need to deconst ourselves)
51 lua_setallocf(L_, protected_lua_Alloc, static_cast<void*>(const_cast<ProtectedAllocator*>(this))); 47 lua_setallocf(L_, Protected_lua_Alloc, static_cast<void*>(const_cast<ProtectedAllocator*>(this)));
52 } 48 }
53 49
54 void removeFrom(lua_State* const L_) const 50 void removeFrom(lua_State* const L_) const
@@ -75,9 +71,9 @@ class Universe final
75 71
76#ifdef PLATFORM_LINUX 72#ifdef PLATFORM_LINUX
77 // Linux needs to check, whether it's been run as root 73 // Linux needs to check, whether it's been run as root
78 bool const sudo{ geteuid() == 0 }; 74 SudoFlag const sudo{ geteuid() == 0 };
79#else 75#else
80 bool const sudo{ false }; 76 SudoFlag const sudo{ false };
81#endif // PLATFORM_LINUX 77#endif // PLATFORM_LINUX
82 78
83 // for verbose errors 79 // for verbose errors
@@ -132,7 +128,7 @@ class Universe final
132 128
133 public: 129 public:
134 [[nodiscard]] 130 [[nodiscard]]
135 static void* operator new([[maybe_unused]] size_t const size_, lua_State* const L_) noexcept { return luaG_newuserdatauv<Universe>(L_, UserValueCount{ 0 }); }; 131 static void* operator new([[maybe_unused]] size_t const size_, lua_State* const L_) noexcept { return luaW_newuserdatauv<Universe>(L_, UserValueCount{ 0 }); };
136 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception 132 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
137 static void operator delete([[maybe_unused]] void* const p_, [[maybe_unused]] lua_State* const L_) {} // nothing to do, as nothing is allocated independently 133 static void operator delete([[maybe_unused]] void* const p_, [[maybe_unused]] lua_State* const L_) {} // nothing to do, as nothing is allocated independently
138 134