diff options
| author | Benoit Germain <b n t DOT g e r m a i n AT g m a i l DOT c o m> | 2019-04-22 17:17:03 +0200 |
|---|---|---|
| committer | Benoit Germain <b n t DOT g e r m a i n AT g m a i l DOT c o m> | 2019-04-22 17:17:03 +0200 |
| commit | 32f8cdfc73ed90dcf88ffcf4bfc1a3e4d5a69e6c (patch) | |
| tree | 9f348cc5cf26f37b8801aaccd0e1d2647e4a3675 /src | |
| parent | b735db9ef342973deb628129fc04f0d295616164 (diff) | |
| download | lanes-32f8cdfc73ed90dcf88ffcf4bfc1a3e4d5a69e6c.tar.gz lanes-32f8cdfc73ed90dcf88ffcf4bfc1a3e4d5a69e6c.tar.bz2 lanes-32f8cdfc73ed90dcf88ffcf4bfc1a3e4d5a69e6c.zip | |
Moved cancellation code in separate files
Diffstat (limited to 'src')
| -rw-r--r-- | src/cancel.c | 252 | ||||
| -rw-r--r-- | src/cancel.h | 57 | ||||
| -rw-r--r-- | src/lanes.c | 203 | ||||
| -rw-r--r-- | src/lanes_private.h | 24 | ||||
| -rw-r--r-- | src/macros_and_utils.h | 2 | ||||
| -rw-r--r-- | src/tools.c | 4 | ||||
| -rw-r--r-- | src/tools.h | 2 |
7 files changed, 316 insertions, 228 deletions
diff --git a/src/cancel.c b/src/cancel.c new file mode 100644 index 0000000..11e100d --- /dev/null +++ b/src/cancel.c | |||
| @@ -0,0 +1,252 @@ | |||
| 1 | /* | ||
| 2 | -- | ||
| 3 | -- CANCEL.C | ||
| 4 | -- | ||
| 5 | -- Lane cancellation support | ||
| 6 | -- | ||
| 7 | -- Author: Benoit Germain <bnt.germain@gmail.com> | ||
| 8 | -- | ||
| 9 | --[[ | ||
| 10 | =============================================================================== | ||
| 11 | |||
| 12 | Copyright (C) 2011-2019 Benoit Germain <bnt.germain@gmail.com> | ||
| 13 | |||
| 14 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 15 | of this software and associated documentation files (the "Software"), to deal | ||
| 16 | in the Software without restriction, including without limitation the rights | ||
| 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 18 | copies of the Software, and to permit persons to whom the Software is | ||
| 19 | furnished to do so, subject to the following conditions: | ||
| 20 | |||
| 21 | The above copyright notice and this permission notice shall be included in | ||
| 22 | all copies or substantial portions of the Software. | ||
| 23 | |||
| 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 30 | THE SOFTWARE. | ||
| 31 | |||
| 32 | =============================================================================== | ||
| 33 | ]]-- | ||
| 34 | */ | ||
| 35 | |||
| 36 | #include <assert.h> | ||
| 37 | |||
| 38 | #include "threading.h" | ||
| 39 | #include "cancel.h" | ||
| 40 | #include "tools.h" | ||
| 41 | #include "lanes_private.h" | ||
| 42 | |||
| 43 | // ################################################################################################ | ||
| 44 | // ################################################################################################ | ||
| 45 | |||
| 46 | /* | ||
| 47 | * Check if the thread in question ('L') has been signalled for cancel. | ||
| 48 | * | ||
| 49 | * Called by cancellation hooks and/or pending Linda operations (because then | ||
| 50 | * the check won't affect performance). | ||
| 51 | * | ||
| 52 | * Returns TRUE if any locks are to be exited, and 'cancel_error()' called, | ||
| 53 | * to make execution of the lane end. | ||
| 54 | */ | ||
| 55 | static inline enum e_cancel_request cancel_test( lua_State* L) | ||
| 56 | { | ||
| 57 | Lane* const s = get_lane_from_registry( L); | ||
| 58 | // 's' is NULL for the original main state (and no-one can cancel that) | ||
| 59 | return s ? s->cancel_request : CANCEL_NONE; | ||
| 60 | } | ||
| 61 | |||
| 62 | // ################################################################################################ | ||
| 63 | |||
| 64 | //--- | ||
| 65 | // bool = cancel_test() | ||
| 66 | // | ||
| 67 | // Available inside the global namespace of lanes | ||
| 68 | // returns a boolean saying if a cancel request is pending | ||
| 69 | // | ||
| 70 | LUAG_FUNC( cancel_test) | ||
| 71 | { | ||
| 72 | enum e_cancel_request test = cancel_test( L); | ||
| 73 | lua_pushboolean( L, test != CANCEL_NONE); | ||
| 74 | return 1; | ||
| 75 | } | ||
| 76 | |||
| 77 | // ################################################################################################ | ||
| 78 | // ################################################################################################ | ||
| 79 | |||
| 80 | void cancel_hook( lua_State* L, lua_Debug* ar) | ||
| 81 | { | ||
| 82 | (void)ar; | ||
| 83 | DEBUGSPEW_CODE( fprintf( stderr, "cancel_hook\n")); | ||
| 84 | if( cancel_test( L) != CANCEL_NONE) | ||
| 85 | { | ||
| 86 | cancel_error( L); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | // ################################################################################################ | ||
| 91 | // ################################################################################################ | ||
| 92 | |||
| 93 | //--- | ||
| 94 | // = thread_cancel( lane_ud [,timeout_secs=0.0] [,force_kill_bool=false] ) | ||
| 95 | // | ||
| 96 | // The originator thread asking us specifically to cancel the other thread. | ||
| 97 | // | ||
| 98 | // 'timeout': <0: wait forever, until the lane is finished | ||
| 99 | // 0.0: just signal it to cancel, no time waited | ||
| 100 | // >0: time to wait for the lane to detect cancellation | ||
| 101 | // | ||
| 102 | // 'force_kill': if true, and lane does not detect cancellation within timeout, | ||
| 103 | // it is forcefully killed. Using this with 0.0 timeout means just kill | ||
| 104 | // (unless the lane is already finished). | ||
| 105 | // | ||
| 106 | // Returns: true if the lane was already finished (DONE/ERROR_ST/CANCELLED) or if we | ||
| 107 | // managed to cancel it. | ||
| 108 | // false if the cancellation timed out, or a kill was needed. | ||
| 109 | // | ||
| 110 | |||
| 111 | // ################################################################################################ | ||
| 112 | |||
| 113 | static cancel_result thread_cancel_soft( Lane* s, bool_t wake_lindas_) | ||
| 114 | { | ||
| 115 | s->cancel_request = CANCEL_SOFT; // it's now signaled to stop | ||
| 116 | // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own | ||
| 117 | if( wake_lindas_) // wake the thread so that execution returns from any pending linda operation if desired | ||
| 118 | { | ||
| 119 | SIGNAL_T *waiting_on = s->waiting_on; | ||
| 120 | if( s->status == WAITING && waiting_on != NULL) | ||
| 121 | { | ||
| 122 | SIGNAL_ALL( waiting_on); | ||
| 123 | } | ||
| 124 | } | ||
| 125 | // say we succeeded though | ||
| 126 | return CR_Cancelled; | ||
| 127 | } | ||
| 128 | |||
| 129 | // ################################################################################################ | ||
| 130 | |||
| 131 | static cancel_result thread_cancel_hard( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_) | ||
| 132 | { | ||
| 133 | cancel_result result; | ||
| 134 | |||
| 135 | s->cancel_request = CANCEL_HARD; // it's now signaled to stop | ||
| 136 | { | ||
| 137 | SIGNAL_T *waiting_on = s->waiting_on; | ||
| 138 | if( s->status == WAITING && waiting_on != NULL) | ||
| 139 | { | ||
| 140 | SIGNAL_ALL( waiting_on); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | result = THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; | ||
| 145 | |||
| 146 | if( (result == CR_Timeout) && force_) | ||
| 147 | { | ||
| 148 | // Killing is asynchronous; we _will_ wait for it to be done at | ||
| 149 | // GC, to make sure the data structure can be released (alternative | ||
| 150 | // would be use of "cancellation cleanup handlers" that at least | ||
| 151 | // PThread seems to have). | ||
| 152 | // | ||
| 153 | THREAD_KILL( &s->thread); | ||
| 154 | #if THREADAPI == THREADAPI_PTHREAD | ||
| 155 | // pthread: make sure the thread is really stopped! | ||
| 156 | // note that this may block forever if the lane doesn't call a cancellation point and pthread doesn't honor PTHREAD_CANCEL_ASYNCHRONOUS | ||
| 157 | result = THREAD_WAIT( &s->thread, waitkill_timeout_, &s->done_signal, &s->done_lock, &s->status); | ||
| 158 | if( result == CR_Timeout) | ||
| 159 | { | ||
| 160 | return luaL_error( L, "force-killed lane failed to terminate within %f second%s", waitkill_timeout_, waitkill_timeout_ > 1 ? "s" : ""); | ||
| 161 | } | ||
| 162 | #else | ||
| 163 | (void) waitkill_timeout_; // unused | ||
| 164 | (void) L; // unused | ||
| 165 | #endif // THREADAPI == THREADAPI_PTHREAD | ||
| 166 | s->mstatus = KILLED; // mark 'gc' to wait for it | ||
| 167 | // note that s->status value must remain to whatever it was at the time of the kill | ||
| 168 | // because we need to know if we can lua_close() the Lua State or not. | ||
| 169 | result = CR_Killed; | ||
| 170 | } | ||
| 171 | return result; | ||
| 172 | } | ||
| 173 | |||
| 174 | // ################################################################################################ | ||
| 175 | |||
| 176 | cancel_result thread_cancel( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_) | ||
| 177 | { | ||
| 178 | // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here | ||
| 179 | // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) | ||
| 180 | if( s->mstatus == KILLED) | ||
| 181 | { | ||
| 182 | return CR_Killed; | ||
| 183 | } | ||
| 184 | |||
| 185 | if( s->status >= DONE) | ||
| 186 | { | ||
| 187 | // say "ok" by default, including when lane is already done | ||
| 188 | return CR_Cancelled; | ||
| 189 | } | ||
| 190 | |||
| 191 | // signal the linda the wake up the thread so that it can react to the cancel query | ||
| 192 | // let us hope we never land here with a pointer on a linda that has been destroyed... | ||
| 193 | if( secs_ < 0.0) | ||
| 194 | { | ||
| 195 | return thread_cancel_soft( s, force_); | ||
| 196 | } | ||
| 197 | |||
| 198 | return thread_cancel_hard( L, s, secs_, force_, waitkill_timeout_); | ||
| 199 | } | ||
| 200 | |||
| 201 | // ################################################################################################ | ||
| 202 | // ################################################################################################ | ||
| 203 | |||
| 204 | // lane_h:cancel( [timeout] [, force [, forcekill_timeout]]) | ||
| 205 | LUAG_FUNC( thread_cancel) | ||
| 206 | { | ||
| 207 | Lane* s = lua_toLane( L, 1); | ||
| 208 | double secs = 0.0; | ||
| 209 | int force_i = 2; | ||
| 210 | int forcekill_timeout_i = 3; | ||
| 211 | |||
| 212 | if( lua_isnumber( L, 2)) | ||
| 213 | { | ||
| 214 | secs = lua_tonumber( L, 2); | ||
| 215 | if( secs < 0.0 && lua_gettop( L) > 3) | ||
| 216 | { | ||
| 217 | return luaL_error( L, "can't force_kill a soft cancel"); | ||
| 218 | } | ||
| 219 | // negative timeout and force flag means we want to wake linda-waiting threads | ||
| 220 | ++ force_i; | ||
| 221 | ++ forcekill_timeout_i; | ||
| 222 | } | ||
| 223 | else if( lua_isnil( L, 2)) | ||
| 224 | { | ||
| 225 | ++ force_i; | ||
| 226 | ++ forcekill_timeout_i; | ||
| 227 | } | ||
| 228 | |||
| 229 | { | ||
| 230 | bool_t force = lua_toboolean( L, force_i); // FALSE if nothing there | ||
| 231 | double forcekill_timeout = luaL_optnumber( L, forcekill_timeout_i, 0.0); | ||
| 232 | |||
| 233 | switch( thread_cancel( L, s, secs, force, forcekill_timeout)) | ||
| 234 | { | ||
| 235 | case CR_Timeout: | ||
| 236 | lua_pushboolean( L, 0); | ||
| 237 | lua_pushstring( L, "timeout"); | ||
| 238 | return 2; | ||
| 239 | |||
| 240 | case CR_Cancelled: | ||
| 241 | lua_pushboolean( L, 1); | ||
| 242 | return 1; | ||
| 243 | |||
| 244 | case CR_Killed: | ||
| 245 | lua_pushboolean( L, 0); | ||
| 246 | lua_pushstring( L, "killed"); | ||
| 247 | return 2; | ||
| 248 | } | ||
| 249 | } | ||
| 250 | // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value" | ||
| 251 | return 0; | ||
| 252 | } | ||
diff --git a/src/cancel.h b/src/cancel.h new file mode 100644 index 0000000..3112809 --- /dev/null +++ b/src/cancel.h | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | #if !defined( __LANES_CANCEL_H__) | ||
| 2 | #define __LANES_CANCEL_H__ 1 | ||
| 3 | |||
| 4 | #include "lua.h" | ||
| 5 | #include "lualib.h" | ||
| 6 | #include "lauxlib.h" | ||
| 7 | |||
| 8 | #include "uniquekey.h" | ||
| 9 | #include "macros_and_utils.h" | ||
| 10 | |||
| 11 | // ################################################################################################ | ||
| 12 | |||
| 13 | typedef struct s_Lane Lane; // forward | ||
| 14 | |||
| 15 | /* | ||
| 16 | * Lane cancellation request modes | ||
| 17 | */ | ||
| 18 | enum e_cancel_request | ||
| 19 | { | ||
| 20 | CANCEL_NONE, // no pending cancel request | ||
| 21 | CANCEL_SOFT, // user wants the lane to cancel itself manually on cancel_test() | ||
| 22 | CANCEL_HARD // user wants the lane to be interrupted (meaning code won't return from those functions) from inside linda:send/receive calls | ||
| 23 | }; | ||
| 24 | |||
| 25 | typedef enum | ||
| 26 | { | ||
| 27 | CR_Timeout, | ||
| 28 | CR_Cancelled, | ||
| 29 | CR_Killed | ||
| 30 | } cancel_result; | ||
| 31 | |||
| 32 | // crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/ | ||
| 33 | static DECLARE_CONST_UNIQUE_KEY(CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel | ||
| 34 | |||
| 35 | // crc64/we of string "CANCEL_TEST_KEY" generated at http://www.nitrxgen.net/hashgen/ | ||
| 36 | static DECLARE_CONST_UNIQUE_KEY(CANCEL_TEST_KEY, 0xe66f5960c57d133a); // used as registry key | ||
| 37 | |||
| 38 | void cancel_hook( lua_State* L, lua_Debug* ar); | ||
| 39 | |||
| 40 | cancel_result thread_cancel( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_); | ||
| 41 | |||
| 42 | static inline int cancel_error( lua_State* L) | ||
| 43 | { | ||
| 44 | STACK_GROW( L, 1); | ||
| 45 | push_unique_key( L, CANCEL_ERROR); // special error value | ||
| 46 | return lua_error( L); // doesn't return | ||
| 47 | } | ||
| 48 | |||
| 49 | // ################################################################################################ | ||
| 50 | // ################################################################################################ | ||
| 51 | |||
| 52 | LUAG_FUNC( cancel_test); | ||
| 53 | LUAG_FUNC( thread_cancel); | ||
| 54 | |||
| 55 | // ################################################################################################ | ||
| 56 | |||
| 57 | #endif // __LANES_CANCEL_H__ | ||
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 | // |
diff --git a/src/lanes_private.h b/src/lanes_private.h index 16c178d..ac05129 100644 --- a/src/lanes_private.h +++ b/src/lanes_private.h | |||
| @@ -2,16 +2,7 @@ | |||
| 2 | #define __lanes_private_h__ 1 | 2 | #define __lanes_private_h__ 1 |
| 3 | 3 | ||
| 4 | #include "uniquekey.h" | 4 | #include "uniquekey.h" |
| 5 | 5 | #include "cancel.h" | |
| 6 | /* | ||
| 7 | * Lane cancellation request modes | ||
| 8 | */ | ||
| 9 | enum e_cancel_request | ||
| 10 | { | ||
| 11 | CANCEL_NONE, // no pending cancel request | ||
| 12 | CANCEL_SOFT, // user wants the lane to cancel itself manually on cancel_test() | ||
| 13 | CANCEL_HARD // user wants the lane to be interrupted (meaning code won't return from those functions) from inside linda:send/receive calls | ||
| 14 | }; | ||
| 15 | 6 | ||
| 16 | // NOTE: values to be changed by either thread, during execution, without | 7 | // NOTE: values to be changed by either thread, during execution, without |
| 17 | // locking, are marked "volatile" | 8 | // locking, are marked "volatile" |
| @@ -86,12 +77,6 @@ typedef struct s_Lane Lane; | |||
| 86 | // | 77 | // |
| 87 | #define lua_toLane( L, i) (*((Lane**) luaL_checkudata( L, i, "Lane"))) | 78 | #define lua_toLane( L, i) (*((Lane**) luaL_checkudata( L, i, "Lane"))) |
| 88 | 79 | ||
| 89 | // crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/ | ||
| 90 | static DECLARE_CONST_UNIQUE_KEY(CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel | ||
| 91 | |||
| 92 | // crc64/we of string "CANCEL_TEST_KEY" generated at http://www.nitrxgen.net/hashgen/ | ||
| 93 | static DECLARE_CONST_UNIQUE_KEY(CANCEL_TEST_KEY, 0xe66f5960c57d133a); // used as registry key | ||
| 94 | |||
| 95 | static inline Lane* get_lane_from_registry( lua_State* L) | 80 | static inline Lane* get_lane_from_registry( lua_State* L) |
| 96 | { | 81 | { |
| 97 | Lane* s; | 82 | Lane* s; |
| @@ -104,11 +89,4 @@ static inline Lane* get_lane_from_registry( lua_State* L) | |||
| 104 | return s; | 89 | return s; |
| 105 | } | 90 | } |
| 106 | 91 | ||
| 107 | static inline int cancel_error( lua_State* L) | ||
| 108 | { | ||
| 109 | STACK_GROW( L, 1); | ||
| 110 | push_unique_key( L, CANCEL_ERROR); // special error value | ||
| 111 | return lua_error( L); // doesn't return | ||
| 112 | } | ||
| 113 | |||
| 114 | #endif // __lanes_private_h__ \ No newline at end of file | 92 | #endif // __lanes_private_h__ \ No newline at end of file |
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h index 4b0b9d3..ae3a6c9 100644 --- a/src/macros_and_utils.h +++ b/src/macros_and_utils.h | |||
| @@ -97,4 +97,6 @@ extern char const* debugspew_indent; | |||
| 97 | lua_rawget( L, LUA_REGISTRYINDEX); \ | 97 | lua_rawget( L, LUA_REGISTRYINDEX); \ |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | #define LUAG_FUNC( func_name) int LG_##func_name( lua_State* L) | ||
| 101 | |||
| 100 | #endif // MACROS_AND_UTILS_H | 102 | #endif // MACROS_AND_UTILS_H |
diff --git a/src/tools.c b/src/tools.c index 48e904c..02d51ae 100644 --- a/src/tools.c +++ b/src/tools.c | |||
| @@ -1736,7 +1736,7 @@ static void inter_copy_keyvaluepair( Universe* U, lua_State* L2, uint_t L2_cache | |||
| 1736 | } | 1736 | } |
| 1737 | } | 1737 | } |
| 1738 | 1738 | ||
| 1739 | bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_) | 1739 | static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) |
| 1740 | { | 1740 | { |
| 1741 | STACK_CHECK( L, 0); | 1741 | STACK_CHECK( L, 0); |
| 1742 | STACK_CHECK( L2, 0); | 1742 | STACK_CHECK( L2, 0); |
| @@ -1849,7 +1849,7 @@ static bool_t inter_copy_userdata( Universe* U, lua_State* L2, uint_t L2_cache_i | |||
| 1849 | STACK_MID( L, 0); | 1849 | STACK_MID( L, 0); |
| 1850 | STACK_MID( L2, 0); | 1850 | STACK_MID( L2, 0); |
| 1851 | 1851 | ||
| 1852 | if( copyclone( U, L2, L2_cache_i, L, i, vt, mode_, upName_)) | 1852 | if( copyclone( U, L2, L2_cache_i, L, i, mode_, upName_)) |
| 1853 | { | 1853 | { |
| 1854 | STACK_MID( L, 0); | 1854 | STACK_MID( L, 0); |
| 1855 | STACK_MID( L2, 1); | 1855 | STACK_MID( L2, 1); |
diff --git a/src/tools.h b/src/tools.h index 71460c3..8a8ad7b 100644 --- a/src/tools.h +++ b/src/tools.h | |||
| @@ -13,8 +13,6 @@ typedef struct s_Universe Universe; | |||
| 13 | 13 | ||
| 14 | // ################################################################################################ | 14 | // ################################################################################################ |
| 15 | 15 | ||
| 16 | #define LUAG_FUNC( func_name) int LG_##func_name( lua_State* L) | ||
| 17 | |||
| 18 | #define luaG_optunsigned(L,i,d) ((uint_t) luaL_optinteger(L,i,d)) | 16 | #define luaG_optunsigned(L,i,d) ((uint_t) luaL_optinteger(L,i,d)) |
| 19 | #define luaG_tounsigned(L,i) ((uint_t) lua_tointeger(L,i)) | 17 | #define luaG_tounsigned(L,i) ((uint_t) lua_tointeger(L,i)) |
| 20 | 18 | ||
