diff options
-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 | ||