diff options
| author | Benoit Germain <bnt.germain@gmail.com> | 2011-03-01 21:12:56 +0100 |
|---|---|---|
| committer | Benoit Germain <bnt.germain@gmail.com> | 2011-03-01 21:12:56 +0100 |
| commit | e97adefde985e30fe31ffa036c74ffb0ce10ca26 (patch) | |
| tree | 4465decc5f07e7399f9760d6302dbaf7163120d7 | |
| parent | 974aa4343cf900938b5d357d10798d91faf60f5a (diff) | |
| download | lanes-e97adefde985e30fe31ffa036c74ffb0ce10ca26.tar.gz lanes-e97adefde985e30fe31ffa036c74ffb0ce10ca26.tar.bz2 lanes-e97adefde985e30fe31ffa036c74ffb0ce10ca26.zip | |
* fixed potential crash at application shutdown when calling lua_close() on a killed thread's VM.
* exposed cancel_test() in the lanes to enable manual testing for cancellation requests.
* removed kludgy {globals={threadName}} support, replaced with a new function set_debug_threadname().
| -rw-r--r-- | CHANGES | 14 | ||||
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | docs/index.html | 72 | ||||
| -rw-r--r-- | src/keeper.c | 6 | ||||
| -rw-r--r-- | src/lanes.c | 238 | ||||
| -rw-r--r-- | src/lanes.lua | 4 | ||||
| -rw-r--r-- | tests/atexit.lua | 45 |
7 files changed, 250 insertions, 133 deletions
| @@ -3,12 +3,14 @@ CHANGES: | |||
| 3 | 3 | ||
| 4 | CHANGE X: | 4 | CHANGE X: |
| 5 | 5 | ||
| 6 | CHANGE 29 BGe 21-Feb-2011 | 6 | CHANGE 29 BGe 1-Mar-2011 |
| 7 | Make the number of internal keeper states selectable by an optional parameter passed to require. | 7 | fixed potential crash at application shutdown when calling lua_close() on a killed thread's VM. |
| 8 | exposed cancel_test() in the lanes to enable manual testing for cancellation requests. | ||
| 9 | removed kludgy {globals={threadName}} support, replaced with a new function set_debug_threadname(). | ||
| 8 | 10 | ||
| 9 | CHANGE 28 BGe 18-Feb-2011 | 11 | CHANGE 28 BGe 18-Feb-2011 |
| 10 | Moved keeper-related code in a separate source file | 12 | - moved keeper-related code in a separate source file |
| 11 | keeper.lua is now embedded in text form instead of bytecode to improve LuaJIT2-compatibility | 13 | - keeper.lua is now embedded in text form instead of bytecode to improve LuaJIT2-compatibility |
| 12 | 14 | ||
| 13 | CHANGE 27 BGe 17-Feb-2011 | 15 | CHANGE 27 BGe 17-Feb-2011 |
| 14 | - we know Lanes is loaded in the master state, so we don't force it | 16 | - we know Lanes is loaded in the master state, so we don't force it |
| @@ -29,7 +31,7 @@ CHANGE 26 BGe 14-Feb-2011: | |||
| 29 | inter-state data copy for unsupported types | 31 | inter-state data copy for unsupported types |
| 30 | 32 | ||
| 31 | CHANGE 25 BGe 12-Feb-2011: | 33 | CHANGE 25 BGe 12-Feb-2011: |
| 32 | Changed idfunc signature and contract to clarify the fact it is not lua-callable | 34 | Changed idfunc signature and contract to clarify that fact it is not lua-callable |
| 33 | and to be able to require the module it was exported from in the target lanes | 35 | and to be able to require the module it was exported from in the target lanes |
| 34 | 36 | ||
| 35 | CHANGE 24 DPtr 25-Jan-2011: | 37 | CHANGE 24 DPtr 25-Jan-2011: |
| @@ -59,7 +61,7 @@ CHANGE 19 BGe 2-Dec-2010: | |||
| 59 | 61 | ||
| 60 | CHANGE 18 BGe 6-Oct-2010: | 62 | CHANGE 18 BGe 6-Oct-2010: |
| 61 | Fixed 'memory leak' in some situations where a free running lane is collected before application shutdown | 63 | Fixed 'memory leak' in some situations where a free running lane is collected before application shutdown |
| 62 | A bit of code cleanup | 64 | A bit of code cleanup |
| 63 | 65 | ||
| 64 | CHANGE 17 BGe 21-Sept-2010: | 66 | CHANGE 17 BGe 21-Sept-2010: |
| 65 | Fixed stupid compilation errors. | 67 | Fixed stupid compilation errors. |
| @@ -84,6 +84,7 @@ test: | |||
| 84 | $(MAKE) fibonacci | 84 | $(MAKE) fibonacci |
| 85 | $(MAKE) recursive | 85 | $(MAKE) recursive |
| 86 | $(MAKE) func_is_string | 86 | $(MAKE) func_is_string |
| 87 | $(MAKE) atexit | ||
| 87 | 88 | ||
| 88 | basic: tests/basic.lua $(_TARGET_SO) | 89 | basic: tests/basic.lua $(_TARGET_SO) |
| 89 | $(_PREFIX) $(LUA) $< | 90 | $(_PREFIX) $(LUA) $< |
| @@ -154,6 +155,9 @@ appendud: tests/appendud.lua $(_TARGET_SO) | |||
| 154 | func_is_string: tests/func_is_string.lua $(_TARGET_SO) | 155 | func_is_string: tests/func_is_string.lua $(_TARGET_SO) |
| 155 | $(_PREFIX) $(LUA) $< | 156 | $(_PREFIX) $(LUA) $< |
| 156 | 157 | ||
| 158 | atexit: tests/atexit.lua $(_TARGET_SO) | ||
| 159 | $(_PREFIX) $(LUA) $< | ||
| 160 | |||
| 157 | #--- | 161 | #--- |
| 158 | perftest-plain: tests/perftest.lua $(_TARGET_SO) | 162 | perftest-plain: tests/perftest.lua $(_TARGET_SO) |
| 159 | $(MAKE) _perftest ARGS="$< $(N) -plain" | 163 | $(MAKE) _perftest ARGS="$< $(N) -plain" |
diff --git a/docs/index.html b/docs/index.html index 3d2ecf2..ba25515 100644 --- a/docs/index.html +++ b/docs/index.html | |||
| @@ -56,7 +56,7 @@ | |||
| 56 | 56 | ||
| 57 | <p><br/><font size="-1"><i>Copyright © 2007-11 Asko Kauppi. All rights reserved.</i> | 57 | <p><br/><font size="-1"><i>Copyright © 2007-11 Asko Kauppi. 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. | 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. |
| 59 | </p><p>This document was revised on 21-Feb-11, and applies to version 2.1.0. | 59 | </p><p>This document was revised on 1-Mar-11, and applies to version 2.1.0. |
| 60 | </font></p> | 60 | </font></p> |
| 61 | 61 | ||
| 62 | </center> | 62 | </center> |
| @@ -243,6 +243,7 @@ also in the new lanes. | |||
| 243 | <tt>:receive()</tt> or <tt>:send()</tt> call. | 243 | <tt>:receive()</tt> or <tt>:send()</tt> call. |
| 244 | With this option, one can set cancellation check to occur every <tt>N</tt> | 244 | With this option, one can set cancellation check to occur every <tt>N</tt> |
| 245 | Lua statements. The value <tt>true</tt> uses a default value (100). | 245 | Lua statements. The value <tt>true</tt> uses a default value (100). |
| 246 | It is also possible to manually test for cancel requests with <tt>cancel_test()</tt>. | ||
| 246 | </td></tr> | 247 | </td></tr> |
| 247 | 248 | ||
| 248 | <tr valign=top><td/><td> | 249 | <tr valign=top><td/><td> |
| @@ -250,20 +251,21 @@ also in the new lanes. | |||
| 250 | <td> | 251 | <td> |
| 251 | Sets the globals table for the launched threads. This can be used for giving | 252 | Sets the globals table for the launched threads. This can be used for giving |
| 252 | them constants. | 253 | them constants. |
| 253 | </p><p> | 254 | <br> |
| 254 | The global values of different lanes are in no manner connected; | 255 | The global values of different lanes are in no manner connected; |
| 255 | modifying one will only affect the particular lane. Settings the variable 'threadName' in this table makes VS display the sent name instead of the normal thread name while debugging. | 256 | modifying one will only affect the particular lane. |
| 256 | </td></tr> | 257 | </td></tr> |
| 257 | 258 | ||
| 258 | <tr valign=top><td width=40><td> | 259 | <tr valign=top><td width=40><td> |
| 259 | <code>.priority</code> <br/><nobr>-2..+2</nobr></td> | 260 | <code>.priority</code> <br/><nobr>-2..+2</nobr></td> |
| 260 | <td>The priority of lanes generated. -2 is lowest, +2 is highest. | 261 | <td>The priority of lanes generated. -2 is lowest, +2 is highest. |
| 261 | <p> | 262 | <br> |
| 262 | Implementation and dependability of priorities varies | 263 | Implementation and dependability of priorities varies |
| 263 | by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode. | 264 | by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode. |
| 264 | </td></tr> | 265 | </td></tr> |
| 265 | </table> | 266 | </table> |
| 266 | 267 | <p>Each lane also gets a function <tt>set_debug_threadname()</tt> that it can use anytime to do as the name says. | |
| 268 | Supported debuggers are Microsoft Visual Studio (for the C side) and Decoda (for the Lua side). | ||
| 267 | </p> | 269 | </p> |
| 268 | 270 | ||
| 269 | <h3>Free running lanes</h3> | 271 | <h3>Free running lanes</h3> |
| @@ -374,10 +376,15 @@ that id over a Linda once that thread is done (as the last thing you do). | |||
| 374 | <h2 id="cancelling">Cancelling</h2> | 376 | <h2 id="cancelling">Cancelling</h2> |
| 375 | 377 | ||
| 376 | <table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td> | 378 | <table border=1 bgcolor="#E0E0FF" cellpadding=10><tr><td> |
| 377 | <code>bool= lane_h:cancel( [timeout_secs=0.0,] [force_kill_bool=false] )</code> | 379 | <code>bool= lane_h:cancel( [timeout_secs=0.0,] [force_kill_bool=false] ) |
| 380 | <br/> | ||
| 381 | <br/> | ||
| 382 | <code> | ||
| 383 | bool= cancel_test() | ||
| 384 | </code> | ||
| 378 | </table> | 385 | </table> |
| 379 | 386 | ||
| 380 | <p>Sends a cancellation request to the lane. If <tt>timeout_secs</tt> is non-zero, waits | 387 | <p><tt>cancel()</tt>sends a cancellation request to the lane. If <tt>timeout_secs</tt> is non-zero, waits |
| 381 | for the request to be processed, or a timeout to occur. | 388 | for the request to be processed, or a timeout to occur. |
| 382 | Returns <tt>true</tt> if the lane was already done (in <tt>"done"</tt>, <tt>"error"</tt> or <tt>"cancelled"</tt> status) | 389 | Returns <tt>true</tt> if the lane was already done (in <tt>"done"</tt>, <tt>"error"</tt> or <tt>"cancelled"</tt> status) |
| 383 | or if the cancellation was fruitful within timeout period. | 390 | or if the cancellation was fruitful within timeout period. |
| @@ -387,8 +394,9 @@ OS thread running the lane is forcefully killed. This means no GC, and should | |||
| 387 | generally be the last resort. | 394 | generally be the last resort. |
| 388 | </p> | 395 | </p> |
| 389 | <p>Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls | 396 | <p>Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls |
| 390 | and after executing <tt>cancelstep</tt> Lua statements. A currently pending <tt>receive</tt> | 397 | and after executing <tt>cancelstep</tt> Lua statements. A currently pending <tt>receive()</tt> |
| 391 | or <tt>send</tt> call is currently not awakened, and may be a reason for a non-detected cancel. | 398 | or <tt>send()</tt> call is currently not awakened, and may be a reason for a non-detected cancel. |
| 399 | It is also possible to manually test for cancel requests with <tt>cancel_test()</tt>. | ||
| 392 | </p> | 400 | </p> |
| 393 | 401 | ||
| 394 | 402 | ||
| @@ -799,7 +807,7 @@ can be used for custom userdata as well. Here's what to do. | |||
| 799 | </p> | 807 | </p> |
| 800 | <ol> | 808 | <ol> |
| 801 | <li>Provide an <i>identity function</i> for your userdata, in C. This function is | 809 | <li>Provide an <i>identity function</i> for your userdata, in C. This function is |
| 802 | used for creation and deletion of your deep userdata (the shared resource), | 810 | used for creation and deletion of your deep userdata (the shared resource), |
| 803 | and for making metatables for the state-specific proxies for accessing it. The | 811 | and for making metatables for the state-specific proxies for accessing it. The |
| 804 | prototype is | 812 | prototype is |
| 805 | <table border="1" bgcolor="#E0E0FF" cellpadding="10"> | 813 | <table border="1" bgcolor="#E0E0FF" cellpadding="10"> |
| @@ -817,12 +825,12 @@ can be used for custom userdata as well. Here's what to do. | |||
| 817 | <tt>"delete"</tt>: receives this same pointer on the stack, and should cleanup the object.</li> | 825 | <tt>"delete"</tt>: receives this same pointer on the stack, and should cleanup the object.</li> |
| 818 | <li> | 826 | <li> |
| 819 | <tt>"metatable"</tt>: should build a metatable for the object. Don't cache the metatable | 827 | <tt>"metatable"</tt>: should build a metatable for the object. Don't cache the metatable |
| 820 | yourself, Lanes takes care of it ("metatable" should only be invoked once).</li> | 828 | yourself, Lanes takes care of it ("metatable" should only be invoked once).</li> |
| 821 | <li> | 829 | <li> |
| 822 | <tt>"module"</tt>: is the name of the module that exports the idfunc, | 830 | <tt>"module"</tt>: is the name of the module that exports the idfunc, |
| 823 | to be pushed on the stack as a string. It is necessary so that Lanes can require it in | 831 | to be pushed on the stack as a string. It is necessary so that Lanes can require it in |
| 824 | any Lane and keeper state that receives a userdata. This is to prevent crashes in situations | 832 | any Lane and keeper state that receives a userdata. This is to prevent crashes in situations |
| 825 | where the module could be unloaded while the idfunc pointer is still held.</li> | 833 | where the module could be unloaded while the idfunc pointer is still held.</li> |
| 826 | </ul> | 834 | </ul> |
| 827 | Take a look at <tt>linda_id</tt> in <tt>lanes.c</tt>. | 835 | Take a look at <tt>linda_id</tt> in <tt>lanes.c</tt>. |
| 828 | </li> | 836 | </li> |
| @@ -917,10 +925,10 @@ Here are some things one should consider, if best performance is vital: | |||
| 917 | merged into one main timer state (see <tt>timer.lua</tt>); no OS side | 925 | merged into one main timer state (see <tt>timer.lua</tt>); no OS side |
| 918 | timers are utilized. | 926 | timers are utilized. |
| 919 | </li> | 927 | </li> |
| 920 | <li>Lindas are hashed to a number of "keeper states", which are a locking entity. | 928 | <li>Lindas are hashed to a fixed number of "keeper states", which are a locking entity. |
| 921 | If you are using a lot of Linda objects, it may be useful to try having more of | 929 | If you are using a lot of Linda objects, |
| 922 | these keeper states. By default, only one is used but this is an implementation detail. | 930 | it may be useful to try having more of these keeper states. By default, |
| 923 | It is possible to <tt>require( "lanes", N)</tt> to use more keeper states. | 931 | only one is used (see <tt>KEEPER_STATES_N</tt>), but this is an implementation detail. |
| 924 | </li> | 932 | </li> |
| 925 | </ul> | 933 | </ul> |
| 926 | </p> | 934 | </p> |
| @@ -952,31 +960,15 @@ its actual value. | |||
| 952 | <h2 id="changes">Change log</h2> | 960 | <h2 id="changes">Change log</h2> |
| 953 | 961 | ||
| 954 | <p> | 962 | <p> |
| 955 | Feb-2011 (2.1.0) | 963 | |
| 964 | Mar-2011 (2.1.0) | ||
| 956 | <ul> | 965 | <ul> |
| 957 | <li>Added an auto-require mechanism to ensure any deep userdata transiting in a lane causes its parent module to be required in that lane | 966 | <li>fixed potential crash at application shutdown when calling lua_close() on a killed thread's VM.</li> |
| 958 | <ul> | 967 | <li>exposed cancel_test() in the lanes to enable manual testing for cancellation requests.</li> |
| 959 | <li>Changed idfunc signature and contract to clarify the fact it is not lua-callable and to be able to require the module it was exported from in the target lanes</li> | 968 | <li>removed kludgy {globals={threadName}} support, replaced with a new function set_debug_threadname().</li> |
| 960 | <li>When a deep userdata idfunc requests a module to be required, manually check that it is not loaded before requiring it instead of relying on the require function's loop detection feature</li> | ||
| 961 | <li>When a module must be required, raise an error if the 'require' function is not found in the target state</li> | ||
| 962 | <li>We know Lanes is loaded in the master state, so we don't force it to be required in every lane too when a linda deep userdata is copied</li> | ||
| 963 | </ul> | ||
| 964 | </li> | ||
| 965 | <li>Fixed application hang-up because keeper state was not released in case of errors thrown by inter-state data copy for unsupported types</li> | ||
| 966 | <li>Refactor lane proxy implementation: it is now a full userdata instead of a table, and its methods are implemented in C instead of Lua | ||
| 967 | <ul> | ||
| 968 | <li>its metatable is no longer accessible</li> | ||
| 969 | <li>writing to the proxy raises an error</li> | ||
| 970 | <li>it is no longer possible to overwrite its join() and cancel() methods</li> | ||
| 971 | </ul> | ||
| 972 | </li> | ||
| 973 | <li>Moved keeper-related code in a separate source file</li> | ||
| 974 | <li>keeper.lua is now embedded in text form instead of bytecode to improve LuaJIT2-compatibility</li> | ||
| 975 | <li>Make the number of internal keeper states selctable by an optional parameter passed to require.</li> | ||
| 976 | </ul> | 969 | </ul> |
| 977 | |||
| 978 | Feb-2011 (2.0.11): | 970 | Feb-2011 (2.0.11): |
| 979 | <ul> | 971 | <ul> |
| 980 | <li>Fixed bug where reference to Linda object was dropped for a short time (crashing if GC was run during that time).</li> | 972 | <li>Fixed bug where reference to Linda object was dropped for a short time (crashing if GC was run during that time).</li> |
| 981 | <li>Changed the atexit code to trip the timer thread's write signal.</li> | 973 | <li>Changed the atexit code to trip the timer thread's write signal.</li> |
| 982 | <li>Changed lanes.c to export functions as a module rather than writing them directly to the globals table.</li> | 974 | <li>Changed lanes.c to export functions as a module rather than writing them directly to the globals table.</li> |
diff --git a/src/keeper.c b/src/keeper.c index f89c638..01e8880 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
| @@ -94,6 +94,12 @@ char const *init_keepers( int const _nbKeepers) | |||
| 94 | if (!L) | 94 | if (!L) |
| 95 | return "out of memory"; | 95 | return "out of memory"; |
| 96 | 96 | ||
| 97 | // to see VM name in Decoda debugger | ||
| 98 | lua_pushliteral( L, "Keeper #"); | ||
| 99 | lua_pushinteger( L, i + 1); | ||
| 100 | lua_concat( L, 2); | ||
| 101 | lua_setglobal( L, "decoda_name"); | ||
| 102 | |||
| 97 | luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs | 103 | luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs |
| 98 | serialize_require( L); | 104 | serialize_require( L); |
| 99 | 105 | ||
diff --git a/src/lanes.c b/src/lanes.c index 8b62532..e5a2987 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
| @@ -119,8 +119,6 @@ struct s_lane { | |||
| 119 | // | 119 | // |
| 120 | // M: sub-thread OS thread | 120 | // M: sub-thread OS thread |
| 121 | // S: not used | 121 | // S: not used |
| 122 | |||
| 123 | char threadName[64]; //Optional, for debugging and such. owerflowable by a strcpy. | ||
| 124 | 122 | ||
| 125 | lua_State *L; | 123 | lua_State *L; |
| 126 | // | 124 | // |
| @@ -132,6 +130,10 @@ struct s_lane { | |||
| 132 | // M: sets to PENDING (before launching) | 130 | // M: sets to PENDING (before launching) |
| 133 | // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED | 131 | // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED |
| 134 | 132 | ||
| 133 | volatile SIGNAL_T waiting_on; | ||
| 134 | // | ||
| 135 | // When status is WAITING, points on the linda's signal the thread waits on, else NULL | ||
| 136 | |||
| 135 | volatile bool_t cancel_request; | 137 | volatile bool_t cancel_request; |
| 136 | // | 138 | // |
| 137 | // M: sets to FALSE, flags TRUE for cancel request | 139 | // M: sets to FALSE, flags TRUE for cancel request |
| @@ -333,17 +335,21 @@ LUAG_FUNC( linda_send) | |||
| 333 | { | 335 | { |
| 334 | prev_status = s->status; | 336 | prev_status = s->status; |
| 335 | s->status = WAITING; | 337 | s->status = WAITING; |
| 338 | ASSERT_L( s->waiting_on == NULL); | ||
| 339 | s->waiting_on = &linda->read_happened; | ||
| 336 | } | 340 | } |
| 337 | if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout)) | 341 | if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout)) |
| 338 | { | 342 | { |
| 339 | if( s) | 343 | if( s) |
| 340 | { | 344 | { |
| 345 | s->waiting_on = NULL; | ||
| 341 | s->status = prev_status; | 346 | s->status = prev_status; |
| 342 | } | 347 | } |
| 343 | break; | 348 | break; |
| 344 | } | 349 | } |
| 345 | if( s) | 350 | if( s) |
| 346 | { | 351 | { |
| 352 | s->waiting_on = NULL; | ||
| 347 | s->status = prev_status; | 353 | s->status = prev_status; |
| 348 | } | 354 | } |
| 349 | } | 355 | } |
| @@ -450,17 +456,21 @@ LUAG_FUNC( linda_receive) | |||
| 450 | { | 456 | { |
| 451 | prev_status = s->status; | 457 | prev_status = s->status; |
| 452 | s->status = WAITING; | 458 | s->status = WAITING; |
| 459 | ASSERT_L( s->waiting_on == NULL); | ||
| 460 | s->waiting_on = &linda->write_happened; | ||
| 453 | } | 461 | } |
| 454 | if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout)) | 462 | if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout)) |
| 455 | { | 463 | { |
| 456 | if( s) | 464 | if( s) |
| 457 | { | 465 | { |
| 466 | s->waiting_on = NULL; | ||
| 458 | s->status = prev_status; | 467 | s->status = prev_status; |
| 459 | } | 468 | } |
| 460 | break; | 469 | break; |
| 461 | } | 470 | } |
| 462 | if( s) | 471 | if( s) |
| 463 | { | 472 | { |
| 473 | s->waiting_on = NULL; | ||
| 464 | s->status = prev_status; | 474 | s->status = prev_status; |
| 465 | } | 475 | } |
| 466 | } | 476 | } |
| @@ -870,28 +880,33 @@ volatile DEEP_PRELUDE *timer_deep; // = NULL | |||
| 870 | /* | 880 | /* |
| 871 | * Process end; cancel any still free-running threads | 881 | * Process end; cancel any still free-running threads |
| 872 | */ | 882 | */ |
| 873 | static void selfdestruct_atexit( void ) { | 883 | static void selfdestruct_atexit( void ) |
| 874 | 884 | { | |
| 875 | if (selfdestruct_first == SELFDESTRUCT_END) return; // no free-running threads | 885 | if (selfdestruct_first == SELFDESTRUCT_END) return; // no free-running threads |
| 876 | 886 | ||
| 877 | // Signal _all_ still running threads to exit | 887 | // Signal _all_ still running threads to exit (including the timer thread) |
| 878 | // | 888 | // |
| 879 | MUTEX_LOCK( &selfdestruct_cs ); | 889 | MUTEX_LOCK( &selfdestruct_cs ); |
| 880 | { | 890 | { |
| 881 | struct s_lane *s= selfdestruct_first; | 891 | struct s_lane *s= selfdestruct_first; |
| 882 | while( s != SELFDESTRUCT_END ) { | 892 | while( s != SELFDESTRUCT_END ) |
| 883 | s->cancel_request= TRUE; | 893 | { |
| 884 | s= s->selfdestruct_next; | 894 | // attempt a regular unforced cancel with a small timeout |
| 895 | bool_t cancelled = thread_cancel( s, 0.0001, FALSE); | ||
| 896 | // if we failed, and we know the thread is waiting on a linda | ||
| 897 | if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL) | ||
| 898 | { | ||
| 899 | // signal the linda the wake up the thread so that it can react to the cancel query | ||
| 900 | // let us hope we never land here with a pointer on a linda that has been destroyed... | ||
| 901 | SIGNAL_T waiting_on = s->waiting_on; | ||
| 902 | s->waiting_on = NULL; | ||
| 903 | SIGNAL_ALL( waiting_on); | ||
| 904 | } | ||
| 905 | s = s->selfdestruct_next; | ||
| 885 | } | 906 | } |
| 886 | } | 907 | } |
| 887 | MUTEX_UNLOCK( &selfdestruct_cs ); | 908 | MUTEX_UNLOCK( &selfdestruct_cs ); |
| 888 | 909 | ||
| 889 | // Tell the timer thread to check it's cancel request | ||
| 890 | { | ||
| 891 | struct s_Linda *td = timer_deep->deep; | ||
| 892 | SIGNAL_ALL( &td->write_happened); | ||
| 893 | } | ||
| 894 | |||
| 895 | // When noticing their cancel, the lanes will remove themselves from | 910 | // When noticing their cancel, the lanes will remove themselves from |
| 896 | // the selfdestruct chain. | 911 | // the selfdestruct chain. |
| 897 | 912 | ||
| @@ -914,15 +929,37 @@ static void selfdestruct_atexit( void ) { | |||
| 914 | // -- AKa 25-Oct-2008 | 929 | // -- AKa 25-Oct-2008 |
| 915 | // | 930 | // |
| 916 | #ifndef ATEXIT_WAIT_SECS | 931 | #ifndef ATEXIT_WAIT_SECS |
| 917 | # define ATEXIT_WAIT_SECS (0.1) | 932 | # define ATEXIT_WAIT_SECS (0.25) |
| 918 | #endif | 933 | #endif |
| 919 | { | 934 | { |
| 920 | double t_until= now_secs() + ATEXIT_WAIT_SECS; | 935 | double t_until= now_secs() + ATEXIT_WAIT_SECS; |
| 921 | 936 | ||
| 922 | while( selfdestruct_first != SELFDESTRUCT_END ) { | 937 | while( selfdestruct_first != SELFDESTRUCT_END ) |
| 938 | { | ||
| 923 | YIELD(); // give threads time to act on their cancel | 939 | YIELD(); // give threads time to act on their cancel |
| 924 | 940 | { | |
| 925 | if (now_secs() >= t_until) break; | 941 | // count the number of cancelled thread that didn't have the time to act yet |
| 942 | int n = 0; | ||
| 943 | double t_now = 0.0; | ||
| 944 | MUTEX_LOCK( &selfdestruct_cs ); | ||
| 945 | { | ||
| 946 | struct s_lane *s = selfdestruct_first; | ||
| 947 | while( s != SELFDESTRUCT_END) | ||
| 948 | { | ||
| 949 | if( s->cancel_request) | ||
| 950 | ++ n; | ||
| 951 | s = s->selfdestruct_next; | ||
| 952 | } | ||
| 953 | } | ||
| 954 | MUTEX_UNLOCK( &selfdestruct_cs ); | ||
| 955 | // if timeout elapsed, or we know all threads have acted, stop waiting | ||
| 956 | t_now = now_secs(); | ||
| 957 | if( n == 0 || ( t_now >= t_until)) | ||
| 958 | { | ||
| 959 | DEBUGEXEC(fprintf( stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, ATEXIT_WAIT_SECS - (t_until - t_now))); | ||
| 960 | break; | ||
| 961 | } | ||
| 962 | } | ||
| 926 | } | 963 | } |
| 927 | } | 964 | } |
| 928 | #endif | 965 | #endif |
| @@ -951,18 +988,21 @@ static void selfdestruct_atexit( void ) { | |||
| 951 | // | 988 | // |
| 952 | DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n )); | 989 | DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n )); |
| 953 | #else | 990 | #else |
| 991 | // first thing we did was to raise the linda signals the threads were waiting on (if any) | ||
| 992 | // therefore, any well-behaved thread should be in CANCELLED state | ||
| 993 | // these are not running, and the state can be closed | ||
| 954 | n=0; | 994 | n=0; |
| 955 | MUTEX_LOCK( &selfdestruct_cs ); | 995 | MUTEX_LOCK( &selfdestruct_cs ); |
| 956 | { | 996 | { |
| 957 | struct s_lane *s= selfdestruct_first; | 997 | struct s_lane *s= selfdestruct_first; |
| 958 | while( s != SELFDESTRUCT_END ) { | 998 | while( s != SELFDESTRUCT_END) |
| 999 | { | ||
| 959 | struct s_lane *next_s= s->selfdestruct_next; | 1000 | struct s_lane *next_s= s->selfdestruct_next; |
| 960 | s->selfdestruct_next= NULL; // detach from selfdestruct chain | 1001 | s->selfdestruct_next= NULL; // detach from selfdestruct chain |
| 961 | 1002 | THREAD_KILL( &s->thread); | |
| 962 | THREAD_KILL( &s->thread ); | 1003 | // NO lua_close() in this case because we don't know where execution of the state was interrupted |
| 963 | lua_close(s->L); | 1004 | free( s); |
| 964 | free(s); | 1005 | s = next_s; |
| 965 | s= next_s; | ||
| 966 | n++; | 1006 | n++; |
| 967 | } | 1007 | } |
| 968 | selfdestruct_first= SELFDESTRUCT_END; | 1008 | selfdestruct_first= SELFDESTRUCT_END; |
| @@ -1022,6 +1062,19 @@ static void cancel_hook( lua_State *L, lua_Debug *ar ) { | |||
| 1022 | 1062 | ||
| 1023 | 1063 | ||
| 1024 | //--- | 1064 | //--- |
| 1065 | // bool= cancel_test() | ||
| 1066 | // | ||
| 1067 | // Available inside the global namespace of lanes | ||
| 1068 | // returns a boolean saying if a cancel request is pending | ||
| 1069 | // | ||
| 1070 | LUAG_FUNC( cancel_test) | ||
| 1071 | { | ||
| 1072 | bool_t test = cancel_test( L); | ||
| 1073 | lua_pushboolean( L, test); | ||
| 1074 | return 1; | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | //--- | ||
| 1025 | // = _single( [cores_uint=1] ) | 1078 | // = _single( [cores_uint=1] ) |
| 1026 | // | 1079 | // |
| 1027 | // Limits the process to use only 'cores' CPU cores. To be used for performance | 1080 | // Limits the process to use only 'cores' CPU cores. To be used for performance |
| @@ -1131,12 +1184,12 @@ typedef struct tagTHREADNAME_INFO | |||
| 1131 | } THREADNAME_INFO; | 1184 | } THREADNAME_INFO; |
| 1132 | #pragma pack(pop) | 1185 | #pragma pack(pop) |
| 1133 | 1186 | ||
| 1134 | void SetThreadName( DWORD dwThreadID, char* threadName) | 1187 | void SetThreadName( DWORD dwThreadID, char const *_threadName) |
| 1135 | { | 1188 | { |
| 1136 | THREADNAME_INFO info; | 1189 | THREADNAME_INFO info; |
| 1137 | Sleep(10); | 1190 | Sleep(10); |
| 1138 | info.dwType = 0x1000; | 1191 | info.dwType = 0x1000; |
| 1139 | info.szName = threadName; | 1192 | info.szName = _threadName; |
| 1140 | info.dwThreadID = dwThreadID; | 1193 | info.dwThreadID = dwThreadID; |
| 1141 | info.dwFlags = 0; | 1194 | info.dwFlags = 0; |
| 1142 | 1195 | ||
| @@ -1150,6 +1203,22 @@ void SetThreadName( DWORD dwThreadID, char* threadName) | |||
| 1150 | } | 1203 | } |
| 1151 | #endif | 1204 | #endif |
| 1152 | 1205 | ||
| 1206 | LUAG_FUNC( set_debug_threadname) | ||
| 1207 | { | ||
| 1208 | char const *threadName; | ||
| 1209 | luaL_checktype( L, -1, LUA_TSTRING); | ||
| 1210 | threadName = lua_tostring( L, -1); | ||
| 1211 | |||
| 1212 | #if defined PLATFORM_WIN32 && !defined __GNUC__ | ||
| 1213 | // to see thead name in Visual Studio C debugger | ||
| 1214 | SetThreadName(-1, threadName); | ||
| 1215 | #endif | ||
| 1216 | |||
| 1217 | // to see VM name in Decoda debugger Virtual Machine window | ||
| 1218 | lua_setglobal( L, "decoda_name"); | ||
| 1219 | |||
| 1220 | return 0; | ||
| 1221 | } | ||
| 1153 | 1222 | ||
| 1154 | //--- | 1223 | //--- |
| 1155 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 1224 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) |
| @@ -1162,11 +1231,6 @@ void SetThreadName( DWORD dwThreadID, char* threadName) | |||
| 1162 | int rc, rc2; | 1231 | int rc, rc2; |
| 1163 | lua_State *L= s->L; | 1232 | lua_State *L= s->L; |
| 1164 | 1233 | ||
| 1165 | |||
| 1166 | #if defined PLATFORM_WIN32 && !defined __GNUC__ | ||
| 1167 | SetThreadName(-1, s->threadName); | ||
| 1168 | #endif | ||
| 1169 | |||
| 1170 | s->status= RUNNING; // PENDING -> RUNNING | 1234 | s->status= RUNNING; // PENDING -> RUNNING |
| 1171 | 1235 | ||
| 1172 | // Tie "set_finalizer()" to the state | 1236 | // Tie "set_finalizer()" to the state |
| @@ -1174,6 +1238,16 @@ void SetThreadName( DWORD dwThreadID, char* threadName) | |||
| 1174 | lua_pushcfunction( L, LG_set_finalizer ); | 1238 | lua_pushcfunction( L, LG_set_finalizer ); |
| 1175 | lua_setglobal( L, "set_finalizer" ); | 1239 | lua_setglobal( L, "set_finalizer" ); |
| 1176 | 1240 | ||
| 1241 | // Tie "set_debug_threadname()" to the state | ||
| 1242 | // | ||
| 1243 | lua_pushcfunction( L, LG_set_debug_threadname); | ||
| 1244 | lua_setglobal( L, "set_debug_threadname" ); | ||
| 1245 | |||
| 1246 | // Tie "cancel_test()" to the state | ||
| 1247 | // | ||
| 1248 | lua_pushcfunction( L, LG_cancel_test); | ||
| 1249 | lua_setglobal( L, "cancel_test" ); | ||
| 1250 | |||
| 1177 | #ifdef ERROR_FULL_STACK | 1251 | #ifdef ERROR_FULL_STACK |
| 1178 | STACK_GROW( L, 1 ); | 1252 | STACK_GROW( L, 1 ); |
| 1179 | lua_pushcfunction( L, lane_error ); | 1253 | lua_pushcfunction( L, lane_error ); |
| @@ -1240,7 +1314,7 @@ void SetThreadName( DWORD dwThreadID, char* threadName) | |||
| 1240 | // | 1314 | // |
| 1241 | lua_newtable(L); | 1315 | lua_newtable(L); |
| 1242 | } | 1316 | } |
| 1243 | 1317 | s->waiting_on = NULL; // just in case | |
| 1244 | if (s->selfdestruct_next != NULL) { | 1318 | if (s->selfdestruct_next != NULL) { |
| 1245 | // We're a free-running thread and no-one's there to clean us up. | 1319 | // We're a free-running thread and no-one's there to clean us up. |
| 1246 | // | 1320 | // |
| @@ -1276,7 +1350,6 @@ void SetThreadName( DWORD dwThreadID, char* threadName) | |||
| 1276 | MUTEX_UNLOCK( &s->done_lock_ ); | 1350 | MUTEX_UNLOCK( &s->done_lock_ ); |
| 1277 | #endif | 1351 | #endif |
| 1278 | } | 1352 | } |
| 1279 | |||
| 1280 | return 0; // ignored | 1353 | return 0; // ignored |
| 1281 | } | 1354 | } |
| 1282 | 1355 | ||
| @@ -1295,7 +1368,6 @@ LUAG_FUNC( thread_new ) | |||
| 1295 | lua_State *L2; | 1368 | lua_State *L2; |
| 1296 | struct s_lane *s; | 1369 | struct s_lane *s; |
| 1297 | struct s_lane **ud; | 1370 | struct s_lane **ud; |
| 1298 | const char *threadName = 0; | ||
| 1299 | 1371 | ||
| 1300 | const char *libs= lua_tostring( L, 2 ); | 1372 | const char *libs= lua_tostring( L, 2 ); |
| 1301 | uint_t cs= luaG_optunsigned( L, 3,0); | 1373 | uint_t cs= luaG_optunsigned( L, 3,0); |
| @@ -1330,10 +1402,7 @@ LUAG_FUNC( thread_new ) | |||
| 1330 | luaL_error( L, "Expected table, got %s", luaG_typename(L,glob) ); | 1402 | luaL_error( L, "Expected table, got %s", luaG_typename(L,glob) ); |
| 1331 | 1403 | ||
| 1332 | lua_pushvalue( L, glob ); | 1404 | lua_pushvalue( L, glob ); |
| 1333 | lua_pushstring( L, "threadName"); | 1405 | |
| 1334 | lua_gettable( L, -2); | ||
| 1335 | threadName = lua_tostring( L, -1); | ||
| 1336 | lua_pop( L, 1); | ||
| 1337 | luaG_inter_move( L, L2, 1); // moves the table to L2 | 1406 | luaG_inter_move( L, L2, 1); // moves the table to L2 |
| 1338 | 1407 | ||
| 1339 | // L2 [-1]: table of globals | 1408 | // L2 [-1]: table of globals |
| @@ -1398,11 +1467,9 @@ LUAG_FUNC( thread_new ) | |||
| 1398 | //memset( s, 0, sizeof(struct s_lane) ); | 1467 | //memset( s, 0, sizeof(struct s_lane) ); |
| 1399 | s->L= L2; | 1468 | s->L= L2; |
| 1400 | s->status= PENDING; | 1469 | s->status= PENDING; |
| 1470 | s->waiting_on = NULL; | ||
| 1401 | s->cancel_request= FALSE; | 1471 | s->cancel_request= FALSE; |
| 1402 | 1472 | ||
| 1403 | threadName = threadName ? threadName : "<unnamed thread>"; | ||
| 1404 | strcpy(s->threadName, threadName); | ||
| 1405 | |||
| 1406 | #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) | 1473 | #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) |
| 1407 | MUTEX_INIT( &s->done_lock_ ); | 1474 | MUTEX_INIT( &s->done_lock_ ); |
| 1408 | SIGNAL_INIT( &s->done_signal_ ); | 1475 | SIGNAL_INIT( &s->done_signal_ ); |
| @@ -1484,7 +1551,7 @@ LUAG_FUNC( thread_gc ) | |||
| 1484 | #if 0 | 1551 | #if 0 |
| 1485 | lua_close( s->L ); | 1552 | lua_close( s->L ); |
| 1486 | s->L = 0; | 1553 | s->L = 0; |
| 1487 | #else | 1554 | #else // 0 |
| 1488 | DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" )); | 1555 | DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" )); |
| 1489 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) | 1556 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) |
| 1490 | THREAD_WAIT( &s->thread, -1 ); | 1557 | THREAD_WAIT( &s->thread, -1 ); |
| @@ -1492,7 +1559,7 @@ LUAG_FUNC( thread_gc ) | |||
| 1492 | THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, -1 ); | 1559 | THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, -1 ); |
| 1493 | #endif | 1560 | #endif |
| 1494 | DEBUGEXEC(fprintf( stderr, "** Joined ok **" )); | 1561 | DEBUGEXEC(fprintf( stderr, "** Joined ok **" )); |
| 1495 | #endif | 1562 | #endif // 0 |
| 1496 | } | 1563 | } |
| 1497 | else if( s->L) | 1564 | else if( s->L) |
| 1498 | { | 1565 | { |
| @@ -1529,54 +1596,55 @@ LUAG_FUNC( thread_gc ) | |||
| 1529 | // managed to cancel it. | 1596 | // managed to cancel it. |
| 1530 | // false if the cancellation timed out, or a kill was needed. | 1597 | // false if the cancellation timed out, or a kill was needed. |
| 1531 | // | 1598 | // |
| 1532 | LUAG_FUNC( thread_cancel ) | 1599 | static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force) |
| 1533 | { | 1600 | { |
| 1534 | struct s_lane *s= lua_toLane(L,1); | 1601 | bool_t done= TRUE; |
| 1535 | double secs= 0.0; | 1602 | // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) |
| 1536 | uint_t force_i=2; | 1603 | // |
| 1537 | bool_t force, done= TRUE; | 1604 | if( s->status < DONE) |
| 1538 | 1605 | { | |
| 1539 | if (lua_isnumber(L,2)) { | 1606 | s->cancel_request = TRUE; // it's now signalled to stop |
| 1540 | secs= lua_tonumber(L,2); | 1607 | done= |
| 1541 | force_i++; | ||
| 1542 | } else if (lua_isnil(L,2)) | ||
| 1543 | force_i++; | ||
| 1544 | |||
| 1545 | force= lua_toboolean(L,force_i); // FALSE if nothing there | ||
| 1546 | |||
| 1547 | // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) | ||
| 1548 | // | ||
| 1549 | if (s->status < DONE) { | ||
| 1550 | s->cancel_request= TRUE; // it's now signalled to stop | ||
| 1551 | |||
| 1552 | done= thread_cancel( s, secs, force ); | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | lua_pushboolean( L, done ); | ||
| 1556 | return 1; | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force ) | ||
| 1560 | { | ||
| 1561 | bool_t done= | ||
| 1562 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) | 1608 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) |
| 1563 | THREAD_WAIT( &s->thread, secs ); | 1609 | THREAD_WAIT( &s->thread, secs); |
| 1564 | #else | 1610 | #else |
| 1565 | THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, secs ); | 1611 | THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, secs); |
| 1566 | #endif | 1612 | #endif |
| 1567 | 1613 | ||
| 1568 | if ((!done) && force) { | 1614 | if ((!done) && force) |
| 1569 | // Killing is asynchronous; we _will_ wait for it to be done at | 1615 | { |
| 1570 | // GC, to make sure the data structure can be released (alternative | 1616 | // Killing is asynchronous; we _will_ wait for it to be done at |
| 1571 | // would be use of "cancellation cleanup handlers" that at least | 1617 | // GC, to make sure the data structure can be released (alternative |
| 1572 | // PThread seems to have). | 1618 | // would be use of "cancellation cleanup handlers" that at least |
| 1573 | // | 1619 | // PThread seems to have). |
| 1574 | THREAD_KILL( &s->thread ); | 1620 | // |
| 1575 | s->mstatus= KILLED; // mark 'gc' to wait for it | 1621 | THREAD_KILL( &s->thread); |
| 1576 | } | 1622 | s->mstatus= KILLED; // mark 'gc' to wait for it |
| 1577 | return done; | 1623 | } |
| 1624 | } | ||
| 1625 | return done; | ||
| 1578 | } | 1626 | } |
| 1579 | 1627 | ||
| 1628 | LUAG_FUNC( thread_cancel) | ||
| 1629 | { | ||
| 1630 | struct s_lane *s= lua_toLane(L,1); | ||
| 1631 | double secs= 0.0; | ||
| 1632 | uint_t force_i=2; | ||
| 1633 | bool_t force, done= TRUE; | ||
| 1634 | |||
| 1635 | if (lua_isnumber(L,2)) { | ||
| 1636 | secs= lua_tonumber(L,2); | ||
| 1637 | force_i++; | ||
| 1638 | } else if (lua_isnil(L,2)) | ||
| 1639 | force_i++; | ||
| 1640 | |||
| 1641 | force= lua_toboolean(L,force_i); // FALSE if nothing there | ||
| 1642 | |||
| 1643 | done = thread_cancel( s, secs, force); | ||
| 1644 | |||
| 1645 | lua_pushboolean( L, done); | ||
| 1646 | return 1; | ||
| 1647 | } | ||
| 1580 | 1648 | ||
| 1581 | //--- | 1649 | //--- |
| 1582 | // str= thread_status( lane ) | 1650 | // str= thread_status( lane ) |
diff --git a/src/lanes.lua b/src/lanes.lua index 78582f9..704559a 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
| @@ -399,8 +399,8 @@ if first_time then | |||
| 399 | -- We let the timer lane be a "free running" thread; no handle to it | 399 | -- We let the timer lane be a "free running" thread; no handle to it |
| 400 | -- remains. | 400 | -- remains. |
| 401 | -- | 401 | -- |
| 402 | gen( "io,package", { priority=max_prio, globals={threadName="LanesTimer"} }, function() | 402 | gen( "io,package", { priority=max_prio}, function() |
| 403 | 403 | set_debugger_threadname( "LanesTimer") | |
| 404 | while true do | 404 | while true do |
| 405 | local next_wakeup= check_timers() | 405 | local next_wakeup= check_timers() |
| 406 | 406 | ||
diff --git a/tests/atexit.lua b/tests/atexit.lua new file mode 100644 index 0000000..fb4f34a --- /dev/null +++ b/tests/atexit.lua | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | require "lanes" | ||
| 2 | |||
| 3 | -- create a free-running lane | ||
| 4 | |||
| 5 | local linda = lanes.linda() | ||
| 6 | |||
| 7 | local f = function( _linda) | ||
| 8 | _linda:receive("oy") | ||
| 9 | end | ||
| 10 | |||
| 11 | local g = function() | ||
| 12 | local cancelled | ||
| 13 | repeat | ||
| 14 | cancelled = cancel_test() | ||
| 15 | until cancelled | ||
| 16 | print "User cancellation detected!" | ||
| 17 | end | ||
| 18 | |||
| 19 | local genF = lanes.gen( "", {globals = {threadName = "mylane"}}, f) | ||
| 20 | local genG = lanes.gen( "", g) | ||
| 21 | |||
| 22 | |||
| 23 | -- launch a good batch of free running lanes | ||
| 24 | for i = 1, 10 do | ||
| 25 | -- if i%50 == 0 then print( i) end | ||
| 26 | local h = genF( linda) | ||
| 27 | local status | ||
| 28 | repeat | ||
| 29 | status = h.status | ||
| 30 | --print( status) | ||
| 31 | until status == "waiting" | ||
| 32 | |||
| 33 | -- [[ | ||
| 34 | local h = genG() | ||
| 35 | local status | ||
| 36 | repeat | ||
| 37 | status = h.status | ||
| 38 | --print( status) | ||
| 39 | until status == "running" | ||
| 40 | --]] | ||
| 41 | end | ||
| 42 | |||
| 43 | print "exiting" | ||
| 44 | |||
| 45 | -- let process end terminate them and see what happens \ No newline at end of file | ||
