aboutsummaryrefslogtreecommitdiff
path: root/src/lanes.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lanes.cpp')
-rw-r--r--src/lanes.cpp329
1 files changed, 152 insertions, 177 deletions
diff --git a/src/lanes.cpp b/src/lanes.cpp
index 17d4f67..3aa3365 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -101,8 +101,63 @@ THE SOFTWARE.
101 101
102#include <atomic> 102#include <atomic>
103 103
104// forwarding (will do things better later) 104// #################################################################################################
105static void tracking_add(Lane* lane_); 105
106#if HAVE_LANE_TRACKING()
107
108// The chain is ended by '(Lane*)(-1)', not nullptr:
109// 'tracking_first -> ... -> ... -> (-1)'
110#define TRACKING_END ((Lane *)(-1))
111
112/*
113 * Add the lane to tracking chain; the ones still running at the end of the
114 * whole process will be cancelled.
115 */
116static void tracking_add(Lane* lane_)
117{
118 std::lock_guard<std::mutex> guard{ lane_->U->tracking_cs };
119 assert(lane_->tracking_next == nullptr);
120
121 lane_->tracking_next = lane_->U->tracking_first;
122 lane_->U->tracking_first = lane_;
123}
124
125// #################################################################################################
126
127/*
128 * A free-running lane has ended; remove it from tracking chain
129 */
130[[nodiscard]] static bool tracking_remove(Lane* lane_)
131{
132 bool found{ false };
133 std::lock_guard<std::mutex> guard{ lane_->U->tracking_cs };
134 // Make sure (within the MUTEX) that we actually are in the chain
135 // still (at process exit they will remove us from chain and then
136 // cancel/kill).
137 //
138 if (lane_->tracking_next != nullptr)
139 {
140 Lane** ref = (Lane**) &lane_->U->tracking_first;
141
142 while( *ref != TRACKING_END)
143 {
144 if (*ref == lane_)
145 {
146 *ref = lane_->tracking_next;
147 lane_->tracking_next = nullptr;
148 found = true;
149 break;
150 }
151 ref = (Lane**) &((*ref)->tracking_next);
152 }
153 assert( found);
154 }
155 return found;
156}
157
158#endif // HAVE_LANE_TRACKING()
159
160// #################################################################################################
106 161
107Lane::Lane(Universe* U_, lua_State* L_) 162Lane::Lane(Universe* U_, lua_State* L_)
108: U{ U_ } 163: U{ U_ }
@@ -180,114 +235,44 @@ static constexpr UniqueKey FINALIZER_REGKEY{ 0x188fccb8bf348e09ull };
180 235
181// ################################################################################################# 236// #################################################################################################
182 237
183/* 238Lane::~Lane()
184* Push a table stored in registry onto Lua stack.
185*
186* If there is no existing table, create one if 'create' is true.
187*
188* Returns: true if a table was pushed
189* false if no table found, not created, and nothing pushed
190*/
191[[nodiscard]] static bool push_registry_table(lua_State* L, UniqueKey key, bool create)
192{ 239{
193 STACK_GROW(L, 3); 240 // Clean up after a (finished) thread
194 STACK_CHECK_START_REL(L, 0); 241 //
195 242#if HAVE_LANE_TRACKING()
196 key.pushValue(L); // ? 243 if (U->tracking_first != nullptr)
197 if (lua_isnil(L, -1)) // nil?
198 { 244 {
199 lua_pop(L, 1); // 245 // Lane was cleaned up, no need to handle at process termination
200 STACK_CHECK(L, 0); 246 std::ignore = tracking_remove(this);
201
202 if (!create)
203 {
204 return false;
205 }
206
207 lua_newtable(L); // t
208 key.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); });
209 } 247 }
210 STACK_CHECK(L, 1); 248#endif // HAVE_LANE_TRACKING()
211 return true; // table pushed
212} 249}
213 250
214// ################################################################################################# 251// #################################################################################################
252// ########################################## Finalizer ############################################
253// #################################################################################################
215 254
216#if HAVE_LANE_TRACKING()
217 255
218// The chain is ended by '(Lane*)(-1)', not nullptr: 256// Push the finalizers table on the stack.
219// 'tracking_first -> ... -> ... -> (-1)' 257// If there is no existing table, create ti.
220#define TRACKING_END ((Lane *)(-1)) 258static void push_finalizers_table(lua_State* L)
221
222/*
223 * Add the lane to tracking chain; the ones still running at the end of the
224 * whole process will be cancelled.
225 */
226static void tracking_add(Lane* lane_)
227{ 259{
228 std::lock_guard<std::mutex> guard{ lane_->U->tracking_cs }; 260 STACK_GROW(L, 3);
229 assert(lane_->tracking_next == nullptr); 261 STACK_CHECK_START_REL(L, 0);
230
231 lane_->tracking_next = lane_->U->tracking_first;
232 lane_->U->tracking_first = lane_;
233}
234
235// #################################################################################################
236 262
237/* 263 FINALIZER_REGKEY.pushValue(L); // ?
238 * A free-running lane has ended; remove it from tracking chain 264 if (lua_isnil(L, -1)) // nil?
239 */
240[[nodiscard]] static bool tracking_remove(Lane* lane_)
241{
242 bool found{ false };
243 std::lock_guard<std::mutex> guard{ lane_->U->tracking_cs };
244 // Make sure (within the MUTEX) that we actually are in the chain
245 // still (at process exit they will remove us from chain and then
246 // cancel/kill).
247 //
248 if (lane_->tracking_next != nullptr)
249 { 265 {
250 Lane** ref = (Lane**) &lane_->U->tracking_first; 266 lua_pop(L, 1); //
251 267 // store a newly created table in the registry, but leave it on the stack too
252 while( *ref != TRACKING_END) 268 lua_newtable(L); // t
253 { 269 FINALIZER_REGKEY.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); }); // t
254 if (*ref == lane_)
255 {
256 *ref = lane_->tracking_next;
257 lane_->tracking_next = nullptr;
258 found = true;
259 break;
260 }
261 ref = (Lane**) &((*ref)->tracking_next);
262 }
263 assert( found);
264 } 270 }
265 return found; 271 STACK_CHECK(L, 1);
266} 272}
267 273
268#endif // HAVE_LANE_TRACKING()
269
270// ################################################################################################# 274// #################################################################################################
271 275
272Lane::~Lane()
273{
274 // Clean up after a (finished) thread
275 //
276#if HAVE_LANE_TRACKING()
277 if (U->tracking_first != nullptr)
278 {
279 // Lane was cleaned up, no need to handle at process termination
280 std::ignore = tracking_remove(this);
281 }
282#endif // HAVE_LANE_TRACKING()
283}
284
285/*
286 * ###############################################################################################
287 * ########################################## Finalizer ##########################################
288 * ###############################################################################################
289 */
290
291//--- 276//---
292// void= finalizer( finalizer_func ) 277// void= finalizer( finalizer_func )
293// 278//
@@ -296,12 +281,12 @@ Lane::~Lane()
296// Add a function that will be called when exiting the lane, either via 281// Add a function that will be called when exiting the lane, either via
297// normal return or an error. 282// normal return or an error.
298// 283//
299LUAG_FUNC( set_finalizer) 284LUAG_FUNC(set_finalizer)
300{ 285{
301 luaL_argcheck(L, lua_isfunction(L, 1), 1, "finalizer should be a function"); 286 luaL_argcheck(L, lua_isfunction(L, 1), 1, "finalizer should be a function");
302 luaL_argcheck(L, lua_gettop( L) == 1, 1, "too many arguments"); 287 luaL_argcheck(L, lua_gettop( L) == 1, 1, "too many arguments");
303 // Get the current finalizer table (if any), create one if it doesn't exist 288 // Get the current finalizer table (if any), create one if it doesn't exist
304 std::ignore = push_registry_table(L, FINALIZER_REGKEY, true); // finalizer {finalisers} 289 push_finalizers_table(L); // finalizer {finalisers}
305 STACK_GROW(L, 2); 290 STACK_GROW(L, 2);
306 lua_pushinteger(L, lua_rawlen(L, -1) + 1); // finalizer {finalisers} idx 291 lua_pushinteger(L, lua_rawlen(L, -1) + 1); // finalizer {finalisers} idx
307 lua_pushvalue(L, 1); // finalizer {finalisers} idx finalizer 292 lua_pushvalue(L, 1); // finalizer {finalisers} idx finalizer
@@ -310,7 +295,44 @@ LUAG_FUNC( set_finalizer)
310 return 0; 295 return 0;
311} 296}
312 297
298// #################################################################################################
299
300static void push_stack_trace(lua_State* L, int rc_, int stk_base_)
301{
302 // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry
303 switch(rc_)
304 {
305 case LUA_OK: // no error, body return values are on the stack
306 break;
313 307
308 case LUA_ERRRUN: // cancellation or a runtime error
309#if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler
310 {
311 STACK_CHECK_START_REL(L, 0);
312 // fetch the call stack table from the registry where the handler stored it
313 STACK_GROW(L, 1);
314 // yields nil if no stack was generated (in case of cancellation for example)
315 STACKTRACE_REGKEY.pushValue(L); // err trace|nil
316 STACK_CHECK(L, 1);
317
318 // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed
319 // For other errors, the message can be whatever was thrown, and we should have a stack trace table
320 ASSERT_L(lua_type(L, 1 + stk_base_) == (CANCEL_ERROR.equals(L, stk_base_) ? LUA_TNIL : LUA_TTABLE));
321 // Just leaving the stack trace table on the stack is enough to get it through to the master.
322 break;
323 }
324#endif // fall through if not ERROR_FULL_STACK
325
326 case LUA_ERRMEM: // memory allocation error (handler not called)
327 case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition)
328 default:
329 // we should have a single value which is either a string (the error message) or CANCEL_ERROR
330 ASSERT_L((lua_gettop(L) == stk_base_) && ((lua_type(L, stk_base_) == LUA_TSTRING) || CANCEL_ERROR.equals(L, stk_base_)));
331 break;
332 }
333}
334
335// #################################################################################################
314//--- 336//---
315// Run finalizers - if any - with the given parameters 337// Run finalizers - if any - with the given parameters
316// 338//
@@ -324,43 +346,37 @@ LUAG_FUNC( set_finalizer)
324// 346//
325// TBD: should we add stack trace on failing finalizer, wouldn't be hard.. 347// TBD: should we add stack trace on failing finalizer, wouldn't be hard..
326// 348//
327static void push_stack_trace( lua_State* L, int rc_, int stk_base_);
328 349
329[[nodiscard]] static int run_finalizers(lua_State* L, int lua_rc) 350[[nodiscard]] static int run_finalizers(lua_State* L, int lua_rc_)
330{ 351{
331 int finalizers_index; 352 FINALIZER_REGKEY.pushValue(L); // ... finalizers?
332 int n; 353 if (lua_isnil(L, -1))
333 int err_handler_index = 0;
334 int rc = LUA_OK; // ...
335 if (!push_registry_table(L, FINALIZER_REGKEY, false)) // ... finalizers?
336 { 354 {
355 lua_pop(L, 1);
337 return 0; // no finalizers 356 return 0; // no finalizers
338 } 357 }
339 358
340 STACK_GROW(L, 5); 359 STACK_GROW(L, 5);
341 360
342 finalizers_index = lua_gettop( L); 361 int const finalizers_index{ lua_gettop(L) };
343 362 int const err_handler_index{ ERROR_FULL_STACK ? (lua_pushcfunction(L, lane_error), lua_gettop(L)) : 0 };
344#if ERROR_FULL_STACK
345 lua_pushcfunction(L, lane_error); // ... finalizers lane_error
346 err_handler_index = lua_gettop( L);
347#endif // ERROR_FULL_STACK
348 363
349 for( n = (int) lua_rawlen(L, finalizers_index); n > 0; -- n) 364 int rc{ LUA_OK };
365 for (int n = static_cast<int>(lua_rawlen(L, finalizers_index)); n > 0; --n)
350 { 366 {
351 int args = 0; 367 int args = 0;
352 lua_pushinteger(L, n); // ... finalizers lane_error n 368 lua_pushinteger(L, n); // ... finalizers lane_error n
353 lua_rawget(L, finalizers_index); // ... finalizers lane_error finalizer 369 lua_rawget(L, finalizers_index); // ... finalizers lane_error finalizer
354 ASSERT_L( lua_isfunction(L, -1)); 370 ASSERT_L(lua_isfunction(L, -1));
355 if (lua_rc != LUA_OK) // we have an error message and an optional stack trace at the bottom of the stack 371 if (lua_rc_ != LUA_OK) // we have an error message and an optional stack trace at the bottom of the stack
356 { 372 {
357 ASSERT_L( finalizers_index == 2 || finalizers_index == 3); 373 ASSERT_L( finalizers_index == 2 || finalizers_index == 3);
358 //char const* err_msg = lua_tostring(L, 1); 374 //char const* err_msg = lua_tostring(L, 1);
359 lua_pushvalue(L, 1); // ... finalizers lane_error finalizer err_msg 375 lua_pushvalue(L, 1); // ... finalizers lane_error finalizer err_msg
360 // 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 376 // 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
361 if (finalizers_index == 3) 377 if (finalizers_index == 3)
362 { 378 {
363 lua_pushvalue(L, 2); // ... finalizers lane_error finalizer err_msg stack_trace 379 lua_pushvalue(L, 2); // ... finalizers lane_error finalizer err_msg stack_trace
364 } 380 }
365 args = finalizers_index - 1; 381 args = finalizers_index - 1;
366 } 382 }
@@ -369,21 +385,21 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_);
369 rc = lua_pcall(L, args, 0, err_handler_index); // ... finalizers lane_error err_msg2? 385 rc = lua_pcall(L, args, 0, err_handler_index); // ... finalizers lane_error err_msg2?
370 if (rc != LUA_OK) 386 if (rc != LUA_OK)
371 { 387 {
372 push_stack_trace(L, rc, lua_gettop( L)); 388 push_stack_trace(L, rc, lua_gettop(L));
373 // If one finalizer fails, don't run the others. Return this 389 // If one finalizer fails, don't run the others. Return this
374 // as the 'real' error, replacing what we could have had (or not) 390 // as the 'real' error, replacing what we could have had (or not)
375 // from the actual code. 391 // from the actual code.
376 break; 392 break;
377 } 393 }
378 // no error, proceed to next finalizer // ... finalizers lane_error 394 // no error, proceed to next finalizer // ... finalizers lane_error
379 } 395 }
380 396
381 if (rc != LUA_OK) 397 if (rc != LUA_OK)
382 { 398 {
383 // ERROR_FULL_STACK accounts for the presence of lane_error on the stack 399 // ERROR_FULL_STACK accounts for the presence of lane_error on the stack
384 int nb_err_slots = lua_gettop( L) - finalizers_index - ERROR_FULL_STACK; 400 int const nb_err_slots{ lua_gettop(L) - finalizers_index - ERROR_FULL_STACK };
385 // a finalizer generated an error, this is what we leave of the stack 401 // a finalizer generated an error, this is what we leave of the stack
386 for( n = nb_err_slots; n > 0; -- n) 402 for (int n = nb_err_slots; n > 0; --n)
387 { 403 {
388 lua_replace(L, n); 404 lua_replace(L, n);
389 } 405 }
@@ -399,9 +415,9 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_);
399} 415}
400 416
401/* 417/*
402 * ############################################################################################### 418 * ################################################################################################
403 * ########################################### Threads ########################################### 419 * ########################################### Threads ############################################
404 * ############################################################################################### 420 * ################################################################################################
405 */ 421 */
406 422
407// 423//
@@ -425,7 +441,7 @@ static void selfdestruct_add(Lane* lane_)
425 lane_->U->selfdestruct_first = lane_; 441 lane_->U->selfdestruct_first = lane_;
426} 442}
427 443
428// ############################################################################################### 444// #################################################################################################
429 445
430/* 446/*
431 * A free-running lane has ended; remove it from selfdestruct chain 447 * A free-running lane has ended; remove it from selfdestruct chain
@@ -460,7 +476,7 @@ static void selfdestruct_add(Lane* lane_)
460 return found; 476 return found;
461} 477}
462 478
463// ############################################################################################### 479// #################################################################################################
464 480
465/* 481/*
466* Process end; cancel any still free-running threads 482* Process end; cancel any still free-running threads
@@ -567,7 +583,7 @@ static void selfdestruct_add(Lane* lane_)
567 return 0; 583 return 0;
568} 584}
569 585
570// ############################################################################################### 586// #################################################################################################
571 587
572//--- 588//---
573// = _single( [cores_uint=1] ) 589// = _single( [cores_uint=1] )
@@ -597,7 +613,7 @@ LUAG_FUNC( set_singlethreaded)
597#endif 613#endif
598} 614}
599 615
600// ############################################################################################### 616// #################################################################################################
601 617
602/* 618/*
603* str= lane_error( error_val|str ) 619* str= lane_error( error_val|str )
@@ -710,41 +726,6 @@ LUAG_FUNC( set_error_reporting)
710} 726}
711#endif // ERROR_FULL_STACK 727#endif // ERROR_FULL_STACK
712 728
713static void push_stack_trace( lua_State* L, int rc_, int stk_base_)
714{
715 // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry
716 switch( rc_)
717 {
718 case LUA_OK: // no error, body return values are on the stack
719 break;
720
721 case LUA_ERRRUN: // cancellation or a runtime error
722#if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler
723 {
724 STACK_CHECK_START_REL(L, 0);
725 // fetch the call stack table from the registry where the handler stored it
726 STACK_GROW(L, 1);
727 // yields nil if no stack was generated (in case of cancellation for example)
728 STACKTRACE_REGKEY.pushValue(L); // err trace|nil
729 STACK_CHECK(L, 1);
730
731 // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed
732 // For other errors, the message can be whatever was thrown, and we should have a stack trace table
733 ASSERT_L(lua_type(L, 1 + stk_base_) == (CANCEL_ERROR.equals(L, stk_base_) ? LUA_TNIL : LUA_TTABLE));
734 // Just leaving the stack trace table on the stack is enough to get it through to the master.
735 break;
736 }
737#endif // fall through if not ERROR_FULL_STACK
738
739 case LUA_ERRMEM: // memory allocation error (handler not called)
740 case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition)
741 default:
742 // we should have a single value which is either a string (the error message) or CANCEL_ERROR
743 ASSERT_L((lua_gettop(L) == stk_base_) && ((lua_type(L, stk_base_) == LUA_TSTRING) || CANCEL_ERROR.equals(L, stk_base_)));
744 break;
745 }
746}
747
748// ################################################################################################# 729// #################################################################################################
749 730
750LUAG_FUNC(set_debug_threadname) 731LUAG_FUNC(set_debug_threadname)
@@ -772,7 +753,7 @@ LUAG_FUNC(set_debug_threadname)
772 753
773LUAG_FUNC(get_debug_threadname) 754LUAG_FUNC(get_debug_threadname)
774{ 755{
775 Lane* const lane{ lua_toLane(L, 1) }; 756 Lane* const lane{ ToLane(L, 1) };
776 luaL_argcheck(L, lua_gettop(L) == 1, 2, "too many arguments"); 757 luaL_argcheck(L, lua_gettop(L) == 1, 2, "too many arguments");
777 lua_pushstring(L, lane->debug_name); 758 lua_pushstring(L, lane->debug_name);
778 return 1; 759 return 1;
@@ -828,8 +809,7 @@ static struct errcode_name s_errcodes[] =
828}; 809};
829static char const* get_errcode_name( int _code) 810static char const* get_errcode_name( int _code)
830{ 811{
831 int i; 812 for (int i{ 0 }; i < 7; ++i)
832 for( i = 0; i < 7; ++ i)
833 { 813 {
834 if (s_errcodes[i].code == _code) 814 if (s_errcodes[i].code == _code)
835 { 815 {
@@ -891,7 +871,6 @@ static void lane_main(Lane* lane)
891 push_stack_trace(L, rc, 1); // retvals|error [trace] 871 push_stack_trace(L, rc, 1); // retvals|error [trace]
892 872
893 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)))); 873 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))));
894 // STACK_DUMP(L);
895 // Call finalizers, if the script has set them up. 874 // Call finalizers, if the script has set them up.
896 // 875 //
897 int rc2{ run_finalizers(L, rc) }; 876 int rc2{ run_finalizers(L, rc) };
@@ -1295,7 +1274,7 @@ LUAG_FUNC(lane_new)
1295[[nodiscard]] static int lane_gc(lua_State* L) 1274[[nodiscard]] static int lane_gc(lua_State* L)
1296{ 1275{
1297 bool have_gc_cb{ false }; 1276 bool have_gc_cb{ false };
1298 Lane* const lane{ lua_toLane(L, 1) }; // ud 1277 Lane* const lane{ ToLane(L, 1) }; // ud
1299 1278
1300 // if there a gc callback? 1279 // if there a gc callback?
1301 lua_getiuservalue(L, 1, 1); // ud uservalue 1280 lua_getiuservalue(L, 1, 1); // ud uservalue
@@ -1393,7 +1372,7 @@ void push_thread_status(lua_State* L, Lane* lane_)
1393// 1372//
1394LUAG_FUNC(thread_join) 1373LUAG_FUNC(thread_join)
1395{ 1374{
1396 Lane* const lane{ lua_toLane(L, 1) }; 1375 Lane* const lane{ ToLane(L, 1) };
1397 lua_Duration const duration{ luaL_optnumber(L, 2, -1.0) }; 1376 lua_Duration const duration{ luaL_optnumber(L, 2, -1.0) };
1398 lua_State* const L2{ lane->L }; 1377 lua_State* const L2{ lane->L };
1399 1378
@@ -1474,7 +1453,7 @@ LUAG_FUNC(thread_index)
1474 static constexpr int UD{ 1 }; 1453 static constexpr int UD{ 1 };
1475 static constexpr int KEY{ 2 }; 1454 static constexpr int KEY{ 2 };
1476 static constexpr int USR{ 3 }; 1455 static constexpr int USR{ 3 };
1477 Lane* const lane{ lua_toLane(L, UD) }; 1456 Lane* const lane{ ToLane(L, UD) };
1478 ASSERT_L(lua_gettop(L) == 2); 1457 ASSERT_L(lua_gettop(L) == 2);
1479 1458
1480 STACK_GROW(L, 8); // up to 8 positions are needed in case of error propagation 1459 STACK_GROW(L, 8); // up to 8 positions are needed in case of error propagation
@@ -1647,11 +1626,9 @@ LUAG_FUNC(threads)
1647} 1626}
1648#endif // HAVE_LANE_TRACKING() 1627#endif // HAVE_LANE_TRACKING()
1649 1628
1650/* 1629// #################################################################################################
1651 * ############################################################################################### 1630// ######################################## Timer support ##########################################
1652 * ######################################## Timer support ######################################## 1631// #################################################################################################
1653 * ###############################################################################################
1654 */
1655 1632
1656/* 1633/*
1657* secs = now_secs() 1634* secs = now_secs()
@@ -1722,11 +1699,9 @@ LUAG_FUNC(wakeup_conv)
1722 return 1; 1699 return 1;
1723} 1700}
1724 1701
1725/* 1702// #################################################################################################
1726 * ############################################################################################### 1703// ######################################## Module linkage #########################################
1727 * ######################################## Module linkage ####################################### 1704// #################################################################################################
1728 * ###############################################################################################
1729 */
1730 1705
1731extern int LG_linda(lua_State* L); 1706extern int LG_linda(lua_State* L);
1732static struct luaL_Reg const lanes_functions[] = 1707static struct luaL_Reg const lanes_functions[] =