diff options
Diffstat (limited to 'src/lanes.c')
-rw-r--r-- | src/lanes.c | 156 |
1 files changed, 96 insertions, 60 deletions
diff --git a/src/lanes.c b/src/lanes.c index 08bdb9a..e36a073 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -52,7 +52,7 @@ | |||
52 | * ... | 52 | * ... |
53 | */ | 53 | */ |
54 | 54 | ||
55 | char const* VERSION = "3.6.2"; | 55 | char const* VERSION = "3.6.3"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -115,10 +115,21 @@ THE SOFTWARE. | |||
115 | */ | 115 | */ |
116 | #define ERROR_FULL_STACK | 116 | #define ERROR_FULL_STACK |
117 | 117 | ||
118 | /* | ||
119 | * Lane cancellation request modes | ||
120 | */ | ||
121 | enum e_cancel_request | ||
122 | { | ||
123 | CANCEL_NONE, // no pending cancel request | ||
124 | CANCEL_SOFT, // user wants the lane to cancel itself manually on cancel_test() | ||
125 | CANCEL_HARD // user wants the lane to be interrupted (meaning code won't return from those functions) from inside linda:send/receive calls | ||
126 | }; | ||
127 | |||
118 | // NOTE: values to be changed by either thread, during execution, without | 128 | // NOTE: values to be changed by either thread, during execution, without |
119 | // locking, are marked "volatile" | 129 | // locking, are marked "volatile" |
120 | // | 130 | // |
121 | struct s_lane { | 131 | struct s_lane |
132 | { | ||
122 | THREAD_T thread; | 133 | THREAD_T thread; |
123 | // | 134 | // |
124 | // M: sub-thread OS thread | 135 | // M: sub-thread OS thread |
@@ -140,7 +151,7 @@ struct s_lane { | |||
140 | // | 151 | // |
141 | // When status is WAITING, points on the linda's signal the thread waits on, else NULL | 152 | // When status is WAITING, points on the linda's signal the thread waits on, else NULL |
142 | 153 | ||
143 | volatile bool_t cancel_request; | 154 | volatile enum e_cancel_request cancel_request; |
144 | // | 155 | // |
145 | // M: sets to FALSE, flags TRUE for cancel request | 156 | // M: sets to FALSE, flags TRUE for cancel request |
146 | // S: reads to see if cancel is requested | 157 | // S: reads to see if cancel is requested |
@@ -178,7 +189,7 @@ struct s_lane { | |||
178 | // For tracking only | 189 | // For tracking only |
179 | }; | 190 | }; |
180 | 191 | ||
181 | static bool_t cancel_test( lua_State*L ); | 192 | static enum e_cancel_request cancel_test( lua_State* L); |
182 | static void cancel_error( lua_State*L ); | 193 | static void cancel_error( lua_State*L ); |
183 | 194 | ||
184 | #define CANCEL_TEST_KEY ((void*)cancel_test) // used as registry key | 195 | #define CANCEL_TEST_KEY ((void*)cancel_test) // used as registry key |
@@ -372,7 +383,7 @@ LUAG_FUNC( linda_send) | |||
372 | { | 383 | { |
373 | struct s_Linda *linda = lua_toLinda( L, 1); | 384 | struct s_Linda *linda = lua_toLinda( L, 1); |
374 | bool_t ret; | 385 | bool_t ret; |
375 | bool_t cancel = FALSE; | 386 | enum e_cancel_request cancel = CANCEL_NONE; |
376 | int pushed; | 387 | int pushed; |
377 | time_d timeout= -1.0; | 388 | time_d timeout= -1.0; |
378 | uint_t key_i = 2; // index of first key, if timeout not there | 389 | uint_t key_i = 2; // index of first key, if timeout not there |
@@ -434,7 +445,7 @@ LUAG_FUNC( linda_send) | |||
434 | /* limit faced; push until timeout */ | 445 | /* limit faced; push until timeout */ |
435 | 446 | ||
436 | cancel = cancel_test( L); // testing here causes no delays | 447 | cancel = cancel_test( L); // testing here causes no delays |
437 | if (cancel) | 448 | if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without sending anything |
438 | { | 449 | { |
439 | break; | 450 | break; |
440 | } | 451 | } |
@@ -486,7 +497,8 @@ LUAG_FUNC( linda_send) | |||
486 | return luaL_error( L, "tried to copy unsupported types"); | 497 | return luaL_error( L, "tried to copy unsupported types"); |
487 | } | 498 | } |
488 | 499 | ||
489 | if( cancel) | 500 | // raise an error interrupting execution only in case of hard cancel |
501 | if( cancel == CANCEL_HARD) | ||
490 | cancel_error( L); | 502 | cancel_error( L); |
491 | 503 | ||
492 | lua_pushboolean( L, ret); | 504 | lua_pushboolean( L, ret); |
@@ -510,7 +522,7 @@ LUAG_FUNC( linda_receive) | |||
510 | { | 522 | { |
511 | struct s_Linda *linda = lua_toLinda( L, 1); | 523 | struct s_Linda *linda = lua_toLinda( L, 1); |
512 | int pushed, expected_pushed_min, expected_pushed_max; | 524 | int pushed, expected_pushed_min, expected_pushed_max; |
513 | bool_t cancel = FALSE; | 525 | enum e_cancel_request cancel = CANCEL_NONE; |
514 | keeper_api_t keeper_receive; | 526 | keeper_api_t keeper_receive; |
515 | 527 | ||
516 | time_d timeout = -1.0; | 528 | time_d timeout = -1.0; |
@@ -592,16 +604,16 @@ LUAG_FUNC( linda_receive) | |||
592 | /* nothing received; wait until timeout */ | 604 | /* nothing received; wait until timeout */ |
593 | 605 | ||
594 | cancel = cancel_test( L); // testing here causes no delays | 606 | cancel = cancel_test( L); // testing here causes no delays |
595 | if( cancel) | 607 | if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without providing anything |
596 | { | 608 | { |
597 | break; | 609 | break; |
598 | } | 610 | } |
599 | 611 | ||
600 | // change status of lane to "waiting" | 612 | // change status of lane to "waiting" |
601 | { | 613 | { |
602 | struct s_lane *s; | 614 | struct s_lane* s; |
603 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | 615 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings |
604 | STACK_GROW(L,1); | 616 | STACK_GROW( L, 1); |
605 | 617 | ||
606 | STACK_CHECK( L); | 618 | STACK_CHECK( L); |
607 | lua_pushlightuserdata( L, CANCEL_TEST_KEY); | 619 | lua_pushlightuserdata( L, CANCEL_TEST_KEY); |
@@ -643,7 +655,8 @@ LUAG_FUNC( linda_receive) | |||
643 | return luaL_error( L, "tried to copy unsupported types"); | 655 | return luaL_error( L, "tried to copy unsupported types"); |
644 | } | 656 | } |
645 | 657 | ||
646 | if( cancel) | 658 | // raise an error interrupting execution only in case of hard cancel |
659 | if( cancel == CANCEL_HARD) | ||
647 | cancel_error( L); | 660 | cancel_error( L); |
648 | 661 | ||
649 | return pushed; | 662 | return pushed; |
@@ -1176,31 +1189,41 @@ static cancel_result thread_cancel( struct s_lane *s, double secs, bool_t force) | |||
1176 | } | 1189 | } |
1177 | else if( s->status < DONE) | 1190 | else if( s->status < DONE) |
1178 | { | 1191 | { |
1179 | s->cancel_request = TRUE; // it's now signaled to stop | ||
1180 | // signal the linda the wake up the thread so that it can react to the cancel query | 1192 | // signal the linda the wake up the thread so that it can react to the cancel query |
1181 | // let us hope we never land here with a pointer on a linda that has been destroyed... | 1193 | // let us hope we never land here with a pointer on a linda that has been destroyed... |
1194 | if( secs < 0.0) | ||
1195 | { | ||
1196 | s->cancel_request = CANCEL_SOFT; // it's now signaled to stop | ||
1197 | // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own | ||
1198 | // say we succeeded though | ||
1199 | result = CR_Cancelled; | ||
1200 | } | ||
1201 | else | ||
1182 | { | 1202 | { |
1183 | SIGNAL_T *waiting_on = s->waiting_on; | 1203 | s->cancel_request = CANCEL_HARD; // it's now signaled to stop |
1184 | if( s->status == WAITING && waiting_on != NULL) | ||
1185 | { | 1204 | { |
1186 | SIGNAL_ALL( waiting_on); | 1205 | SIGNAL_T *waiting_on = s->waiting_on; |
1206 | if( s->status == WAITING && waiting_on != NULL) | ||
1207 | { | ||
1208 | SIGNAL_ALL( waiting_on); | ||
1209 | } | ||
1187 | } | 1210 | } |
1188 | } | ||
1189 | 1211 | ||
1190 | result = THREAD_WAIT( &s->thread, secs, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; | 1212 | result = THREAD_WAIT( &s->thread, secs, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; |
1191 | 1213 | ||
1192 | if( (result == CR_Timeout) && force) | 1214 | if( (result == CR_Timeout) && force) |
1193 | { | 1215 | { |
1194 | // Killing is asynchronous; we _will_ wait for it to be done at | 1216 | // Killing is asynchronous; we _will_ wait for it to be done at |
1195 | // GC, to make sure the data structure can be released (alternative | 1217 | // GC, to make sure the data structure can be released (alternative |
1196 | // would be use of "cancellation cleanup handlers" that at least | 1218 | // would be use of "cancellation cleanup handlers" that at least |
1197 | // PThread seems to have). | 1219 | // PThread seems to have). |
1198 | // | 1220 | // |
1199 | THREAD_KILL( &s->thread); | 1221 | THREAD_KILL( &s->thread); |
1200 | s->mstatus = KILLED; // mark 'gc' to wait for it | 1222 | s->mstatus = KILLED; // mark 'gc' to wait for it |
1201 | // note that s->status value must remain to whatever it was at the time of the kill | 1223 | // note that s->status value must remain to whatever it was at the time of the kill |
1202 | // because we need to know if we can lua_close() the Lua State or not. | 1224 | // because we need to know if we can lua_close() the Lua State or not. |
1203 | result = CR_Killed; | 1225 | result = CR_Killed; |
1226 | } | ||
1204 | } | 1227 | } |
1205 | } | 1228 | } |
1206 | else | 1229 | else |
@@ -1307,7 +1330,7 @@ static int selfdestruct_gc( lua_State* L) | |||
1307 | struct s_lane* s = selfdestruct_first; | 1330 | struct s_lane* s = selfdestruct_first; |
1308 | while( s != SELFDESTRUCT_END ) | 1331 | while( s != SELFDESTRUCT_END ) |
1309 | { | 1332 | { |
1310 | // attempt a regular unforced cancel with a small timeout | 1333 | // attempt a regular unforced hard cancel with a small timeout |
1311 | bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( s, 0.0001, FALSE); | 1334 | bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( s, 0.0001, FALSE); |
1312 | // if we failed, and we know the thread is waiting on a linda | 1335 | // if we failed, and we know the thread is waiting on a linda |
1313 | if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL) | 1336 | if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL) |
@@ -1353,7 +1376,7 @@ static int selfdestruct_gc( lua_State* L) | |||
1353 | struct s_lane* s = selfdestruct_first; | 1376 | struct s_lane* s = selfdestruct_first; |
1354 | while( s != SELFDESTRUCT_END) | 1377 | while( s != SELFDESTRUCT_END) |
1355 | { | 1378 | { |
1356 | if( s->cancel_request) | 1379 | if( s->cancel_request == CANCEL_HARD) |
1357 | ++ n; | 1380 | ++ n; |
1358 | s = s->selfdestruct_next; | 1381 | s = s->selfdestruct_next; |
1359 | } | 1382 | } |
@@ -1444,21 +1467,22 @@ static int selfdestruct_gc( lua_State* L) | |||
1444 | * Returns TRUE if any locks are to be exited, and 'cancel_error()' called, | 1467 | * Returns TRUE if any locks are to be exited, and 'cancel_error()' called, |
1445 | * to make execution of the lane end. | 1468 | * to make execution of the lane end. |
1446 | */ | 1469 | */ |
1447 | static bool_t cancel_test( lua_State*L ) { | 1470 | static enum e_cancel_request cancel_test( lua_State* L) |
1448 | struct s_lane *s; | 1471 | { |
1472 | struct s_lane* s; | ||
1449 | 1473 | ||
1450 | STACK_GROW(L,1); | 1474 | STACK_GROW( L, 1); |
1451 | 1475 | ||
1452 | STACK_CHECK( L); | 1476 | STACK_CHECK( L); |
1453 | lua_pushlightuserdata( L, CANCEL_TEST_KEY ); | 1477 | lua_pushlightuserdata( L, CANCEL_TEST_KEY); |
1454 | lua_rawget( L, LUA_REGISTRYINDEX ); | 1478 | lua_rawget( L, LUA_REGISTRYINDEX); |
1455 | s= lua_touserdata( L, -1 ); // lightuserdata (true 's_lane' pointer) / nil | 1479 | s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil |
1456 | lua_pop(L,1); | 1480 | lua_pop( L, 1); |
1457 | STACK_END( L, 0); | 1481 | STACK_END( L, 0); |
1458 | 1482 | ||
1459 | // 's' is NULL for the original main state (no-one can cancel that) | 1483 | // 's' is NULL for the original main state (no-one can cancel that) |
1460 | // | 1484 | // |
1461 | return s && s->cancel_request; | 1485 | return s ? s->cancel_request : CANCEL_NONE; |
1462 | } | 1486 | } |
1463 | 1487 | ||
1464 | static void cancel_error( lua_State*L ) { | 1488 | static void cancel_error( lua_State*L ) { |
@@ -1467,22 +1491,24 @@ static void cancel_error( lua_State*L ) { | |||
1467 | lua_error(L); // no return | 1491 | lua_error(L); // no return |
1468 | } | 1492 | } |
1469 | 1493 | ||
1470 | static void cancel_hook( lua_State*L, lua_Debug *ar ) { | 1494 | static void cancel_hook( lua_State*L, lua_Debug *ar ) |
1471 | (void)ar; | 1495 | { |
1472 | if (cancel_test(L)) cancel_error(L); | 1496 | (void)ar; |
1497 | if( cancel_test( L) != CANCEL_NONE) | ||
1498 | cancel_error( L); | ||
1473 | } | 1499 | } |
1474 | 1500 | ||
1475 | 1501 | ||
1476 | //--- | 1502 | //--- |
1477 | // bool= cancel_test() | 1503 | // bool = cancel_test() |
1478 | // | 1504 | // |
1479 | // Available inside the global namespace of lanes | 1505 | // Available inside the global namespace of lanes |
1480 | // returns a boolean saying if a cancel request is pending | 1506 | // returns a boolean saying if a cancel request is pending |
1481 | // | 1507 | // |
1482 | LUAG_FUNC( cancel_test) | 1508 | LUAG_FUNC( cancel_test) |
1483 | { | 1509 | { |
1484 | bool_t test = cancel_test( L); | 1510 | enum e_cancel_request test = cancel_test( L); |
1485 | lua_pushboolean( L, test); | 1511 | lua_pushboolean( L, test != CANCEL_NONE); |
1486 | return 1; | 1512 | return 1; |
1487 | } | 1513 | } |
1488 | 1514 | ||
@@ -1724,7 +1750,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1724 | // Tie "cancel_test()" to the state | 1750 | // Tie "cancel_test()" to the state |
1725 | // | 1751 | // |
1726 | lua_pushcfunction( L, LG_cancel_test); | 1752 | lua_pushcfunction( L, LG_cancel_test); |
1727 | lua_setglobal( L, "cancel_test" ); | 1753 | lua_setglobal( L, "cancel_test"); |
1728 | 1754 | ||
1729 | #ifdef ERROR_FULL_STACK | 1755 | #ifdef ERROR_FULL_STACK |
1730 | // Tie "set_error_reporting()" to the state | 1756 | // Tie "set_error_reporting()" to the state |
@@ -2065,7 +2091,7 @@ LUAG_FUNC( thread_new) | |||
2065 | s->status= PENDING; | 2091 | s->status= PENDING; |
2066 | s->waiting_on = NULL; | 2092 | s->waiting_on = NULL; |
2067 | s->debug_name = NULL; | 2093 | s->debug_name = NULL; |
2068 | s->cancel_request= FALSE; | 2094 | s->cancel_request = CANCEL_NONE; |
2069 | 2095 | ||
2070 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 2096 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
2071 | MUTEX_INIT( &s->done_lock); | 2097 | MUTEX_INIT( &s->done_lock); |
@@ -2183,6 +2209,10 @@ LUAG_FUNC( thread_cancel) | |||
2183 | if( lua_isnumber( L, 2)) | 2209 | if( lua_isnumber( L, 2)) |
2184 | { | 2210 | { |
2185 | secs = lua_tonumber( L, 2); | 2211 | secs = lua_tonumber( L, 2); |
2212 | if( secs < 0.0 && lua_gettop( L) > 2) | ||
2213 | { | ||
2214 | return luaL_error( L, "can't force a soft cancel"); | ||
2215 | } | ||
2186 | ++ force_i; | 2216 | ++ force_i; |
2187 | } | 2217 | } |
2188 | else if( lua_isnil( L, 2)) | 2218 | else if( lua_isnil( L, 2)) |
@@ -2611,8 +2641,9 @@ void register_core_libfuncs_for_keeper( lua_State* L) | |||
2611 | /* | 2641 | /* |
2612 | ** One-time initializations | 2642 | ** One-time initializations |
2613 | */ | 2643 | */ |
2614 | static void init_once_LOCKED( lua_State* L, int const _on_state_create, int const nbKeepers, lua_Number _shutdown_timeout, bool_t _track_lanes) | 2644 | static void init_once_LOCKED( lua_State* L, int const _on_state_create, int const nbKeepers, lua_Number _shutdown_timeout, bool_t _track_lanes, bool_t verbose_errors) |
2615 | { | 2645 | { |
2646 | GVerboseErrors = verbose_errors; | ||
2616 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 2647 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) |
2617 | now_secs(); // initialize 'now_secs()' internal offset | 2648 | now_secs(); // initialize 'now_secs()' internal offset |
2618 | #endif | 2649 | #endif |
@@ -2724,10 +2755,12 @@ LUAG_FUNC( configure) | |||
2724 | char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); | 2755 | char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); |
2725 | // all parameter checks are done lua-side | 2756 | // all parameter checks are done lua-side |
2726 | int const nbKeepers = (int)lua_tointeger( L, 1); | 2757 | int const nbKeepers = (int)lua_tointeger( L, 1); |
2758 | // all these can be nil when lanes.core is required internally! (but are only processed at first init anyway) | ||
2727 | int const on_state_create = lua_isfunction( L, 2) ? 2 : 0; | 2759 | int const on_state_create = lua_isfunction( L, 2) ? 2 : 0; |
2728 | lua_Number shutdown_timeout = lua_tonumber( L, 3); | 2760 | lua_Number shutdown_timeout = lua_tonumber( L, 3); |
2729 | bool_t track_lanes = lua_toboolean( L, 4); | 2761 | bool_t track_lanes = lua_toboolean( L, 4); |
2730 | bool_t protect_allocator = lua_toboolean( L, 5); | 2762 | bool_t protect_allocator = lua_toboolean( L, 5); |
2763 | bool_t verbose_errors = lua_toboolean( L, 6); | ||
2731 | 2764 | ||
2732 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L)); | 2765 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L)); |
2733 | DEBUGSPEW_CODE( ++ debugspew_indent_depth); | 2766 | DEBUGSPEW_CODE( ++ debugspew_indent_depth); |
@@ -2738,11 +2771,14 @@ LUAG_FUNC( configure) | |||
2738 | { | 2771 | { |
2739 | void* ud; | 2772 | void* ud; |
2740 | lua_Alloc allocf = lua_getallocf( L, &ud); | 2773 | lua_Alloc allocf = lua_getallocf( L, &ud); |
2741 | struct ProtectedAllocator_s* s = (struct ProtectedAllocator_s*) allocf( ud, NULL, 0, sizeof( struct ProtectedAllocator_s)); | 2774 | if( allocf != protected_lua_Alloc) // just in case |
2742 | s->allocf = allocf; | 2775 | { |
2743 | s->ud = ud; | 2776 | struct ProtectedAllocator_s* s = (struct ProtectedAllocator_s*) allocf( ud, NULL, 0, sizeof( struct ProtectedAllocator_s)); |
2744 | MUTEX_INIT( &s->lock); | 2777 | s->allocf = allocf; |
2745 | lua_setallocf( L, protected_lua_Alloc, s); | 2778 | s->ud = ud; |
2779 | MUTEX_INIT( &s->lock); | ||
2780 | lua_setallocf( L, protected_lua_Alloc, s); | ||
2781 | } | ||
2746 | } | 2782 | } |
2747 | 2783 | ||
2748 | // Create main module interface table | 2784 | // Create main module interface table |
@@ -2818,7 +2854,7 @@ LUAG_FUNC( configure) | |||
2818 | static volatile int /*bool*/ go_ahead; // = 0 | 2854 | static volatile int /*bool*/ go_ahead; // = 0 |
2819 | if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) | 2855 | if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) |
2820 | { | 2856 | { |
2821 | init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes); | 2857 | init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes, verbose_errors); |
2822 | go_ahead = 1; // let others pass | 2858 | go_ahead = 1; // let others pass |
2823 | } | 2859 | } |
2824 | else | 2860 | else |
@@ -2836,7 +2872,7 @@ LUAG_FUNC( configure) | |||
2836 | // | 2872 | // |
2837 | if( s_initCount == 0) | 2873 | if( s_initCount == 0) |
2838 | { | 2874 | { |
2839 | init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes); | 2875 | init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes, verbose_errors); |
2840 | s_initCount = 1; | 2876 | s_initCount = 1; |
2841 | } | 2877 | } |
2842 | } | 2878 | } |