diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/allocator.cpp | 2 | ||||
-rw-r--r-- | src/allocator.hpp | 4 | ||||
-rw-r--r-- | src/cancel.cpp | 140 | ||||
-rw-r--r-- | src/compat.cpp | 50 | ||||
-rw-r--r-- | src/compat.hpp | 138 | ||||
-rw-r--r-- | src/deep.cpp | 28 | ||||
-rw-r--r-- | src/intercopycontext.cpp | 189 | ||||
-rw-r--r-- | src/keeper.cpp | 113 | ||||
-rw-r--r-- | src/keeper.hpp | 3 | ||||
-rw-r--r-- | src/lane.cpp | 424 | ||||
-rw-r--r-- | src/lane.hpp | 10 | ||||
-rw-r--r-- | src/lanes.cpp | 126 | ||||
-rw-r--r-- | src/lanes.lua | 42 | ||||
-rw-r--r-- | src/lanesconf.h | 8 | ||||
-rw-r--r-- | src/linda.cpp | 169 | ||||
-rw-r--r-- | src/linda.hpp | 24 | ||||
-rw-r--r-- | src/lindafactory.cpp | 16 | ||||
-rw-r--r-- | src/nameof.cpp | 34 | ||||
-rw-r--r-- | src/state.cpp | 18 | ||||
-rw-r--r-- | src/threading.cpp | 150 | ||||
-rw-r--r-- | src/threading.hpp | 14 | ||||
-rw-r--r-- | src/tools.cpp | 101 | ||||
-rw-r--r-- | src/tools.hpp | 3 | ||||
-rw-r--r-- | src/tracker.cpp | 2 | ||||
-rw-r--r-- | src/uniquekey.hpp | 6 | ||||
-rw-r--r-- | src/universe.cpp | 105 | ||||
-rw-r--r-- | src/universe.hpp | 18 |
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 | ||
41 | namespace { | ||
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 | |||
84 | static 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]] | ||
107 | static 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_) | |||
133 | LUAG_FUNC(cancel_test) | 120 | LUAG_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]) |
143 | LUAG_FUNC(lane_cancel) | 151 | LUAG_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 | ||
8 | UserValueCount luaG_getalluservalues(lua_State* const L_, StackIndex const idx_) | 8 | UserValueCount 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>"] |
27 | LuaType luaG_getmodule(lua_State* const L_, std::string_view const& name_) | 27 | LuaType 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 | ||
172 | unsigned 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 | |||
82 | inline 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 | ||
93 | void* lua_newuserdatauv(lua_State* L_, size_t sz_, UserValueCount nuvalue_); | 80 | void* 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 | ||
106 | inline int luaL_optint(lua_State* L_, StackIndex n_, lua_Integer d_) | 92 | unsigned 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]] |
139 | inline LuaType luaG_type(lua_State* const L_, StackIndex const idx_) | 122 | inline 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]] |
155 | inline std::string_view luaG_tostring(lua_State* const L_, StackIndex const idx_) | 138 | inline 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]] |
163 | inline std::string_view luaG_checkstring(lua_State* const L_, StackIndex const idx_) | 146 | inline 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]] |
171 | inline std::string_view luaG_optstring(lua_State* const L_, StackIndex const idx_, std::string_view const& default_) | 154 | inline 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 | ||
181 | template <typename... EXTRA> | 164 | template <typename... EXTRA> |
182 | inline std::string_view luaG_pushstring(lua_State* const L_, std::string_view const& str_, EXTRA&&... extra_) | 165 | inline 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 |
201 | inline StackIndex luaG_absindex(lua_State* const L_, StackIndex const idx_) | 184 | inline 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 | ||
230 | static inline int luaG_dump(lua_State* const L_, lua_Writer const writer_, void* const data_, int const strip_) | 213 | static 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 | ||
237 | UserValueCount luaG_getalluservalues(lua_State* L_, StackIndex idx_); | 220 | UserValueCount 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]] |
275 | static inline LuaType luaG_getfield(lua_State* const L_, StackIndex const idx_, std::string_view const& name_) | 258 | static 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]] |
283 | LuaType luaG_getmodule(lua_State* L_, std::string_view const& name_); | 266 | LuaType luaW_getmodule(lua_State* L_, std::string_view const& name_); |
267 | |||
268 | // ################################################################################################# | ||
269 | |||
270 | template <typename LUA_NEWSTATE> | ||
271 | concept RequiresOldLuaNewState = requires(LUA_NEWSTATE f_) | ||
272 | { | ||
273 | { | ||
274 | f_(nullptr, 0) | ||
275 | } -> std::same_as<lua_State*>; | ||
276 | }; | ||
277 | |||
278 | template <RequiresOldLuaNewState LUA_NEWSTATE> | ||
279 | static 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 | |||
287 | template <typename LUA_NEWSTATE> | ||
288 | concept RequiresNewLuaNewState = requires(LUA_NEWSTATE f_) | ||
289 | { | ||
290 | { | ||
291 | f_(nullptr, nullptr, 0) | ||
292 | } -> std::same_as<lua_State*>; | ||
293 | }; | ||
294 | |||
295 | template <RequiresNewLuaNewState LUA_NEWSTATE> | ||
296 | static 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 | |||
304 | static 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 | ||
287 | template<typename ENUM> | 311 | template<typename ENUM> |
288 | requires std::is_enum_v<ENUM> | 312 | requires std::is_enum_v<ENUM> |
289 | [[nodiscard]] | 313 | [[nodiscard]] |
290 | ENUM luaG_optenum(lua_State* const L_, StackIndex const idx_, ENUM const def_) | 314 | ENUM 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 | ||
297 | inline void luaG_registerlibfuncs(lua_State* const L_, luaL_Reg const* funcs_) | 321 | inline 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]] |
356 | static inline LuaError luaG_resume(lua_State* const L_, lua_State* const from_, int const nargs_, int* const nresults_) | 380 | static 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> | |||
364 | concept RequiresOldLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<void>; }; | 388 | concept RequiresOldLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<void>; }; |
365 | 389 | ||
366 | template <RequiresOldLuaRawget LUA_RAWGET> | 390 | template <RequiresOldLuaRawget LUA_RAWGET> |
367 | static inline LuaType WrapLuaRawget(LUA_RAWGET lua_rawget_, lua_State* const L_, StackIndex const idx_) | 391 | static 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> | |||
377 | concept RequiresNewLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<int>; }; | 401 | concept RequiresNewLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<int>; }; |
378 | 402 | ||
379 | template <RequiresNewLuaRawget LUA_RAWGET> | 403 | template <RequiresNewLuaRawget LUA_RAWGET> |
380 | static inline LuaType WrapLuaRawget(LUA_RAWGET lua_rawget_, lua_State* const L_, StackIndex const idx_) | 404 | static 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 | ||
388 | static inline LuaType luaG_rawget(lua_State* const L_, StackIndex const idx_) | 412 | static 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 | ||
395 | static inline LuaType luaG_rawgetfield(lua_State* const L_, StackIndex const idx_, std::string_view const& name_) | 419 | static 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 | ||
405 | template <size_t N> | 429 | template <size_t N> |
406 | static inline void luaG_newlib(lua_State* const L_, luaL_Reg const (&funcs_)[N]) | 430 | static 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 | ||
414 | template <typename T> | 438 | template <typename T> |
415 | [[nodiscard]] | 439 | [[nodiscard]] |
416 | T* luaG_newuserdatauv(lua_State* const L_, UserValueCount const nuvalue_) | 440 | T* 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 | ||
423 | inline void luaG_pushglobaltable(lua_State* const L_) | 447 | inline 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 | ||
434 | inline void luaG_setfield(lua_State* const L_, StackIndex const idx_, char const* const k_) = delete; | 458 | inline void luaW_setfield(lua_State* const L_, StackIndex const idx_, char const* const k_) = delete; |
435 | inline void luaG_setfield(lua_State* const L_, StackIndex const idx_, std::string_view const& k_) | 459 | inline 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 | ||
442 | inline void luaG_setmetatable(lua_State* const L_, std::string_view const& tname_) | 466 | inline 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 |
457 | template <typename T> | 481 | template <typename T> |
458 | [[nodiscard]] | 482 | [[nodiscard]] |
459 | T* luaG_tofulluserdata(lua_State* const L_, StackIndex const index_) | 483 | T* 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 | ||
467 | template <typename T> | 491 | template <typename T> |
468 | [[nodiscard]] | 492 | [[nodiscard]] |
469 | auto luaG_tolightuserdata(lua_State* const L_, StackIndex const index_) | 493 | auto 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]] |
482 | inline std::string_view luaG_typename(lua_State* const L_, LuaType const t_) | 506 | inline 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]] |
490 | inline std::string_view luaG_typename(lua_State* const L_, StackIndex const idx_) | 514 | inline 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]] |
104 | int DeepFactory::DeepGC(lua_State* const L_) | 104 | int 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]] | ||
44 | static 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]] |
58 | static int func_lookup_sentinel(lua_State* const L_) | 41 | static 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]] |
94 | std::string_view InterCopyContext::findLookupName() const | 77 | std::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]] |
159 | static lua_Integer get_mt_id(Universe* const U_, lua_State* const L_, StackIndex const idx_) | 142 | static 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 |
196 | void InterCopyContext::copyFunction() const | 179 | void 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'. |
369 | void InterCopyContext::copyCachedFunction() const | 354 | void 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]] |
737 | bool InterCopyContext::tryCopyClonable() const | 722 | bool 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]] |
1061 | bool InterCopyContext::interCopyString() const | 1046 | bool 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]] | ||
107 | bool KeyUD::changeLimit(LindaLimit const limit_) | 108 | bool 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]] | ||
130 | KeyUD* KeyUD::Create(KeeperState const K_) | 132 | KeyUD* 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]] | ||
144 | KeyUD* KeyUD::GetPtr(KeeperState const K_, StackIndex const idx_) | 147 | KeyUD* 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]] | ||
184 | int KeyUD::pop(KeeperState const K_, int const minCount_, int const maxCount_) | 188 | int 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) |
223 | void KeyUD::prepareAccess(KeeperState const K_, StackIndex const idx_) const | 237 | void 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]] | ||
236 | bool KeyUD::push(KeeperState const K_, int const count_, bool const enforceLimit_) | 251 | bool 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 | |||
259 | void KeyUD::pushFillStatus(KeeperState const K_) const | 274 | void 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]] | ||
289 | bool KeyUD::reset(KeeperState const K_) | 307 | bool 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]] | ||
340 | int keepercall_collectgarbage(lua_State* const L_) | 359 | int 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]] | ||
349 | int keepercall_count(lua_State* const L_) | 369 | int 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]] | ||
412 | int keepercall_destruct(lua_State* const L_) | 433 | int 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]] | ||
430 | int keepercall_get(lua_State* const L_) | 452 | int 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]] | ||
464 | int keepercall_limit(lua_State* const L_) | 487 | int 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]] | ||
507 | int keepercall_receive(lua_State* const L_) | 531 | int 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]] | ||
551 | int keepercall_receive_batched(lua_State* const L_) | 576 | int 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]] | ||
584 | int keepercall_restrict(lua_State* const L_) | 610 | int 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]] | ||
648 | int keepercall_send(lua_State* const L_) | 675 | int 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]] | ||
687 | int keepercall_set(lua_State* const L_) | 715 | int 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]] | ||
756 | KeeperCallResult keeper_call(KeeperState const K_, keeper_api_t const func_, lua_State* const L_, Linda* const linda_, StackIndex const starting_index_) | 785 | KeeperCallResult 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]] | ||
825 | int Keeper::PushLindaStorage(Linda& linda_, DestState const L_) | 855 | int 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]] | |
955 | void Keepers::close() | 985 | bool 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) | |||
85 | static LUAG_FUNC(lane_threadname) | 85 | static 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>) | ||
810 | static LUAG_FUNC(lane_close) | 793 | static 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]] |
1097 | int 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]] | ||
1111 | std::string_view Lane::pushErrorTraceLevel(lua_State* L_) const | 1173 | std::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 | ||
1170 | void Lane::startThread(int const priority_) | 1232 | void 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 | ||
1335 | bool Lane::waitForCompletion(std::chrono::time_point<std::chrono::steady_clock> until_) | 1400 | bool 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]] | ||
1416 | bool 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) | |||
137 | LUAG_FUNC(set_thread_priority) | 137 | LUAG_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 |
195 | LUAG_FUNC(require) | 202 | LUAG_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 | ||
246 | LUAG_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 | ||
301 | local process_gen_opt = function(...) | 305 | local 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 |
371 | end -- process_gen_opt | 380 | end -- 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 | 422 | local make_generator = function(is_coro_, ...) |
412 | local 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 |
431 | end -- make_generator | ||
432 | |||
433 | -- ################################################################################################# | ||
434 | |||
435 | -- receives a sequence of strings and tables, plus a function | ||
436 | local gen = function(...) | ||
437 | return make_generator(false, ...) | ||
420 | end -- gen() | 438 | end -- gen() |
421 | 439 | ||
440 | -- ################################################################################################# | ||
441 | |||
422 | local coro = function(...) | 442 | local 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 | ||
430 | end -- coro() | 444 | end -- 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 | ||
447 | void Linda::pushCancelString(lua_State* L_) const | 453 | void 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_) | |||
498 | LUAG_FUNC(linda_cancel) | 505 | LUAG_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) | |||
1102 | LUAG_FUNC(linda_wake) | 1079 | LUAG_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 | ||
86 | static 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 |
90 | static void FAIL(char const* funcname_, DWORD const rc_) | 102 | |
103 | template <typename F, typename... ARGS> | ||
104 | void 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 | ||
124 | void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) | 142 | std::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 | ||
134 | void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, [[maybe_unused]] bool sudo_) | 149 | [[nodiscard]] |
150 | void 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 | ||
146 | void THREAD_SET_AFFINITY(unsigned int aff_) | 158 | [[nodiscard]] |
159 | void 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]] | ||
168 | void 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 | ||
218 | static void _PT_FAIL(int rc, const char* name, const char* file, int line) | 238 | template <typename F, typename... ARGS> |
239 | void 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 |
238 | static int const gs_prio_remap[] = { | 258 | static 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 | ||
360 | void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) | 380 | // ################################################################################################# |
381 | |||
382 | std::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 | |||
391 | void 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 | ||
375 | void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, [[maybe_unused]] bool sudo_) | 406 | void 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 | ||
392 | void THREAD_SET_AFFINITY(unsigned int aff_) | 423 | void 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 | ||
399 | void THREAD_SET_AFFINITY(unsigned int aff_) | 430 | void 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 | ||
448 | void THREAD_SETNAME(std::string_view const& name_) | 478 | void 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 | ||
77 | DECLARE_UNIQUE_TYPE(SudoFlag, bool); | ||
78 | DECLARE_UNIQUE_TYPE(NativePrioFlag, bool); | ||
79 | |||
80 | std::pair<int, int> THREAD_NATIVE_PRIOS(); | ||
81 | |||
76 | void THREAD_SETNAME(std::string_view const& name_); | 82 | void THREAD_SETNAME(std::string_view const& name_); |
77 | void THREAD_SET_PRIORITY(int prio_, bool sudo_); | ||
78 | void THREAD_SET_AFFINITY(unsigned int aff_); | ||
79 | 83 | ||
80 | void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, bool sudo_); | 84 | void THREAD_SET_PRIORITY(lua_State* L_, int prio_, NativePrioFlag native_, SudoFlag sudo_); |
85 | |||
86 | void THREAD_SET_AFFINITY(lua_State* L_, unsigned int aff_); | ||
87 | |||
88 | void 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 | ||
48 | static constexpr int kWriterReturnCode{ 666 }; | 48 | namespace { |
49 | [[nodiscard]] | 49 | namespace local { |
50 | static 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]] |
71 | FuncSubType luaG_getfuncsubtype(lua_State* const L_, StackIndex const i_) | 85 | FuncSubType 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 | ||
94 | namespace tools { | 108 | namespace 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]] |
24 | FuncSubType luaG_getfuncsubtype(lua_State* L_, StackIndex i_); | 24 | FuncSubType 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]] | ||
55 | void* 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 | |||
54 | Universe::Universe() | 64 | Universe::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]] |
210 | static int luaG_provide_protected_allocator(lua_State* const L_) | 220 | static 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 | ||
220 | void Universe::flagDanglingLanes() const | 231 | void 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_) | |||
311 | void Universe::initializeOnStateCreate(lua_State* const L_) | 321 | void 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 | ||