diff options
Diffstat (limited to 'src/lanes.c')
-rw-r--r-- | src/lanes.c | 203 |
1 files changed, 2 insertions, 201 deletions
diff --git a/src/lanes.c b/src/lanes.c index 8762766..cbac6da 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * LANES.C Copyright (c) 2007-08, Asko Kauppi | 2 | * LANES.C Copyright (c) 2007-08, Asko Kauppi |
3 | * Copyright (C) 2009-17, Benoit Germain | 3 | * Copyright (C) 2009-19, Benoit Germain |
4 | * | 4 | * |
5 | * Multithreading in Lua. | 5 | * Multithreading in Lua. |
6 | * | 6 | * |
@@ -56,7 +56,7 @@ | |||
56 | =============================================================================== | 56 | =============================================================================== |
57 | 57 | ||
58 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> | 58 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> |
59 | 2011-17 Benoit Germain <bnt.germain@gmail.com> | 59 | 2011-19 Benoit Germain <bnt.germain@gmail.com> |
60 | 60 | ||
61 | Permission is hereby granted, free of charge, to any person obtaining a copy | 61 | Permission is hereby granted, free of charge, to any person obtaining a copy |
62 | of this software and associated documentation files (the "Software"), to deal | 62 | of this software and associated documentation files (the "Software"), to deal |
@@ -124,33 +124,6 @@ static void securize_debug_threadname( lua_State* L, Lane* s) | |||
124 | STACK_END( L, 0); | 124 | STACK_END( L, 0); |
125 | } | 125 | } |
126 | 126 | ||
127 | /* | ||
128 | * Check if the thread in question ('L') has been signalled for cancel. | ||
129 | * | ||
130 | * Called by cancellation hooks and/or pending Linda operations (because then | ||
131 | * the check won't affect performance). | ||
132 | * | ||
133 | * Returns TRUE if any locks are to be exited, and 'cancel_error()' called, | ||
134 | * to make execution of the lane end. | ||
135 | */ | ||
136 | static inline enum e_cancel_request cancel_test( lua_State* L) | ||
137 | { | ||
138 | Lane* const s = get_lane_from_registry( L); | ||
139 | // 's' is NULL for the original main state (and no-one can cancel that) | ||
140 | return s ? s->cancel_request : CANCEL_NONE; | ||
141 | } | ||
142 | |||
143 | static void cancel_hook( lua_State* L, lua_Debug* ar) | ||
144 | { | ||
145 | (void)ar; | ||
146 | DEBUGSPEW_CODE( fprintf( stderr, "cancel_hook\n")); | ||
147 | if( cancel_test( L) != CANCEL_NONE) | ||
148 | { | ||
149 | cancel_error( L); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | |||
154 | #if ERROR_FULL_STACK | 127 | #if ERROR_FULL_STACK |
155 | static int lane_error( lua_State* L); | 128 | static int lane_error( lua_State* L); |
156 | // crc64/we of string "STACKTRACE_REGKEY" generated at http://www.nitrxgen.net/hashgen/ | 129 | // crc64/we of string "STACKTRACE_REGKEY" generated at http://www.nitrxgen.net/hashgen/ |
@@ -404,115 +377,6 @@ static int run_finalizers( lua_State* L, int lua_rc) | |||
404 | * ############################################################################################### | 377 | * ############################################################################################### |
405 | */ | 378 | */ |
406 | 379 | ||
407 | //--- | ||
408 | // = thread_cancel( lane_ud [,timeout_secs=0.0] [,force_kill_bool=false] ) | ||
409 | // | ||
410 | // The originator thread asking us specifically to cancel the other thread. | ||
411 | // | ||
412 | // 'timeout': <0: wait forever, until the lane is finished | ||
413 | // 0.0: just signal it to cancel, no time waited | ||
414 | // >0: time to wait for the lane to detect cancellation | ||
415 | // | ||
416 | // 'force_kill': if true, and lane does not detect cancellation within timeout, | ||
417 | // it is forcefully killed. Using this with 0.0 timeout means just kill | ||
418 | // (unless the lane is already finished). | ||
419 | // | ||
420 | // Returns: true if the lane was already finished (DONE/ERROR_ST/CANCELLED) or if we | ||
421 | // managed to cancel it. | ||
422 | // false if the cancellation timed out, or a kill was needed. | ||
423 | // | ||
424 | |||
425 | typedef enum | ||
426 | { | ||
427 | CR_Timeout, | ||
428 | CR_Cancelled, | ||
429 | CR_Killed | ||
430 | } cancel_result; | ||
431 | |||
432 | static cancel_result thread_cancel_soft( lua_State* L, Lane* s, bool_t wake_lindas_) | ||
433 | { | ||
434 | s->cancel_request = CANCEL_SOFT; // it's now signaled to stop | ||
435 | // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own | ||
436 | if( wake_lindas_) // wake the thread so that execution returns from any pending linda operation if desired | ||
437 | { | ||
438 | SIGNAL_T *waiting_on = s->waiting_on; | ||
439 | if( s->status == WAITING && waiting_on != NULL) | ||
440 | { | ||
441 | SIGNAL_ALL( waiting_on); | ||
442 | } | ||
443 | } | ||
444 | // say we succeeded though | ||
445 | return CR_Cancelled; | ||
446 | } | ||
447 | |||
448 | static cancel_result thread_cancel_hard( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_) | ||
449 | { | ||
450 | cancel_result result; | ||
451 | |||
452 | s->cancel_request = CANCEL_HARD; // it's now signaled to stop | ||
453 | { | ||
454 | SIGNAL_T *waiting_on = s->waiting_on; | ||
455 | if( s->status == WAITING && waiting_on != NULL) | ||
456 | { | ||
457 | SIGNAL_ALL( waiting_on); | ||
458 | } | ||
459 | } | ||
460 | |||
461 | result = THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; | ||
462 | |||
463 | if( (result == CR_Timeout) && force_) | ||
464 | { | ||
465 | // Killing is asynchronous; we _will_ wait for it to be done at | ||
466 | // GC, to make sure the data structure can be released (alternative | ||
467 | // would be use of "cancellation cleanup handlers" that at least | ||
468 | // PThread seems to have). | ||
469 | // | ||
470 | THREAD_KILL( &s->thread); | ||
471 | #if THREADAPI == THREADAPI_PTHREAD | ||
472 | // pthread: make sure the thread is really stopped! | ||
473 | // note that this may block forever if the lane doesn't call a cancellation point and pthread doesn't honor PTHREAD_CANCEL_ASYNCHRONOUS | ||
474 | result = THREAD_WAIT( &s->thread, waitkill_timeout_, &s->done_signal, &s->done_lock, &s->status); | ||
475 | if( result == CR_Timeout) | ||
476 | { | ||
477 | return luaL_error( L, "force-killed lane failed to terminate within %f second%s", waitkill_timeout_, waitkill_timeout_ > 1 ? "s" : ""); | ||
478 | } | ||
479 | #else | ||
480 | (void) waitkill_timeout_; // unused | ||
481 | (void) L; // unused | ||
482 | #endif // THREADAPI == THREADAPI_PTHREAD | ||
483 | s->mstatus = KILLED; // mark 'gc' to wait for it | ||
484 | // note that s->status value must remain to whatever it was at the time of the kill | ||
485 | // because we need to know if we can lua_close() the Lua State or not. | ||
486 | result = CR_Killed; | ||
487 | } | ||
488 | return result; | ||
489 | } | ||
490 | |||
491 | static cancel_result thread_cancel( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_) | ||
492 | { | ||
493 | // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here | ||
494 | // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) | ||
495 | if( s->mstatus == KILLED) | ||
496 | { | ||
497 | return CR_Killed; | ||
498 | } | ||
499 | |||
500 | if( s->status >= DONE) | ||
501 | { | ||
502 | // say "ok" by default, including when lane is already done | ||
503 | return CR_Cancelled; | ||
504 | } | ||
505 | |||
506 | // signal the linda the wake up the thread so that it can react to the cancel query | ||
507 | // let us hope we never land here with a pointer on a linda that has been destroyed... | ||
508 | if( secs_ < 0.0) | ||
509 | { | ||
510 | return thread_cancel_soft( L, s, force_); | ||
511 | } | ||
512 | |||
513 | return thread_cancel_hard( L, s, secs_, force_, waitkill_timeout_); | ||
514 | } | ||
515 | |||
516 | // | 380 | // |
517 | // Protects modifying the selfdestruct chain | 381 | // Protects modifying the selfdestruct chain |
518 | 382 | ||
@@ -734,19 +598,6 @@ static int selfdestruct_gc( lua_State* L) | |||
734 | 598 | ||
735 | 599 | ||
736 | //--- | 600 | //--- |
737 | // bool = cancel_test() | ||
738 | // | ||
739 | // Available inside the global namespace of lanes | ||
740 | // returns a boolean saying if a cancel request is pending | ||
741 | // | ||
742 | LUAG_FUNC( cancel_test) | ||
743 | { | ||
744 | enum e_cancel_request test = cancel_test( L); | ||
745 | lua_pushboolean( L, test != CANCEL_NONE); | ||
746 | return 1; | ||
747 | } | ||
748 | |||
749 | //--- | ||
750 | // = _single( [cores_uint=1] ) | 601 | // = _single( [cores_uint=1] ) |
751 | // | 602 | // |
752 | // Limits the process to use only 'cores' CPU cores. To be used for performance | 603 | // Limits the process to use only 'cores' CPU cores. To be used for performance |
@@ -1527,56 +1378,6 @@ LUAG_FUNC( thread_gc) | |||
1527 | return 0; | 1378 | return 0; |
1528 | } | 1379 | } |
1529 | 1380 | ||
1530 | // lane_h:cancel( [timeout] [, force [, forcekill_timeout]]) | ||
1531 | LUAG_FUNC( thread_cancel) | ||
1532 | { | ||
1533 | Lane* s = lua_toLane( L, 1); | ||
1534 | double secs = 0.0; | ||
1535 | int force_i = 2; | ||
1536 | int forcekill_timeout_i = 3; | ||
1537 | |||
1538 | if( lua_isnumber( L, 2)) | ||
1539 | { | ||
1540 | secs = lua_tonumber( L, 2); | ||
1541 | if( secs < 0.0 && lua_gettop( L) > 3) | ||
1542 | { | ||
1543 | return luaL_error( L, "can't force_kill a soft cancel"); | ||
1544 | } | ||
1545 | // negative timeout and force flag means we want to wake linda-waiting threads | ||
1546 | ++ force_i; | ||
1547 | ++ forcekill_timeout_i; | ||
1548 | } | ||
1549 | else if( lua_isnil( L, 2)) | ||
1550 | { | ||
1551 | ++ force_i; | ||
1552 | ++ forcekill_timeout_i; | ||
1553 | } | ||
1554 | |||
1555 | { | ||
1556 | bool_t force = lua_toboolean( L, force_i); // FALSE if nothing there | ||
1557 | double forcekill_timeout = luaL_optnumber( L, forcekill_timeout_i, 0.0); | ||
1558 | |||
1559 | switch( thread_cancel( L, s, secs, force, forcekill_timeout)) | ||
1560 | { | ||
1561 | case CR_Timeout: | ||
1562 | lua_pushboolean( L, 0); | ||
1563 | lua_pushstring( L, "timeout"); | ||
1564 | return 2; | ||
1565 | |||
1566 | case CR_Cancelled: | ||
1567 | lua_pushboolean( L, 1); | ||
1568 | return 1; | ||
1569 | |||
1570 | case CR_Killed: | ||
1571 | lua_pushboolean( L, 0); | ||
1572 | lua_pushstring( L, "killed"); | ||
1573 | return 2; | ||
1574 | } | ||
1575 | } | ||
1576 | // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value" | ||
1577 | return 0; | ||
1578 | } | ||
1579 | |||
1580 | //--- | 1381 | //--- |
1581 | // str= thread_status( lane ) | 1382 | // str= thread_status( lane ) |
1582 | // | 1383 | // |