aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES10
-rw-r--r--docs/index.html33
-rw-r--r--src/lanes.c417
-rw-r--r--src/lanes.lua142
-rw-r--r--src/threading.h3
-rw-r--r--tests/fibonacci.lua6
-rw-r--r--tests/finalizer.lua2
7 files changed, 369 insertions, 244 deletions
diff --git a/CHANGES b/CHANGES
index cc7b4a2..36f06ab 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,15 @@
1CHANGES: 1CHANGES:
2 2
3CHAGE 46: BGe 10-Sep-2012
4 * version 3.3.0
5 * lane.status can return "killed" if lane was forcefully killed with lanes:cancel()
6 * lane:join(): return nil, "killed" if called on a killed lane.
7 * lane[<n>]: produces [1] = nil, [2] = "killed" if the lane was killed
8 * lane:join(): fixed an assertion in debug builds when joining a lane forcefully cancelled with lane:cancel( <x>, true).
9 * indexing a lane with a string other than "join", "cancel" or "status" raises an error.
10 * fixed configure() to correctly apply defaults when they are missing from the provided settings
11 * added a shutdown_timeout to control the duration Lanes will wait for graceful termination of running lanes at application shutdown. Default is 0.25.
12
3CHANGE 45: BGe 21-Aug-2012 13CHANGE 45: BGe 21-Aug-2012
4 * keeper internals implemented in C instead of Lua for better performances 14 * keeper internals implemented in C instead of Lua for better performances
5 * fixed arguments checks in linda:limit() and linda:set() 15 * fixed arguments checks in linda:limit() and linda:set()
diff --git a/docs/index.html b/docs/index.html
index 84ae5c0..932a712 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -56,7 +56,7 @@
56 56
57<p><br/><font size="-1"><i>Copyright &copy; 2007-12 Asko Kauppi, Benoit Germain. All rights reserved.</i> 57<p><br/><font size="-1"><i>Copyright &copy; 2007-12 Asko Kauppi, Benoit Germain. All rights reserved.</i>
58 <br>Lua Lanes is published under the same <A HREF="http://en.wikipedia.org/wiki/MIT_License">MIT license</A> as Lua 5.1 and 5.2. 58 <br>Lua Lanes is published under the same <A HREF="http://en.wikipedia.org/wiki/MIT_License">MIT license</A> as Lua 5.1 and 5.2.
59 </p><p>This document was revised on 21-Aug-12, and applies to version 3.2.0 59 </p><p>This document was revised on 10-Sep-12, and applies to version 3.3.0
60</font></p> 60</font></p>
61 61
62</center> 62</center>
@@ -205,7 +205,7 @@ Or use <A HREF="http://www.luarocks.org" TARGET="_blank">Lua Rocks</A> package m
205 <tr valign=top><td width=40></td><td> 205 <tr valign=top><td width=40></td><td>
206 <code>.nb_keepers</code> <br/><nobr>N</nobr></td><td width=40></td> 206 <code>.nb_keepers</code> <br/><nobr>N</nobr></td><td width=40></td>
207 <td> 207 <td>
208 Controls the number of keeper states used internally by lindas to transfer data between lanes. (see below). Default is 1. 208 Controls the number of keeper states used internally by lindas to transfer data between lanes. (see below). Default is <tt>1</tt>.
209 </td></tr> 209 </td></tr>
210 210
211 <tr valign=top><td/><td> 211 <tr valign=top><td/><td>
@@ -213,7 +213,7 @@ Or use <A HREF="http://www.luarocks.org" TARGET="_blank">Lua Rocks</A> package m
213 <td> 213 <td>
214 If equal to <tt>false</tt>, Lanes doesn't start the timer service, 214 If equal to <tt>false</tt>, Lanes doesn't start the timer service,
215 and the associated API will be absent from the interface (see below). 215 and the associated API will be absent from the interface (see below).
216 Any other value (including <tt>nil</tt>), starts the timer service. 216 Any other value (including <tt>nil</tt>), starts the timer service. Default is <tt>true</tt>.
217 </td></tr> 217 </td></tr>
218 218
219 <tr valign="top"> 219 <tr valign="top">
@@ -223,9 +223,15 @@ Or use <A HREF="http://www.luarocks.org" TARGET="_blank">Lua Rocks</A> package m
223 </td><td/> 223 </td><td/>
224 <td> 224 <td>
225 If provided, will be called in every created Lua state (keepers and lanes) right after it is created, and *before* any library is loaded. 225 If provided, will be called in every created Lua state (keepers and lanes) right after it is created, and *before* any library is loaded.
226 That way, all C functions it loads in the state can be added to the function lookup database. 226 That way, all C functions it loads in the state can be added to the function lookup database. Default is <tt>nil</tt>.
227 </td> 227 </td>
228 </tr> 228 </tr>
229
230 <tr valign=top><td width=40></td><td>
231 <code>.shutdown_timeout</code> <br/><nobr>N</nobr></td><td width=40></td>
232 <td>
233 (Since v3.3.0) Sets the duration in seconds Lanes will wait for graceful termination of running lanes at application shutdown. Irrelevant for builds using pthreads. Default is <tt>0.25</tt>.
234 </td></tr>
229 </table> 235 </table>
230<h2 id="creation">Creation</h2> 236<h2 id="creation">Creation</h2>
231 237
@@ -292,7 +298,7 @@ also in the new lanes.
292 <tr><td/><td><tt>"string"</tt></td><td/><td><tt>string.*</tt> namespace</td></tr> 298 <tr><td/><td><tt>"string"</tt></td><td/><td><tt>string.*</tt> namespace</td></tr>
293 <tr><td/><td><tt>"table"</tt></td><td/><td><tt>table.*</tt> namespace</td></tr> 299 <tr><td/><td><tt>"table"</tt></td><td/><td><tt>table.*</tt> namespace</td></tr>
294 <br/> 300 <br/>
295 <tr><td/><td><tt>"*"</tt></td><td/><td>all standard libraries</td></tr> 301 <tr><td/><td><tt>"*"</tt></td><td/><td>all standard libraries (including those specific to LuaJIT and not listed above)</td></tr>
296 </table> 302 </table>
297 303
298</p><p> 304</p><p>
@@ -318,7 +324,7 @@ also in the new lanes.
318 <code>.globals</code> <br/>globals_tbl</td><td/> 324 <code>.globals</code> <br/>globals_tbl</td><td/>
319 <td> 325 <td>
320 Sets the globals table for the launched threads. This can be used for giving 326 Sets the globals table for the launched threads. This can be used for giving
321 them constants. 327 them constants. The key/value pairs of <tt>globals_tbl</tt> are transfered in the lane globals after the libraries have been loaded and the modules required.
322 <br> 328 <br>
323 The global values of different lanes are in no manner connected; 329 The global values of different lanes are in no manner connected;
324 modifying one will only affect the particular lane. 330 modifying one will only affect the particular lane.
@@ -354,7 +360,7 @@ also in the new lanes.
354 <td><td/> 360 <td><td/>
355 <code>package</code> contents overrides, if needed. 361 <code>package</code> contents overrides, if needed.
356 Specifying it when <code>libs_str</code> doesn't cause the <code>package</code> library to be loaded will generate an error. 362 Specifying it when <code>libs_str</code> doesn't cause the <code>package</code> library to be loaded will generate an error.
357 If not specified, the created lane will receive the current values of <tt>package</tt>. Only path, cpath, preload and loaders are transfered. 363 If not specified, the created lane will receive the current values of <tt>package</tt>. Only <tt>path</tt>, <tt>cpath</tt>, <tt>preload</tt> and <tt>loaders</tt> are transfered.
358 <br> 364 <br>
359 </td> 365 </td>
360 </tr> 366 </tr>
@@ -406,9 +412,10 @@ member, providing one of these values: <sup>(<a href="#2">2</a></sup>
406 <tr><td width=40><td><tt>"pending"</tt></td><td>not started, yet</td></tr> 412 <tr><td width=40><td><tt>"pending"</tt></td><td>not started, yet</td></tr>
407 <tr><td/><td><tt>"running"</tt></td><td>running</td></tr> 413 <tr><td/><td><tt>"running"</tt></td><td>running</td></tr>
408 <tr><td/><td><tt>"waiting"</tt></td><td>waiting at a Linda <tt>:receive()</tt> or <tt>:send()</tt></td></tr> 414 <tr><td/><td><tt>"waiting"</tt></td><td>waiting at a Linda <tt>:receive()</tt> or <tt>:send()</tt></td></tr>
409 <tr><td/><td><tt>"done"</tt></td><td>finished executing (results are ready)</td></tr> 415 <tr><td/><td><tt>"done"</tt></td><td>finished executing (results are ready)</td></tr>
410 <tr><td/><td><tt>"error"</tt></td><td>met an error (reading results will propagate it)</td></tr> 416 <tr><td/><td><tt>"error"</tt></td><td>met an error (reading results will propagate it)</td></tr>
411 <tr><td/><td><tt>"cancelled"</tt></td><td>received cancellation and finished itself</td></tr> 417 <tr><td/><td><tt>"cancelled"</tt></td><td>received cancellation and finished itself</td></tr>
418 <tr><td/><td><tt>"killed"</tt></td><td>was forcefully killed by <tt>lane_h:cancel()</tt> (since v3.3.0)</td></tr>
412 </table> 419 </table>
413</p><p> 420</p><p>
414 This is similar to <tt>coroutine.status</tt>, which has: <tt>"running"</tt> / 421 This is similar to <tt>coroutine.status</tt>, which has: <tt>"running"</tt> /
@@ -441,8 +448,9 @@ If the lane ended in an error, it is propagated to master state at this place.
441<p> 448<p>
442Waits until the lane finishes, or <tt>timeout</tt> seconds have passed. 449Waits until the lane finishes, or <tt>timeout</tt> seconds have passed.
443Returns <tt>nil</tt> on timeout, <tt>nil,err,stack_tbl</tt> if the lane hit an error, 450Returns <tt>nil</tt> on timeout, <tt>nil,err,stack_tbl</tt> if the lane hit an error,
444or the return values of the lane. Unlike in reading the results in table 451<tt>nil, "killed"</tt> if forcefully killed (starting with v3.3.0), or the return values of the lane.
445fashion, errors are not propagated. 452Unlike in reading the results in table fashion, errors are not propagated.
453
446</p><p> 454</p><p>
447 455
448<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td> 456<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td>
@@ -490,7 +498,7 @@ that id over a Linda once that thread is done (as the last thing you do).
490<h2 id="cancelling">Cancelling</h2> 498<h2 id="cancelling">Cancelling</h2>
491 499
492<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td> 500<table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td>
493 <code>bool= lane_h:cancel( [timeout_secs=0.0,] [force_kill_bool=false] ) 501 <code>bool[,reason]= lane_h:cancel( [timeout_secs=0.0,] [force_kill_bool=false] )</code>
494 <br/> 502 <br/>
495 <br/> 503 <br/>
496 <code> 504 <code>
@@ -507,6 +515,9 @@ If the lane is still running and <tt>force_kill</tt> is <tt>true</tt>, the
507OS thread running the lane is forcefully killed. This means no GC, and should 515OS thread running the lane is forcefully killed. This means no GC, and should
508generally be the last resort. 516generally be the last resort.
509</p> 517</p>
518<p>
519Starting with v3.3.0, if <tt>cancel()</tt> returns false, it also returns either <tt>"timeout"</tt> or <tt>"killed"</tt> as second return value.
520</p>
510<p>Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls 521<p>Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls
511and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt> 522and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt>
512or <tt>send()</tt> call is awakened. This means the execution of the lane will resume although the operation has 523or <tt>send()</tt> call is awakened. This means the execution of the lane will resume although the operation has
diff --git a/src/lanes.c b/src/lanes.c
index 9f455b2..462999f 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -47,16 +47,17 @@
47 * 47 *
48 * To-do: 48 * To-do:
49 * 49 *
50 * Make waiting threads cancelable. 50 * Make waiting threads cancellable.
51 * ... 51 * ...
52 */ 52 */
53 53
54char const* VERSION = "3.2.0"; 54char const* VERSION = "3.3.0";
55 55
56/* 56/*
57=============================================================================== 57===============================================================================
58 58
59Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> 59Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com>
60 2011-12 Benoit Germain <bnt.germain@gmail.com>
60 61
61Permission is hereby granted, free of charge, to any person obtaining a copy 62Permission is hereby granted, free of charge, to any person obtaining a copy
62of this software and associated documentation files (the "Software"), to deal 63of this software and associated documentation files (the "Software"), to deal
@@ -136,12 +137,12 @@ struct s_lane {
136 // S: reads to see if cancel is requested 137 // S: reads to see if cancel is requested
137 138
138#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 139#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
139 SIGNAL_T done_signal_; 140 SIGNAL_T done_signal;
140 // 141 //
141 // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN) 142 // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN)
142 // S: sets the signal once cancellation is noticed (avoids a kill) 143 // S: sets the signal once cancellation is noticed (avoids a kill)
143 144
144 MUTEX_T done_lock_; 145 MUTEX_T done_lock;
145 // 146 //
146 // Lock required by 'done_signal' condition variable, protecting 147 // Lock required by 'done_signal' condition variable, protecting
147 // lane status changes to DONE/ERROR_ST/CANCELLED. 148 // lane status changes to DONE/ERROR_ST/CANCELLED.
@@ -256,7 +257,7 @@ static void check_key_types( lua_State *L, int _start, int _end)
256 { 257 {
257 continue; 258 continue;
258 } 259 }
259 luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i); 260 (void) luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i);
260 } 261 }
261} 262}
262 263
@@ -295,7 +296,7 @@ LUAG_FUNC( linda_send)
295 // make sure there is something to send 296 // make sure there is something to send
296 if( (uint_t)lua_gettop( L) == key_i) 297 if( (uint_t)lua_gettop( L) == key_i)
297 { 298 {
298 luaL_error( L, "no data to send"); 299 return luaL_error( L, "no data to send");
299 } 300 }
300 301
301 // convert nils to some special non-nil sentinel in sent values 302 // convert nils to some special non-nil sentinel in sent values
@@ -353,7 +354,8 @@ LUAG_FUNC( linda_send)
353 STACK_END(L,0) 354 STACK_END(L,0)
354 if( s) 355 if( s)
355 { 356 {
356 prev_status = s->status; 357 prev_status = s->status; // RUNNING, most likely
358 ASSERT_L( prev_status == RUNNING); // but check, just in case
357 s->status = WAITING; 359 s->status = WAITING;
358 ASSERT_L( s->waiting_on == NULL); 360 ASSERT_L( s->waiting_on == NULL);
359 s->waiting_on = &linda->read_happened; 361 s->waiting_on = &linda->read_happened;
@@ -382,7 +384,7 @@ LUAG_FUNC( linda_send)
382 // must trigger error after keeper state has been released 384 // must trigger error after keeper state has been released
383 if( pushed < 0) 385 if( pushed < 0)
384 { 386 {
385 luaL_error( L, "tried to copy unsupported types"); 387 return luaL_error( L, "tried to copy unsupported types");
386 } 388 }
387 389
388 if( cancel) 390 if( cancel)
@@ -446,7 +448,7 @@ LUAG_FUNC( linda_receive)
446 expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); 448 expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min);
447 if( expected_pushed_min > expected_pushed_max) 449 if( expected_pushed_min > expected_pushed_max)
448 { 450 {
449 luaL_error( L, "batched min/max error"); 451 return luaL_error( L, "batched min/max error");
450 } 452 }
451 } 453 }
452 else 454 else
@@ -507,7 +509,8 @@ LUAG_FUNC( linda_receive)
507 STACK_END(L, 0) 509 STACK_END(L, 0)
508 if( s) 510 if( s)
509 { 511 {
510 prev_status = s->status; 512 prev_status = s->status; // RUNNING, most likely
513 ASSERT_L( prev_status == RUNNING); // but check, just in case
511 s->status = WAITING; 514 s->status = WAITING;
512 ASSERT_L( s->waiting_on == NULL); 515 ASSERT_L( s->waiting_on == NULL);
513 s->waiting_on = &linda->write_happened; 516 s->waiting_on = &linda->write_happened;
@@ -535,7 +538,7 @@ LUAG_FUNC( linda_receive)
535 // must trigger error after keeper state has been released 538 // must trigger error after keeper state has been released
536 if( pushed < 0) 539 if( pushed < 0)
537 { 540 {
538 luaL_error( L, "tried to copy unsupported types"); 541 return luaL_error( L, "tried to copy unsupported types");
539 } 542 }
540 543
541 if( cancel) 544 if( cancel)
@@ -583,7 +586,7 @@ LUAG_FUNC( linda_set)
583 // must trigger error after keeper state has been released 586 // must trigger error after keeper state has been released
584 if( pushed < 0) 587 if( pushed < 0)
585 { 588 {
586 luaL_error( L, "tried to copy unsupported types"); 589 return luaL_error( L, "tried to copy unsupported types");
587 } 590 }
588 } 591 }
589 592
@@ -611,7 +614,7 @@ LUAG_FUNC( linda_count)
611 keeper_release( K); 614 keeper_release( K);
612 if( pushed < 0) 615 if( pushed < 0)
613 { 616 {
614 luaL_error( L, "tried to count an invalid key"); 617 return luaL_error( L, "tried to count an invalid key");
615 } 618 }
616 } 619 }
617 return pushed; 620 return pushed;
@@ -645,7 +648,7 @@ LUAG_FUNC( linda_get)
645 // must trigger error after keeper state has been released 648 // must trigger error after keeper state has been released
646 if( pushed < 0) 649 if( pushed < 0)
647 { 650 {
648 luaL_error( L, "tried to copy unsupported types"); 651 return luaL_error( L, "tried to copy unsupported types");
649 } 652 }
650 } 653 }
651 654
@@ -678,7 +681,7 @@ LUAG_FUNC( linda_limit)
678 // must trigger error after keeper state has been released 681 // must trigger error after keeper state has been released
679 if( pushed < 0) 682 if( pushed < 0)
680 { 683 {
681 luaL_error( L, "tried to copy unsupported types"); 684 return luaL_error( L, "tried to copy unsupported types");
682 } 685 }
683 } 686 }
684 687
@@ -1125,11 +1128,9 @@ static int selfdestruct_gc( lua_State *L)
1125 // Tested on MacBook Core Duo 2GHz and 10.5.5: 1128 // Tested on MacBook Core Duo 2GHz and 10.5.5:
1126 // -- AKa 25-Oct-2008 1129 // -- AKa 25-Oct-2008
1127 // 1130 //
1128 #ifndef ATEXIT_WAIT_SECS
1129 # define ATEXIT_WAIT_SECS (0.25)
1130 #endif
1131 { 1131 {
1132 double t_until= now_secs() + ATEXIT_WAIT_SECS; 1132 lua_Number const shutdown_timeout = lua_tonumber( L, lua_upvalueindex( 1));
1133 double const t_until = now_secs() + shutdown_timeout;
1133 1134
1134 while( selfdestruct_first != SELFDESTRUCT_END ) 1135 while( selfdestruct_first != SELFDESTRUCT_END )
1135 { 1136 {
@@ -1153,7 +1154,7 @@ static int selfdestruct_gc( lua_State *L)
1153 t_now = now_secs(); 1154 t_now = now_secs();
1154 if( n == 0 || ( t_now >= t_until)) 1155 if( n == 0 || ( t_now >= t_until))
1155 { 1156 {
1156 DEBUGEXEC(fprintf( stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, ATEXIT_WAIT_SECS - (t_until - t_now))); 1157 DEBUGEXEC(fprintf( stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout - (t_until - t_now)));
1157 break; 1158 break;
1158 } 1159 }
1159 } 1160 }
@@ -1201,13 +1202,13 @@ static int selfdestruct_gc( lua_State *L)
1201 THREAD_KILL( &s->thread); 1202 THREAD_KILL( &s->thread);
1202#if THREADAPI == THREADAPI_PTHREAD 1203#if THREADAPI == THREADAPI_PTHREAD
1203 // pthread: make sure the thread is really stopped! 1204 // pthread: make sure the thread is really stopped!
1204 THREAD_WAIT( &s->thread, -1, &s->done_signal_, &s->done_lock_, &s->status); 1205 THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status);
1205#endif // THREADAPI == THREADAPI_PTHREAD 1206#endif // THREADAPI == THREADAPI_PTHREAD
1206 } 1207 }
1207 // NO lua_close() in this case because we don't know where execution of the state was interrupted 1208 // NO lua_close() in this case because we don't know where execution of the state was interrupted
1208#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 1209#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1209 SIGNAL_FREE( &s->done_signal_); 1210 SIGNAL_FREE( &s->done_signal);
1210 MUTEX_FREE( &s->done_lock_); 1211 MUTEX_FREE( &s->done_lock);
1211#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 1212#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1212 free( s); 1213 free( s);
1213 s = next_s; 1214 s = next_s;
@@ -1288,22 +1289,26 @@ LUAG_FUNC( cancel_test)
1288// Limits the process to use only 'cores' CPU cores. To be used for performance 1289// Limits the process to use only 'cores' CPU cores. To be used for performance
1289// testing on multicore devices. DEBUGGING ONLY! 1290// testing on multicore devices. DEBUGGING ONLY!
1290// 1291//
1291LUAG_FUNC( _single ) { 1292LUAG_FUNC( set_singlethreaded)
1292 uint_t cores= luaG_optunsigned(L,1,1); 1293{
1294 uint_t cores = luaG_optunsigned( L, 1, 1);
1295 (void) cores; // prevent "unused" warning
1293 1296
1294#ifdef PLATFORM_OSX 1297#ifdef PLATFORM_OSX
1295 #ifdef _UTILBINDTHREADTOCPU 1298#ifdef _UTILBINDTHREADTOCPU
1296 if (cores > 1) luaL_error( L, "Limiting to N>1 cores not possible." ); 1299 if( cores > 1)
1297 // requires 'chudInitialize()' 1300 {
1298 utilBindThreadToCPU(0); // # of CPU to run on (we cannot limit to 2..N CPUs?) 1301 return luaL_error( L, "Limiting to N>1 cores not possible");
1299 #else 1302 }
1300 luaL_error( L, "Not available: compile with _UTILBINDTHREADTOCPU" ); 1303 // requires 'chudInitialize()'
1301 #endif 1304 utilBindThreadToCPU(0); // # of CPU to run on (we cannot limit to 2..N CPUs?)
1302#else 1305#else
1303 luaL_error( L, "not implemented!" ); 1306 return luaL_error( L, "Not available: compile with _UTILBINDTHREADTOCPU");
1304#endif 1307#endif
1305 (void)cores; 1308#else
1306 1309 return luaL_error( L, "not implemented");
1310#endif
1311
1307 return 0; 1312 return 0;
1308} 1313}
1309 1314
@@ -1592,8 +1597,8 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs)
1592 s->L = L = 0; 1597 s->L = L = 0;
1593 1598
1594 #if THREADWAIT_METHOD == THREADWAIT_CONDVAR 1599 #if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1595 SIGNAL_FREE( &s->done_signal_); 1600 SIGNAL_FREE( &s->done_signal);
1596 MUTEX_FREE( &s->done_lock_); 1601 MUTEX_FREE( &s->done_lock);
1597 #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 1602 #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1598 free(s); 1603 free(s);
1599 1604
@@ -1611,14 +1616,14 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs)
1611 // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change 1616 // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change
1612 // 1617 //
1613#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 1618#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1614 MUTEX_LOCK( &s->done_lock_); 1619 MUTEX_LOCK( &s->done_lock);
1615 { 1620 {
1616#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 1621#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1617 s->status = st; 1622 s->status = st;
1618#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 1623#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1619 SIGNAL_ONE( &s->done_signal_); // wake up master (while 's->done_lock' is on) 1624 SIGNAL_ONE( &s->done_signal); // wake up master (while 's->done_lock' is on)
1620 } 1625 }
1621 MUTEX_UNLOCK( &s->done_lock_); 1626 MUTEX_UNLOCK( &s->done_lock);
1622#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 1627#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1623 } 1628 }
1624 return 0; // ignored 1629 return 0; // ignored
@@ -1680,8 +1685,7 @@ LUAG_FUNC( thread_new )
1680 1685
1681 if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) 1686 if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX)
1682 { 1687 {
1683 luaL_error( L, "Priority out of range: %d..+%d (%d)", 1688 return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio);
1684 THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio );
1685 } 1689 }
1686 1690
1687 /* --- Create and prepare the sub state --- */ 1691 /* --- Create and prepare the sub state --- */
@@ -1689,7 +1693,10 @@ LUAG_FUNC( thread_new )
1689 // populate with selected libraries at the same time 1693 // populate with selected libraries at the same time
1690 // 1694 //
1691 L2 = luaG_newstate( libs, on_state_create); 1695 L2 = luaG_newstate( libs, on_state_create);
1692 if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" ); 1696 if (!L2)
1697 {
1698 return luaL_error( L, "'luaL_newstate()' failed; out of memory");
1699 }
1693 1700
1694 STACK_GROW( L, 2); 1701 STACK_GROW( L, 2);
1695 STACK_GROW( L2, 3); 1702 STACK_GROW( L2, 3);
@@ -1701,8 +1708,10 @@ LUAG_FUNC( thread_new )
1701 STACK_CHECK(L2) 1708 STACK_CHECK(L2)
1702 if( package) 1709 if( package)
1703 { 1710 {
1704 if (lua_type(L,package) != LUA_TTABLE) 1711 if( lua_type( L, package) != LUA_TTABLE)
1705 luaL_error( L, "expected package as table, got %s", luaL_typename(L,package)); 1712 {
1713 return luaL_error( L, "expected package as table, got %s", luaL_typename( L, package));
1714 }
1706 lua_getglobal( L2, "package"); 1715 lua_getglobal( L2, "package");
1707 if( !lua_isnil( L2, -1)) // package library not loaded: do nothing 1716 if( !lua_isnil( L2, -1)) // package library not loaded: do nothing
1708 { 1717 {
@@ -1746,13 +1755,15 @@ LUAG_FUNC( thread_new )
1746 int nbRequired = 1; 1755 int nbRequired = 1;
1747 // should not happen, was checked in lanes.lua before calling thread_new() 1756 // should not happen, was checked in lanes.lua before calling thread_new()
1748 if (lua_type(L, required) != LUA_TTABLE) 1757 if (lua_type(L, required) != LUA_TTABLE)
1749 luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required)); 1758 {
1759 return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required));
1760 }
1750 lua_pushnil( L); 1761 lua_pushnil( L);
1751 while( lua_next( L, required) != 0) 1762 while( lua_next( L, required) != 0)
1752 { 1763 {
1753 if (lua_type(L,-1) != LUA_TSTRING || lua_type(L,-2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) 1764 if (lua_type(L,-1) != LUA_TSTRING || lua_type(L,-2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired)
1754 { 1765 {
1755 luaL_error( L, "required module list should be a list of strings."); 1766 return luaL_error( L, "required module list should be a list of strings");
1756 } 1767 }
1757 else 1768 else
1758 { 1769 {
@@ -1772,8 +1783,10 @@ LUAG_FUNC( thread_new )
1772 { 1783 {
1773 STACK_CHECK(L) 1784 STACK_CHECK(L)
1774 STACK_CHECK(L2) 1785 STACK_CHECK(L2)
1775 if (!lua_istable(L,glob)) 1786 if( !lua_istable( L, glob))
1776 luaL_error( L, "Expected table, got %s", luaL_typename(L,glob)); 1787 {
1788 return luaL_error( L, "Expected table, got %s", luaL_typename(L,glob));
1789 }
1777 1790
1778 lua_pushnil( L); 1791 lua_pushnil( L);
1779 lua_pushglobaltable( L2); // Lua 5.2 wants us to push the globals table on the stack 1792 lua_pushglobaltable( L2); // Lua 5.2 wants us to push the globals table on the stack
@@ -1799,7 +1812,9 @@ LUAG_FUNC( thread_new )
1799 { 1812 {
1800 lua_pushvalue( L, 1); 1813 lua_pushvalue( L, 1);
1801 if( luaG_inter_move( L, L2, 1) != 0) // L->L2 1814 if( luaG_inter_move( L, L2, 1) != 0) // L->L2
1802 luaL_error( L, "tried to copy unsupported types"); 1815 {
1816 return luaL_error( L, "tried to copy unsupported types");
1817 }
1803 STACK_MID(L,0) 1818 STACK_MID(L,0)
1804 } 1819 }
1805 else if( lua_type(L, 1) == LUA_TSTRING) 1820 else if( lua_type(L, 1) == LUA_TSTRING)
@@ -1807,7 +1822,7 @@ LUAG_FUNC( thread_new )
1807 // compile the string 1822 // compile the string
1808 if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) 1823 if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0)
1809 { 1824 {
1810 luaL_error( L, "error when parsing lane function code"); 1825 return luaL_error( L, "error when parsing lane function code");
1811 } 1826 }
1812 } 1827 }
1813 1828
@@ -1817,7 +1832,9 @@ LUAG_FUNC( thread_new )
1817 // revive arguments 1832 // revive arguments
1818 // 1833 //
1819 if( (args > 0) && (luaG_inter_copy( L, L2, args) != 0)) // L->L2 1834 if( (args > 0) && (luaG_inter_copy( L, L2, args) != 0)) // L->L2
1820 luaL_error( L, "tried to copy unsupported types"); 1835 {
1836 return luaL_error( L, "tried to copy unsupported types");
1837 }
1821 STACK_MID(L,0) 1838 STACK_MID(L,0)
1822 1839
1823 ASSERT_L( (uint_t)lua_gettop(L2) == 1+args ); 1840 ASSERT_L( (uint_t)lua_gettop(L2) == 1+args );
@@ -1839,8 +1856,8 @@ LUAG_FUNC( thread_new )
1839 s->cancel_request= FALSE; 1856 s->cancel_request= FALSE;
1840 1857
1841#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 1858#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1842 MUTEX_INIT( &s->done_lock_); 1859 MUTEX_INIT( &s->done_lock);
1843 SIGNAL_INIT( &s->done_signal_); 1860 SIGNAL_INIT( &s->done_signal);
1844#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 1861#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1845 s->mstatus= NORMAL; 1862 s->mstatus= NORMAL;
1846 s->selfdestruct_next= NULL; 1863 s->selfdestruct_next= NULL;
@@ -1884,50 +1901,44 @@ LUAG_FUNC( thread_new )
1884// * Why NOT cancel/kill a loose thread: 1901// * Why NOT cancel/kill a loose thread:
1885// 1902//
1886// At least timer system uses a free-running thread, they should be handy 1903// At least timer system uses a free-running thread, they should be handy
1887// and the issue of cancelling/killing threads at gc is not very nice, either 1904// and the issue of canceling/killing threads at gc is not very nice, either
1888// (would easily cause waits at gc cycle, which we don't want). 1905// (would easily cause waits at gc cycle, which we don't want).
1889// 1906//
1890// * Why YES kill a loose thread: 1907LUAG_FUNC( thread_gc)
1891//
1892// Current way causes segfaults at program exit, if free-running threads are
1893// in certain stages. Details are not clear, but this is the core reason.
1894// If gc would kill threads then at process exit only one thread would remain.
1895//
1896// Todo: Maybe we should have a clear #define for selecting either behaviour.
1897//
1898LUAG_FUNC( thread_gc )
1899{ 1908{
1900 struct s_lane *s= lua_toLane(L,1); 1909 struct s_lane* s = lua_toLane( L, 1);
1901 1910
1902 // We can read 's->status' without locks, but not wait for it 1911 // We can read 's->status' without locks, but not wait for it
1903 // 1912 // test KILLED state first, as it doesn't need to enter the selfdestruct chain
1904 if (s->status < DONE) 1913 if( s->mstatus == KILLED)
1905 {
1906 //
1907 selfdestruct_add(s);
1908 assert( s->selfdestruct_next );
1909 return 0;
1910
1911 }
1912 else if (s->mstatus==KILLED)
1913 { 1914 {
1914 // Make sure a kill has proceeded, before cleaning up the data structure. 1915 // Make sure a kill has proceeded, before cleaning up the data structure.
1915 // 1916 //
1916 // NO lua_close() in this case because we don't know where execution of the state was interrupted 1917 // NO lua_close() in this case because we don't know where execution of the state was interrupted
1917 // If not doing 'THREAD_WAIT()' we should close the Lua state here
1918 // (can it be out of order, since we killed the lane abruptly?)
1919 //
1920#if 0
1921 lua_close( s->L );
1922 s->L = 0;
1923#else // 0
1924 DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" )); 1918 DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" ));
1925 THREAD_WAIT( &s->thread, -1, &s->done_signal_, &s->done_lock_, &s->status); 1919 // make sure the thread is no longer running, just like thread_join()
1920 if(! THREAD_ISNULL( s->thread))
1921 THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status);
1922 // we know the thread was killed while the Lua VM was not doing anything: we should be able to close it without crashing
1923 // now, thread_cancel() will not forcefully kill a lane with s->status >= DONE, so I am not sure it can ever happen
1924 if( s->status >= DONE && s->L)
1925 {
1926 lua_close( s->L);
1927 s->L = 0;
1928 }
1926 DEBUGEXEC(fprintf( stderr, "** Joined ok **" )); 1929 DEBUGEXEC(fprintf( stderr, "** Joined ok **" ));
1927#endif // 0 1930 }
1931 else if( s->status < DONE)
1932 {
1933 // still running: will have to be cleaned up later
1934 selfdestruct_add( s);
1935 assert( s->selfdestruct_next);
1936 return 0;
1937
1928 } 1938 }
1929 else if( s->L) 1939 else if( s->L)
1930 { 1940 {
1941 // no longer accessing the Lua VM: we can close right now
1931 lua_close( s->L); 1942 lua_close( s->L);
1932 s->L = 0; 1943 s->L = 0;
1933 } 1944 }
@@ -1935,12 +1946,11 @@ LUAG_FUNC( thread_gc )
1935 // Clean up after a (finished) thread 1946 // Clean up after a (finished) thread
1936 // 1947 //
1937#if THREADWAIT_METHOD == THREADWAIT_CONDVAR 1948#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1938 SIGNAL_FREE( &s->done_signal_); 1949 SIGNAL_FREE( &s->done_signal);
1939 MUTEX_FREE( &s->done_lock_); 1950 MUTEX_FREE( &s->done_lock);
1940#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR 1951#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1941 1952
1942 free( s); 1953 free( s);
1943
1944 return 0; 1954 return 0;
1945} 1955}
1946 1956
@@ -1961,17 +1971,29 @@ LUAG_FUNC( thread_gc )
1961// managed to cancel it. 1971// managed to cancel it.
1962// false if the cancellation timed out, or a kill was needed. 1972// false if the cancellation timed out, or a kill was needed.
1963// 1973//
1964static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force) 1974
1975typedef enum
1976{
1977 CR_Timeout,
1978 CR_Cancelled,
1979 CR_Killed
1980} cancel_result;
1981
1982static cancel_result thread_cancel( struct s_lane *s, double secs, bool_t force)
1965{ 1983{
1966 bool_t done= TRUE; 1984 cancel_result result;
1985
1986 // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here
1967 // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) 1987 // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN)
1968 // 1988 if( s->mstatus == KILLED)
1969 if( s->status < DONE) 1989 {
1990 result = CR_Killed;
1991 }
1992 else if( s->status < DONE)
1970 { 1993 {
1971 s->cancel_request = TRUE; // it's now signaled to stop 1994 s->cancel_request = TRUE; // it's now signaled to stop
1972 // signal the linda the wake up the thread so that it can react to the cancel query 1995 // signal the linda the wake up the thread so that it can react to the cancel query
1973 // let us hope we never land here with a pointer on a linda that has been destroyed... 1996 // let us hope we never land here with a pointer on a linda that has been destroyed...
1974 //MUTEX_LOCK( &selfdestruct_cs );
1975 { 1997 {
1976 SIGNAL_T *waiting_on = s->waiting_on; 1998 SIGNAL_T *waiting_on = s->waiting_on;
1977 if( s->status == WAITING && waiting_on != NULL) 1999 if( s->status == WAITING && waiting_on != NULL)
@@ -1979,10 +2001,10 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force)
1979 SIGNAL_ALL( waiting_on); 2001 SIGNAL_ALL( waiting_on);
1980 } 2002 }
1981 } 2003 }
1982 //MUTEX_UNLOCK( &selfdestruct_cs );
1983 done = THREAD_WAIT( &s->thread, secs, &s->done_signal_, &s->done_lock_, &s->status);
1984 2004
1985 if ((!done) && force) 2005 result = THREAD_WAIT( &s->thread, secs, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout;
2006
2007 if( (result == CR_Timeout) && force)
1986 { 2008 {
1987 // Killing is asynchronous; we _will_ wait for it to be done at 2009 // Killing is asynchronous; we _will_ wait for it to be done at
1988 // GC, to make sure the data structure can be released (alternative 2010 // GC, to make sure the data structure can be released (alternative
@@ -1990,10 +2012,18 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force)
1990 // PThread seems to have). 2012 // PThread seems to have).
1991 // 2013 //
1992 THREAD_KILL( &s->thread); 2014 THREAD_KILL( &s->thread);
1993 s->mstatus= KILLED; // mark 'gc' to wait for it 2015 s->mstatus = KILLED; // mark 'gc' to wait for it
2016 // note that s->status value must remain to whatever it was at the time of the kill
2017 // because we need to know if we can lua_close() the Lua State or not.
2018 result = CR_Killed;
1994 } 2019 }
1995 } 2020 }
1996 return done; 2021 else
2022 {
2023 // say "ok" by default, including when lane is already done
2024 result = CR_Cancelled;
2025 }
2026 return result;
1997} 2027}
1998 2028
1999LUAG_FUNC( thread_cancel) 2029LUAG_FUNC( thread_cancel)
@@ -2004,10 +2034,11 @@ LUAG_FUNC( thread_cancel)
2004 } 2034 }
2005 else 2035 else
2006 { 2036 {
2007 struct s_lane *s = lua_toLane( L, 1); 2037 struct s_lane* s = lua_toLane( L, 1);
2008 double secs = 0.0; 2038 double secs = 0.0;
2009 uint_t force_i = 2; 2039 uint_t force_i = 2;
2010 bool_t force, done= TRUE; 2040 cancel_result result;
2041 bool_t force;
2011 2042
2012 if( lua_isnumber( L, 2)) 2043 if( lua_isnumber( L, 2))
2013 { 2044 {
@@ -2015,15 +2046,32 @@ LUAG_FUNC( thread_cancel)
2015 ++ force_i; 2046 ++ force_i;
2016 } 2047 }
2017 else if( lua_isnil( L, 2)) 2048 else if( lua_isnil( L, 2))
2049 {
2018 ++ force_i; 2050 ++ force_i;
2051 }
2019 2052
2020 force = lua_toboolean( L, force_i); // FALSE if nothing there 2053 force = lua_toboolean( L, force_i); // FALSE if nothing there
2021 2054
2022 done = thread_cancel( s, secs, force); 2055 result = thread_cancel( s, secs, force);
2023 2056 switch( result)
2024 lua_pushboolean( L, done); 2057 {
2025 return 1; 2058 case CR_Timeout:
2059 lua_pushboolean( L, 0);
2060 lua_pushstring( L, "timeout");
2061 return 2;
2062
2063 case CR_Cancelled:
2064 lua_pushboolean( L, 1);
2065 return 1;
2066
2067 case CR_Killed:
2068 lua_pushboolean( L, 0);
2069 lua_pushstring( L, "killed");
2070 return 2;
2071 }
2026 } 2072 }
2073 // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value"
2074 return 0;
2027} 2075}
2028 2076
2029//--- 2077//---
@@ -2039,12 +2087,9 @@ LUAG_FUNC( thread_cancel)
2039static char const * thread_status_string( struct s_lane *s) 2087static char const * thread_status_string( struct s_lane *s)
2040{ 2088{
2041 enum e_status st = s->status; // read just once (volatile) 2089 enum e_status st = s->status; // read just once (volatile)
2042 char const * str; 2090 char const * str =
2043 2091 (s->mstatus == KILLED) ? "killed" : // new to v3.3.0!
2044 if (s->mstatus == KILLED) 2092 (st==PENDING) ? "pending" :
2045 st= CANCELLED;
2046
2047 str= (st==PENDING) ? "pending" :
2048 (st==RUNNING) ? "running" : // like in 'co.status()' 2093 (st==RUNNING) ? "running" : // like in 'co.status()'
2049 (st==WAITING) ? "waiting" : 2094 (st==WAITING) ? "waiting" :
2050 (st==DONE) ? "done" : 2095 (st==DONE) ? "done" :
@@ -2053,12 +2098,13 @@ static char const * thread_status_string( struct s_lane *s)
2053 return str; 2098 return str;
2054} 2099}
2055 2100
2056static void push_thread_status( lua_State *L, struct s_lane *s) 2101static int push_thread_status( lua_State *L, struct s_lane *s)
2057{ 2102{
2058 char const * const str = thread_status_string( s); 2103 char const * const str = thread_status_string( s);
2059 ASSERT_L( str); 2104 ASSERT_L( str);
2060 2105
2061 lua_pushstring( L, str ); 2106 lua_pushstring( L, str );
2107 return 1;
2062} 2108}
2063 2109
2064 2110
@@ -2070,15 +2116,15 @@ static void push_thread_status( lua_State *L, struct s_lane *s)
2070// error: returns nil + error value + stack table 2116// error: returns nil + error value + stack table
2071// cancelled: returns nil 2117// cancelled: returns nil
2072// 2118//
2073LUAG_FUNC( thread_join ) 2119LUAG_FUNC( thread_join)
2074{ 2120{
2075 struct s_lane *s= lua_toLane(L,1); 2121 struct s_lane* const s = lua_toLane( L, 1);
2076 double wait_secs= luaL_optnumber(L,2,-1.0); 2122 double wait_secs= luaL_optnumber(L,2,-1.0);
2077 lua_State *L2= s->L; 2123 lua_State *L2= s->L;
2078 int ret; 2124 int ret;
2079 bool_t done; 2125 bool_t done;
2080 2126
2081 done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal_, &s->done_lock_, &s->status); 2127 done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status);
2082 if (!done || !L2) 2128 if (!done || !L2)
2083 return 0; // timeout: pushes none, leaves 'L2' alive 2129 return 0; // timeout: pushes none, leaves 'L2' alive
2084 2130
@@ -2086,34 +2132,49 @@ LUAG_FUNC( thread_join )
2086 2132
2087 STACK_GROW( L, 1); 2133 STACK_GROW( L, 1);
2088 2134
2089 switch( s->status) 2135 if( s->mstatus == KILLED) // OS thread was killed if thread_cancel was forced
2090 { 2136 {
2091 case DONE: 2137 // in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values
2138
2139 lua_pushnil( L);
2140 lua_pushliteral( L, "killed");
2141 ret = 2;
2142 }
2143 else
2144 {
2145 switch( s->status)
2092 { 2146 {
2093 uint_t n = lua_gettop( L2); // whole L2 stack 2147 case DONE:
2094 if( (n > 0) && (luaG_inter_move( L2, L, n) != 0)) 2148 {
2095 luaL_error( L, "tried to copy unsupported types"); 2149 uint_t n = lua_gettop( L2); // whole L2 stack
2096 ret = n; 2150 if( (n > 0) && (luaG_inter_move( L2, L, n) != 0))
2097 } 2151 {
2098 break; 2152 return luaL_error( L, "tried to copy unsupported types");
2153 }
2154 ret = n;
2155 }
2156 break;
2099 2157
2100 case ERROR_ST: 2158 case ERROR_ST:
2101 lua_pushnil( L); 2159 lua_pushnil( L);
2102 if( luaG_inter_move( L2, L, 2) != 0) // error message at [-2], stack trace at [-1] 2160 if( luaG_inter_move( L2, L, 2) != 0) // error message at [-2], stack trace at [-1]
2103 luaL_error( L, "tried to copy unsupported types"); 2161 {
2104 ret= 3; 2162 return luaL_error( L, "tried to copy unsupported types");
2105 break; 2163 }
2106 2164 ret= 3;
2107 case CANCELLED: 2165 break;
2108 ret= 0; 2166
2109 break; 2167 case CANCELLED:
2110 2168 ret= 0;
2111 default: 2169 break;
2112 DEBUGEXEC(fprintf( stderr, "Status: %d\n", s->status)); 2170
2113 ASSERT_L( FALSE ); ret= 0; 2171 default:
2172 DEBUGEXEC(fprintf( stderr, "Status: %d\n", s->status));
2173 ASSERT_L( FALSE ); ret= 0;
2174 }
2175 lua_close( L2);
2114 } 2176 }
2115 lua_close( L2); 2177 s->L = 0;
2116 s->L = L2 = 0;
2117 2178
2118 return ret; 2179 return ret;
2119} 2180}
@@ -2131,7 +2192,7 @@ LUAG_FUNC( thread_index)
2131{ 2192{
2132 int const UD = 1; 2193 int const UD = 1;
2133 int const KEY = 2; 2194 int const KEY = 2;
2134 int const ENV = 3; 2195 int const USR = 3;
2135 struct s_lane *s = lua_toLane( L, UD); 2196 struct s_lane *s = lua_toLane( L, UD);
2136 ASSERT_L( lua_gettop( L) == 2); 2197 ASSERT_L( lua_gettop( L) == 2);
2137 2198
@@ -2142,10 +2203,10 @@ LUAG_FUNC( thread_index)
2142 { 2203 {
2143 // first, check that we don't already have an environment that holds the requested value 2204 // first, check that we don't already have an environment that holds the requested value
2144 { 2205 {
2145 // If key is found in the environment, return it 2206 // If key is found in the uservalue, return it
2146 lua_getuservalue( L, UD); 2207 lua_getuservalue( L, UD);
2147 lua_pushvalue( L, KEY); 2208 lua_pushvalue( L, KEY);
2148 lua_rawget( L, ENV); 2209 lua_rawget( L, USR);
2149 if( !lua_isnil( L, -1)) 2210 if( !lua_isnil( L, -1))
2150 { 2211 {
2151 return 1; 2212 return 1;
@@ -2157,27 +2218,40 @@ LUAG_FUNC( thread_index)
2157 bool_t fetched; 2218 bool_t fetched;
2158 lua_Integer key = lua_tointeger( L, KEY); 2219 lua_Integer key = lua_tointeger( L, KEY);
2159 lua_pushinteger( L, 0); 2220 lua_pushinteger( L, 0);
2160 lua_rawget( L, ENV); 2221 lua_rawget( L, USR);
2161 fetched = !lua_isnil( L, -1); 2222 fetched = !lua_isnil( L, -1);
2162 lua_pop( L, 1); // back to our 2 args + env on the stack 2223 lua_pop( L, 1); // back to our 2 args + uservalue on the stack
2163 if( !fetched) 2224 if( !fetched)
2164 { 2225 {
2165 lua_pushinteger( L, 0); 2226 lua_pushinteger( L, 0);
2166 lua_pushboolean( L, 1); 2227 lua_pushboolean( L, 1);
2167 lua_rawset( L, ENV); 2228 lua_rawset( L, USR);
2168 // wait until thread has completed 2229 // wait until thread has completed
2169 lua_pushcfunction( L, LG_thread_join); 2230 lua_pushcfunction( L, LG_thread_join);
2170 lua_pushvalue( L, UD); 2231 lua_pushvalue( L, UD);
2171 lua_call( L, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+ 2232 lua_call( L, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+
2172 switch( s->status) 2233 switch( s->status)
2173 { 2234 {
2235 default:
2236 if( s->mstatus != KILLED)
2237 {
2238 // this is an internal error, we probably never get here
2239 lua_settop( L, 0);
2240 lua_pushliteral( L, "Unexpected status: ");
2241 lua_pushstring( L, thread_status_string( s));
2242 lua_concat( L, 2);
2243 lua_error( L);
2244 break;
2245 }
2246 // fall through if we are killed, as we got nil, "killed" on the stack
2247
2174 case DONE: // got regular return values 2248 case DONE: // got regular return values
2175 { 2249 {
2176 int i, nvalues = lua_gettop( L) - 3; 2250 int i, nvalues = lua_gettop( L) - 3;
2177 for( i = nvalues; i > 0; -- i) 2251 for( i = nvalues; i > 0; -- i)
2178 { 2252 {
2179 // pop the last element of the stack, to store it in the environment at its proper index 2253 // pop the last element of the stack, to store it in the uservalue at its proper index
2180 lua_rawseti( L, ENV, i); 2254 lua_rawseti( L, USR, i);
2181 } 2255 }
2182 } 2256 }
2183 break; 2257 break;
@@ -2190,28 +2264,19 @@ LUAG_FUNC( thread_index)
2190 // store errstring at key -1 2264 // store errstring at key -1
2191 lua_pushnumber( L, -1); 2265 lua_pushnumber( L, -1);
2192 lua_pushvalue( L, 5); 2266 lua_pushvalue( L, 5);
2193 lua_rawset( L, ENV); 2267 lua_rawset( L, USR);
2194 break; 2268 break;
2195 2269
2196 case CANCELLED: 2270 case CANCELLED:
2197 // do nothing 2271 // do nothing
2198 break; 2272 break;
2199
2200 default:
2201 // this is an internal error, we probably never get here
2202 lua_settop( L, 0);
2203 lua_pushliteral( L, "Unexpected status: ");
2204 lua_pushstring( L, thread_status_string( s));
2205 lua_concat( L, 2);
2206 lua_error( L);
2207 break;
2208 } 2273 }
2209 } 2274 }
2210 lua_settop( L, 3); // UD KEY ENV 2275 lua_settop( L, 3); // UD KEY ENV
2211 if( key != -1) 2276 if( key != -1)
2212 { 2277 {
2213 lua_pushnumber( L, -1); // UD KEY ENV -1 2278 lua_pushnumber( L, -1); // UD KEY ENV -1
2214 lua_rawget( L, ENV); // UD KEY ENV "error" 2279 lua_rawget( L, USR); // UD KEY ENV "error"
2215 if( !lua_isnil( L, -1)) // an error was stored 2280 if( !lua_isnil( L, -1)) // an error was stored
2216 { 2281 {
2217 // Note: Lua 5.1 interpreter is not prepared to show 2282 // Note: Lua 5.1 interpreter is not prepared to show
@@ -2238,7 +2303,7 @@ LUAG_FUNC( thread_index)
2238 lua_pop( L, 1); // back to our 3 arguments on the stack 2303 lua_pop( L, 1); // back to our 3 arguments on the stack
2239 } 2304 }
2240 } 2305 }
2241 lua_rawgeti( L, ENV, (int)key); 2306 lua_rawgeti( L, USR, (int)key);
2242 } 2307 }
2243 return 1; 2308 return 1;
2244 } 2309 }
@@ -2248,17 +2313,18 @@ LUAG_FUNC( thread_index)
2248 lua_settop( L, 2); // keep only our original arguments on the stack 2313 lua_settop( L, 2); // keep only our original arguments on the stack
2249 if( strcmp( keystr, "status") == 0) 2314 if( strcmp( keystr, "status") == 0)
2250 { 2315 {
2251 push_thread_status( L, s); // push the string representing the status 2316 return push_thread_status( L, s); // push the string representing the status
2252 } 2317 }
2253 else if( strcmp( keystr, "cancel") == 0 || strcmp( keystr, "join") == 0) 2318 // return UD.metatable[key]
2319 lua_getmetatable( L, UD); // UD KEY mt
2320 lua_replace( L, -3); // mt KEY
2321 lua_rawget( L, -2); // mt value
2322 // only "cancel" and "join" are registered as functions, any other string will raise an error
2323 if( lua_iscfunction( L, -1))
2254 { 2324 {
2255 // return UD.metatable[key] (should be a function in both cases) 2325 return 1;
2256 lua_getmetatable( L, UD); // UD KEY mt
2257 lua_replace( L, -3); // mt KEY
2258 lua_rawget( L, -2); // mt value
2259 ASSERT_L( lua_iscfunction( L, -1));
2260 } 2326 }
2261 return 1; 2327 return luaL_error( L, "can't index a lane with '%s'", keystr);
2262 } 2328 }
2263 // unknown key 2329 // unknown key
2264 lua_getmetatable( L, UD); 2330 lua_getmetatable( L, UD);
@@ -2338,14 +2404,14 @@ static const struct luaL_Reg lanes_functions [] = {
2338 {"now_secs", LG_now_secs}, 2404 {"now_secs", LG_now_secs},
2339 {"wakeup_conv", LG_wakeup_conv}, 2405 {"wakeup_conv", LG_wakeup_conv},
2340 {"nameof", luaG_nameof}, 2406 {"nameof", luaG_nameof},
2341 {"_single", LG__single}, 2407 {"set_singlethreaded", LG_set_singlethreaded},
2342 {NULL, NULL} 2408 {NULL, NULL}
2343}; 2409};
2344 2410
2345/* 2411/*
2346* One-time initializations 2412* One-time initializations
2347*/ 2413*/
2348static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_ref, int const nbKeepers, lua_CFunction _on_state_create) 2414static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_ref, int const nbKeepers, lua_CFunction _on_state_create, lua_Number _shutdown_timeout)
2349{ 2415{
2350 const char *err; 2416 const char *err;
2351 2417
@@ -2398,7 +2464,7 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r
2398 err = init_keepers( nbKeepers, _on_state_create); 2464 err = init_keepers( nbKeepers, _on_state_create);
2399 if (err) 2465 if (err)
2400 { 2466 {
2401 luaL_error( L, "Unable to initialize: %s", err ); 2467 (void) luaL_error( L, "Unable to initialize: %s", err );
2402 } 2468 }
2403 2469
2404 // Initialize 'timer_deep'; a common Linda object shared by all states 2470 // Initialize 'timer_deep'; a common Linda object shared by all states
@@ -2428,7 +2494,8 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r
2428 { 2494 {
2429 lua_newuserdata( L, 1); 2495 lua_newuserdata( L, 1);
2430 lua_newtable( L); 2496 lua_newtable( L);
2431 lua_pushcfunction( L, selfdestruct_gc); 2497 lua_pushnumber( L, _shutdown_timeout);
2498 lua_pushcclosure( L, selfdestruct_gc, 1);
2432 lua_setfield( L, -2, "__gc"); 2499 lua_setfield( L, -2, "__gc");
2433 lua_pushliteral( L, "AtExit"); 2500 lua_pushliteral( L, "AtExit");
2434 lua_setfield( L, -2, "__metatable"); 2501 lua_setfield( L, -2, "__metatable");
@@ -2446,10 +2513,10 @@ static volatile long s_initCount = 0;
2446LUAG_FUNC( configure ) 2513LUAG_FUNC( configure )
2447{ 2514{
2448 char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); 2515 char const* name = luaL_checkstring( L, lua_upvalueindex( 1));
2449 int const nbKeepers = luaL_optint( L, 1, 1); 2516 // all parameter checks are done lua-side
2517 int const nbKeepers = (int)lua_tointeger( L, 1);
2450 lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL; 2518 lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL;
2451 luaL_argcheck( L, nbKeepers > 0, 1, "Number of keeper states must be > 0"); 2519 lua_Number shutdown_timeout = lua_tonumber( L, 3);
2452 luaL_argcheck( L, lua_iscfunction( L, 2) || lua_isnil( L, 2), 2, "on_state_create should be a C function");
2453 /* 2520 /*
2454 * Making one-time initializations. 2521 * Making one-time initializations.
2455 * 2522 *
@@ -2462,7 +2529,7 @@ LUAG_FUNC( configure )
2462 static volatile int /*bool*/ go_ahead; // = 0 2529 static volatile int /*bool*/ go_ahead; // = 0
2463 if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) 2530 if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0)
2464 { 2531 {
2465 init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create); 2532 init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create, shutdown_timeout);
2466 go_ahead= 1; // let others pass 2533 go_ahead= 1; // let others pass
2467 } 2534 }
2468 else 2535 else
@@ -2480,7 +2547,7 @@ LUAG_FUNC( configure )
2480 // 2547 //
2481 if( s_initCount == 0) 2548 if( s_initCount == 0)
2482 { 2549 {
2483 init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create); 2550 init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create, shutdown_timeout);
2484 s_initCount = 1; 2551 s_initCount = 1;
2485 } 2552 }
2486 } 2553 }
@@ -2525,7 +2592,7 @@ LUAG_FUNC( configure )
2525 lua_setfield(L, -2, "timer_gateway"); 2592 lua_setfield(L, -2, "timer_gateway");
2526 2593
2527 lua_pushstring(L, VERSION); 2594 lua_pushstring(L, VERSION);
2528 lua_setfield(L, -2, "_version"); 2595 lua_setfield(L, -2, "version");
2529 2596
2530 lua_pushinteger(L, THREAD_PRIO_MAX); 2597 lua_pushinteger(L, THREAD_PRIO_MAX);
2531 lua_setfield(L, -2, "max_prio"); 2598 lua_setfield(L, -2, "max_prio");
diff --git a/src/lanes.lua b/src/lanes.lua
index e6400df..c9ab07d 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -40,55 +40,93 @@ THE SOFTWARE.
40]]-- 40]]--
41 41
42-- Lua 5.1: module() creates a global variable 42-- Lua 5.1: module() creates a global variable
43-- Lua 5.2: module() might go away 43-- Lua 5.2: module() is gone
44-- almost everything module() does is done by require() 44-- almost everything module() does is done by require() anyway
45-- -> simply create a table, populate it, return it, and be done 45-- -> simply create a table, populate it, return it, and be done
46local lanes = {} 46local lanes = {}
47 47
48lanes.configure = function( _params) 48lanes.configure = function( _params)
49_params = _params or { nb_keepers = 1, with_timers = true, on_state_create = nil}
50if type( _params) ~= "table" then
51 error( "Bad parameter #1 to lanes.configure(), should be a table")
52end
53-- on_state_create may be nil or a function
54if _params.on_state_create and (type( _params.on_state_create) ~= "function") then
55 error( "Bad on_state_create: " .. tostring( _params.on_state_create), 2)
56end
57 49
58local mm = require "lanes.core" 50 -- This check is for sublanes requiring Lanes
59assert( type(mm)=="table" ) 51 --
52 -- TBD: We could also have the C level expose 'string.gmatch' for us. But this is simpler.
53 --
54 if not string then
55 error( "To use 'lanes', you will also need to have 'string' available.", 2)
56 end
60 57
61-- configure() is available only the first time lanes.core is required process-wide, and we *must* call it to have the other functions in the interface 58 --
62if mm.configure then mm.configure( _params.nb_keepers, _params.on_state_create) end 59 -- Cache globals for code that might run under sandboxing
60 --
61 local assert = assert
62 local string_gmatch = assert( string.gmatch)
63 local select = assert( select)
64 local type = assert( type)
65 local pairs = assert( pairs)
66 local tostring = assert( tostring)
67 local error = assert( error)
68
69 local default_params = { nb_keepers = 1, on_state_create = nil, shutdown_timeout = 0.25, with_timers = true}
70 local param_checkers =
71 {
72 nb_keepers = function( _val)
73 -- nb_keepers should be a number > 0
74 return type( _val) == "number" and _val > 0
75 end,
76 with_timers = function( _val)
77 -- with_timers may be nil or boolean
78 return _val and type( _val) == "boolean" or true
79 end,
80 on_state_create = function( _val)
81 -- on_state_create may be nil or a function
82 return _val and type( _val) == "function" or true
83 end,
84 shutdown_timeout = function( _val)
85 -- nb_keepers should be a number >= 0
86 return type( _val) == "number" and _val >= 0
87 end
88 }
63 89
64local thread_new = assert(mm.thread_new) 90 local params_checker = function( _params)
91 if not _params then
92 return default_params
93 end
94 if type( _params) ~= "table" then
95 error( "Bad parameter #1 to lanes.configure(), should be a table")
96 end
97 -- any setting not present in the provided parameters takes the default value
98 for key, value in pairs( default_params) do
99 local my_param = _params[key]
100 local param
101 if my_param ~= nil then
102 param = my_param
103 else
104 param = default_params[key]
105 end
106 if not param_checkers[key]( param) then
107 error( "Bad " .. key .. ": " .. tostring( param), 2)
108 end
109 _params[key] = param
110 end
111 return _params
112 end
65 113
66local _single= assert(mm._single) 114 _params = params_checker( _params)
67local _version= assert(mm._version)
68 115
69local now_secs= assert( mm.now_secs ) 116 local core = require "lanes.core"
70local wakeup_conv= assert( mm.wakeup_conv ) 117 assert( type( core)=="table")
71 118
72local max_prio= assert( mm.max_prio ) 119 -- configure() is available only the first time lanes.core is required process-wide, and we *must* call it to have the other functions in the interface
120 if core.configure then core.configure( _params.nb_keepers, _params.on_state_create, _params.shutdown_timeout) end
73 121
74-- This check is for sublanes requiring Lanes 122 local thread_new = assert( core.thread_new)
75--
76-- TBD: We could also have the C level expose 'string.gmatch' for us. But this is simpler.
77--
78if not string then
79 error( "To use 'lanes', you will also need to have 'string' available.", 2 )
80end
81 123
82-- 124 local set_singlethreaded = assert( core.set_singlethreaded)
83-- Cache globals for code that might run under sandboxing 125
84-- 126 local now_secs = assert( core.now_secs)
85local assert= assert 127 local wakeup_conv = assert( core.wakeup_conv)
86local string_gmatch= assert( string.gmatch ) 128
87local select= assert( select ) 129 local max_prio = assert( core.max_prio)
88local type= assert( type )
89local pairs= assert( pairs )
90local tostring= assert( tostring )
91local error= assert( error )
92 130
93lanes.ABOUT= 131lanes.ABOUT=
94{ 132{
@@ -96,7 +134,7 @@ lanes.ABOUT=
96 description= "Running multiple Lua states in parallel", 134 description= "Running multiple Lua states in parallel",
97 license= "MIT/X11", 135 license= "MIT/X11",
98 copyright= "Copyright (c) 2007-10, Asko Kauppi; (c) 2011-12, Benoit Germain", 136 copyright= "Copyright (c) 2007-10, Asko Kauppi; (c) 2011-12, Benoit Germain",
99 version= _version, 137 version = assert( core.version)
100} 138}
101 139
102 140
@@ -258,7 +296,7 @@ end
258-- lanes.linda(["name"]) -> linda_ud 296-- lanes.linda(["name"]) -> linda_ud
259-- 297--
260-- PUBLIC LANES API 298-- PUBLIC LANES API
261local linda = mm.linda 299local linda = core.linda
262 300
263 301
264---=== Timers ===--- 302---=== Timers ===---
@@ -268,7 +306,7 @@ local timer = function() error "timers are not active" end
268 306
269if _params.with_timers ~= false then 307if _params.with_timers ~= false then
270 308
271local timer_gateway= assert( mm.timer_gateway ) 309local timer_gateway = assert( core.timer_gateway)
272-- 310--
273-- On first 'require "lanes"', a timer lane is spawned that will maintain 311-- On first 'require "lanes"', a timer lane is spawned that will maintain
274-- timer tables and sleep in between the timer events. All interaction with 312-- timer tables and sleep in between the timer events. All interaction with
@@ -558,28 +596,23 @@ local function genatomic( linda, key, initial_val )
558 end 596 end
559end 597end
560 598
561-- newuserdata = mm.newuserdata
562
563 -- activate full interface 599 -- activate full interface
564 lanes.gen = gen 600 lanes.gen = gen
565 lanes.linda = mm.linda 601 lanes.linda = core.linda
566 lanes.cancel_error = mm.cancel_error 602 lanes.cancel_error = core.cancel_error
567 lanes.nameof = mm.nameof 603 lanes.nameof = core.nameof
568 lanes.timer = timer 604 lanes.timer = timer
569 lanes.genlock = genlock 605 lanes.genlock = genlock
570 lanes.now_secs = now_secs 606 lanes.now_secs = now_secs
571 lanes.genatomic = genatomic 607 lanes.genatomic = genatomic
572 -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation 608 -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation
573 lanes.configure = function( _params2) 609 lanes.configure = function( _params2)
574 _params2 = _params2 or _params 610 _params2 = params_checker( _params2 or _params)
575 if _params2.nb_keepers ~= _params.nb_keepers then 611 for key, value2 in pairs( _params2) do
576 error( "mismatched configuration: " .. tostring( _params2.nb_keepers) .. " keepers instead of " .. tostring( _params.nb_keepers)) 612 local value = _params[key]
577 end 613 if value2 ~= value then
578 if _params2.with_timers ~= _params.with_timers then 614 error( "mismatched configuration: " .. key .. " is " .. tostring( value2) .. " instead of " .. tostring( value))
579 error( "mismatched configuration: " .. tostring( _params2.with_timers) .. " timer activity instead of " .. tostring( _params.with_timers)) 615 end
580 end
581 if _params2.on_create_state and _params2.on_create_state ~= _params.on_create_state then
582 error( "mismatched configuration: " .. tostring( _params2.on_create_state) .. " timer activity instead of " .. tostring( _params.on_create_state))
583 end 616 end
584 return lanes 617 return lanes
585 end 618 end
@@ -588,4 +621,3 @@ end -- lanes.configure
588 621
589--the end 622--the end
590return lanes 623return lanes
591
diff --git a/src/threading.h b/src/threading.h
index b0a3db0..f4f1ada 100644
--- a/src/threading.h
+++ b/src/threading.h
@@ -35,6 +35,9 @@ typedef unsigned int uint_t;
35#include <time.h> 35#include <time.h>
36 36
37/* Note: ERROR is a defined entity on Win32 37/* Note: ERROR is a defined entity on Win32
38 PENDING: The Lua VM hasn't done anything yet.
39 RUNNING, WAITING: Thread is inside the Lua VM. If the thread is forcefully stopped, we can't lua_close() the Lua State.
40 DONE, ERROR_ST, CANCELLED: Thread execution is outside the Lua VM. It can be lua_close()d.
38*/ 41*/
39enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; 42enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED };
40 43
diff --git a/tests/fibonacci.lua b/tests/fibonacci.lua
index 452a770..48cd8d7 100644
--- a/tests/fibonacci.lua
+++ b/tests/fibonacci.lua
@@ -4,7 +4,7 @@
4-- Parallel calculation of Fibonacci numbers 4-- Parallel calculation of Fibonacci numbers
5-- 5--
6-- A sample of task splitting like Intel TBB library does. 6-- A sample of task splitting like Intel TBB library does.
7-- 7--
8-- References: 8-- References:
9-- Intel Threading Building Blocks, 'test all' 9-- Intel Threading Building Blocks, 'test all'
10-- <http://shareit.intel.com/WikiHome/Articles/111111316> 10-- <http://shareit.intel.com/WikiHome/Articles/111111316>
@@ -41,7 +41,7 @@ local function fib( n )
41 -- 41 --
42 -- note that lanes is pulled in as upvalue, so we need package library to require internals properly 42 -- note that lanes is pulled in as upvalue, so we need package library to require internals properly
43 -- (because lua51-lanes is always required internally if possible, which is necessary in that case) 43 -- (because lua51-lanes is always required internally if possible, which is necessary in that case)
44 local gen_f= lanes.gen( "package,string,io,math,debug", fib ) 44 local gen_f= lanes.gen( "*", fib )
45 45
46 local n1=floor(n/2) +1 46 local n1=floor(n/2) +1
47 local n2=floor(n/2) -1 + n%2 47 local n2=floor(n/2) -1 + n%2
@@ -60,7 +60,7 @@ local function fib( n )
60 end 60 end
61 61
62 io.stderr:write( "fib("..n..") = "..sum.."\n" ) 62 io.stderr:write( "fib("..n..") = "..sum.."\n" )
63 63
64 return sum 64 return sum
65end 65end
66 66
diff --git a/tests/finalizer.lua b/tests/finalizer.lua
index 6f186ab..dc9ed34 100644
--- a/tests/finalizer.lua
+++ b/tests/finalizer.lua
@@ -31,6 +31,7 @@ local function lane()
31 io.stderr:write( "File "..FN.." created\n" ) 31 io.stderr:write( "File "..FN.." created\n" )
32 32
33 if which==0 then 33 if which==0 then
34 print "you loose"
34 error("aa") -- exception here; the value needs NOT be a string 35 error("aa") -- exception here; the value needs NOT be a string
35 end 36 end
36 37
@@ -55,6 +56,7 @@ cleanup= function(err)
55 end 56 end
56 57
57 local _,err2= os.remove(FN) 58 local _,err2= os.remove(FN)
59 print( "file removal result: ", tostring( err2))
58 assert(not err2) -- if this fails, it will be shown in the calling script 60 assert(not err2) -- if this fails, it will be shown in the calling script
59 -- as an error from the lane itself 61 -- as an error from the lane itself
60 62