diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2012-09-10 20:41:03 +0200 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2012-09-10 20:41:03 +0200 |
commit | 242feeb342f68999b02c2b8dc4614abefdab8431 (patch) | |
tree | 904a3898035f89e5656036a0e7d454f560eb0455 | |
parent | e37191ab11102ee784dc88578165074d2d0a2fff (diff) | |
download | lanes-242feeb342f68999b02c2b8dc4614abefdab8431.tar.gz lanes-242feeb342f68999b02c2b8dc4614abefdab8431.tar.bz2 lanes-242feeb342f68999b02c2b8dc4614abefdab8431.zip |
version 3.3.0
* lane.status can return "killed" if lane was forcefully killed with lanes:cancel()
* lane:join(): return nil, "killed" if called on a killed lane.
* lane[<n>]: produces [1] = nil, [2] = "killed" if the lane was killed
* lane:join(): fixed an assertion in debug builds when joining a lane forcefully cancelled with lane:cancel( <x>, true).
* indexing a lane with a string other than "join", "cancel" or "status" raises an error.
* fixed configure() to correctly apply defaults when they are missing from the provided settings
* added a shutdown_timeout to control the duration Lanes will wait for graceful termination of running lanes at application shutdown. Default is 0.25.
Among other things, fixes issue #31.
-rw-r--r-- | CHANGES | 10 | ||||
-rw-r--r-- | docs/index.html | 33 | ||||
-rw-r--r-- | src/lanes.c | 417 | ||||
-rw-r--r-- | src/lanes.lua | 142 | ||||
-rw-r--r-- | src/threading.h | 3 | ||||
-rw-r--r-- | tests/fibonacci.lua | 6 | ||||
-rw-r--r-- | tests/finalizer.lua | 2 |
7 files changed, 369 insertions, 244 deletions
@@ -1,5 +1,15 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHAGE 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 | |||
3 | CHANGE 45: BGe 21-Aug-2012 | 13 | CHANGE 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 © 2007-12 Asko Kauppi, Benoit Germain. All rights reserved.</i> | 57 | <p><br/><font size="-1"><i>Copyright © 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> |
442 | Waits until the lane finishes, or <tt>timeout</tt> seconds have passed. | 449 | Waits until the lane finishes, or <tt>timeout</tt> seconds have passed. |
443 | Returns <tt>nil</tt> on timeout, <tt>nil,err,stack_tbl</tt> if the lane hit an error, | 450 | Returns <tt>nil</tt> on timeout, <tt>nil,err,stack_tbl</tt> if the lane hit an error, |
444 | or 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. |
445 | fashion, errors are not propagated. | 452 | Unlike 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 | |||
507 | OS thread running the lane is forcefully killed. This means no GC, and should | 515 | OS thread running the lane is forcefully killed. This means no GC, and should |
508 | generally be the last resort. | 516 | generally be the last resort. |
509 | </p> | 517 | </p> |
518 | <p> | ||
519 | Starting 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 |
511 | and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt> | 522 | and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt> |
512 | or <tt>send()</tt> call is awakened. This means the execution of the lane will resume although the operation has | 523 | or <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 | ||
54 | char const* VERSION = "3.2.0"; | 54 | char const* VERSION = "3.3.0"; |
55 | 55 | ||
56 | /* | 56 | /* |
57 | =============================================================================== | 57 | =============================================================================== |
58 | 58 | ||
59 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> | 59 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> |
60 | 2011-12 Benoit Germain <bnt.germain@gmail.com> | ||
60 | 61 | ||
61 | Permission is hereby granted, free of charge, to any person obtaining a copy | 62 | Permission is hereby granted, free of charge, to any person obtaining a copy |
62 | of this software and associated documentation files (the "Software"), to deal | 63 | of 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 | // |
1291 | LUAG_FUNC( _single ) { | 1292 | LUAG_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: | 1907 | LUAG_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 | // | ||
1898 | LUAG_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 | // |
1964 | static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force) | 1974 | |
1975 | typedef enum | ||
1976 | { | ||
1977 | CR_Timeout, | ||
1978 | CR_Cancelled, | ||
1979 | CR_Killed | ||
1980 | } cancel_result; | ||
1981 | |||
1982 | static 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 | ||
1999 | LUAG_FUNC( thread_cancel) | 2029 | LUAG_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) | |||
2039 | static char const * thread_status_string( struct s_lane *s) | 2087 | static 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 | ||
2056 | static void push_thread_status( lua_State *L, struct s_lane *s) | 2101 | static 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 | // |
2073 | LUAG_FUNC( thread_join ) | 2119 | LUAG_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 | */ |
2348 | static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_ref, int const nbKeepers, lua_CFunction _on_state_create) | 2414 | static 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; | |||
2446 | LUAG_FUNC( configure ) | 2513 | LUAG_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 |
46 | local lanes = {} | 46 | local lanes = {} |
47 | 47 | ||
48 | lanes.configure = function( _params) | 48 | lanes.configure = function( _params) |
49 | _params = _params or { nb_keepers = 1, with_timers = true, on_state_create = nil} | ||
50 | if type( _params) ~= "table" then | ||
51 | error( "Bad parameter #1 to lanes.configure(), should be a table") | ||
52 | end | ||
53 | -- on_state_create may be nil or a function | ||
54 | if _params.on_state_create and (type( _params.on_state_create) ~= "function") then | ||
55 | error( "Bad on_state_create: " .. tostring( _params.on_state_create), 2) | ||
56 | end | ||
57 | 49 | ||
58 | local mm = require "lanes.core" | 50 | -- This check is for sublanes requiring Lanes |
59 | assert( 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 | -- |
62 | if 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 | ||
64 | local 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 | ||
66 | local _single= assert(mm._single) | 114 | _params = params_checker( _params) |
67 | local _version= assert(mm._version) | ||
68 | 115 | ||
69 | local now_secs= assert( mm.now_secs ) | 116 | local core = require "lanes.core" |
70 | local wakeup_conv= assert( mm.wakeup_conv ) | 117 | assert( type( core)=="table") |
71 | 118 | ||
72 | local 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 | -- | ||
78 | if not string then | ||
79 | error( "To use 'lanes', you will also need to have 'string' available.", 2 ) | ||
80 | end | ||
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) |
85 | local assert= assert | 127 | local wakeup_conv = assert( core.wakeup_conv) |
86 | local string_gmatch= assert( string.gmatch ) | 128 | |
87 | local select= assert( select ) | 129 | local max_prio = assert( core.max_prio) |
88 | local type= assert( type ) | ||
89 | local pairs= assert( pairs ) | ||
90 | local tostring= assert( tostring ) | ||
91 | local error= assert( error ) | ||
92 | 130 | ||
93 | lanes.ABOUT= | 131 | lanes.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 |
261 | local linda = mm.linda | 299 | local 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 | ||
269 | if _params.with_timers ~= false then | 307 | if _params.with_timers ~= false then |
270 | 308 | ||
271 | local timer_gateway= assert( mm.timer_gateway ) | 309 | local 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 |
559 | end | 597 | end |
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 |
590 | return lanes | 623 | return 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 | */ |
39 | enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; | 42 | enum 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 |
65 | end | 65 | end |
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 | ||