aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cancel.cpp117
-rw-r--r--src/cancel.h6
-rw-r--r--src/compat.h1
-rw-r--r--src/keeper.cpp2
-rw-r--r--src/lanes.cpp1387
-rw-r--r--src/universe.h6
6 files changed, 773 insertions, 746 deletions
diff --git a/src/cancel.cpp b/src/cancel.cpp
index c0867a9..8b8977b 100644
--- a/src/cancel.cpp
+++ b/src/cancel.cpp
@@ -55,9 +55,9 @@ THE SOFTWARE.
55*/ 55*/
56static inline CancelRequest cancel_test(lua_State* L) 56static inline CancelRequest cancel_test(lua_State* L)
57{ 57{
58 Lane* const s = get_lane_from_registry( L); 58 Lane* const lane{ get_lane_from_registry(L) };
59 // 's' is nullptr for the original main state (and no-one can cancel that) 59 // 's' is nullptr for the original main state (and no-one can cancel that)
60 return s ? s->cancel_request : CancelRequest::None; 60 return lane ? lane->cancel_request : CancelRequest::None;
61} 61}
62 62
63// ################################################################################################ 63// ################################################################################################
@@ -111,38 +111,36 @@ static void cancel_hook(lua_State* L, [[maybe_unused]] lua_Debug* ar)
111 111
112// ################################################################################################ 112// ################################################################################################
113 113
114static CancelResult thread_cancel_soft(Lane* s, double secs_, bool wake_lindas_) 114static CancelResult thread_cancel_soft(Lane* lane_, double secs_, bool wake_lindas_)
115{ 115{
116 s->cancel_request = CancelRequest::Soft; // it's now signaled to stop 116 lane_->cancel_request = CancelRequest::Soft; // it's now signaled to stop
117 // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own 117 // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own
118 if( wake_lindas_) // wake the thread so that execution returns from any pending linda operation if desired 118 if (wake_lindas_) // wake the thread so that execution returns from any pending linda operation if desired
119 { 119 {
120 SIGNAL_T* waiting_on = s->waiting_on; 120 SIGNAL_T* const waiting_on{ lane_->waiting_on };
121 if( s->status == WAITING && waiting_on != nullptr) 121 if (lane_->status == WAITING && waiting_on != nullptr)
122 { 122 {
123 SIGNAL_ALL( waiting_on); 123 SIGNAL_ALL( waiting_on);
124 } 124 }
125 } 125 }
126 126
127 return THREAD_WAIT(&s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CancelResult::Cancelled : CancelResult::Timeout; 127 return THREAD_WAIT(&lane_->thread, secs_, &lane_->done_signal, &lane_->done_lock, &lane_->status) ? CancelResult::Cancelled : CancelResult::Timeout;
128} 128}
129 129
130// ################################################################################################ 130// ################################################################################################
131 131
132static CancelResult thread_cancel_hard(lua_State* L, Lane* s, double secs_, bool force_, double waitkill_timeout_) 132static CancelResult thread_cancel_hard(lua_State* L, Lane* lane_, double secs_, bool force_, double waitkill_timeout_)
133{ 133{
134 CancelResult result; 134 lane_->cancel_request = CancelRequest::Hard; // it's now signaled to stop
135
136 s->cancel_request = CancelRequest::Hard; // it's now signaled to stop
137 { 135 {
138 SIGNAL_T* waiting_on = s->waiting_on; 136 SIGNAL_T* waiting_on = lane_->waiting_on;
139 if( s->status == WAITING && waiting_on != nullptr) 137 if (lane_->status == WAITING && waiting_on != nullptr)
140 { 138 {
141 SIGNAL_ALL( waiting_on); 139 SIGNAL_ALL( waiting_on);
142 } 140 }
143 } 141 }
144 142
145 result = THREAD_WAIT(&s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CancelResult::Cancelled : CancelResult::Timeout; 143 CancelResult result{ THREAD_WAIT(&lane_->thread, secs_, &lane_->done_signal, &lane_->done_lock, &lane_->status) ? CancelResult::Cancelled : CancelResult::Timeout };
146 144
147 if ((result == CancelResult::Timeout) && force_) 145 if ((result == CancelResult::Timeout) && force_)
148 { 146 {
@@ -151,11 +149,11 @@ static CancelResult thread_cancel_hard(lua_State* L, Lane* s, double secs_, bool
151 // would be use of "cancellation cleanup handlers" that at least 149 // would be use of "cancellation cleanup handlers" that at least
152 // PThread seems to have). 150 // PThread seems to have).
153 // 151 //
154 THREAD_KILL( &s->thread); 152 THREAD_KILL(&lane_->thread);
155#if THREADAPI == THREADAPI_PTHREAD 153#if THREADAPI == THREADAPI_PTHREAD
156 // pthread: make sure the thread is really stopped! 154 // pthread: make sure the thread is really stopped!
157 // note that this may block forever if the lane doesn't call a cancellation point and pthread doesn't honor PTHREAD_CANCEL_ASYNCHRONOUS 155 // note that this may block forever if the lane doesn't call a cancellation point and pthread doesn't honor PTHREAD_CANCEL_ASYNCHRONOUS
158 result = THREAD_WAIT(&s->thread, waitkill_timeout_, &s->done_signal, &s->done_lock, &s->status) ? CancelResult::Killed : CancelResult::Timeout; 156 result = THREAD_WAIT(&lane_->thread, waitkill_timeout_, &lane_->done_signal, &lane_->done_lock, &lane_->status) ? CancelResult::Killed : CancelResult::Timeout;
159 if (result == CancelResult::Timeout) 157 if (result == CancelResult::Timeout)
160 { 158 {
161 std::ignore = luaL_error( L, "force-killed lane failed to terminate within %f second%s", waitkill_timeout_, waitkill_timeout_ > 1 ? "s" : ""); 159 std::ignore = luaL_error( L, "force-killed lane failed to terminate within %f second%s", waitkill_timeout_, waitkill_timeout_ > 1 ? "s" : "");
@@ -164,8 +162,8 @@ static CancelResult thread_cancel_hard(lua_State* L, Lane* s, double secs_, bool
164 (void) waitkill_timeout_; // unused 162 (void) waitkill_timeout_; // unused
165 (void) L; // unused 163 (void) L; // unused
166#endif // THREADAPI == THREADAPI_PTHREAD 164#endif // THREADAPI == THREADAPI_PTHREAD
167 s->mstatus = ThreadStatus::Killed; // mark 'gc' to wait for it 165 lane_->mstatus = ThreadStatus::Killed; // mark 'gc' to wait for it
168 // note that s->status value must remain to whatever it was at the time of the kill 166 // note that lane_->status value must remain to whatever it was at the time of the kill
169 // because we need to know if we can lua_close() the Lua State or not. 167 // because we need to know if we can lua_close() the Lua State or not.
170 result = CancelResult::Killed; 168 result = CancelResult::Killed;
171 } 169 }
@@ -174,16 +172,16 @@ static CancelResult thread_cancel_hard(lua_State* L, Lane* s, double secs_, bool
174 172
175// ################################################################################################ 173// ################################################################################################
176 174
177CancelResult thread_cancel(lua_State* L, Lane* s, CancelOp op_, double secs_, bool force_, double waitkill_timeout_) 175CancelResult thread_cancel(lua_State* L, Lane* lane_, CancelOp op_, double secs_, bool force_, double waitkill_timeout_)
178{ 176{
179 // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here 177 // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here
180 // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) 178 // We can read 'lane_->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN)
181 if (s->mstatus == ThreadStatus::Killed) 179 if (lane_->mstatus == ThreadStatus::Killed)
182 { 180 {
183 return CancelResult::Killed; 181 return CancelResult::Killed;
184 } 182 }
185 183
186 if( s->status >= DONE) 184 if (lane_->status >= DONE)
187 { 185 {
188 // say "ok" by default, including when lane is already done 186 // say "ok" by default, including when lane is already done
189 return CancelResult::Cancelled; 187 return CancelResult::Cancelled;
@@ -191,12 +189,12 @@ CancelResult thread_cancel(lua_State* L, Lane* s, CancelOp op_, double secs_, bo
191 189
192 // signal the linda the wake up the thread so that it can react to the cancel query 190 // signal the linda the wake up the thread so that it can react to the cancel query
193 // let us hope we never land here with a pointer on a linda that has been destroyed... 191 // let us hope we never land here with a pointer on a linda that has been destroyed...
194 if( op_ == CO_Soft) 192 if (op_ == CO_Soft)
195 { 193 {
196 return thread_cancel_soft( s, secs_, force_); 194 return thread_cancel_soft(lane_, secs_, force_);
197 } 195 }
198 196
199 return thread_cancel_hard( L, s, secs_, force_, waitkill_timeout_); 197 return thread_cancel_hard(L, lane_, secs_, force_, waitkill_timeout_);
200} 198}
201 199
202// ################################################################################################ 200// ################################################################################################
@@ -247,54 +245,51 @@ static CancelOp which_op( lua_State* L, int idx_)
247// ################################################################################################ 245// ################################################################################################
248 246
249// bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, force [, forcekill_timeout]]) 247// bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, force [, forcekill_timeout]])
250LUAG_FUNC( thread_cancel) 248LUAG_FUNC(thread_cancel)
251{ 249{
252 Lane* s = lua_toLane( L, 1); 250 Lane* const lane{ lua_toLane(L, 1) };
253 double secs = 0.0; 251 CancelOp const op{ which_op(L, 2) }; // this removes the op string from the stack
254 CancelOp op = which_op( L, 2); // this removes the op string from the stack
255 252
256 if( op > 0) // hook is requested 253 if (op > 0) // hook is requested
257 { 254 {
258 int hook_count = (int) lua_tointeger( L, 2); 255 int hook_count = (int) lua_tointeger(L, 2);
259 lua_remove( L, 2); // argument is processed, remove it 256 lua_remove(L, 2); // argument is processed, remove it
260 if( hook_count < 1) 257 if (hook_count < 1)
261 { 258 {
262 return luaL_error( L, "hook count cannot be < 1"); 259 return luaL_error(L, "hook count cannot be < 1");
263 } 260 }
264 lua_sethook( s->L, cancel_hook, op, hook_count); 261 lua_sethook(lane->L, cancel_hook, op, hook_count);
265 } 262 }
266 263
267 if( lua_type( L, 2) == LUA_TNUMBER) 264 double secs{ 0.0 };
265 if (lua_type(L, 2) == LUA_TNUMBER)
268 { 266 {
269 secs = lua_tonumber( L, 2); 267 secs = lua_tonumber(L, 2);
270 lua_remove( L, 2); // argument is processed, remove it 268 lua_remove(L, 2); // argument is processed, remove it
271 if( secs < 0.0) 269 if (secs < 0.0)
272 { 270 {
273 return luaL_error( L, "cancel timeout cannot be < 0"); 271 return luaL_error(L, "cancel timeout cannot be < 0");
274 } 272 }
275 } 273 }
276 274
275 bool const force{ lua_toboolean(L, 2) ? true : false }; // false if nothing there
276 double const forcekill_timeout{ luaL_optnumber(L, 3, 0.0) };
277 switch (thread_cancel(L, lane, op, secs, force, forcekill_timeout))
277 { 278 {
278 bool const force = lua_toboolean( L, 2) ? true : false; // false if nothing there 279 case CancelResult::Timeout:
279 double forcekill_timeout = luaL_optnumber( L, 3, 0.0); 280 lua_pushboolean(L, 0);
280 281 lua_pushstring(L, "timeout");
281 switch( thread_cancel( L, s, op, secs, force, forcekill_timeout)) 282 return 2;
282 { 283
283 case CancelResult::Timeout: 284 case CancelResult::Cancelled:
284 lua_pushboolean( L, 0); 285 lua_pushboolean(L, 1);
285 lua_pushstring( L, "timeout"); 286 push_thread_status(L, lane);
286 return 2; 287 return 2;
287 288
288 case CancelResult::Cancelled: 289 case CancelResult::Killed:
289 lua_pushboolean( L, 1); 290 lua_pushboolean(L, 1);
290 push_thread_status( L, s); 291 push_thread_status(L, lane);
291 return 2; 292 return 2;
292
293 case CancelResult::Killed:
294 lua_pushboolean( L, 1);
295 push_thread_status( L, s);
296 return 2;
297 }
298 } 293 }
299 // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value" 294 // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value"
300 return 0; 295 return 0;
diff --git a/src/cancel.h b/src/cancel.h
index 9299ae1..9cd7d5b 100644
--- a/src/cancel.h
+++ b/src/cancel.h
@@ -51,7 +51,7 @@ static constexpr UniqueKey CANCEL_ERROR{ 0xe97d41626cc97577ull }; // 'raise_canc
51// crc64/we of string "CANCEL_TEST_KEY" generated at http://www.nitrxgen.net/hashgen/ 51// crc64/we of string "CANCEL_TEST_KEY" generated at http://www.nitrxgen.net/hashgen/
52static constexpr UniqueKey CANCEL_TEST_KEY{ 0xe66f5960c57d133aull }; // used as registry key 52static constexpr UniqueKey CANCEL_TEST_KEY{ 0xe66f5960c57d133aull }; // used as registry key
53 53
54CancelResult thread_cancel(lua_State* L, Lane* s, CancelOp op_, double secs_, bool force_, double waitkill_timeout_); 54CancelResult thread_cancel(lua_State* L, Lane* lane_, CancelOp op_, double secs_, bool force_, double waitkill_timeout_);
55 55
56[[noreturn]] static inline void raise_cancel_error(lua_State* L) 56[[noreturn]] static inline void raise_cancel_error(lua_State* L)
57{ 57{
@@ -63,7 +63,7 @@ CancelResult thread_cancel(lua_State* L, Lane* s, CancelOp op_, double secs_, bo
63// ################################################################################################ 63// ################################################################################################
64// ################################################################################################ 64// ################################################################################################
65 65
66LUAG_FUNC( cancel_test); 66LUAG_FUNC(cancel_test);
67LUAG_FUNC( thread_cancel); 67LUAG_FUNC(thread_cancel);
68 68
69// ################################################################################################ 69// ################################################################################################
diff --git a/src/compat.h b/src/compat.h
index 46c31cf..8ef1b6c 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -95,5 +95,6 @@ int lua_setiuservalue( lua_State* L, int idx, int n);
95#define luaG_registerlibfuncs( L, _funcs) luaL_setfuncs( L, _funcs, 0) 95#define luaG_registerlibfuncs( L, _funcs) luaL_setfuncs( L, _funcs, 0)
96#define lua504_dump lua_dump 96#define lua504_dump lua_dump
97#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) 97#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
98#define LUA_ERRGCMM 666 // doesn't exist in Lua 5.4, we don't care about the actual value
98 99
99#endif // LUA_VERSION_NUM == 504 100#endif // LUA_VERSION_NUM == 504
diff --git a/src/keeper.cpp b/src/keeper.cpp
index 9c41b1c..f88d0ab 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -64,7 +64,7 @@ class keeper_fifo
64 static void* operator new(size_t size_, lua_State* L) noexcept { return lua_newuserdatauv<keeper_fifo>(L, 1); } 64 static void* operator new(size_t size_, lua_State* L) noexcept { return lua_newuserdatauv<keeper_fifo>(L, 1); }
65 // always embedded somewhere else or "in-place constructed" as a full userdata 65 // always embedded somewhere else or "in-place constructed" as a full userdata
66 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception 66 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
67 static void operator delete(void* p_, lua_State* L){ ASSERT_L(!"should never be called") }; 67 static void operator delete(void* p_, lua_State* L) { ASSERT_L(!"should never be called") };
68 68
69 static keeper_fifo* getPtr(lua_State* L, int idx_) 69 static keeper_fifo* getPtr(lua_State* L, int idx_)
70 { 70 {
diff --git a/src/lanes.cpp b/src/lanes.cpp
index 2f17e6e..86972f7 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -111,18 +111,18 @@ THE SOFTWARE.
111#define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it! 111#define ERROR_FULL_STACK 1 // must be either 0 or 1 as we do some index arithmetics with it!
112 112
113// intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed 113// intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed
114static void securize_debug_threadname( lua_State* L, Lane* s) 114static void securize_debug_threadname(lua_State* L, Lane* lane_)
115{ 115{
116 STACK_CHECK_START_REL(L, 0); 116 STACK_CHECK_START_REL(L, 0);
117 STACK_GROW( L, 3); 117 STACK_GROW(L, 3);
118 lua_getiuservalue( L, 1, 1); 118 lua_getiuservalue(L, 1, 1);
119 lua_newtable( L); 119 lua_newtable(L);
120 // Lua 5.1 can't do 's->debug_name = lua_pushstring( L, s->debug_name);' 120 // Lua 5.1 can't do 'lane_->debug_name = lua_pushstring(L, lane_->debug_name);'
121 lua_pushstring( L, s->debug_name); 121 lua_pushstring(L, lane_->debug_name);
122 s->debug_name = lua_tostring( L, -1); 122 lane_->debug_name = lua_tostring(L, -1);
123 lua_rawset( L, -3); 123 lua_rawset(L, -3);
124 lua_pop( L, 1); 124 lua_pop(L, 1);
125 STACK_CHECK( L, 0); 125 STACK_CHECK(L, 0);
126} 126}
127 127
128#if ERROR_FULL_STACK 128#if ERROR_FULL_STACK
@@ -142,6 +142,8 @@ static constexpr UniqueKey STACKTRACE_REGKEY{ 0x534af7d3226a429full };
142// crc64/we of string "FINALIZER_REGKEY" generated at http://www.nitrxgen.net/hashgen/ 142// crc64/we of string "FINALIZER_REGKEY" generated at http://www.nitrxgen.net/hashgen/
143static constexpr UniqueKey FINALIZER_REGKEY{ 0x188fccb8bf348e09ull }; 143static constexpr UniqueKey FINALIZER_REGKEY{ 0x188fccb8bf348e09ull };
144 144
145// #################################################################################################
146
145/* 147/*
146* Push a table stored in registry onto Lua stack. 148* Push a table stored in registry onto Lua stack.
147* 149*
@@ -152,16 +154,16 @@ static constexpr UniqueKey FINALIZER_REGKEY{ 0x188fccb8bf348e09ull };
152*/ 154*/
153static bool push_registry_table( lua_State* L, UniqueKey key, bool create) 155static bool push_registry_table( lua_State* L, UniqueKey key, bool create)
154{ 156{
155 STACK_GROW( L, 3); 157 STACK_GROW(L, 3);
156 STACK_CHECK_START_REL(L, 0); 158 STACK_CHECK_START_REL(L, 0);
157 159
158 key.query_registry(L); // ? 160 key.query_registry(L); // ?
159 if( lua_isnil( L, -1)) // nil? 161 if (lua_isnil(L, -1)) // nil?
160 { 162 {
161 lua_pop( L, 1); // 163 lua_pop(L, 1); //
162 STACK_CHECK(L, 0); 164 STACK_CHECK(L, 0);
163 165
164 if( !create) 166 if (!create)
165 { 167 {
166 return false; 168 return false;
167 } 169 }
@@ -169,10 +171,12 @@ static bool push_registry_table( lua_State* L, UniqueKey key, bool create)
169 lua_newtable( L); // t 171 lua_newtable( L); // t
170 key.set_registry(L, [](lua_State* L) { lua_pushvalue(L, -2); }); 172 key.set_registry(L, [](lua_State* L) { lua_pushvalue(L, -2); });
171 } 173 }
172 STACK_CHECK( L, 1); 174 STACK_CHECK(L, 1);
173 return true; // table pushed 175 return true; // table pushed
174} 176}
175 177
178// #################################################################################################
179
176#if HAVE_LANE_TRACKING() 180#if HAVE_LANE_TRACKING()
177 181
178// The chain is ended by '(Lane*)(-1)', not nullptr: 182// The chain is ended by '(Lane*)(-1)', not nullptr:
@@ -183,76 +187,73 @@ static bool push_registry_table( lua_State* L, UniqueKey key, bool create)
183 * Add the lane to tracking chain; the ones still running at the end of the 187 * Add the lane to tracking chain; the ones still running at the end of the
184 * whole process will be cancelled. 188 * whole process will be cancelled.
185 */ 189 */
186static void tracking_add(Lane* s) 190static void tracking_add(Lane* lane_)
187{ 191{
192 std::lock_guard<std::mutex> guard{ lane_->U->tracking_cs };
193 assert(lane_->tracking_next == nullptr);
188 194
189 s->U->tracking_cs.lock(); 195 lane_->tracking_next = lane_->U->tracking_first;
190 { 196 lane_->U->tracking_first = lane_;
191 assert( s->tracking_next == nullptr);
192
193 s->tracking_next = s->U->tracking_first;
194 s->U->tracking_first = s;
195 }
196 s->U->tracking_cs.unlock();
197} 197}
198 198
199// #################################################################################################
200
199/* 201/*
200 * A free-running lane has ended; remove it from tracking chain 202 * A free-running lane has ended; remove it from tracking chain
201 */ 203 */
202static bool tracking_remove(Lane* s) 204static bool tracking_remove(Lane* lane_)
203{ 205{
204 bool found{ false }; 206 bool found{ false };
205 s->U->tracking_cs.lock(); 207 std::lock_guard<std::mutex> guard{ lane_->U->tracking_cs };
208 // Make sure (within the MUTEX) that we actually are in the chain
209 // still (at process exit they will remove us from chain and then
210 // cancel/kill).
211 //
212 if (lane_->tracking_next != nullptr)
206 { 213 {
207 // Make sure (within the MUTEX) that we actually are in the chain 214 Lane** ref = (Lane**) &lane_->U->tracking_first;
208 // still (at process exit they will remove us from chain and then
209 // cancel/kill).
210 //
211 if( s->tracking_next != nullptr)
212 {
213 Lane** ref = (Lane**) &s->U->tracking_first;
214 215
215 while( *ref != TRACKING_END) 216 while( *ref != TRACKING_END)
217 {
218 if (*ref == lane_)
216 { 219 {
217 if( *ref == s) 220 *ref = lane_->tracking_next;
218 { 221 lane_->tracking_next = nullptr;
219 *ref = s->tracking_next; 222 found = true;
220 s->tracking_next = nullptr; 223 break;
221 found = true;
222 break;
223 }
224 ref = (Lane**) &((*ref)->tracking_next);
225 } 224 }
226 assert( found); 225 ref = (Lane**) &((*ref)->tracking_next);
227 } 226 }
227 assert( found);
228 } 228 }
229 s->U->tracking_cs.unlock();
230 return found; 229 return found;
231} 230}
232 231
233#endif // HAVE_LANE_TRACKING() 232#endif // HAVE_LANE_TRACKING()
234 233
234// #################################################################################################
235
235//--- 236//---
236// low-level cleanup 237// low-level cleanup
237 238
238static void lane_cleanup( Lane* s) 239static void lane_cleanup(Lane* lane_)
239{ 240{
240 // Clean up after a (finished) thread 241 // Clean up after a (finished) thread
241 // 242 //
242#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 243#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
243 SIGNAL_FREE( &s->done_signal); 244 SIGNAL_FREE(&lane_->done_signal);
244 MUTEX_FREE( &s->done_lock); 245 MUTEX_FREE(&lane_->done_lock);
245#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 246#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
246 247
247#if HAVE_LANE_TRACKING() 248#if HAVE_LANE_TRACKING()
248 if( s->U->tracking_first != nullptr) 249 if (lane_->U->tracking_first != nullptr)
249 { 250 {
250 // Lane was cleaned up, no need to handle at process termination 251 // Lane was cleaned up, no need to handle at process termination
251 tracking_remove( s); 252 tracking_remove(lane_);
252 } 253 }
253#endif // HAVE_LANE_TRACKING() 254#endif // HAVE_LANE_TRACKING()
254 255
255 s->U->internal_allocator.free(s, sizeof(Lane)); 256 lane_->U->internal_allocator.free(lane_, sizeof(Lane));
256} 257}
257 258
258/* 259/*
@@ -271,15 +272,15 @@ static void lane_cleanup( Lane* s)
271// 272//
272LUAG_FUNC( set_finalizer) 273LUAG_FUNC( set_finalizer)
273{ 274{
274 luaL_argcheck( L, lua_isfunction( L, 1), 1, "finalizer should be a function"); 275 luaL_argcheck(L, lua_isfunction(L, 1), 1, "finalizer should be a function");
275 luaL_argcheck( L, lua_gettop( L) == 1, 1, "too many arguments"); 276 luaL_argcheck(L, lua_gettop( L) == 1, 1, "too many arguments");
276 // Get the current finalizer table (if any) 277 // Get the current finalizer table (if any)
277 push_registry_table( L, FINALIZER_REGKEY, true /*do create if none*/); // finalizer {finalisers} 278 push_registry_table(L, FINALIZER_REGKEY, true /*do create if none*/); // finalizer {finalisers}
278 STACK_GROW( L, 2); 279 STACK_GROW(L, 2);
279 lua_pushinteger( L, lua_rawlen( L, -1) + 1); // finalizer {finalisers} idx 280 lua_pushinteger(L, lua_rawlen(L, -1) + 1); // finalizer {finalisers} idx
280 lua_pushvalue( L, 1); // finalizer {finalisers} idx finalizer 281 lua_pushvalue(L, 1); // finalizer {finalisers} idx finalizer
281 lua_rawset( L, -3); // finalizer {finalisers} 282 lua_rawset(L, -3); // finalizer {finalisers}
282 lua_pop( L, 2); // 283 lua_pop(L, 2); //
283 return 0; 284 return 0;
284} 285}
285 286
@@ -305,44 +306,44 @@ static int run_finalizers( lua_State* L, int lua_rc)
305 int n; 306 int n;
306 int err_handler_index = 0; 307 int err_handler_index = 0;
307 int rc = LUA_OK; // ... 308 int rc = LUA_OK; // ...
308 if( !push_registry_table( L, FINALIZER_REGKEY, false)) // ... finalizers? 309 if (!push_registry_table(L, FINALIZER_REGKEY, false)) // ... finalizers?
309 { 310 {
310 return 0; // no finalizers 311 return 0; // no finalizers
311 } 312 }
312 313
313 STACK_GROW( L, 5); 314 STACK_GROW(L, 5);
314 315
315 finalizers_index = lua_gettop( L); 316 finalizers_index = lua_gettop( L);
316 317
317#if ERROR_FULL_STACK 318#if ERROR_FULL_STACK
318 lua_pushcfunction( L, lane_error); // ... finalizers lane_error 319 lua_pushcfunction(L, lane_error); // ... finalizers lane_error
319 err_handler_index = lua_gettop( L); 320 err_handler_index = lua_gettop( L);
320#endif // ERROR_FULL_STACK 321#endif // ERROR_FULL_STACK
321 322
322 for( n = (int) lua_rawlen( L, finalizers_index); n > 0; -- n) 323 for( n = (int) lua_rawlen(L, finalizers_index); n > 0; -- n)
323 { 324 {
324 int args = 0; 325 int args = 0;
325 lua_pushinteger( L, n); // ... finalizers lane_error n 326 lua_pushinteger(L, n); // ... finalizers lane_error n
326 lua_rawget( L, finalizers_index); // ... finalizers lane_error finalizer 327 lua_rawget(L, finalizers_index); // ... finalizers lane_error finalizer
327 ASSERT_L( lua_isfunction( L, -1)); 328 ASSERT_L( lua_isfunction(L, -1));
328 if( lua_rc != LUA_OK) // we have an error message and an optional stack trace at the bottom of the stack 329 if (lua_rc != LUA_OK) // we have an error message and an optional stack trace at the bottom of the stack
329 { 330 {
330 ASSERT_L( finalizers_index == 2 || finalizers_index == 3); 331 ASSERT_L( finalizers_index == 2 || finalizers_index == 3);
331 //char const* err_msg = lua_tostring( L, 1); 332 //char const* err_msg = lua_tostring(L, 1);
332 lua_pushvalue( L, 1); // ... finalizers lane_error finalizer err_msg 333 lua_pushvalue(L, 1); // ... finalizers lane_error finalizer err_msg
333 // note we don't always have a stack trace for example when CANCEL_ERROR, or when we got an error that doesn't call our handler, such as LUA_ERRMEM 334 // note we don't always have a stack trace for example when CANCEL_ERROR, or when we got an error that doesn't call our handler, such as LUA_ERRMEM
334 if( finalizers_index == 3) 335 if (finalizers_index == 3)
335 { 336 {
336 lua_pushvalue( L, 2); // ... finalizers lane_error finalizer err_msg stack_trace 337 lua_pushvalue(L, 2); // ... finalizers lane_error finalizer err_msg stack_trace
337 } 338 }
338 args = finalizers_index - 1; 339 args = finalizers_index - 1;
339 } 340 }
340 341
341 // if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace 342 // if no error from the main body, finalizer doesn't receive any argument, else it gets the error message and optional stack trace
342 rc = lua_pcall( L, args, 0, err_handler_index); // ... finalizers lane_error err_msg2? 343 rc = lua_pcall(L, args, 0, err_handler_index); // ... finalizers lane_error err_msg2?
343 if( rc != LUA_OK) 344 if (rc != LUA_OK)
344 { 345 {
345 push_stack_trace( L, rc, lua_gettop( L)); 346 push_stack_trace(L, rc, lua_gettop( L));
346 // If one finalizer fails, don't run the others. Return this 347 // If one finalizer fails, don't run the others. Return this
347 // as the 'real' error, replacing what we could have had (or not) 348 // as the 'real' error, replacing what we could have had (or not)
348 // from the actual code. 349 // from the actual code.
@@ -351,21 +352,21 @@ static int run_finalizers( lua_State* L, int lua_rc)
351 // no error, proceed to next finalizer // ... finalizers lane_error 352 // no error, proceed to next finalizer // ... finalizers lane_error
352 } 353 }
353 354
354 if( rc != LUA_OK) 355 if (rc != LUA_OK)
355 { 356 {
356 // ERROR_FULL_STACK accounts for the presence of lane_error on the stack 357 // ERROR_FULL_STACK accounts for the presence of lane_error on the stack
357 int nb_err_slots = lua_gettop( L) - finalizers_index - ERROR_FULL_STACK; 358 int nb_err_slots = lua_gettop( L) - finalizers_index - ERROR_FULL_STACK;
358 // a finalizer generated an error, this is what we leave of the stack 359 // a finalizer generated an error, this is what we leave of the stack
359 for( n = nb_err_slots; n > 0; -- n) 360 for( n = nb_err_slots; n > 0; -- n)
360 { 361 {
361 lua_replace( L, n); 362 lua_replace(L, n);
362 } 363 }
363 // leave on the stack only the error and optional stack trace produced by the error in the finalizer 364 // leave on the stack only the error and optional stack trace produced by the error in the finalizer
364 lua_settop( L, nb_err_slots); 365 lua_settop(L, nb_err_slots);
365 } 366 }
366 else // no error from the finalizers, make sure only the original return values from the lane body remain on the stack 367 else // no error from the finalizers, make sure only the original return values from the lane body remain on the stack
367 { 368 {
368 lua_settop( L, finalizers_index - 1); 369 lua_settop(L, finalizers_index - 1);
369 } 370 }
370 371
371 return rc; 372 return rc;
@@ -389,49 +390,45 @@ static int run_finalizers( lua_State* L, int lua_rc)
389 * Add the lane to selfdestruct chain; the ones still running at the end of the 390 * Add the lane to selfdestruct chain; the ones still running at the end of the
390 * whole process will be cancelled. 391 * whole process will be cancelled.
391 */ 392 */
392static void selfdestruct_add( Lane* s) 393static void selfdestruct_add(Lane* lane_)
393{ 394{
394 s->U->selfdestruct_cs.lock(); 395 std::lock_guard<std::mutex> guard{ lane_->U->selfdestruct_cs };
395 assert( s->selfdestruct_next == nullptr); 396 assert(lane_->selfdestruct_next == nullptr);
396 397
397 s->selfdestruct_next = s->U->selfdestruct_first; 398 lane_->selfdestruct_next = lane_->U->selfdestruct_first;
398 s->U->selfdestruct_first= s; 399 lane_->U->selfdestruct_first = lane_;
399 s->U->selfdestruct_cs.unlock();
400} 400}
401 401
402/* 402/*
403 * A free-running lane has ended; remove it from selfdestruct chain 403 * A free-running lane has ended; remove it from selfdestruct chain
404 */ 404 */
405static bool selfdestruct_remove( Lane* s) 405static bool selfdestruct_remove(Lane* lane_)
406{ 406{
407 bool found{ false }; 407 bool found{ false };
408 s->U->selfdestruct_cs.lock(); 408 std::lock_guard<std::mutex> guard{ lane_->U->selfdestruct_cs };
409 // Make sure (within the MUTEX) that we actually are in the chain
410 // still (at process exit they will remove us from chain and then
411 // cancel/kill).
412 //
413 if (lane_->selfdestruct_next != nullptr)
409 { 414 {
410 // Make sure (within the MUTEX) that we actually are in the chain 415 Lane** ref = (Lane**) &lane_->U->selfdestruct_first;
411 // still (at process exit they will remove us from chain and then
412 // cancel/kill).
413 //
414 if( s->selfdestruct_next != nullptr)
415 {
416 Lane** ref = (Lane**) &s->U->selfdestruct_first;
417 416
418 while( *ref != SELFDESTRUCT_END ) 417 while (*ref != SELFDESTRUCT_END)
418 {
419 if (*ref == lane_)
419 { 420 {
420 if( *ref == s) 421 *ref = lane_->selfdestruct_next;
421 { 422 lane_->selfdestruct_next = nullptr;
422 *ref = s->selfdestruct_next; 423 // the terminal shutdown should wait until the lane is done with its lua_close()
423 s->selfdestruct_next = nullptr; 424 ++lane_->U->selfdestructing_count;
424 // the terminal shutdown should wait until the lane is done with its lua_close() 425 found = true;
425 ++ s->U->selfdestructing_count; 426 break;
426 found = true;
427 break;
428 }
429 ref = (Lane**) &((*ref)->selfdestruct_next);
430 } 427 }
431 assert( found); 428 ref = (Lane**) &((*ref)->selfdestruct_next);
432 } 429 }
430 assert(found);
433 } 431 }
434 s->U->selfdestruct_cs.unlock();
435 return found; 432 return found;
436} 433}
437 434
@@ -442,30 +439,29 @@ static int selfdestruct_gc( lua_State* L)
442{ 439{
443 Universe* const U{ lua_tofulluserdata<Universe>(L, 1) }; 440 Universe* const U{ lua_tofulluserdata<Universe>(L, 1) };
444 441
445 while( U->selfdestruct_first != SELFDESTRUCT_END) // true at most once! 442 while (U->selfdestruct_first != SELFDESTRUCT_END) // true at most once!
446 { 443 {
447 // Signal _all_ still running threads to exit (including the timer thread) 444 // Signal _all_ still running threads to exit (including the timer thread)
448 // 445 //
449 U->selfdestruct_cs.lock();
450 { 446 {
451 Lane* s = U->selfdestruct_first; 447 std::lock_guard<std::mutex> guard{ U->selfdestruct_cs };
452 while( s != SELFDESTRUCT_END) 448 Lane* lane{ U->selfdestruct_first };
449 while (lane != SELFDESTRUCT_END)
453 { 450 {
454 // attempt a regular unforced hard cancel with a small timeout 451 // attempt a regular unforced hard cancel with a small timeout
455 bool const cancelled = THREAD_ISNULL( s->thread) || thread_cancel( L, s, CO_Hard, 0.0001, false, 0.0) != CancelResult::Timeout; 452 bool const cancelled = THREAD_ISNULL(lane->thread) || thread_cancel(L, lane, CO_Hard, 0.0001, false, 0.0) != CancelResult::Timeout;
456 // if we failed, and we know the thread is waiting on a linda 453 // if we failed, and we know the thread is waiting on a linda
457 if( cancelled == false && s->status == WAITING && s->waiting_on != nullptr) 454 if (cancelled == false && lane->status == WAITING && lane->waiting_on != nullptr)
458 { 455 {
459 // signal the linda to wake up the thread so that it can react to the cancel query 456 // signal the linda to wake up the thread so that it can react to the cancel query
460 // let us hope we never land here with a pointer on a linda that has been destroyed... 457 // let us hope we never land here with a pointer on a linda that has been destroyed...
461 SIGNAL_T* waiting_on = s->waiting_on; 458 SIGNAL_T* const waiting_on{ lane->waiting_on };
462 //s->waiting_on = nullptr; // useful, or not? 459 // lane->waiting_on = nullptr; // useful, or not?
463 SIGNAL_ALL( waiting_on); 460 SIGNAL_ALL(waiting_on);
464 } 461 }
465 s = s->selfdestruct_next; 462 lane = lane->selfdestruct_next;
466 } 463 }
467 } 464 }
468 U->selfdestruct_cs.unlock();
469 465
470 // When noticing their cancel, the lanes will remove themselves from 466 // When noticing their cancel, the lanes will remove themselves from
471 // the selfdestruct chain. 467 // the selfdestruct chain.
@@ -482,32 +478,31 @@ static int selfdestruct_gc( lua_State* L)
482 // -- AKa 25-Oct-2008 478 // -- AKa 25-Oct-2008
483 // 479 //
484 { 480 {
485 lua_Number const shutdown_timeout = lua_tonumber( L, lua_upvalueindex( 1)); 481 lua_Number const shutdown_timeout = lua_tonumber(L, lua_upvalueindex(1));
486 double const t_until = now_secs() + shutdown_timeout; 482 double const t_until = now_secs() + shutdown_timeout;
487 483
488 while( U->selfdestruct_first != SELFDESTRUCT_END) 484 while (U->selfdestruct_first != SELFDESTRUCT_END)
489 { 485 {
490 YIELD(); // give threads time to act on their cancel 486 YIELD(); // give threads time to act on their cancel
491 { 487 {
492 // count the number of cancelled thread that didn't have the time to act yet 488 // count the number of cancelled thread that didn't have the time to act yet
493 int n = 0; 489 int n = 0;
494 double t_now = 0.0; 490 double t_now = 0.0;
495 U->selfdestruct_cs.lock();
496 { 491 {
497 Lane* s = U->selfdestruct_first; 492 std::lock_guard<std::mutex> guard{ U->selfdestruct_cs };
498 while( s != SELFDESTRUCT_END) 493 Lane* lane{ U->selfdestruct_first };
494 while (lane != SELFDESTRUCT_END)
499 { 495 {
500 if (s->cancel_request == CancelRequest::Hard) 496 if (lane->cancel_request == CancelRequest::Hard)
501 ++ n; 497 ++n;
502 s = s->selfdestruct_next; 498 lane = lane->selfdestruct_next;
503 } 499 }
504 } 500 }
505 U->selfdestruct_cs.unlock();
506 // if timeout elapsed, or we know all threads have acted, stop waiting 501 // if timeout elapsed, or we know all threads have acted, stop waiting
507 t_now = now_secs(); 502 t_now = now_secs();
508 if( n == 0 || (t_now >= t_until)) 503 if (n == 0 || (t_now >= t_until))
509 { 504 {
510 DEBUGSPEW_CODE( fprintf( stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout - (t_until - t_now))); 505 DEBUGSPEW_CODE(fprintf(stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout - (t_until - t_now)));
511 break; 506 break;
512 } 507 }
513 } 508 }
@@ -516,7 +511,7 @@ static int selfdestruct_gc( lua_State* L)
516 511
517 // If some lanes are currently cleaning after themselves, wait until they are done. 512 // If some lanes are currently cleaning after themselves, wait until they are done.
518 // They are no longer listed in the selfdestruct chain, but they still have to lua_close(). 513 // They are no longer listed in the selfdestruct chain, but they still have to lua_close().
519 while( U->selfdestructing_count > 0) 514 while (U->selfdestructing_count > 0)
520 { 515 {
521 YIELD(); 516 YIELD();
522 } 517 }
@@ -524,37 +519,36 @@ static int selfdestruct_gc( lua_State* L)
524 //--- 519 //---
525 // Kill the still free running threads 520 // Kill the still free running threads
526 // 521 //
527 if( U->selfdestruct_first != SELFDESTRUCT_END) 522 if (U->selfdestruct_first != SELFDESTRUCT_END)
528 { 523 {
529 unsigned int n = 0; 524 unsigned int n = 0;
530 // first thing we did was to raise the linda signals the threads were waiting on (if any) 525 // first thing we did was to raise the linda signals the threads were waiting on (if any)
531 // therefore, any well-behaved thread should be in CANCELLED state 526 // therefore, any well-behaved thread should be in CANCELLED state
532 // these are not running, and the state can be closed 527 // these are not running, and the state can be closed
533 U->selfdestruct_cs.lock();
534 { 528 {
535 Lane* s = U->selfdestruct_first; 529 std::lock_guard<std::mutex> guard{ U->selfdestruct_cs };
536 while( s != SELFDESTRUCT_END) 530 Lane* lane{ U->selfdestruct_first };
531 while (lane != SELFDESTRUCT_END)
537 { 532 {
538 Lane* next_s = s->selfdestruct_next; 533 Lane* const next_s{ lane->selfdestruct_next };
539 s->selfdestruct_next = nullptr; // detach from selfdestruct chain 534 lane->selfdestruct_next = nullptr; // detach from selfdestruct chain
540 if( !THREAD_ISNULL( s->thread)) // can be nullptr if previous 'soft' termination succeeded 535 if (!THREAD_ISNULL(lane->thread)) // can be nullptr if previous 'soft' termination succeeded
541 { 536 {
542 THREAD_KILL( &s->thread); 537 THREAD_KILL(&lane->thread);
543#if THREADAPI == THREADAPI_PTHREAD 538#if THREADAPI == THREADAPI_PTHREAD
544 // pthread: make sure the thread is really stopped! 539 // pthread: make sure the thread is really stopped!
545 THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status); 540 THREAD_WAIT(&lane->thread, -1, &lane->done_signal, &lane->done_lock, &lane->status);
546#endif // THREADAPI == THREADAPI_PTHREAD 541#endif // THREADAPI == THREADAPI_PTHREAD
547 } 542 }
548 // NO lua_close() in this case because we don't know where execution of the state was interrupted 543 // NO lua_close() in this case because we don't know where execution of the state was interrupted
549 lane_cleanup( s); 544 lane_cleanup(lane);
550 s = next_s; 545 lane = next_s;
551 ++ n; 546 ++n;
552 } 547 }
553 U->selfdestruct_first = SELFDESTRUCT_END; 548 U->selfdestruct_first = SELFDESTRUCT_END;
554 } 549 }
555 U->selfdestruct_cs.unlock();
556 550
557 DEBUGSPEW_CODE( fprintf( stderr, "Killed %d lane(s) at process end.\n", n)); 551 DEBUGSPEW_CODE(fprintf(stderr, "Killed %d lane(s) at process end.\n", n));
558 } 552 }
559 } 553 }
560 554
@@ -566,9 +560,9 @@ static int selfdestruct_gc( lua_State* L)
566 } 560 }
567 561
568 // necessary so that calling free_deep_prelude doesn't crash because linda_id expects a linda lightuserdata at absolute slot 1 562 // necessary so that calling free_deep_prelude doesn't crash because linda_id expects a linda lightuserdata at absolute slot 1
569 lua_settop( L, 0); 563 lua_settop(L, 0);
570 // no need to mutex-protect this as all threads in the universe are gone at that point 564 // no need to mutex-protect this as all threads in the universe are gone at that point
571 if( U->timer_deep != nullptr) // test ins case some early internal error prevented Lanes from creating the deep timer 565 if (U->timer_deep != nullptr) // test ins case some early internal error prevented Lanes from creating the deep timer
572 { 566 {
573 [[maybe_unused]] int const prev_ref_count{ U->timer_deep->m_refcount.fetch_sub(1, std::memory_order_relaxed) }; 567 [[maybe_unused]] int const prev_ref_count{ U->timer_deep->m_refcount.fetch_sub(1, std::memory_order_relaxed) };
574 ASSERT_L(prev_ref_count == 1); // this should be the last reference 568 ASSERT_L(prev_ref_count == 1); // this should be the last reference
@@ -586,7 +580,7 @@ static int selfdestruct_gc( lua_State* L)
586 // universe is no longer available (nor necessary) 580 // universe is no longer available (nor necessary)
587 // we need to do this in case some deep userdata objects were created before Lanes was initialized, 581 // we need to do this in case some deep userdata objects were created before Lanes was initialized,
588 // as potentially they will be garbage collected after Lanes at application shutdown 582 // as potentially they will be garbage collected after Lanes at application shutdown
589 universe_store( L, nullptr); 583 universe_store(L, nullptr);
590 return 0; 584 return 0;
591} 585}
592 586
@@ -604,18 +598,18 @@ LUAG_FUNC( set_singlethreaded)
604 598
605#ifdef PLATFORM_OSX 599#ifdef PLATFORM_OSX
606#ifdef _UTILBINDTHREADTOCPU 600#ifdef _UTILBINDTHREADTOCPU
607 if( cores > 1) 601 if (cores > 1)
608 { 602 {
609 return luaL_error( L, "Limiting to N>1 cores not possible"); 603 return luaL_error(L, "Limiting to N>1 cores not possible");
610 } 604 }
611 // requires 'chudInitialize()' 605 // requires 'chudInitialize()'
612 utilBindThreadToCPU(0); // # of CPU to run on (we cannot limit to 2..N CPUs?) 606 utilBindThreadToCPU(0); // # of CPU to run on (we cannot limit to 2..N CPUs?)
613 return 0; 607 return 0;
614#else 608#else
615 return luaL_error( L, "Not available: compile with _UTILBINDTHREADTOCPU"); 609 return luaL_error(L, "Not available: compile with _UTILBINDTHREADTOCPU");
616#endif 610#endif
617#else 611#else
618 return luaL_error( L, "not implemented"); 612 return luaL_error(L, "not implemented");
619#endif 613#endif
620} 614}
621 615
@@ -643,20 +637,20 @@ static constexpr UniqueKey EXTENDED_STACKTRACE_REGKEY{ 0x2357c69a7c92c936ull };
643 637
644LUAG_FUNC( set_error_reporting) 638LUAG_FUNC( set_error_reporting)
645{ 639{
646 luaL_checktype( L, 1, LUA_TSTRING); 640 luaL_checktype(L, 1, LUA_TSTRING);
647 lua_pushliteral( L, "extended"); 641 lua_pushliteral(L, "extended");
648 int equal = lua_rawequal( L, -1, 1); 642 int equal = lua_rawequal(L, -1, 1);
649 lua_pop( L, 1); 643 lua_pop(L, 1);
650 if( equal) 644 if (equal)
651 { 645 {
652 goto done; 646 goto done;
653 } 647 }
654 lua_pushliteral( L, "basic"); 648 lua_pushliteral(L, "basic");
655 equal = !lua_rawequal( L, -1, 1); 649 equal = !lua_rawequal(L, -1, 1);
656 lua_pop( L, 1); 650 lua_pop(L, 1);
657 if( equal) 651 if (equal)
658 { 652 {
659 return luaL_error( L, "unsupported error reporting model"); 653 return luaL_error(L, "unsupported error reporting model");
660 } 654 }
661done: 655done:
662 EXTENDED_STACKTRACE_REGKEY.set_registry(L, [equal](lua_State* L) { lua_pushboolean(L, equal); }); 656 EXTENDED_STACKTRACE_REGKEY.set_registry(L, [equal](lua_State* L) { lua_pushboolean(L, equal); });
@@ -675,10 +669,10 @@ static int lane_error( lua_State* L)
675 return 1; // just pass on 669 return 1; // just pass on
676 } 670 }
677 671
678 STACK_GROW( L, 3); 672 STACK_GROW(L, 3);
679 EXTENDED_STACKTRACE_REGKEY.query_registry(L); // some_error basic|extended 673 EXTENDED_STACKTRACE_REGKEY.query_registry(L); // some_error basic|extended
680 bool const extended{ lua_toboolean(L, -1) ? true : false}; 674 bool const extended{ lua_toboolean(L, -1) ? true : false};
681 lua_pop( L, 1); // some_error 675 lua_pop(L, 1); // some_error
682 STACK_CHECK(L, 1); 676 STACK_CHECK(L, 1);
683 677
684 // Place stack trace at 'registry[STACKTRACE_REGKEY]' for the 'lua_pcall()' 678 // Place stack trace at 'registry[STACKTRACE_REGKEY]' for the 'lua_pcall()'
@@ -700,35 +694,35 @@ static int lane_error( lua_State* L)
700 lua_Debug ar; 694 lua_Debug ar;
701 for (int n = 1; lua_getstack(L, n, &ar); ++n) 695 for (int n = 1; lua_getstack(L, n, &ar); ++n)
702 { 696 {
703 lua_getinfo( L, extended ? "Sln" : "Sl", &ar); 697 lua_getinfo(L, extended ? "Sln" : "Sl", &ar);
704 if( extended) 698 if (extended)
705 { 699 {
706 lua_newtable( L); // some_error {} {} 700 lua_newtable( L); // some_error {} {}
707 701
708 lua_pushstring( L, ar.source); // some_error {} {} source 702 lua_pushstring(L, ar.source); // some_error {} {} source
709 lua_setfield( L, -2, "source"); // some_error {} {} 703 lua_setfield(L, -2, "source"); // some_error {} {}
710 704
711 lua_pushinteger( L, ar.currentline); // some_error {} {} currentline 705 lua_pushinteger(L, ar.currentline); // some_error {} {} currentline
712 lua_setfield( L, -2, "currentline"); // some_error {} {} 706 lua_setfield(L, -2, "currentline"); // some_error {} {}
713 707
714 lua_pushstring( L, ar.name); // some_error {} {} name 708 lua_pushstring(L, ar.name); // some_error {} {} name
715 lua_setfield( L, -2, "name"); // some_error {} {} 709 lua_setfield(L, -2, "name"); // some_error {} {}
716 710
717 lua_pushstring( L, ar.namewhat); // some_error {} {} namewhat 711 lua_pushstring(L, ar.namewhat); // some_error {} {} namewhat
718 lua_setfield( L, -2, "namewhat"); // some_error {} {} 712 lua_setfield(L, -2, "namewhat"); // some_error {} {}
719 713
720 lua_pushstring( L, ar.what); // some_error {} {} what 714 lua_pushstring(L, ar.what); // some_error {} {} what
721 lua_setfield( L, -2, "what"); // some_error {} {} 715 lua_setfield(L, -2, "what"); // some_error {} {}
722 } 716 }
723 else if( ar.currentline > 0) 717 else if (ar.currentline > 0)
724 { 718 {
725 lua_pushfstring( L, "%s:%d", ar.short_src, ar.currentline); // some_error {} "blah:blah" 719 lua_pushfstring(L, "%s:%d", ar.short_src, ar.currentline); // some_error {} "blah:blah"
726 } 720 }
727 else 721 else
728 { 722 {
729 lua_pushfstring( L, "%s:?", ar.short_src); // some_error {} "blah" 723 lua_pushfstring(L, "%s:?", ar.short_src); // some_error {} "blah"
730 } 724 }
731 lua_rawseti( L, -2, (lua_Integer) n); // some_error {} 725 lua_rawseti(L, -2, (lua_Integer) n); // some_error {}
732 } 726 }
733 727
734 // store the stack trace table in the registry 728 // store the stack trace table in the registry
@@ -752,10 +746,10 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_)
752 { 746 {
753 STACK_CHECK_START_REL(L, 0); 747 STACK_CHECK_START_REL(L, 0);
754 // fetch the call stack table from the registry where the handler stored it 748 // fetch the call stack table from the registry where the handler stored it
755 STACK_GROW( L, 1); 749 STACK_GROW(L, 1);
756 // yields nil if no stack was generated (in case of cancellation for example) 750 // yields nil if no stack was generated (in case of cancellation for example)
757 STACKTRACE_REGKEY.query_registry(L); // err trace|nil 751 STACKTRACE_REGKEY.query_registry(L); // err trace|nil
758 STACK_CHECK( L, 1); 752 STACK_CHECK(L, 1);
759 753
760 // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed 754 // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed
761 // For other errors, the message can be whatever was thrown, and we should have a stack trace table 755 // For other errors, the message can be whatever was thrown, and we should have a stack trace table
@@ -774,57 +768,65 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_)
774 } 768 }
775} 769}
776 770
777LUAG_FUNC( set_debug_threadname) 771// #################################################################################################
772
773LUAG_FUNC(set_debug_threadname)
778{ 774{
779 // fnv164 of string "debug_threadname" generated at https://www.pelock.com/products/hash-calculator 775 // fnv164 of string "debug_threadname" generated at https://www.pelock.com/products/hash-calculator
780 constexpr UniqueKey hidden_regkey{ 0x79C0669AAAE04440ull }; 776 constexpr UniqueKey hidden_regkey{ 0x79C0669AAAE04440ull };
781 // C s_lane structure is a light userdata upvalue 777 // C s_lane structure is a light userdata upvalue
782 Lane* const s{ lua_tolightuserdata<Lane>(L, lua_upvalueindex(1)) }; 778 Lane* const lane{ lua_tolightuserdata<Lane>(L, lua_upvalueindex(1)) };
783 luaL_checktype( L, -1, LUA_TSTRING); // "name" 779 luaL_checktype(L, -1, LUA_TSTRING); // "name"
784 lua_settop( L, 1); 780 lua_settop(L, 1);
785 STACK_CHECK_START_ABS( L, 1); 781 STACK_CHECK_START_ABS(L, 1);
786 // 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... 782 // 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...
787 hidden_regkey.set_registry(L, [](lua_State* L) { lua_pushvalue(L, -2); }); 783 hidden_regkey.set_registry(L, [](lua_State* L) { lua_pushvalue(L, -2); });
788 STACK_CHECK( L, 1); 784 STACK_CHECK(L, 1);
789 s->debug_name = lua_tostring( L, -1); 785 lane->debug_name = lua_tostring(L, -1);
790 // keep a direct pointer on the string 786 // keep a direct pointer on the string
791 THREAD_SETNAME( s->debug_name); 787 THREAD_SETNAME(lane->debug_name);
792 // to see VM name in Decoda debugger Virtual Machine window 788 // to see VM name in Decoda debugger Virtual Machine window
793 lua_setglobal( L, "decoda_name"); // 789 lua_setglobal(L, "decoda_name"); //
794 STACK_CHECK( L, 0); 790 STACK_CHECK(L, 0);
795 return 0; 791 return 0;
796} 792}
797 793
798LUAG_FUNC( get_debug_threadname) 794// #################################################################################################
795
796LUAG_FUNC(get_debug_threadname)
799{ 797{
800 Lane* const s = lua_toLane( L, 1); 798 Lane* const lane{ lua_toLane(L, 1) };
801 luaL_argcheck( L, lua_gettop( L) == 1, 2, "too many arguments"); 799 luaL_argcheck(L, lua_gettop(L) == 1, 2, "too many arguments");
802 lua_pushstring( L, s->debug_name); 800 lua_pushstring(L, lane->debug_name);
803 return 1; 801 return 1;
804} 802}
805 803
806LUAG_FUNC( set_thread_priority) 804// #################################################################################################
805
806LUAG_FUNC(set_thread_priority)
807{ 807{
808 int const prio = (int) luaL_checkinteger( L, 1); 808 int const prio{ (int) luaL_checkinteger(L, 1) };
809 // public Lanes API accepts a generic range -3/+3 809 // public Lanes API accepts a generic range -3/+3
810 // that will be remapped into the platform-specific scheduler priority scheme 810 // that will be remapped into the platform-specific scheduler priority scheme
811 // On some platforms, -3 is equivalent to -2 and +3 to +2 811 // On some platforms, -3 is equivalent to -2 and +3 to +2
812 if( prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) 812 if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX)
813 { 813 {
814 return luaL_error( L, "priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio); 814 return luaL_error(L, "priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio);
815 } 815 }
816 THREAD_SET_PRIORITY( prio); 816 THREAD_SET_PRIORITY(prio);
817 return 0; 817 return 0;
818} 818}
819 819
820LUAG_FUNC( set_thread_affinity) 820// #################################################################################################
821
822LUAG_FUNC(set_thread_affinity)
821{ 823{
822 lua_Integer affinity = luaL_checkinteger( L, 1); 824 lua_Integer const affinity{ luaL_checkinteger(L, 1) };
823 if( affinity <= 0) 825 if (affinity <= 0)
824 { 826 {
825 return luaL_error( L, "invalid affinity (%d)", affinity); 827 return luaL_error(L, "invalid affinity (%d)", affinity);
826 } 828 }
827 THREAD_SET_AFFINITY( (unsigned int) affinity); 829 THREAD_SET_AFFINITY( static_cast<unsigned int>(affinity));
828 return 0; 830 return 0;
829} 831}
830 832
@@ -852,7 +854,7 @@ static char const* get_errcode_name( int _code)
852 int i; 854 int i;
853 for( i = 0; i < 7; ++ i) 855 for( i = 0; i < 7; ++ i)
854 { 856 {
855 if( s_errcodes[i].code == _code) 857 if (s_errcodes[i].code == _code)
856 { 858 {
857 return s_errcodes[i].name; 859 return s_errcodes[i].name;
858 } 860 }
@@ -862,89 +864,88 @@ static char const* get_errcode_name( int _code)
862#endif // USE_DEBUG_SPEW() 864#endif // USE_DEBUG_SPEW()
863 865
864#if THREADWAIT_METHOD == THREADWAIT_CONDVAR // implies THREADAPI == THREADAPI_PTHREAD 866#if THREADWAIT_METHOD == THREADWAIT_CONDVAR // implies THREADAPI == THREADAPI_PTHREAD
865static void thread_cleanup_handler( void* opaque) 867static void thread_cleanup_handler(void* opaque)
866{ 868{
867 Lane* s= (Lane*) opaque; 869 Lane* lane{ (Lane*) opaque };
868 MUTEX_LOCK( &s->done_lock); 870 MUTEX_LOCK(&lane->done_lock);
869 s->status = CANCELLED; 871 lane->status = CANCELLED;
870 SIGNAL_ONE( &s->done_signal); // wake up master (while 's->done_lock' is on) 872 SIGNAL_ONE(&lane->done_signal); // wake up master (while 'lane->done_lock' is on)
871 MUTEX_UNLOCK( &s->done_lock); 873 MUTEX_UNLOCK(&lane->done_lock);
872} 874}
873#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 875#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
874 876
875static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) 877static THREAD_RETURN_T THREAD_CALLCONV lane_main(void* vs)
876{ 878{
877 Lane* s = (Lane*) vs; 879 Lane* const lane{ (Lane*) vs };
878 int rc, rc2; 880 lua_State* const L{ lane->L };
879 lua_State* L = s->L;
880 // Called with the lane function and arguments on the stack 881 // Called with the lane function and arguments on the stack
881 int const nargs = lua_gettop( L) - 1; 882 int const nargs{ lua_gettop(L) - 1 };
882 DEBUGSPEW_CODE( Universe* U = universe_get( L)); 883 DEBUGSPEW_CODE(Universe* U = universe_get(L));
883 THREAD_MAKE_ASYNCH_CANCELLABLE(); 884 THREAD_MAKE_ASYNCH_CANCELLABLE();
884 THREAD_CLEANUP_PUSH( thread_cleanup_handler, s); 885 THREAD_CLEANUP_PUSH(thread_cleanup_handler, lane);
885 s->status = RUNNING; // PENDING -> RUNNING 886 lane->status = RUNNING; // PENDING -> RUNNING
886 887
887 // Tie "set_finalizer()" to the state 888 // Tie "set_finalizer()" to the state
888 lua_pushcfunction( L, LG_set_finalizer); 889 lua_pushcfunction(L, LG_set_finalizer);
889 populate_func_lookup_table( L, -1, "set_finalizer"); 890 populate_func_lookup_table(L, -1, "set_finalizer");
890 lua_setglobal( L, "set_finalizer"); 891 lua_setglobal(L, "set_finalizer");
891 892
892 // Tie "set_debug_threadname()" to the state 893 // Tie "set_debug_threadname()" to the state
893 // But don't register it in the lookup database because of the Lane pointer upvalue 894 // But don't register it in the lookup database because of the Lane pointer upvalue
894 lua_pushlightuserdata( L, s); 895 lua_pushlightuserdata(L, lane);
895 lua_pushcclosure( L, LG_set_debug_threadname, 1); 896 lua_pushcclosure(L, LG_set_debug_threadname, 1);
896 lua_setglobal( L, "set_debug_threadname"); 897 lua_setglobal(L, "set_debug_threadname");
897 898
898 // Tie "cancel_test()" to the state 899 // Tie "cancel_test()" to the state
899 lua_pushcfunction( L, LG_cancel_test); 900 lua_pushcfunction(L, LG_cancel_test);
900 populate_func_lookup_table( L, -1, "cancel_test"); 901 populate_func_lookup_table(L, -1, "cancel_test");
901 lua_setglobal( L, "cancel_test"); 902 lua_setglobal(L, "cancel_test");
902 903
903 // this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around 904 // this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around
904#if ERROR_FULL_STACK 905#if ERROR_FULL_STACK
905 // Tie "set_error_reporting()" to the state 906 // Tie "set_error_reporting()" to the state
906 lua_pushcfunction( L, LG_set_error_reporting); 907 lua_pushcfunction(L, LG_set_error_reporting);
907 populate_func_lookup_table( L, -1, "set_error_reporting"); 908 populate_func_lookup_table(L, -1, "set_error_reporting");
908 lua_setglobal( L, "set_error_reporting"); 909 lua_setglobal(L, "set_error_reporting");
909 910
910 STACK_GROW( L, 1); 911 STACK_GROW(L, 1);
911 lua_pushcfunction( L, lane_error); // func args handler 912 lua_pushcfunction(L, lane_error); // func args handler
912 lua_insert( L, 1); // handler func args 913 lua_insert(L, 1); // handler func args
913#endif // ERROR_FULL_STACK 914#endif // ERROR_FULL_STACK
914 915
915 rc = lua_pcall( L, nargs, LUA_MULTRET, ERROR_FULL_STACK); // retvals|err 916 int rc{ lua_pcall(L, nargs, LUA_MULTRET, ERROR_FULL_STACK) }; // retvals|err
916 917
917#if ERROR_FULL_STACK 918#if ERROR_FULL_STACK
918 lua_remove( L, 1); // retvals|error 919 lua_remove(L, 1); // retvals|error
919# endif // ERROR_FULL_STACK 920# endif // ERROR_FULL_STACK
920 921
921 // in case of error and if it exists, fetch stack trace from registry and push it 922 // in case of error and if it exists, fetch stack trace from registry and push it
922 push_stack_trace( L, rc, 1); // retvals|error [trace] 923 push_stack_trace(L, rc, 1); // retvals|error [trace]
923 924
924 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name( rc), equal_unique_key( L, 1, CANCEL_ERROR) ? "cancelled" : lua_typename( L, lua_type( L, 1)))); 925 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name(rc), CANCEL_ERROR.equals(L, 1) ? "cancelled" : lua_typename(L, lua_type(L, 1))));
925 //STACK_DUMP(L); 926 // STACK_DUMP(L);
926 // Call finalizers, if the script has set them up. 927 // Call finalizers, if the script has set them up.
927 // 928 //
928 rc2 = run_finalizers( L, rc); 929 int rc2 = run_finalizers(L, rc);
929 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name( rc2))); 930 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p finalizer: %s\n" INDENT_END, L, get_errcode_name(rc2)));
930 if( rc2 != LUA_OK) // Error within a finalizer! 931 if (rc2 != LUA_OK) // Error within a finalizer!
931 { 932 {
932 // the finalizer generated an error, and left its own error message [and stack trace] on the stack 933 // the finalizer generated an error, and left its own error message [and stack trace] on the stack
933 rc = rc2; // we're overruling the earlier script error or normal return 934 rc = rc2; // we're overruling the earlier script error or normal return
934 } 935 }
935 s->waiting_on = nullptr; // just in case 936 lane->waiting_on = nullptr; // just in case
936 if( selfdestruct_remove( s)) // check and remove (under lock!) 937 if (selfdestruct_remove(lane)) // check and remove (under lock!)
937 { 938 {
938 // We're a free-running thread and no-one's there to clean us up. 939 // We're a free-running thread and no-one's there to clean us up.
939 // 940 //
940 lua_close( s->L); 941 lua_close(lane->L);
941 942
942 s->U->selfdestruct_cs.lock(); 943 lane->U->selfdestruct_cs.lock();
943 // done with lua_close(), terminal shutdown sequence may proceed 944 // done with lua_close(), terminal shutdown sequence may proceed
944 -- s->U->selfdestructing_count; 945 --lane->U->selfdestructing_count;
945 s->U->selfdestruct_cs.unlock(); 946 lane->U->selfdestruct_cs.unlock();
946 947
947 lane_cleanup( s); // s is freed at this point 948 lane_cleanup(lane); // s is freed at this point
948 } 949 }
949 else 950 else
950 { 951 {
@@ -956,64 +957,69 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs)
956 // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change 957 // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change
957 // 958 //
958#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 959#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
959 MUTEX_LOCK( &s->done_lock); 960 MUTEX_LOCK(&lane->done_lock);
960 { 961 {
961#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 962#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
962 s->status = st; 963 lane->status = st;
963#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 964#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
964 SIGNAL_ONE( &s->done_signal); // wake up master (while 's->done_lock' is on) 965 SIGNAL_ONE(&lane->done_signal); // wake up master (while 'lane->done_lock' is on)
965 } 966 }
966 MUTEX_UNLOCK( &s->done_lock); 967 MUTEX_UNLOCK(&lane->done_lock);
967#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 968#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
968 } 969 }
969 THREAD_CLEANUP_POP( false); 970 THREAD_CLEANUP_POP(false);
970 return 0; // ignored 971 return 0; // ignored
971} 972}
972 973
974// #################################################################################################
975
973// --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required 976// --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required
974// with lanes.require, that will call the regular 'require', then populate the lookup database in the source lane 977// with lanes.require, that will call the regular 'require', then populate the lookup database in the source lane
975// module = lanes.require( "modname") 978// module = lanes.require( "modname")
976// upvalue[1]: _G.require 979// upvalue[1]: _G.require
977LUAG_FUNC( require) 980LUAG_FUNC(require)
978{ 981{
979 char const* name = lua_tostring( L, 1); 982 char const* name = lua_tostring(L, 1);
980 int const nargs = lua_gettop( L); 983 int const nargs = lua_gettop(L);
981 DEBUGSPEW_CODE( Universe* U = universe_get( L)); 984 DEBUGSPEW_CODE(Universe* U = universe_get(L));
982 STACK_CHECK_START_REL(L, 0); 985 STACK_CHECK_START_REL(L, 0);
983 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END, name)); 986 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END, name));
984 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 987 DEBUGSPEW_CODE(++U->debugspew_indent_depth);
985 lua_pushvalue( L, lua_upvalueindex(1)); // "name" require 988 lua_pushvalue(L, lua_upvalueindex(1)); // "name" require
986 lua_insert( L, 1); // require "name" 989 lua_insert(L, 1); // require "name"
987 lua_call( L, nargs, 1); // module 990 lua_call(L, nargs, 1); // module
988 populate_func_lookup_table( L, -1, name); 991 populate_func_lookup_table(L, -1, name);
989 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END, name)); 992 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END, name));
990 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 993 DEBUGSPEW_CODE(--U->debugspew_indent_depth);
991 STACK_CHECK( L, 0); 994 STACK_CHECK(L, 0);
992 return 1; 995 return 1;
993} 996}
994 997
998// #################################################################################################
995 999
996// --- If a client wants to transfer stuff of a previously required module from the current state to another Lane, the module must be registered 1000// --- If a client wants to transfer stuff of a previously required module from the current state to another Lane, the module must be registered
997// to populate the lookup database in the source lane (and in the destination too, of course) 1001// to populate the lookup database in the source lane (and in the destination too, of course)
998// lanes.register( "modname", module) 1002// lanes.register( "modname", module)
999LUAG_FUNC( register) 1003LUAG_FUNC(register)
1000{ 1004{
1001 char const* name = luaL_checkstring( L, 1); 1005 char const* name = luaL_checkstring(L, 1);
1002 int const mod_type = lua_type( L, 2); 1006 int const mod_type = lua_type(L, 2);
1003 // ignore extra parameters, just in case 1007 // ignore extra parameters, just in case
1004 lua_settop( L, 2); 1008 lua_settop(L, 2);
1005 luaL_argcheck( L, (mod_type == LUA_TTABLE) || (mod_type == LUA_TFUNCTION), 2, "unexpected module type"); 1009 luaL_argcheck(L, (mod_type == LUA_TTABLE) || (mod_type == LUA_TFUNCTION), 2, "unexpected module type");
1006 DEBUGSPEW_CODE( Universe* U = universe_get( L)); 1010 DEBUGSPEW_CODE(Universe* U = universe_get(L));
1007 STACK_CHECK_START_REL(L, 0); // "name" mod_table 1011 STACK_CHECK_START_REL(L, 0); // "name" mod_table
1008 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END, name)); 1012 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s BEGIN\n" INDENT_END, name));
1009 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1013 DEBUGSPEW_CODE(++U->debugspew_indent_depth);
1010 populate_func_lookup_table( L, -1, name); 1014 populate_func_lookup_table(L, -1, name);
1011 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END, name)); 1015 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lanes.register %s END\n" INDENT_END, name));
1012 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1016 DEBUGSPEW_CODE(--U->debugspew_indent_depth);
1013 STACK_CHECK( L, 0); 1017 STACK_CHECK(L, 0);
1014 return 0; 1018 return 0;
1015} 1019}
1016 1020
1021// #################################################################################################
1022
1017// crc64/we of string "GCCB_KEY" generated at http://www.nitrxgen.net/hashgen/ 1023// crc64/we of string "GCCB_KEY" generated at http://www.nitrxgen.net/hashgen/
1018static constexpr UniqueKey GCCB_KEY{ 0xcfb1f046ef074e88ull }; 1024static constexpr UniqueKey GCCB_KEY{ 0xcfb1f046ef074e88ull };
1019 1025
@@ -1031,10 +1037,10 @@ static constexpr UniqueKey GCCB_KEY{ 0xcfb1f046ef074e88ull };
1031// 1037//
1032LUAG_FUNC( lane_new) 1038LUAG_FUNC( lane_new)
1033{ 1039{
1034 char const* libs_str = lua_tostring( L, 2); 1040 char const* libs_str = lua_tostring(L, 2);
1035 bool const have_priority{ !lua_isnoneornil(L, 3) }; 1041 bool const have_priority{ !lua_isnoneornil(L, 3) };
1036 int const priority = have_priority ? (int) lua_tointeger( L, 3) : THREAD_PRIO_DEFAULT; 1042 int const priority = have_priority ? (int) lua_tointeger(L, 3) : THREAD_PRIO_DEFAULT;
1037 int const globals_idx = lua_isnoneornil( L, 4) ? 0 : 4; 1043 int const globals_idx = lua_isnoneornil(L, 4) ? 0 : 4;
1038 int const package_idx = lua_isnoneornil(L, 5) ? 0 : 5; 1044 int const package_idx = lua_isnoneornil(L, 5) ? 0 : 5;
1039 int const required_idx = lua_isnoneornil(L, 6) ? 0 : 6; 1045 int const required_idx = lua_isnoneornil(L, 6) ? 0 : 6;
1040 int const gc_cb_idx = lua_isnoneornil(L, 7) ? 0 : 7; 1046 int const gc_cb_idx = lua_isnoneornil(L, 7) ? 0 : 7;
@@ -1047,9 +1053,9 @@ LUAG_FUNC( lane_new)
1047 // public Lanes API accepts a generic range -3/+3 1053 // public Lanes API accepts a generic range -3/+3
1048 // that will be remapped into the platform-specific scheduler priority scheme 1054 // that will be remapped into the platform-specific scheduler priority scheme
1049 // On some platforms, -3 is equivalent to -2 and +3 to +2 1055 // On some platforms, -3 is equivalent to -2 and +3 to +2
1050 if( have_priority && (priority < THREAD_PRIO_MIN || priority > THREAD_PRIO_MAX)) 1056 if (have_priority && (priority < THREAD_PRIO_MIN || priority > THREAD_PRIO_MAX))
1051 { 1057 {
1052 return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, priority); 1058 return luaL_error(L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, priority);
1053 } 1059 }
1054 1060
1055 /* --- Create and prepare the sub state --- */ 1061 /* --- Create and prepare the sub state --- */
@@ -1062,7 +1068,7 @@ LUAG_FUNC( lane_new)
1062 STACK_GROW( L2, nargs + 3); // 1068 STACK_GROW( L2, nargs + 3); //
1063 STACK_CHECK_START_REL(L2, 0); 1069 STACK_CHECK_START_REL(L2, 0);
1064 1070
1065 STACK_GROW( L, 3); // func libs priority globals package required gc_cb [... args ...] 1071 STACK_GROW(L, 3); // func libs priority globals package required gc_cb [... args ...]
1066 STACK_CHECK_START_REL(L, 0); 1072 STACK_CHECK_START_REL(L, 0);
1067 1073
1068 // give a default "Lua" name to the thread to see VM name in Decoda debugger 1074 // give a default "Lua" name to the thread to see VM name in Decoda debugger
@@ -1072,7 +1078,7 @@ LUAG_FUNC( lane_new)
1072 1078
1073 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END)); 1079 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: update 'package'\n" INDENT_END));
1074 // package 1080 // package
1075 if( package_idx != 0) 1081 if (package_idx != 0)
1076 { 1082 {
1077 // when copying with mode eLM_LaneBody, should raise an error in case of problem, not leave it one the stack 1083 // when copying with mode eLM_LaneBody, should raise an error in case of problem, not leave it one the stack
1078 (void) luaG_inter_copy_package( U, L, L2, package_idx, eLM_LaneBody); 1084 (void) luaG_inter_copy_package( U, L, L2, package_idx, eLM_LaneBody);
@@ -1080,42 +1086,42 @@ LUAG_FUNC( lane_new)
1080 1086
1081 // modules to require in the target lane *before* the function is transfered! 1087 // modules to require in the target lane *before* the function is transfered!
1082 1088
1083 if( required_idx != 0) 1089 if (required_idx != 0)
1084 { 1090 {
1085 int nbRequired = 1; 1091 int nbRequired = 1;
1086 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END)); 1092 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require 'required' list\n" INDENT_END));
1087 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1093 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1088 // should not happen, was checked in lanes.lua before calling lane_new() 1094 // should not happen, was checked in lanes.lua before calling lane_new()
1089 if( lua_type( L, required_idx) != LUA_TTABLE) 1095 if (lua_type(L, required_idx) != LUA_TTABLE)
1090 { 1096 {
1091 return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required_idx)); 1097 return luaL_error(L, "expected required module list as a table, got %s", luaL_typename(L, required_idx));
1092 } 1098 }
1093 1099
1094 lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil 1100 lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil
1095 while( lua_next( L, required_idx) != 0) // func libs priority globals package required gc_cb [... args ...] n "modname" 1101 while( lua_next(L, required_idx) != 0) // func libs priority globals package required gc_cb [... args ...] n "modname"
1096 { 1102 {
1097 if( lua_type( L, -1) != LUA_TSTRING || lua_type( L, -2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) 1103 if (lua_type(L, -1) != LUA_TSTRING || lua_type(L, -2) != LUA_TNUMBER || lua_tonumber(L, -2) != nbRequired)
1098 { 1104 {
1099 return luaL_error( L, "required module list should be a list of strings"); 1105 return luaL_error(L, "required module list should be a list of strings");
1100 } 1106 }
1101 else 1107 else
1102 { 1108 {
1103 // require the module in the target state, and populate the lookup table there too 1109 // require the module in the target state, and populate the lookup table there too
1104 size_t len; 1110 size_t len;
1105 char const* name = lua_tolstring( L, -1, &len); 1111 char const* name = lua_tolstring(L, -1, &len);
1106 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END, name)); 1112 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END, name));
1107 1113
1108 // require the module in the target lane 1114 // require the module in the target lane
1109 lua_getglobal( L2, "require"); // require()? 1115 lua_getglobal( L2, "require"); // require()?
1110 if( lua_isnil( L2, -1)) 1116 if (lua_isnil( L2, -1))
1111 { 1117 {
1112 lua_pop( L2, 1); // 1118 lua_pop( L2, 1); //
1113 return luaL_error( L, "cannot pre-require modules without loading 'package' library first"); 1119 return luaL_error(L, "cannot pre-require modules without loading 'package' library first");
1114 } 1120 }
1115 else 1121 else
1116 { 1122 {
1117 lua_pushlstring( L2, name, len); // require() name 1123 lua_pushlstring( L2, name, len); // require() name
1118 if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode 1124 if (lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode
1119 { 1125 {
1120 // propagate error to main state if any 1126 // propagate error to main state if any
1121 luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] n "modname" error 1127 luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] n "modname" error
@@ -1126,83 +1132,83 @@ LUAG_FUNC( lane_new)
1126 lua_pop( L2, 1); // 1132 lua_pop( L2, 1); //
1127 } 1133 }
1128 } 1134 }
1129 lua_pop( L, 1); // func libs priority globals package required gc_cb [... args ...] n 1135 lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] n
1130 ++ nbRequired; 1136 ++ nbRequired;
1131 } // func libs priority globals package required gc_cb [... args ...] 1137 } // func libs priority globals package required gc_cb [... args ...]
1132 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1138 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1133 } 1139 }
1134 STACK_CHECK( L, 0); 1140 STACK_CHECK(L, 0);
1135 STACK_CHECK( L2, 0); // 1141 STACK_CHECK( L2, 0); //
1136 1142
1137 // Appending the specified globals to the global environment 1143 // Appending the specified globals to the global environment
1138 // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed... 1144 // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed...
1139 // 1145 //
1140 if( globals_idx != 0) 1146 if (globals_idx != 0)
1141 { 1147 {
1142 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END)); 1148 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer globals\n" INDENT_END));
1143 if( !lua_istable( L, globals_idx)) 1149 if (!lua_istable(L, globals_idx))
1144 { 1150 {
1145 return luaL_error( L, "Expected table, got %s", luaL_typename( L, globals_idx)); 1151 return luaL_error(L, "Expected table, got %s", luaL_typename(L, globals_idx));
1146 } 1152 }
1147 1153
1148 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1154 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1149 lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil 1155 lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil
1150 // Lua 5.2 wants us to push the globals table on the stack 1156 // Lua 5.2 wants us to push the globals table on the stack
1151 lua_pushglobaltable( L2); // _G 1157 lua_pushglobaltable( L2); // _G
1152 while( lua_next( L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v 1158 while( lua_next(L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v
1153 { 1159 {
1154 luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // _G k v 1160 luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // _G k v
1155 // assign it in L2's globals table 1161 // assign it in L2's globals table
1156 lua_rawset( L2, -3); // _G 1162 lua_rawset( L2, -3); // _G
1157 lua_pop( L, 1); // func libs priority globals package required gc_cb [... args ...] k 1163 lua_pop(L, 1); // func libs priority globals package required gc_cb [... args ...] k
1158 } // func libs priority globals package required gc_cb [... args ...] 1164 } // func libs priority globals package required gc_cb [... args ...]
1159 lua_pop( L2, 1); // 1165 lua_pop( L2, 1); //
1160 1166
1161 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1167 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1162 } 1168 }
1163 STACK_CHECK( L, 0); 1169 STACK_CHECK(L, 0);
1164 STACK_CHECK( L2, 0); 1170 STACK_CHECK( L2, 0);
1165 1171
1166 // Lane main function 1172 // Lane main function
1167 if( lua_type( L, 1) == LUA_TFUNCTION) 1173 if (lua_type(L, 1) == LUA_TFUNCTION)
1168 { 1174 {
1169 int res; 1175 int res;
1170 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END)); 1176 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END));
1171 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1177 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1172 lua_pushvalue( L, 1); // func libs priority globals package required gc_cb [... args ...] func 1178 lua_pushvalue(L, 1); // func libs priority globals package required gc_cb [... args ...] func
1173 res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func 1179 res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func
1174 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1180 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1175 if( res != 0) 1181 if (res != 0)
1176 { 1182 {
1177 return luaL_error( L, "tried to copy unsupported types"); 1183 return luaL_error(L, "tried to copy unsupported types");
1178 } 1184 }
1179 } 1185 }
1180 else if( lua_type( L, 1) == LUA_TSTRING) 1186 else if (lua_type(L, 1) == LUA_TSTRING)
1181 { 1187 {
1182 // compile the string 1188 // compile the string
1183 if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) // func 1189 if (luaL_loadstring( L2, lua_tostring(L, 1)) != 0) // func
1184 { 1190 {
1185 return luaL_error( L, "error when parsing lane function code"); 1191 return luaL_error(L, "error when parsing lane function code");
1186 } 1192 }
1187 } 1193 }
1188 STACK_CHECK( L, 0); 1194 STACK_CHECK(L, 0);
1189 STACK_CHECK( L2, 1); 1195 STACK_CHECK( L2, 1);
1190 ASSERT_L( lua_isfunction( L2, 1)); 1196 ASSERT_L( lua_isfunction( L2, 1));
1191 1197
1192 // revive arguments 1198 // revive arguments
1193 if( nargs > 0) 1199 if (nargs > 0)
1194 { 1200 {
1195 int res; 1201 int res;
1196 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END)); 1202 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END));
1197 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1203 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1198 res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...] 1204 res = luaG_inter_move(U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...]
1199 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1205 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1200 if( res != 0) 1206 if (res != 0)
1201 { 1207 {
1202 return luaL_error( L, "tried to copy unsupported types"); 1208 return luaL_error(L, "tried to copy unsupported types");
1203 } 1209 }
1204 } 1210 }
1205 STACK_CHECK( L, -nargs); 1211 STACK_CHECK(L, -nargs);
1206 ASSERT_L( lua_gettop( L) == FIXED_ARGS); 1212 ASSERT_L( lua_gettop( L) == FIXED_ARGS);
1207 STACK_CHECK_RESET_REL(L, 0); 1213 STACK_CHECK_RESET_REL(L, 0);
1208 STACK_CHECK( L2, 1 + nargs); 1214 STACK_CHECK( L2, 1 + nargs);
@@ -1211,66 +1217,67 @@ LUAG_FUNC( lane_new)
1211 // 1217 //
1212 // a Lane full userdata needs a single uservalue 1218 // a Lane full userdata needs a single uservalue
1213 Lane** const ud{ lua_newuserdatauv<Lane*>(L, 1) }; // func libs priority globals package required gc_cb lane 1219 Lane** const ud{ lua_newuserdatauv<Lane*>(L, 1) }; // func libs priority globals package required gc_cb lane
1214 Lane* const s{ *ud = static_cast<Lane*>(U->internal_allocator.alloc(sizeof(Lane))) }; // don't forget to store the pointer in the userdata! 1220 Lane* const lane{ *ud = static_cast<Lane*>(U->internal_allocator.alloc(sizeof(Lane))) }; // don't forget to store the pointer in the userdata!
1215 if( s == nullptr) 1221 if (lane == nullptr)
1216 { 1222 {
1217 return luaL_error( L, "could not create lane: out of memory"); 1223 return luaL_error(L, "could not create lane: out of memory");
1218 } 1224 }
1219 1225
1220 s->L = L2; 1226 lane->L = L2;
1221 s->U = U; 1227 lane->U = U;
1222 s->status = PENDING; 1228 lane->status = PENDING;
1223 s->waiting_on = nullptr; 1229 lane->waiting_on = nullptr;
1224 s->debug_name = "<unnamed>"; 1230 lane->debug_name = "<unnamed>";
1225 s->cancel_request = CancelRequest::None; 1231 lane->cancel_request = CancelRequest::None;
1226 1232
1227#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 1233#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1228 MUTEX_INIT( &s->done_lock); 1234 MUTEX_INIT(&lane->done_lock);
1229 SIGNAL_INIT( &s->done_signal); 1235 SIGNAL_INIT(&lane->done_signal);
1230#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 1236#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1231 s->mstatus = ThreadStatus::Normal; 1237 lane->mstatus = ThreadStatus::Normal;
1232 s->selfdestruct_next = nullptr; 1238 lane->selfdestruct_next = nullptr;
1233#if HAVE_LANE_TRACKING() 1239#if HAVE_LANE_TRACKING()
1234 s->tracking_next = nullptr; 1240 lane->tracking_next = nullptr;
1235 if( s->U->tracking_first) 1241 if (lane->U->tracking_first)
1236 { 1242 {
1237 tracking_add( s); 1243 tracking_add(lane);
1238 } 1244 }
1239#endif // HAVE_LANE_TRACKING() 1245#endif // HAVE_LANE_TRACKING()
1240 1246
1241 // Set metatable for the userdata 1247 // Set metatable for the userdata
1242 // 1248 //
1243 lua_pushvalue( L, lua_upvalueindex( 1)); // func libs priority globals package required gc_cb lane mt 1249 lua_pushvalue(L, lua_upvalueindex( 1)); // func libs priority globals package required gc_cb lane mt
1244 lua_setmetatable( L, -2); // func libs priority globals package required gc_cb lane 1250 lua_setmetatable(L, -2); // func libs priority globals package required gc_cb lane
1245 STACK_CHECK( L, 1); 1251 STACK_CHECK(L, 1);
1246 1252
1247 // Create uservalue for the userdata 1253 // Create uservalue for the userdata
1248 // (this is where lane body return values will be stored when the handle is indexed by a numeric key) 1254 // (this is where lane body return values will be stored when the handle is indexed by a numeric key)
1249 lua_newtable( L); // func libs cancelstep priority globals package required gc_cb lane uv 1255 lua_newtable( L); // func libs cancelstep priority globals package required gc_cb lane uv
1250 1256
1251 // Store the gc_cb callback in the uservalue 1257 // Store the gc_cb callback in the uservalue
1252 if( gc_cb_idx > 0) 1258 if (gc_cb_idx > 0)
1253 { 1259 {
1254 GCCB_KEY.push(L); // func libs priority globals package required gc_cb lane uv k 1260 GCCB_KEY.push(L); // func libs priority globals package required gc_cb lane uv k
1255 lua_pushvalue( L, gc_cb_idx); // func libs priority globals package required gc_cb lane uv k gc_cb 1261 lua_pushvalue(L, gc_cb_idx); // func libs priority globals package required gc_cb lane uv k gc_cb
1256 lua_rawset( L, -3); // func libs priority globals package required gc_cb lane uv 1262 lua_rawset(L, -3); // func libs priority globals package required gc_cb lane uv
1257 } 1263 }
1258 1264
1259 lua_setiuservalue( L, -2, 1); // func libs priority globals package required gc_cb lane 1265 lua_setiuservalue(L, -2, 1); // func libs priority globals package required gc_cb lane
1260 1266
1261 // Store 's' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). 1267 // Store 's' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive).
1262 CANCEL_TEST_KEY.set_registry(L2, [s](lua_State* L) { lua_pushlightuserdata(L, s); }); // func [... args ...] 1268 CANCEL_TEST_KEY.set_registry(L2, [lane](lua_State* L) { lua_pushlightuserdata(L, lane); }); // func [... args ...]
1263 1269
1264 STACK_CHECK( L, 1); 1270 STACK_CHECK(L, 1);
1265 STACK_CHECK( L2, 1 + nargs); 1271 STACK_CHECK( L2, 1 + nargs);
1266 1272
1267 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END)); 1273 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END));
1268 THREAD_CREATE( &s->thread, lane_main, s, priority); 1274 THREAD_CREATE(&lane->thread, lane_main, lane, priority);
1269 1275
1270 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1276 DEBUGSPEW_CODE(--U->debugspew_indent_depth);
1271 return 1; 1277 return 1;
1272} 1278}
1273 1279
1280// #################################################################################################
1274 1281
1275//--- 1282//---
1276// = thread_gc( lane_ud ) 1283// = thread_gc( lane_ud )
@@ -1284,83 +1291,85 @@ LUAG_FUNC( lane_new)
1284// and the issue of canceling/killing threads at gc is not very nice, either 1291// and the issue of canceling/killing threads at gc is not very nice, either
1285// (would easily cause waits at gc cycle, which we don't want). 1292// (would easily cause waits at gc cycle, which we don't want).
1286// 1293//
1287LUAG_FUNC( thread_gc) 1294LUAG_FUNC(thread_gc)
1288{ 1295{
1289 bool have_gc_cb{ false }; 1296 bool have_gc_cb{ false };
1290 Lane* s = lua_toLane( L, 1); // ud 1297 Lane* lane{ lua_toLane(L, 1) }; // ud
1291 1298
1292 // if there a gc callback? 1299 // if there a gc callback?
1293 lua_getiuservalue( L, 1, 1); // ud uservalue 1300 lua_getiuservalue(L, 1, 1); // ud uservalue
1294 GCCB_KEY.push(L); // ud uservalue __gc 1301 GCCB_KEY.push(L); // ud uservalue __gc
1295 lua_rawget( L, -2); // ud uservalue gc_cb|nil 1302 lua_rawget(L, -2); // ud uservalue gc_cb|nil
1296 if( !lua_isnil( L, -1)) 1303 if (!lua_isnil(L, -1))
1297 { 1304 {
1298 lua_remove( L, -2); // ud gc_cb|nil 1305 lua_remove(L, -2); // ud gc_cb|nil
1299 lua_pushstring( L, s->debug_name); // ud gc_cb name 1306 lua_pushstring(L, lane->debug_name); // ud gc_cb name
1300 have_gc_cb = true; 1307 have_gc_cb = true;
1301 } 1308 }
1302 else 1309 else
1303 { 1310 {
1304 lua_pop( L, 2); // ud 1311 lua_pop(L, 2); // ud
1305 } 1312 }
1306 1313
1307 // We can read 's->status' without locks, but not wait for it 1314 // We can read 'lane->status' without locks, but not wait for it
1308 // test Killed state first, as it doesn't need to enter the selfdestruct chain 1315 // test Killed state first, as it doesn't need to enter the selfdestruct chain
1309 if (s->mstatus == ThreadStatus::Killed) 1316 if (lane->mstatus == ThreadStatus::Killed)
1310 { 1317 {
1311 // Make sure a kill has proceeded, before cleaning up the data structure. 1318 // Make sure a kill has proceeded, before cleaning up the data structure.
1312 // 1319 //
1313 // NO lua_close() in this case because we don't know where execution of the state was interrupted 1320 // NO lua_close() in this case because we don't know where execution of the state was interrupted
1314 DEBUGSPEW_CODE( fprintf( stderr, "** Joining with a killed thread (needs testing) **")); 1321 DEBUGSPEW_CODE(fprintf(stderr, "** Joining with a killed thread (needs testing) **"));
1315 // make sure the thread is no longer running, just like thread_join() 1322 // make sure the thread is no longer running, just like thread_join()
1316 if(! THREAD_ISNULL( s->thread)) 1323 if (!THREAD_ISNULL(lane->thread))
1317 { 1324 {
1318 THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status); 1325 THREAD_WAIT(&lane->thread, -1, &lane->done_signal, &lane->done_lock, &lane->status);
1319 } 1326 }
1320 if( s->status >= DONE && s->L) 1327 if (lane->status >= DONE && lane->L)
1321 { 1328 {
1322 // we know the thread was killed while the Lua VM was not doing anything: we should be able to close it without crashing 1329 // we know the thread was killed while the Lua VM was not doing anything: we should be able to close it without crashing
1323 // now, thread_cancel() will not forcefully kill a lane with s->status >= DONE, so I am not sure it can ever happen 1330 // now, thread_cancel() will not forcefully kill a lane with lane->status >= DONE, so I am not sure it can ever happen
1324 lua_close( s->L); 1331 lua_close(lane->L);
1325 s->L = 0; 1332 lane->L = nullptr;
1326 // just in case, but s will be freed soon so... 1333 // just in case, but s will be freed soon so...
1327 s->debug_name = "<gc>"; 1334 lane->debug_name = "<gc>";
1328 } 1335 }
1329 DEBUGSPEW_CODE( fprintf( stderr, "** Joined ok **")); 1336 DEBUGSPEW_CODE(fprintf(stderr, "** Joined ok **"));
1330 } 1337 }
1331 else if( s->status < DONE) 1338 else if (lane->status < DONE)
1332 { 1339 {
1333 // still running: will have to be cleaned up later 1340 // still running: will have to be cleaned up later
1334 selfdestruct_add( s); 1341 selfdestruct_add(lane);
1335 assert( s->selfdestruct_next); 1342 assert(lane->selfdestruct_next);
1336 if( have_gc_cb) 1343 if (have_gc_cb)
1337 { 1344 {
1338 lua_pushliteral( L, "selfdestruct"); // ud gc_cb name status 1345 lua_pushliteral(L, "selfdestruct"); // ud gc_cb name status
1339 lua_call( L, 2, 0); // ud 1346 lua_call(L, 2, 0); // ud
1340 } 1347 }
1341 return 0; 1348 return 0;
1342 } 1349 }
1343 else if( s->L) 1350 else if (lane->L)
1344 { 1351 {
1345 // no longer accessing the Lua VM: we can close right now 1352 // no longer accessing the Lua VM: we can close right now
1346 lua_close( s->L); 1353 lua_close(lane->L);
1347 s->L = 0; 1354 lane->L = nullptr;
1348 // just in case, but s will be freed soon so... 1355 // just in case, but s will be freed soon so...
1349 s->debug_name = "<gc>"; 1356 lane->debug_name = "<gc>";
1350 } 1357 }
1351 1358
1352 // Clean up after a (finished) thread 1359 // Clean up after a (finished) thread
1353 lane_cleanup( s); 1360 lane_cleanup(lane);
1354 1361
1355 // do this after lane cleanup in case the callback triggers an error 1362 // do this after lane cleanup in case the callback triggers an error
1356 if( have_gc_cb) 1363 if (have_gc_cb)
1357 { 1364 {
1358 lua_pushliteral( L, "closed"); // ud gc_cb name status 1365 lua_pushliteral(L, "closed"); // ud gc_cb name status
1359 lua_call( L, 2, 0); // ud 1366 lua_call(L, 2, 0); // ud
1360 } 1367 }
1361 return 0; 1368 return 0;
1362} 1369}
1363 1370
1371// #################################################################################################
1372
1364//--- 1373//---
1365// str= thread_status( lane ) 1374// str= thread_status( lane )
1366// 1375//
@@ -1371,11 +1380,11 @@ LUAG_FUNC( thread_gc)
1371// / "error" finished at an error, error value is there 1380// / "error" finished at an error, error value is there
1372// / "cancelled" execution cancelled by M (state gone) 1381// / "cancelled" execution cancelled by M (state gone)
1373// 1382//
1374static char const * thread_status_string( Lane* s) 1383static char const * thread_status_string(Lane* lane_)
1375{ 1384{
1376 enum e_status st = s->status; // read just once (volatile) 1385 enum e_status const st{ lane_->status }; // read just once (volatile)
1377 char const* str = 1386 char const* str =
1378 (s->mstatus == ThreadStatus::Killed) ? "killed" : // new to v3.3.0! 1387 (lane_->mstatus == ThreadStatus::Killed) ? "killed" : // new to v3.3.0!
1379 (st == PENDING) ? "pending" : 1388 (st == PENDING) ? "pending" :
1380 (st == RUNNING) ? "running" : // like in 'co.status()' 1389 (st == RUNNING) ? "running" : // like in 'co.status()'
1381 (st == WAITING) ? "waiting" : 1390 (st == WAITING) ? "waiting" :
@@ -1385,15 +1394,18 @@ static char const * thread_status_string( Lane* s)
1385 return str; 1394 return str;
1386} 1395}
1387 1396
1388int push_thread_status( lua_State* L, Lane* s) 1397// #################################################################################################
1398
1399int push_thread_status(lua_State* L, Lane* lane_)
1389{ 1400{
1390 char const* const str = thread_status_string( s); 1401 char const* const str{ thread_status_string(lane_) };
1391 ASSERT_L( str); 1402 ASSERT_L(str);
1392 1403
1393 lua_pushstring( L, str); 1404 lua_pushstring(L, str);
1394 return 1; 1405 return 1;
1395} 1406}
1396 1407
1408// #################################################################################################
1397 1409
1398//--- 1410//---
1399// [...] | [nil, err_any, stack_tbl]= thread_join( lane_ud [, wait_secs=-1] ) 1411// [...] | [nil, err_any, stack_tbl]= thread_join( lane_ud [, wait_secs=-1] )
@@ -1403,46 +1415,46 @@ int push_thread_status( lua_State* L, Lane* s)
1403// error: returns nil + error value [+ stack table] 1415// error: returns nil + error value [+ stack table]
1404// cancelled: returns nil 1416// cancelled: returns nil
1405// 1417//
1406LUAG_FUNC( thread_join) 1418LUAG_FUNC(thread_join)
1407{ 1419{
1408 Lane* const s = lua_toLane( L, 1); 1420 Lane* const lane{ lua_toLane(L, 1) };
1409 double wait_secs = luaL_optnumber( L, 2, -1.0); 1421 lua_Number const wait_secs{ luaL_optnumber(L, 2, -1.0) };
1410 lua_State* L2 = s->L; 1422 lua_State* const L2{ lane->L };
1411 int ret; 1423 int ret;
1412 bool const done{ THREAD_ISNULL(s->thread) || THREAD_WAIT(&s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status) }; 1424 bool const done{ THREAD_ISNULL(lane->thread) || THREAD_WAIT(&lane->thread, wait_secs, &lane->done_signal, &lane->done_lock, &lane->status) };
1413 if( !done || !L2) 1425 if (!done || !L2)
1414 { 1426 {
1415 STACK_GROW( L, 2); 1427 STACK_GROW(L, 2);
1416 lua_pushnil( L); 1428 lua_pushnil(L);
1417 lua_pushliteral( L, "timeout"); 1429 lua_pushliteral(L, "timeout");
1418 return 2; 1430 return 2;
1419 } 1431 }
1420 1432
1421 STACK_CHECK_START_REL(L, 0); 1433 STACK_CHECK_START_REL(L, 0);
1422 // Thread is DONE/ERROR_ST/CANCELLED; all ours now 1434 // Thread is DONE/ERROR_ST/CANCELLED; all ours now
1423 1435
1424 if (s->mstatus == ThreadStatus::Killed) // OS thread was killed if thread_cancel was forced 1436 if (lane->mstatus == ThreadStatus::Killed) // OS thread was killed if thread_cancel was forced
1425 { 1437 {
1426 // in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values 1438 // in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values
1427 STACK_GROW( L, 2); 1439 STACK_GROW(L, 2);
1428 lua_pushnil( L); 1440 lua_pushnil(L);
1429 lua_pushliteral( L, "killed"); 1441 lua_pushliteral(L, "killed");
1430 ret = 2; 1442 ret = 2;
1431 } 1443 }
1432 else 1444 else
1433 { 1445 {
1434 Universe* U = universe_get( L); 1446 Universe* const U{ lane->U };
1435 // debug_name is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed 1447 // debug_name is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed
1436 // so store it in the userdata uservalue at a key that can't possibly collide 1448 // so store it in the userdata uservalue at a key that can't possibly collide
1437 securize_debug_threadname( L, s); 1449 securize_debug_threadname(L, lane);
1438 switch( s->status) 1450 switch (lane->status)
1439 { 1451 {
1440 case DONE: 1452 case DONE:
1441 { 1453 {
1442 int n = lua_gettop( L2); // whole L2 stack 1454 int const n{ lua_gettop(L2) }; // whole L2 stack
1443 if( (n > 0) && (luaG_inter_move( U, L2, L, n, eLM_LaneBody) != 0)) 1455 if ((n > 0) && (luaG_inter_move(U, L2, L, n, eLM_LaneBody) != 0))
1444 { 1456 {
1445 return luaL_error( L, "tried to copy unsupported types"); 1457 return luaL_error(L, "tried to copy unsupported types");
1446 } 1458 }
1447 ret = n; 1459 ret = n;
1448 } 1460 }
@@ -1450,13 +1462,13 @@ LUAG_FUNC( thread_join)
1450 1462
1451 case ERROR_ST: 1463 case ERROR_ST:
1452 { 1464 {
1453 int const n = lua_gettop( L2); 1465 int const n{ lua_gettop(L2) };
1454 STACK_GROW( L, 3); 1466 STACK_GROW(L, 3);
1455 lua_pushnil( L); 1467 lua_pushnil(L);
1456 // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ... 1468 // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ...
1457 if( luaG_inter_move( U, L2, L, n, eLM_LaneBody) != 0) // nil "err" [trace] 1469 if (luaG_inter_move(U, L2, L, n, eLM_LaneBody) != 0) // nil "err" [trace]
1458 { 1470 {
1459 return luaL_error( L, "tried to copy unsupported types: %s", lua_tostring( L, -n)); 1471 return luaL_error(L, "tried to copy unsupported types: %s", lua_tostring(L, -n));
1460 } 1472 }
1461 ret = 1 + n; 1473 ret = 1 + n;
1462 } 1474 }
@@ -1467,14 +1479,14 @@ LUAG_FUNC( thread_join)
1467 break; 1479 break;
1468 1480
1469 default: 1481 default:
1470 DEBUGSPEW_CODE( fprintf( stderr, "Status: %d\n", s->status)); 1482 DEBUGSPEW_CODE(fprintf(stderr, "Status: %d\n", lane->status));
1471 ASSERT_L( false); 1483 ASSERT_L(false);
1472 ret = 0; 1484 ret = 0;
1473 } 1485 }
1474 lua_close( L2); 1486 lua_close(L2);
1475 } 1487 }
1476 s->L = 0; 1488 lane->L = nullptr;
1477 STACK_CHECK( L, ret); 1489 STACK_CHECK(L, ret);
1478 return ret; 1490 return ret;
1479} 1491}
1480 1492
@@ -1487,57 +1499,57 @@ LUAG_FUNC( thread_join)
1487// If the return values signal an error, propagate it 1499// If the return values signal an error, propagate it
1488// If key is "status" return the thread status 1500// If key is "status" return the thread status
1489// Else raise an error 1501// Else raise an error
1490LUAG_FUNC( thread_index) 1502LUAG_FUNC(thread_index)
1491{ 1503{
1492 int const UD = 1; 1504 static constexpr int UD{ 1 };
1493 int const KEY = 2; 1505 static constexpr int KEY{ 2 };
1494 int const USR = 3; 1506 static constexpr int USR{ 3 };
1495 Lane* const s = lua_toLane( L, UD); 1507 Lane* const lane{ lua_toLane(L, UD) };
1496 ASSERT_L( lua_gettop( L) == 2); 1508 ASSERT_L(lua_gettop(L) == 2);
1497 1509
1498 STACK_GROW( L, 8); // up to 8 positions are needed in case of error propagation 1510 STACK_GROW(L, 8); // up to 8 positions are needed in case of error propagation
1499 1511
1500 // If key is numeric, wait until the thread returns and populate the environment with the return values 1512 // If key is numeric, wait until the thread returns and populate the environment with the return values
1501 if( lua_type( L, KEY) == LUA_TNUMBER) 1513 if (lua_type(L, KEY) == LUA_TNUMBER)
1502 { 1514 {
1503 // first, check that we don't already have an environment that holds the requested value 1515 // first, check that we don't already have an environment that holds the requested value
1504 { 1516 {
1505 // If key is found in the uservalue, return it 1517 // If key is found in the uservalue, return it
1506 lua_getiuservalue( L, UD, 1); 1518 lua_getiuservalue(L, UD, 1);
1507 lua_pushvalue( L, KEY); 1519 lua_pushvalue(L, KEY);
1508 lua_rawget( L, USR); 1520 lua_rawget(L, USR);
1509 if( !lua_isnil( L, -1)) 1521 if (!lua_isnil(L, -1))
1510 { 1522 {
1511 return 1; 1523 return 1;
1512 } 1524 }
1513 lua_pop( L, 1); 1525 lua_pop(L, 1);
1514 } 1526 }
1515 { 1527 {
1516 // check if we already fetched the values from the thread or not 1528 // check if we already fetched the values from the thread or not
1517 lua_Integer key = lua_tointeger( L, KEY); 1529 lua_Integer key = lua_tointeger(L, KEY);
1518 lua_pushinteger( L, 0); 1530 lua_pushinteger(L, 0);
1519 lua_rawget( L, USR); 1531 lua_rawget(L, USR);
1520 bool const fetched{ !lua_isnil(L, -1) }; 1532 bool const fetched{ !lua_isnil(L, -1) };
1521 lua_pop( L, 1); // back to our 2 args + uservalue on the stack 1533 lua_pop(L, 1); // back to our 2 args + uservalue on the stack
1522 if( !fetched) 1534 if (!fetched)
1523 { 1535 {
1524 lua_pushinteger( L, 0); 1536 lua_pushinteger(L, 0);
1525 lua_pushboolean( L, 1); 1537 lua_pushboolean(L, 1);
1526 lua_rawset( L, USR); 1538 lua_rawset(L, USR);
1527 // wait until thread has completed 1539 // wait until thread has completed
1528 lua_pushcfunction( L, LG_thread_join); 1540 lua_pushcfunction(L, LG_thread_join);
1529 lua_pushvalue( L, UD); 1541 lua_pushvalue(L, UD);
1530 lua_call( L, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+ 1542 lua_call(L, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+
1531 switch( s->status) 1543 switch (lane->status)
1532 { 1544 {
1533 default: 1545 default:
1534 if (s->mstatus != ThreadStatus::Killed) 1546 if (lane->mstatus != ThreadStatus::Killed)
1535 { 1547 {
1536 // this is an internal error, we probably never get here 1548 // this is an internal error, we probably never get here
1537 lua_settop( L, 0); 1549 lua_settop(L, 0);
1538 lua_pushliteral( L, "Unexpected status: "); 1550 lua_pushliteral(L, "Unexpected status: ");
1539 lua_pushstring( L, thread_status_string( s)); 1551 lua_pushstring(L, thread_status_string(lane));
1540 lua_concat( L, 2); 1552 lua_concat(L, 2);
1541 raise_lua_error(L); 1553 raise_lua_error(L);
1542 } 1554 }
1543 [[fallthrough]]; // fall through if we are killed, as we got nil, "killed" on the stack 1555 [[fallthrough]]; // fall through if we are killed, as we got nil, "killed" on the stack
@@ -1545,23 +1557,23 @@ LUAG_FUNC( thread_index)
1545 case DONE: // got regular return values 1557 case DONE: // got regular return values
1546 { 1558 {
1547 int const nvalues{ lua_gettop(L) - 3 }; 1559 int const nvalues{ lua_gettop(L) - 3 };
1548 for( int i = nvalues; i > 0; -- i) 1560 for (int i = nvalues; i > 0; --i)
1549 { 1561 {
1550 // pop the last element of the stack, to store it in the uservalue at its proper index 1562 // pop the last element of the stack, to store it in the uservalue at its proper index
1551 lua_rawseti( L, USR, i); 1563 lua_rawseti(L, USR, i);
1552 } 1564 }
1553 } 1565 }
1554 break; 1566 break;
1555 1567
1556 case ERROR_ST: // got 3 values: nil, errstring, callstack table 1568 case ERROR_ST: // got 3 values: nil, errstring, callstack table
1557 // me[-2] could carry the stack table, but even 1569 // me[-2] could carry the stack table, but even
1558 // me[-1] is rather unnecessary (and undocumented); 1570 // me[-1] is rather unnecessary (and undocumented);
1559 // use ':join()' instead. --AKa 22-Jan-2009 1571 // use ':join()' instead. --AKa 22-Jan-2009
1560 ASSERT_L( lua_isnil( L, 4) && !lua_isnil( L, 5) && lua_istable( L, 6)); 1572 ASSERT_L(lua_isnil(L, 4) && !lua_isnil(L, 5) && lua_istable(L, 6));
1561 // store errstring at key -1 1573 // store errstring at key -1
1562 lua_pushnumber( L, -1); 1574 lua_pushnumber(L, -1);
1563 lua_pushvalue( L, 5); 1575 lua_pushvalue(L, 5);
1564 lua_rawset( L, USR); 1576 lua_rawset(L, USR);
1565 break; 1577 break;
1566 1578
1567 case CANCELLED: 1579 case CANCELLED:
@@ -1569,12 +1581,12 @@ LUAG_FUNC( thread_index)
1569 break; 1581 break;
1570 } 1582 }
1571 } 1583 }
1572 lua_settop( L, 3); // UD KEY ENV 1584 lua_settop(L, 3); // UD KEY ENV
1573 if( key != -1) 1585 if (key != -1)
1574 { 1586 {
1575 lua_pushnumber( L, -1); // UD KEY ENV -1 1587 lua_pushnumber(L, -1); // UD KEY ENV -1
1576 lua_rawget( L, USR); // UD KEY ENV "error" 1588 lua_rawget(L, USR); // UD KEY ENV "error"
1577 if( !lua_isnil( L, -1)) // an error was stored 1589 if (!lua_isnil(L, -1)) // an error was stored
1578 { 1590 {
1579 // Note: Lua 5.1 interpreter is not prepared to show 1591 // Note: Lua 5.1 interpreter is not prepared to show
1580 // non-string errors, so we use 'tostring()' here 1592 // non-string errors, so we use 'tostring()' here
@@ -1587,49 +1599,49 @@ LUAG_FUNC( thread_index)
1587 // Level 3 should show the line where 'h[x]' was read 1599 // Level 3 should show the line where 'h[x]' was read
1588 // but this only seems to work for string messages 1600 // but this only seems to work for string messages
1589 // (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009 1601 // (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009
1590 lua_getmetatable( L, UD); // UD KEY ENV "error" mt 1602 lua_getmetatable(L, UD); // UD KEY ENV "error" mt
1591 lua_getfield( L, -1, "cached_error"); // UD KEY ENV "error" mt error() 1603 lua_getfield(L, -1, "cached_error"); // UD KEY ENV "error" mt error()
1592 lua_getfield( L, -2, "cached_tostring"); // UD KEY ENV "error" mt error() tostring() 1604 lua_getfield(L, -2, "cached_tostring"); // UD KEY ENV "error" mt error() tostring()
1593 lua_pushvalue( L, 4); // UD KEY ENV "error" mt error() tostring() "error" 1605 lua_pushvalue(L, 4); // UD KEY ENV "error" mt error() tostring() "error"
1594 lua_call( L, 1, 1); // tostring( errstring) -- just in case // UD KEY ENV "error" mt error() "error" 1606 lua_call(L, 1, 1); // tostring( errstring) -- just in case // UD KEY ENV "error" mt error() "error"
1595 lua_pushinteger( L, 3); // UD KEY ENV "error" mt error() "error" 3 1607 lua_pushinteger(L, 3); // UD KEY ENV "error" mt error() "error" 3
1596 lua_call( L, 2, 0); // error( tostring( errstring), 3) // UD KEY ENV "error" mt 1608 lua_call(L, 2, 0); // error( tostring( errstring), 3) // UD KEY ENV "error" mt
1597 } 1609 }
1598 else 1610 else
1599 { 1611 {
1600 lua_pop( L, 1); // back to our 3 arguments on the stack 1612 lua_pop(L, 1); // back to our 3 arguments on the stack
1601 } 1613 }
1602 } 1614 }
1603 lua_rawgeti( L, USR, (int)key); 1615 lua_rawgeti(L, USR, (int)key);
1604 } 1616 }
1605 return 1; 1617 return 1;
1606 } 1618 }
1607 if( lua_type( L, KEY) == LUA_TSTRING) 1619 if (lua_type(L, KEY) == LUA_TSTRING)
1608 { 1620 {
1609 char const * const keystr = lua_tostring( L, KEY); 1621 char const * const keystr = lua_tostring(L, KEY);
1610 lua_settop( L, 2); // keep only our original arguments on the stack 1622 lua_settop(L, 2); // keep only our original arguments on the stack
1611 if( strcmp( keystr, "status") == 0) 1623 if (strcmp( keystr, "status") == 0)
1612 { 1624 {
1613 return push_thread_status( L, s); // push the string representing the status 1625 return push_thread_status(L, lane); // push the string representing the status
1614 } 1626 }
1615 // return UD.metatable[key] 1627 // return UD.metatable[key]
1616 lua_getmetatable( L, UD); // UD KEY mt 1628 lua_getmetatable(L, UD); // UD KEY mt
1617 lua_replace( L, -3); // mt KEY 1629 lua_replace(L, -3); // mt KEY
1618 lua_rawget( L, -2); // mt value 1630 lua_rawget(L, -2); // mt value
1619 // only "cancel" and "join" are registered as functions, any other string will raise an error 1631 // only "cancel" and "join" are registered as functions, any other string will raise an error
1620 if( lua_iscfunction( L, -1)) 1632 if (lua_iscfunction(L, -1))
1621 { 1633 {
1622 return 1; 1634 return 1;
1623 } 1635 }
1624 return luaL_error( L, "can't index a lane with '%s'", keystr); 1636 return luaL_error(L, "can't index a lane with '%s'", keystr);
1625 } 1637 }
1626 // unknown key 1638 // unknown key
1627 lua_getmetatable( L, UD); 1639 lua_getmetatable(L, UD);
1628 lua_getfield( L, -1, "cached_error"); 1640 lua_getfield(L, -1, "cached_error");
1629 lua_pushliteral( L, "Unknown key: "); 1641 lua_pushliteral(L, "Unknown key: ");
1630 lua_pushvalue( L, KEY); 1642 lua_pushvalue(L, KEY);
1631 lua_concat( L, 2); 1643 lua_concat(L, 2);
1632 lua_call( L, 1, 0); // error( "Unknown key: " .. key) -> doesn't return 1644 lua_call(L, 1, 0); // error( "Unknown key: " .. key) -> doesn't return
1633 return 0; 1645 return 0;
1634} 1646}
1635 1647
@@ -1638,33 +1650,32 @@ LUAG_FUNC( thread_index)
1638// threads() -> {}|nil 1650// threads() -> {}|nil
1639// 1651//
1640// Return a list of all known lanes 1652// Return a list of all known lanes
1641LUAG_FUNC( threads) 1653LUAG_FUNC(threads)
1642{ 1654{
1643 int const top = lua_gettop( L); 1655 int const top{ lua_gettop(L) };
1644 Universe* U = universe_get( L); 1656 Universe* const U{ universe_get(L) };
1645 1657
1646 // List _all_ still running threads 1658 // List _all_ still running threads
1647 // 1659 //
1648 U->tracking_cs.lock(); 1660 std::lock_guard<std::mutex> guard{ U->tracking_cs };
1649 if( U->tracking_first && U->tracking_first != TRACKING_END) 1661 if (U->tracking_first && U->tracking_first != TRACKING_END)
1650 {
1651 Lane* s = U->tracking_first;
1652 int index = 0;
1653 lua_newtable( L); // {}
1654 while( s != TRACKING_END)
1655 { 1662 {
1656 // insert a { name, status } tuple, so that several lanes with the same name can't clobber each other 1663 Lane* lane{ U->tracking_first };
1657 lua_newtable( L); // {} {} 1664 int index = 0;
1658 lua_pushstring( L, s->debug_name); // {} {} "name" 1665 lua_newtable(L); // {}
1659 lua_setfield( L, -2, "name"); // {} {} 1666 while (lane != TRACKING_END)
1660 push_thread_status( L, s); // {} {} "status" 1667 {
1661 lua_setfield( L, -2, "status"); // {} {} 1668 // insert a { name, status } tuple, so that several lanes with the same name can't clobber each other
1662 lua_rawseti( L, -2, ++ index); // {} 1669 lua_newtable(L); // {} {}
1663 s = s->tracking_next; 1670 lua_pushstring(L, lane->debug_name); // {} {} "name"
1671 lua_setfield(L, -2, "name"); // {} {}
1672 push_thread_status(L, lane); // {} {} "status"
1673 lua_setfield(L, -2, "status"); // {} {}
1674 lua_rawseti(L, -2, ++index); // {}
1675 lane = lane->tracking_next;
1676 }
1664 } 1677 }
1665 } 1678 return lua_gettop(L) - top; // 0 or 1
1666 U->tracking_cs.unlock();
1667 return lua_gettop( L) - top; // 0 or 1
1668} 1679}
1669#endif // HAVE_LANE_TRACKING() 1680#endif // HAVE_LANE_TRACKING()
1670 1681
@@ -1679,47 +1690,56 @@ LUAG_FUNC( threads)
1679* 1690*
1680* Returns the current time, as seconds (millisecond resolution). 1691* Returns the current time, as seconds (millisecond resolution).
1681*/ 1692*/
1682LUAG_FUNC( now_secs ) 1693LUAG_FUNC(now_secs)
1683{ 1694{
1684 lua_pushnumber( L, now_secs() ); 1695 lua_pushnumber(L, now_secs());
1685 return 1; 1696 return 1;
1686} 1697}
1687 1698
1699// #################################################################################################
1700
1688/* 1701/*
1689* wakeup_at_secs= wakeup_conv( date_tbl ) 1702* wakeup_at_secs= wakeup_conv(date_tbl)
1690*/ 1703*/
1691LUAG_FUNC( wakeup_conv ) 1704LUAG_FUNC(wakeup_conv)
1692{ 1705{
1693 int year, month, day, hour, min, sec, isdst; 1706 // date_tbl
1694 struct tm t; 1707 // .year (four digits)
1695 memset( &t, 0, sizeof( t)); 1708 // .month (1..12)
1696 // 1709 // .day (1..31)
1697 // .year (four digits) 1710 // .hour (0..23)
1698 // .month (1..12) 1711 // .min (0..59)
1699 // .day (1..31) 1712 // .sec (0..61)
1700 // .hour (0..23) 1713 // .yday (day of the year)
1701 // .min (0..59) 1714 // .isdst (daylight saving on/off)
1702 // .sec (0..61)
1703 // .yday (day of the year)
1704 // .isdst (daylight saving on/off)
1705 1715
1706 STACK_CHECK_START_REL(L, 0); 1716 STACK_CHECK_START_REL(L, 0);
1707 lua_getfield( L, 1, "year" ); year= (int)lua_tointeger(L,-1); lua_pop(L,1); 1717 auto readInteger = [L](char const* name_)
1708 lua_getfield( L, 1, "month" ); month= (int)lua_tointeger(L,-1); lua_pop(L,1); 1718 {
1709 lua_getfield( L, 1, "day" ); day= (int)lua_tointeger(L,-1); lua_pop(L,1); 1719 lua_getfield(L, 1, name_);
1710 lua_getfield( L, 1, "hour" ); hour= (int)lua_tointeger(L,-1); lua_pop(L,1); 1720 lua_Integer const val{ lua_tointeger(L, -1) };
1711 lua_getfield( L, 1, "min" ); min= (int)lua_tointeger(L,-1); lua_pop(L,1); 1721 lua_pop(L, 1);
1712 lua_getfield( L, 1, "sec" ); sec= (int)lua_tointeger(L,-1); lua_pop(L,1); 1722 return static_cast<int>(val);
1723 };
1724 int const year{ readInteger("year") };
1725 int const month{ readInteger("month") };
1726 int const day{ readInteger("day") };
1727 int const hour{ readInteger("hour") };
1728 int const min{ readInteger("min") };
1729 int const sec{ readInteger("sec") };
1730 STACK_CHECK(L, 0);
1713 1731
1714 // If Lua table has '.isdst' we trust that. If it does not, we'll let 1732 // If Lua table has '.isdst' we trust that. If it does not, we'll let
1715 // 'mktime' decide on whether the time is within DST or not (value -1). 1733 // 'mktime' decide on whether the time is within DST or not (value -1).
1716 // 1734 //
1717 lua_getfield( L, 1, "isdst" ); 1735 lua_getfield(L, 1, "isdst" );
1718 isdst= lua_isboolean(L,-1) ? lua_toboolean(L,-1) : -1; 1736 int const isdst{ lua_isboolean(L, -1) ? lua_toboolean(L, -1) : -1 };
1719 lua_pop(L,1); 1737 lua_pop(L,1);
1720 STACK_CHECK( L, 0); 1738 STACK_CHECK(L, 0);
1721 1739
1722 t.tm_year= year-1900; 1740 struct tm t;
1741 memset(&t, 0, sizeof(t));
1742 t.tm_year = year - 1900;
1723 t.tm_mon= month-1; // 0..11 1743 t.tm_mon= month-1; // 0..11
1724 t.tm_mday= day; // 1..31 1744 t.tm_mday= day; // 1..31
1725 t.tm_hour= hour; // 0..23 1745 t.tm_hour= hour; // 0..23
@@ -1727,7 +1747,7 @@ LUAG_FUNC( wakeup_conv )
1727 t.tm_sec= sec; // 0..60 1747 t.tm_sec= sec; // 0..60
1728 t.tm_isdst= isdst; // 0/1/negative 1748 t.tm_isdst= isdst; // 0/1/negative
1729 1749
1730 lua_pushnumber( L, (double) mktime( &t)); // ms=0 1750 lua_pushnumber(L, static_cast<lua_Number>(mktime(&t))); // ms=0
1731 return 1; 1751 return 1;
1732} 1752}
1733 1753
@@ -1737,17 +1757,18 @@ LUAG_FUNC( wakeup_conv )
1737 * ############################################################################################### 1757 * ###############################################################################################
1738 */ 1758 */
1739 1759
1740extern int LG_linda( lua_State* L); 1760extern int LG_linda(lua_State* L);
1741static const struct luaL_Reg lanes_functions [] = { 1761static const struct luaL_Reg lanes_functions[] =
1742 {"linda", LG_linda}, 1762{
1743 {"now_secs", LG_now_secs}, 1763 { "linda", LG_linda },
1744 {"wakeup_conv", LG_wakeup_conv}, 1764 { "now_secs", LG_now_secs },
1745 {"set_thread_priority", LG_set_thread_priority}, 1765 { "wakeup_conv", LG_wakeup_conv },
1746 {"set_thread_affinity", LG_set_thread_affinity}, 1766 { "set_thread_priority", LG_set_thread_priority },
1747 {"nameof", luaG_nameof}, 1767 { "set_thread_affinity", LG_set_thread_affinity },
1748 {"register", LG_register}, 1768 { "nameof", luaG_nameof },
1749 {"set_singlethreaded", LG_set_singlethreaded}, 1769 { "register", LG_register },
1750 {nullptr, nullptr} 1770 { "set_singlethreaded", LG_set_singlethreaded },
1771 { nullptr, nullptr }
1751}; 1772};
1752 1773
1753/* 1774/*
@@ -1778,31 +1799,33 @@ static void init_once_LOCKED( void)
1778 sudo = (geteuid() == 0); // we are root? 1799 sudo = (geteuid() == 0); // we are root?
1779 1800
1780 // If lower priorities (-2..-1) are wanted, we need to lift the main 1801 // If lower priorities (-2..-1) are wanted, we need to lift the main
1781 // thread to SCHED_RR and 50 (medium) level. Otherwise, we're always below 1802 // thread to SCHED_RR and 50 (medium) level. Otherwise, we're always below
1782 // the launched threads (even -2). 1803 // the launched threads (even -2).
1783 // 1804 //
1784#ifdef LINUX_SCHED_RR 1805#ifdef LINUX_SCHED_RR
1785 if( sudo) 1806 if (sudo)
1786 { 1807 {
1787 struct sched_param sp; 1808 struct sched_param sp;
1788 sp.sched_priority = _PRIO_0; 1809 sp.sched_priority = _PRIO_0;
1789 PT_CALL( pthread_setschedparam( pthread_self(), SCHED_RR, &sp)); 1810 PT_CALL(pthread_setschedparam(pthread_self(), SCHED_RR, &sp));
1790 } 1811 }
1791#endif // LINUX_SCHED_RR 1812#endif // LINUX_SCHED_RR
1792#endif // PLATFORM_LINUX 1813#endif // PLATFORM_LINUX
1793} 1814}
1794 1815
1816// #################################################################################################
1817
1795static volatile long s_initCount = 0; 1818static volatile long s_initCount = 0;
1796 1819
1797// upvalue 1: module name 1820// upvalue 1: module name
1798// upvalue 2: module table 1821// upvalue 2: module table
1799// param 1: settings table 1822// param 1: settings table
1800LUAG_FUNC( configure) 1823LUAG_FUNC(configure)
1801{ 1824{
1802 Universe* U = universe_get( L); 1825 Universe* U = universe_get(L);
1803 bool const from_master_state{ U == nullptr }; 1826 bool const from_master_state{ U == nullptr };
1804 char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); 1827 char const* name = luaL_checkstring(L, lua_upvalueindex(1));
1805 _ASSERT_L( L, lua_type( L, 1) == LUA_TTABLE); 1828 ASSERT_L(lua_type(L, 1) == LUA_TTABLE);
1806 1829
1807 /* 1830 /*
1808 ** Making one-time initializations. 1831 ** Making one-time initializations.
@@ -1814,199 +1837,204 @@ LUAG_FUNC( configure)
1814#if THREADAPI == THREADAPI_WINDOWS 1837#if THREADAPI == THREADAPI_WINDOWS
1815 { 1838 {
1816 static volatile int /*bool*/ go_ahead; // = 0 1839 static volatile int /*bool*/ go_ahead; // = 0
1817 if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) 1840 if (InterlockedCompareExchange(&s_initCount, 1, 0) == 0)
1818 { 1841 {
1819 init_once_LOCKED(); 1842 init_once_LOCKED();
1820 go_ahead = 1; // let others pass 1843 go_ahead = 1; // let others pass
1821 } 1844 }
1822 else 1845 else
1823 { 1846 {
1824 while( !go_ahead) { Sleep(1); } // changes threads 1847 while (!go_ahead)
1848 {
1849 Sleep(1);
1850 } // changes threads
1825 } 1851 }
1826 } 1852 }
1827#else // THREADAPI == THREADAPI_PTHREAD 1853#else // THREADAPI == THREADAPI_PTHREAD
1828 if( s_initCount == 0) 1854 if (s_initCount == 0)
1829 { 1855 {
1830 static pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER; 1856 static pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER;
1831 pthread_mutex_lock( &my_lock); 1857 pthread_mutex_lock(&my_lock);
1832 { 1858 {
1833 // Recheck now that we're within the lock 1859 // Recheck now that we're within the lock
1834 // 1860 //
1835 if( s_initCount == 0) 1861 if (s_initCount == 0)
1836 { 1862 {
1837 init_once_LOCKED(); 1863 init_once_LOCKED();
1838 s_initCount = 1; 1864 s_initCount = 1;
1839 } 1865 }
1840 } 1866 }
1841 pthread_mutex_unlock( &my_lock); 1867 pthread_mutex_unlock(&my_lock);
1842 } 1868 }
1843#endif // THREADAPI == THREADAPI_PTHREAD 1869#endif // THREADAPI == THREADAPI_PTHREAD
1844 1870
1845 STACK_GROW( L, 4); 1871 STACK_GROW(L, 4);
1846 STACK_CHECK_START_ABS( L, 1); // settings 1872 STACK_CHECK_START_ABS(L, 1); // settings
1847 1873
1848 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L)); 1874 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L));
1849 DEBUGSPEW_CODE( if( U) ++ U->debugspew_indent_depth); 1875 DEBUGSPEW_CODE( if (U) ++ U->debugspew_indent_depth);
1850 1876
1851 if(U == nullptr) 1877 if(U == nullptr)
1852 { 1878 {
1853 U = universe_create( L); // settings universe 1879 U = universe_create( L); // settings universe
1854 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1880 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1855 lua_newtable( L); // settings universe mt 1881 lua_newtable( L); // settings universe mt
1856 lua_getfield( L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout 1882 lua_getfield(L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout
1857 lua_pushcclosure( L, selfdestruct_gc, 1); // settings universe mt selfdestruct_gc 1883 lua_pushcclosure(L, selfdestruct_gc, 1); // settings universe mt selfdestruct_gc
1858 lua_setfield( L, -2, "__gc"); // settings universe mt 1884 lua_setfield(L, -2, "__gc"); // settings universe mt
1859 lua_setmetatable( L, -2); // settings universe 1885 lua_setmetatable(L, -2); // settings universe
1860 lua_pop( L, 1); // settings 1886 lua_pop(L, 1); // settings
1861 lua_getfield( L, 1, "verbose_errors"); // settings verbose_errors 1887 lua_getfield(L, 1, "verbose_errors"); // settings verbose_errors
1862 U->verboseErrors = lua_toboolean( L, -1) ? true : false; 1888 U->verboseErrors = lua_toboolean(L, -1) ? true : false;
1863 lua_pop( L, 1); // settings 1889 lua_pop(L, 1); // settings
1864 lua_getfield( L, 1, "demote_full_userdata"); // settings demote_full_userdata 1890 lua_getfield(L, 1, "demote_full_userdata"); // settings demote_full_userdata
1865 U->demoteFullUserdata = lua_toboolean( L, -1) ? true : false; 1891 U->demoteFullUserdata = lua_toboolean(L, -1) ? true : false;
1866 lua_pop( L, 1); // settings 1892 lua_pop(L, 1); // settings
1867#if HAVE_LANE_TRACKING() 1893#if HAVE_LANE_TRACKING()
1868 lua_getfield( L, 1, "track_lanes"); // settings track_lanes 1894 lua_getfield(L, 1, "track_lanes"); // settings track_lanes
1869 U->tracking_first = lua_toboolean( L, -1) ? TRACKING_END : nullptr; 1895 U->tracking_first = lua_toboolean(L, -1) ? TRACKING_END : nullptr;
1870 lua_pop( L, 1); // settings 1896 lua_pop(L, 1); // settings
1871#endif // HAVE_LANE_TRACKING() 1897#endif // HAVE_LANE_TRACKING()
1872 // Linked chains handling 1898 // Linked chains handling
1873 U->selfdestruct_first = SELFDESTRUCT_END; 1899 U->selfdestruct_first = SELFDESTRUCT_END;
1874 initialize_allocator_function( U, L); 1900 initialize_allocator_function( U, L);
1875 initialize_on_state_create( U, L); 1901 initialize_on_state_create( U, L);
1876 init_keepers( U, L); 1902 init_keepers( U, L);
1877 STACK_CHECK( L, 1); 1903 STACK_CHECK(L, 1);
1878 1904
1879 // Initialize 'timer_deep'; a common Linda object shared by all states 1905 // Initialize 'timer_deep'; a common Linda object shared by all states
1880 lua_pushcfunction( L, LG_linda); // settings lanes.linda 1906 lua_pushcfunction(L, LG_linda); // settings lanes.linda
1881 lua_pushliteral( L, "lanes-timer"); // settings lanes.linda "lanes-timer" 1907 lua_pushliteral(L, "lanes-timer"); // settings lanes.linda "lanes-timer"
1882 lua_call( L, 1, 1); // settings linda 1908 lua_call(L, 1, 1); // settings linda
1883 STACK_CHECK( L, 2); 1909 STACK_CHECK(L, 2);
1884 1910
1885 // Proxy userdata contents is only a 'DeepPrelude*' pointer 1911 // Proxy userdata contents is only a 'DeepPrelude*' pointer
1886 U->timer_deep = *lua_tofulluserdata<DeepPrelude*>(L, -1); 1912 U->timer_deep = *lua_tofulluserdata<DeepPrelude*>(L, -1);
1887 // increment refcount so that this linda remains alive as long as the universe exists. 1913 // increment refcount so that this linda remains alive as long as the universe exists.
1888 U->timer_deep->m_refcount.fetch_add(1, std::memory_order_relaxed); 1914 U->timer_deep->m_refcount.fetch_add(1, std::memory_order_relaxed);
1889 lua_pop( L, 1); // settings 1915 lua_pop(L, 1); // settings
1890 } 1916 }
1891 STACK_CHECK( L, 1); 1917 STACK_CHECK(L, 1);
1892 1918
1893 // Serialize calls to 'require' from now on, also in the primary state 1919 // Serialize calls to 'require' from now on, also in the primary state
1894 serialize_require( DEBUGSPEW_PARAM_COMMA( U) L); 1920 serialize_require( DEBUGSPEW_PARAM_COMMA( U) L);
1895 1921
1896 // Retrieve main module interface table 1922 // Retrieve main module interface table
1897 lua_pushvalue( L, lua_upvalueindex( 2)); // settings M 1923 lua_pushvalue(L, lua_upvalueindex( 2)); // settings M
1898 // remove configure() (this function) from the module interface 1924 // remove configure() (this function) from the module interface
1899 lua_pushnil( L); // settings M nil 1925 lua_pushnil( L); // settings M nil
1900 lua_setfield( L, -2, "configure"); // settings M 1926 lua_setfield(L, -2, "configure"); // settings M
1901 // add functions to the module's table 1927 // add functions to the module's table
1902 luaG_registerlibfuncs( L, lanes_functions); 1928 luaG_registerlibfuncs(L, lanes_functions);
1903#if HAVE_LANE_TRACKING() 1929#if HAVE_LANE_TRACKING()
1904 // register core.threads() only if settings say it should be available 1930 // register core.threads() only if settings say it should be available
1905 if( U->tracking_first != nullptr) 1931 if (U->tracking_first != nullptr)
1906 { 1932 {
1907 lua_pushcfunction( L, LG_threads); // settings M LG_threads() 1933 lua_pushcfunction(L, LG_threads); // settings M LG_threads()
1908 lua_setfield( L, -2, "threads"); // settings M 1934 lua_setfield(L, -2, "threads"); // settings M
1909 } 1935 }
1910#endif // HAVE_LANE_TRACKING() 1936#endif // HAVE_LANE_TRACKING()
1911 STACK_CHECK( L, 2); 1937 STACK_CHECK(L, 2);
1912 1938
1913 { 1939 {
1914 char const* errmsg{ push_deep_proxy(L, U->timer_deep, 0, eLM_LaneBody) }; // settings M timer_deep 1940 char const* errmsg{ push_deep_proxy(L, U->timer_deep, 0, eLM_LaneBody) }; // settings M timer_deep
1915 if( errmsg != nullptr) 1941 if (errmsg != nullptr)
1916 { 1942 {
1917 return luaL_error( L, errmsg); 1943 return luaL_error(L, errmsg);
1918 } 1944 }
1919 lua_setfield( L, -2, "timer_gateway"); // settings M 1945 lua_setfield(L, -2, "timer_gateway"); // settings M
1920 } 1946 }
1921 STACK_CHECK( L, 2); 1947 STACK_CHECK(L, 2);
1922 1948
1923 // prepare the metatable for threads 1949 // prepare the metatable for threads
1924 // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname } 1950 // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join, get_debug_threadname }
1925 // 1951 //
1926 if( luaL_newmetatable( L, "Lane")) // settings M mt 1952 if (luaL_newmetatable(L, "Lane")) // settings M mt
1927 { 1953 {
1928 lua_pushcfunction( L, LG_thread_gc); // settings M mt LG_thread_gc 1954 lua_pushcfunction(L, LG_thread_gc); // settings M mt LG_thread_gc
1929 lua_setfield( L, -2, "__gc"); // settings M mt 1955 lua_setfield(L, -2, "__gc"); // settings M mt
1930 lua_pushcfunction( L, LG_thread_index); // settings M mt LG_thread_index 1956 lua_pushcfunction(L, LG_thread_index); // settings M mt LG_thread_index
1931 lua_setfield( L, -2, "__index"); // settings M mt 1957 lua_setfield(L, -2, "__index"); // settings M mt
1932 lua_getglobal( L, "error"); // settings M mt error 1958 lua_getglobal(L, "error"); // settings M mt error
1933 ASSERT_L( lua_isfunction( L, -1)); 1959 ASSERT_L( lua_isfunction(L, -1));
1934 lua_setfield( L, -2, "cached_error"); // settings M mt 1960 lua_setfield(L, -2, "cached_error"); // settings M mt
1935 lua_getglobal( L, "tostring"); // settings M mt tostring 1961 lua_getglobal(L, "tostring"); // settings M mt tostring
1936 ASSERT_L( lua_isfunction( L, -1)); 1962 ASSERT_L( lua_isfunction(L, -1));
1937 lua_setfield( L, -2, "cached_tostring"); // settings M mt 1963 lua_setfield(L, -2, "cached_tostring"); // settings M mt
1938 lua_pushcfunction( L, LG_thread_join); // settings M mt LG_thread_join 1964 lua_pushcfunction(L, LG_thread_join); // settings M mt LG_thread_join
1939 lua_setfield( L, -2, "join"); // settings M mt 1965 lua_setfield(L, -2, "join"); // settings M mt
1940 lua_pushcfunction( L, LG_get_debug_threadname); // settings M mt LG_get_debug_threadname 1966 lua_pushcfunction(L, LG_get_debug_threadname); // settings M mt LG_get_debug_threadname
1941 lua_setfield( L, -2, "get_debug_threadname"); // settings M mt 1967 lua_setfield(L, -2, "get_debug_threadname"); // settings M mt
1942 lua_pushcfunction( L, LG_thread_cancel); // settings M mt LG_thread_cancel 1968 lua_pushcfunction(L, LG_thread_cancel); // settings M mt LG_thread_cancel
1943 lua_setfield( L, -2, "cancel"); // settings M mt 1969 lua_setfield(L, -2, "cancel"); // settings M mt
1944 lua_pushliteral( L, "Lane"); // settings M mt "Lane" 1970 lua_pushliteral(L, "Lane"); // settings M mt "Lane"
1945 lua_setfield( L, -2, "__metatable"); // settings M mt 1971 lua_setfield(L, -2, "__metatable"); // settings M mt
1946 } 1972 }
1947 1973
1948 lua_pushcclosure( L, LG_lane_new, 1); // settings M lane_new 1974 lua_pushcclosure(L, LG_lane_new, 1); // settings M lane_new
1949 lua_setfield( L, -2, "lane_new"); // settings M 1975 lua_setfield(L, -2, "lane_new"); // settings M
1950 1976
1951 // we can't register 'lanes.require' normally because we want to create an upvalued closure 1977 // we can't register 'lanes.require' normally because we want to create an upvalued closure
1952 lua_getglobal( L, "require"); // settings M require 1978 lua_getglobal(L, "require"); // settings M require
1953 lua_pushcclosure( L, LG_require, 1); // settings M lanes.require 1979 lua_pushcclosure(L, LG_require, 1); // settings M lanes.require
1954 lua_setfield( L, -2, "require"); // settings M 1980 lua_setfield(L, -2, "require"); // settings M
1955 1981
1956 lua_pushfstring( 1982 lua_pushfstring(
1957 L, "%d.%d.%d" 1983 L, "%d.%d.%d"
1958 , LANES_VERSION_MAJOR, LANES_VERSION_MINOR, LANES_VERSION_PATCH 1984 , LANES_VERSION_MAJOR, LANES_VERSION_MINOR, LANES_VERSION_PATCH
1959 ); // settings M VERSION 1985 ); // settings M VERSION
1960 lua_setfield( L, -2, "version"); // settings M 1986 lua_setfield(L, -2, "version"); // settings M
1961 1987
1962 lua_pushinteger(L, THREAD_PRIO_MAX); // settings M THREAD_PRIO_MAX 1988 lua_pushinteger(L, THREAD_PRIO_MAX); // settings M THREAD_PRIO_MAX
1963 lua_setfield( L, -2, "max_prio"); // settings M 1989 lua_setfield(L, -2, "max_prio"); // settings M
1964 1990
1965 CANCEL_ERROR.push(L); // settings M CANCEL_ERROR 1991 CANCEL_ERROR.push(L); // settings M CANCEL_ERROR
1966 lua_setfield( L, -2, "cancel_error"); // settings M 1992 lua_setfield(L, -2, "cancel_error"); // settings M
1967 1993
1968 STACK_CHECK( L, 2); // reference stack contains only the function argument 'settings' 1994 STACK_CHECK(L, 2); // reference stack contains only the function argument 'settings'
1969 // we'll need this every time we transfer some C function from/to this state 1995 // we'll need this every time we transfer some C function from/to this state
1970 LOOKUP_REGKEY.set_registry(L, [](lua_State* L) { lua_newtable(L); }); // settings M 1996 LOOKUP_REGKEY.set_registry(L, [](lua_State* L) { lua_newtable(L); }); // settings M
1971 STACK_CHECK( L, 2); 1997 STACK_CHECK(L, 2);
1972 1998
1973 // register all native functions found in that module in the transferable functions database 1999 // register all native functions found in that module in the transferable functions database
1974 // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names) 2000 // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names)
1975 // for example in package.loaded["lanes.core"].* 2001 // for example in package.loaded["lanes.core"].*
1976 populate_func_lookup_table( L, -1, name); 2002 populate_func_lookup_table(L, -1, name);
1977 STACK_CHECK( L, 2); 2003 STACK_CHECK(L, 2);
1978 2004
1979 // record all existing C/JIT-fast functions 2005 // record all existing C/JIT-fast functions
1980 // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack 2006 // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack
1981 if( from_master_state) 2007 if (from_master_state)
1982 { 2008 {
1983 // don't do this when called during the initialization of a new lane, 2009 // don't do this when called during the initialization of a new lane,
1984 // because we will do it after on_state_create() is called, 2010 // because we will do it after on_state_create() is called,
1985 // and we don't want to skip _G because of caching in case globals are created then 2011 // and we don't want to skip _G because of caching in case globals are created then
1986 lua_pushglobaltable( L); // settings M _G 2012 lua_pushglobaltable( L); // settings M _G
1987 populate_func_lookup_table( L, -1, nullptr); 2013 populate_func_lookup_table(L, -1, nullptr);
1988 lua_pop( L, 1); // settings M 2014 lua_pop(L, 1); // settings M
1989 } 2015 }
1990 lua_pop( L, 1); // settings 2016 lua_pop(L, 1); // settings
1991 2017
1992 // set _R[CONFIG_REGKEY] = settings 2018 // set _R[CONFIG_REGKEY] = settings
1993 CONFIG_REGKEY.set_registry(L, [](lua_State* L) { lua_pushvalue(L, -2); }); 2019 CONFIG_REGKEY.set_registry(L, [](lua_State* L) { lua_pushvalue(L, -2); });
1994 STACK_CHECK( L, 1); 2020 STACK_CHECK(L, 1);
1995 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END, L)); 2021 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: lanes.configure() END\n" INDENT_END, L));
1996 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 2022 DEBUGSPEW_CODE(--U->debugspew_indent_depth);
1997 // Return the settings table 2023 // Return the settings table
1998 return 1; 2024 return 1;
1999} 2025}
2000 2026
2027// #################################################################################################
2028
2001#if defined PLATFORM_WIN32 && !defined NDEBUG 2029#if defined PLATFORM_WIN32 && !defined NDEBUG
2002#include <signal.h> 2030#include <signal.h>
2003#include <conio.h> 2031#include <conio.h>
2004 2032
2005void signal_handler( int signal) 2033void signal_handler(int signal)
2006{ 2034{
2007 if( signal == SIGABRT) 2035 if (signal == SIGABRT)
2008 { 2036 {
2009 _cprintf( "caught abnormal termination!"); 2037 _cprintf("caught abnormal termination!");
2010 abort(); 2038 abort();
2011 } 2039 }
2012} 2040}
@@ -2015,12 +2043,12 @@ void signal_handler( int signal)
2015// don't forget to toggle Debug/Exceptions/Win32 in visual Studio too! 2043// don't forget to toggle Debug/Exceptions/Win32 in visual Studio too!
2016static volatile long s_ecoc_initCount = 0; 2044static volatile long s_ecoc_initCount = 0;
2017static volatile int s_ecoc_go_ahead = 0; 2045static volatile int s_ecoc_go_ahead = 0;
2018static void EnableCrashingOnCrashes( void) 2046static void EnableCrashingOnCrashes(void)
2019{ 2047{
2020 if( InterlockedCompareExchange( &s_ecoc_initCount, 1, 0) == 0) 2048 if (InterlockedCompareExchange(&s_ecoc_initCount, 1, 0) == 0)
2021 { 2049 {
2022 typedef BOOL (WINAPI* tGetPolicy)( LPDWORD lpFlags); 2050 typedef BOOL(WINAPI * tGetPolicy)(LPDWORD lpFlags);
2023 typedef BOOL (WINAPI* tSetPolicy)( DWORD dwFlags); 2051 typedef BOOL(WINAPI * tSetPolicy)(DWORD dwFlags);
2024 const DWORD EXCEPTION_SWALLOWING = 0x1; 2052 const DWORD EXCEPTION_SWALLOWING = 0x1;
2025 2053
2026 HMODULE kernel32 = LoadLibraryA("kernel32.dll"); 2054 HMODULE kernel32 = LoadLibraryA("kernel32.dll");
@@ -2039,14 +2067,17 @@ static void EnableCrashingOnCrashes( void)
2039 } 2067 }
2040 FreeLibrary(kernel32); 2068 FreeLibrary(kernel32);
2041 } 2069 }
2042 //typedef void (* SignalHandlerPointer)( int); 2070 // typedef void (* SignalHandlerPointer)( int);
2043 /*SignalHandlerPointer previousHandler =*/ signal( SIGABRT, signal_handler); 2071 /*SignalHandlerPointer previousHandler =*/signal(SIGABRT, signal_handler);
2044 2072
2045 s_ecoc_go_ahead = 1; // let others pass 2073 s_ecoc_go_ahead = 1; // let others pass
2046 } 2074 }
2047 else 2075 else
2048 { 2076 {
2049 while( !s_ecoc_go_ahead) { Sleep(1); } // changes threads 2077 while (!s_ecoc_go_ahead)
2078 {
2079 Sleep(1);
2080 } // changes threads
2050 } 2081 }
2051} 2082}
2052#endif // PLATFORM_WIN32 && !defined NDEBUG 2083#endif // PLATFORM_WIN32 && !defined NDEBUG
@@ -2057,51 +2088,51 @@ LANES_API int luaopen_lanes_core( lua_State* L)
2057 EnableCrashingOnCrashes(); 2088 EnableCrashingOnCrashes();
2058#endif // defined PLATFORM_WIN32 && !defined NDEBUG 2089#endif // defined PLATFORM_WIN32 && !defined NDEBUG
2059 2090
2060 STACK_GROW( L, 4); 2091 STACK_GROW(L, 4);
2061 STACK_CHECK_START_REL(L, 0); 2092 STACK_CHECK_START_REL(L, 0);
2062 2093
2063 // Prevent PUC-Lua/LuaJIT mismatch. Hopefully this works for MoonJIT too 2094 // Prevent PUC-Lua/LuaJIT mismatch. Hopefully this works for MoonJIT too
2064 lua_getglobal( L, "jit"); // {jit?} 2095 lua_getglobal(L, "jit"); // {jit?}
2065#if LUAJIT_FLAVOR() == 0 2096#if LUAJIT_FLAVOR() == 0
2066 if (!lua_isnil( L, -1)) 2097 if (!lua_isnil(L, -1))
2067 return luaL_error( L, "Lanes is built for PUC-Lua, don't run from LuaJIT"); 2098 return luaL_error(L, "Lanes is built for PUC-Lua, don't run from LuaJIT");
2068#else 2099#else
2069 if (lua_isnil( L, -1)) 2100 if (lua_isnil(L, -1))
2070 return luaL_error( L, "Lanes is built for LuaJIT, don't run from PUC-Lua"); 2101 return luaL_error(L, "Lanes is built for LuaJIT, don't run from PUC-Lua");
2071#endif 2102#endif
2072 lua_pop( L, 1); // 2103 lua_pop(L, 1); //
2073 STACK_CHECK(L, 0); 2104 STACK_CHECK(L, 0);
2074 2105
2075 // Create main module interface table 2106 // Create main module interface table
2076 // we only have 1 closure, which must be called to configure Lanes 2107 // we only have 1 closure, which must be called to configure Lanes
2077 lua_newtable( L); // M 2108 lua_newtable( L); // M
2078 lua_pushvalue( L, 1); // M "lanes.core" 2109 lua_pushvalue(L, 1); // M "lanes.core"
2079 lua_pushvalue( L, -2); // M "lanes.core" M 2110 lua_pushvalue(L, -2); // M "lanes.core" M
2080 lua_pushcclosure( L, LG_configure, 2); // M LG_configure() 2111 lua_pushcclosure(L, LG_configure, 2); // M LG_configure()
2081 CONFIG_REGKEY.query_registry(L); // M LG_configure() settings 2112 CONFIG_REGKEY.query_registry(L); // M LG_configure() settings
2082 if( !lua_isnil( L, -1)) // this is not the first require "lanes.core": call configure() immediately 2113 if (!lua_isnil(L, -1)) // this is not the first require "lanes.core": call configure() immediately
2083 { 2114 {
2084 lua_pushvalue( L, -1); // M LG_configure() settings settings 2115 lua_pushvalue(L, -1); // M LG_configure() settings settings
2085 lua_setfield( L, -4, "settings"); // M LG_configure() settings 2116 lua_setfield(L, -4, "settings"); // M LG_configure() settings
2086 lua_call( L, 1, 0); // M 2117 lua_call(L, 1, 0); // M
2087 } 2118 }
2088 else 2119 else
2089 { 2120 {
2090 // will do nothing on first invocation, as we haven't stored settings in the registry yet 2121 // will do nothing on first invocation, as we haven't stored settings in the registry yet
2091 lua_setfield( L, -3, "settings"); // M LG_configure() 2122 lua_setfield(L, -3, "settings"); // M LG_configure()
2092 lua_setfield( L, -2, "configure"); // M 2123 lua_setfield(L, -2, "configure"); // M
2093 } 2124 }
2094 2125
2095 STACK_CHECK( L, 1); 2126 STACK_CHECK(L, 1);
2096 return 1; 2127 return 1;
2097} 2128}
2098 2129
2099static int default_luaopen_lanes( lua_State* L) 2130static int default_luaopen_lanes( lua_State* L)
2100{ 2131{
2101 int rc = luaL_loadfile( L, "lanes.lua") || lua_pcall( L, 0, 1, 0); 2132 int rc = luaL_loadfile(L, "lanes.lua") || lua_pcall(L, 0, 1, 0);
2102 if( rc != LUA_OK) 2133 if (rc != LUA_OK)
2103 { 2134 {
2104 return luaL_error( L, "failed to initialize embedded Lanes"); 2135 return luaL_error(L, "failed to initialize embedded Lanes");
2105 } 2136 }
2106 return 1; 2137 return 1;
2107} 2138}
@@ -2111,10 +2142,10 @@ LANES_API void luaopen_lanes_embedded( lua_State* L, lua_CFunction _luaopen_lane
2111{ 2142{
2112 STACK_CHECK_START_REL(L, 0); 2143 STACK_CHECK_START_REL(L, 0);
2113 // pre-require lanes.core so that when lanes.lua calls require "lanes.core" it finds it is already loaded 2144 // pre-require lanes.core so that when lanes.lua calls require "lanes.core" it finds it is already loaded
2114 luaL_requiref( L, "lanes.core", luaopen_lanes_core, 0); // ... lanes.core 2145 luaL_requiref(L, "lanes.core", luaopen_lanes_core, 0); // ... lanes.core
2115 lua_pop( L, 1); // ... 2146 lua_pop(L, 1); // ...
2116 STACK_CHECK( L, 0); 2147 STACK_CHECK(L, 0);
2117 // call user-provided function that runs the chunk "lanes.lua" from wherever they stored it 2148 // call user-provided function that runs the chunk "lanes.lua" from wherever they stored it
2118 luaL_requiref( L, "lanes", _luaopen_lanes ? _luaopen_lanes : default_luaopen_lanes, 0); // ... lanes 2149 luaL_requiref(L, "lanes", _luaopen_lanes ? _luaopen_lanes : default_luaopen_lanes, 0); // ... lanes
2119 STACK_CHECK( L, 1); 2150 STACK_CHECK(L, 1);
2120} 2151}
diff --git a/src/universe.h b/src/universe.h
index a31e6c2..1079915 100644
--- a/src/universe.h
+++ b/src/universe.h
@@ -83,9 +83,9 @@ class ProtectedAllocator : public AllocatorDefinition
83 83
84 static void* protected_lua_Alloc(void* ud_, void* ptr_, size_t osize_, size_t nsize_) 84 static void* protected_lua_Alloc(void* ud_, void* ptr_, size_t osize_, size_t nsize_)
85 { 85 {
86 ProtectedAllocator* const s{ static_cast<ProtectedAllocator*>(ud_) }; 86 ProtectedAllocator* const allocator{ static_cast<ProtectedAllocator*>(ud_) };
87 std::lock_guard<std::mutex> guard{ s->m_lock }; 87 std::lock_guard<std::mutex> guard{ allocator->m_lock };
88 return s->m_allocF(s->m_allocUD, ptr_, osize_, nsize_); 88 return allocator->m_allocF(allocator->m_allocUD, ptr_, osize_, nsize_);
89 } 89 }
90 90
91 public: 91 public: