diff options
Diffstat (limited to 'src/cancel.c')
-rw-r--r-- | src/cancel.c | 334 |
1 files changed, 167 insertions, 167 deletions
diff --git a/src/cancel.c b/src/cancel.c index cd930b5..0a5adb6 100644 --- a/src/cancel.c +++ b/src/cancel.c | |||
@@ -55,9 +55,9 @@ THE SOFTWARE. | |||
55 | */ | 55 | */ |
56 | static inline enum e_cancel_request cancel_test( lua_State* L) | 56 | static inline enum e_cancel_request cancel_test( lua_State* L) |
57 | { | 57 | { |
58 | Lane* const s = get_lane_from_registry( L); | 58 | Lane* const s = get_lane_from_registry( L); |
59 | // 's' is NULL for the original main state (and no-one can cancel that) | 59 | // 's' is NULL for the original main state (and no-one can cancel that) |
60 | return s ? s->cancel_request : CANCEL_NONE; | 60 | return s ? s->cancel_request : CANCEL_NONE; |
61 | } | 61 | } |
62 | 62 | ||
63 | // ################################################################################################ | 63 | // ################################################################################################ |
@@ -70,9 +70,9 @@ static inline enum e_cancel_request cancel_test( lua_State* L) | |||
70 | // | 70 | // |
71 | LUAG_FUNC( cancel_test) | 71 | LUAG_FUNC( cancel_test) |
72 | { | 72 | { |
73 | enum e_cancel_request test = cancel_test( L); | 73 | enum e_cancel_request test = cancel_test( L); |
74 | lua_pushboolean( L, test != CANCEL_NONE); | 74 | lua_pushboolean( L, test != CANCEL_NONE); |
75 | return 1; | 75 | return 1; |
76 | } | 76 | } |
77 | 77 | ||
78 | // ################################################################################################ | 78 | // ################################################################################################ |
@@ -80,13 +80,13 @@ LUAG_FUNC( cancel_test) | |||
80 | 80 | ||
81 | static void cancel_hook( lua_State* L, lua_Debug* ar) | 81 | static void cancel_hook( lua_State* L, lua_Debug* ar) |
82 | { | 82 | { |
83 | (void)ar; | 83 | (void)ar; |
84 | DEBUGSPEW_CODE( fprintf( stderr, "cancel_hook\n")); | 84 | DEBUGSPEW_CODE( fprintf( stderr, "cancel_hook\n")); |
85 | if( cancel_test( L) != CANCEL_NONE) | 85 | if( cancel_test( L) != CANCEL_NONE) |
86 | { | 86 | { |
87 | lua_sethook( L, NULL, 0, 0); | 87 | lua_sethook( L, NULL, 0, 0); |
88 | cancel_error( L); | 88 | cancel_error( L); |
89 | } | 89 | } |
90 | } | 90 | } |
91 | 91 | ||
92 | // ################################################################################################ | 92 | // ################################################################################################ |
@@ -114,90 +114,90 @@ static void cancel_hook( lua_State* L, lua_Debug* ar) | |||
114 | 114 | ||
115 | static cancel_result thread_cancel_soft( Lane* s, double secs_, bool_t wake_lindas_) | 115 | static cancel_result thread_cancel_soft( Lane* s, double secs_, bool_t wake_lindas_) |
116 | { | 116 | { |
117 | s->cancel_request = CANCEL_SOFT; // it's now signaled to stop | 117 | s->cancel_request = CANCEL_SOFT; // it's now signaled to stop |
118 | // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own | 118 | // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own |
119 | if( wake_lindas_) // wake the thread so that execution returns from any pending linda operation if desired | 119 | if( wake_lindas_) // wake the thread so that execution returns from any pending linda operation if desired |
120 | { | 120 | { |
121 | SIGNAL_T *waiting_on = s->waiting_on; | 121 | SIGNAL_T *waiting_on = s->waiting_on; |
122 | if( s->status == WAITING && waiting_on != NULL) | 122 | if( s->status == WAITING && waiting_on != NULL) |
123 | { | 123 | { |
124 | SIGNAL_ALL( waiting_on); | 124 | SIGNAL_ALL( waiting_on); |
125 | } | 125 | } |
126 | } | 126 | } |
127 | 127 | ||
128 | return THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; | 128 | return THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; |
129 | } | 129 | } |
130 | 130 | ||
131 | // ################################################################################################ | 131 | // ################################################################################################ |
132 | 132 | ||
133 | static cancel_result thread_cancel_hard( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_) | 133 | static cancel_result thread_cancel_hard( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_) |
134 | { | 134 | { |
135 | cancel_result result; | 135 | cancel_result result; |
136 | 136 | ||
137 | s->cancel_request = CANCEL_HARD; // it's now signaled to stop | 137 | s->cancel_request = CANCEL_HARD; // it's now signaled to stop |
138 | { | 138 | { |
139 | SIGNAL_T *waiting_on = s->waiting_on; | 139 | SIGNAL_T *waiting_on = s->waiting_on; |
140 | if( s->status == WAITING && waiting_on != NULL) | 140 | if( s->status == WAITING && waiting_on != NULL) |
141 | { | 141 | { |
142 | SIGNAL_ALL( waiting_on); | 142 | SIGNAL_ALL( waiting_on); |
143 | } | 143 | } |
144 | } | 144 | } |
145 | 145 | ||
146 | result = THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; | 146 | result = THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; |
147 | 147 | ||
148 | if( (result == CR_Timeout) && force_) | 148 | if( (result == CR_Timeout) && force_) |
149 | { | 149 | { |
150 | // Killing is asynchronous; we _will_ wait for it to be done at | 150 | // Killing is asynchronous; we _will_ wait for it to be done at |
151 | // GC, to make sure the data structure can be released (alternative | 151 | // GC, to make sure the data structure can be released (alternative |
152 | // would be use of "cancellation cleanup handlers" that at least | 152 | // would be use of "cancellation cleanup handlers" that at least |
153 | // PThread seems to have). | 153 | // PThread seems to have). |
154 | // | 154 | // |
155 | THREAD_KILL( &s->thread); | 155 | THREAD_KILL( &s->thread); |
156 | #if THREADAPI == THREADAPI_PTHREAD | 156 | #if THREADAPI == THREADAPI_PTHREAD |
157 | // pthread: make sure the thread is really stopped! | 157 | // pthread: make sure the thread is really stopped! |
158 | // note that this may block forever if the lane doesn't call a cancellation point and pthread doesn't honor PTHREAD_CANCEL_ASYNCHRONOUS | 158 | // note that this may block forever if the lane doesn't call a cancellation point and pthread doesn't honor PTHREAD_CANCEL_ASYNCHRONOUS |
159 | result = THREAD_WAIT( &s->thread, waitkill_timeout_, &s->done_signal, &s->done_lock, &s->status); | 159 | result = THREAD_WAIT( &s->thread, waitkill_timeout_, &s->done_signal, &s->done_lock, &s->status); |
160 | if( result == CR_Timeout) | 160 | if( result == CR_Timeout) |
161 | { | 161 | { |
162 | return luaL_error( L, "force-killed lane failed to terminate within %f second%s", waitkill_timeout_, waitkill_timeout_ > 1 ? "s" : ""); | 162 | return luaL_error( L, "force-killed lane failed to terminate within %f second%s", waitkill_timeout_, waitkill_timeout_ > 1 ? "s" : ""); |
163 | } | 163 | } |
164 | #else | 164 | #else |
165 | (void) waitkill_timeout_; // unused | 165 | (void) waitkill_timeout_; // unused |
166 | (void) L; // unused | 166 | (void) L; // unused |
167 | #endif // THREADAPI == THREADAPI_PTHREAD | 167 | #endif // THREADAPI == THREADAPI_PTHREAD |
168 | s->mstatus = KILLED; // mark 'gc' to wait for it | 168 | s->mstatus = KILLED; // mark 'gc' to wait for it |
169 | // note that s->status value must remain to whatever it was at the time of the kill | 169 | // note that s->status value must remain to whatever it was at the time of the kill |
170 | // because we need to know if we can lua_close() the Lua State or not. | 170 | // because we need to know if we can lua_close() the Lua State or not. |
171 | result = CR_Killed; | 171 | result = CR_Killed; |
172 | } | 172 | } |
173 | return result; | 173 | return result; |
174 | } | 174 | } |
175 | 175 | ||
176 | // ################################################################################################ | 176 | // ################################################################################################ |
177 | 177 | ||
178 | cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_) | 178 | cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_) |
179 | { | 179 | { |
180 | // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here | 180 | // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here |
181 | // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) | 181 | // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) |
182 | if( s->mstatus == KILLED) | 182 | if( s->mstatus == KILLED) |
183 | { | 183 | { |
184 | return CR_Killed; | 184 | return CR_Killed; |
185 | } | 185 | } |
186 | 186 | ||
187 | if( s->status >= DONE) | 187 | if( s->status >= DONE) |
188 | { | 188 | { |
189 | // say "ok" by default, including when lane is already done | 189 | // say "ok" by default, including when lane is already done |
190 | return CR_Cancelled; | 190 | return CR_Cancelled; |
191 | } | 191 | } |
192 | 192 | ||
193 | // signal the linda the wake up the thread so that it can react to the cancel query | 193 | // signal the linda the wake up the thread so that it can react to the cancel query |
194 | // let us hope we never land here with a pointer on a linda that has been destroyed... | 194 | // let us hope we never land here with a pointer on a linda that has been destroyed... |
195 | if( op_ == CO_Soft) | 195 | if( op_ == CO_Soft) |
196 | { | 196 | { |
197 | return thread_cancel_soft( s, secs_, force_); | 197 | return thread_cancel_soft( s, secs_, force_); |
198 | } | 198 | } |
199 | 199 | ||
200 | return thread_cancel_hard( L, s, secs_, force_, waitkill_timeout_); | 200 | return thread_cancel_hard( L, s, secs_, force_, waitkill_timeout_); |
201 | } | 201 | } |
202 | 202 | ||
203 | // ################################################################################################ | 203 | // ################################################################################################ |
@@ -208,95 +208,95 @@ cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, | |||
208 | // < 0: hard | 208 | // < 0: hard |
209 | static CancelOp which_op( lua_State* L, int idx_) | 209 | static CancelOp which_op( lua_State* L, int idx_) |
210 | { | 210 | { |
211 | if( lua_type( L, idx_) == LUA_TSTRING) | 211 | if( lua_type( L, idx_) == LUA_TSTRING) |
212 | { | 212 | { |
213 | CancelOp op = CO_Invalid; | 213 | CancelOp op = CO_Invalid; |
214 | char const* str = lua_tostring( L, idx_); | 214 | char const* str = lua_tostring( L, idx_); |
215 | if( strcmp( str, "soft") == 0) | 215 | if( strcmp( str, "soft") == 0) |
216 | { | 216 | { |
217 | op = CO_Soft; | 217 | op = CO_Soft; |
218 | } | 218 | } |
219 | else if( strcmp( str, "count") == 0) | 219 | else if( strcmp( str, "count") == 0) |
220 | { | 220 | { |
221 | op = CO_Count; | 221 | op = CO_Count; |
222 | } | 222 | } |
223 | else if( strcmp( str, "line") == 0) | 223 | else if( strcmp( str, "line") == 0) |
224 | { | 224 | { |
225 | op = CO_Line; | 225 | op = CO_Line; |
226 | } | 226 | } |
227 | else if( strcmp( str, "call") == 0) | 227 | else if( strcmp( str, "call") == 0) |
228 | { | 228 | { |
229 | op = CO_Call; | 229 | op = CO_Call; |
230 | } | 230 | } |
231 | else if( strcmp( str, "ret") == 0) | 231 | else if( strcmp( str, "ret") == 0) |
232 | { | 232 | { |
233 | op = CO_Ret; | 233 | op = CO_Ret; |
234 | } | 234 | } |
235 | else if( strcmp( str, "hard") == 0) | 235 | else if( strcmp( str, "hard") == 0) |
236 | { | 236 | { |
237 | op = CO_Hard; | 237 | op = CO_Hard; |
238 | } | 238 | } |
239 | lua_remove( L, idx_); // argument is processed, remove it | 239 | lua_remove( L, idx_); // argument is processed, remove it |
240 | if( op == CO_Invalid) | 240 | if( op == CO_Invalid) |
241 | { | 241 | { |
242 | luaL_error( L, "invalid hook option %s", str); | 242 | luaL_error( L, "invalid hook option %s", str); |
243 | } | 243 | } |
244 | return op; | 244 | return op; |
245 | } | 245 | } |
246 | return CO_Hard; | 246 | return CO_Hard; |
247 | } | 247 | } |
248 | // ################################################################################################ | 248 | // ################################################################################################ |
249 | 249 | ||
250 | // bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, force [, forcekill_timeout]]) | 250 | // bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, force [, forcekill_timeout]]) |
251 | LUAG_FUNC( thread_cancel) | 251 | LUAG_FUNC( thread_cancel) |
252 | { | 252 | { |
253 | Lane* s = lua_toLane( L, 1); | 253 | Lane* s = lua_toLane( L, 1); |
254 | double secs = 0.0; | 254 | double secs = 0.0; |
255 | CancelOp op = which_op( L, 2); // this removes the op string from the stack | 255 | CancelOp op = which_op( L, 2); // this removes the op string from the stack |
256 | 256 | ||
257 | if( op > 0) // hook is requested | 257 | if( op > 0) // hook is requested |
258 | { | 258 | { |
259 | int hook_count = (int) lua_tointeger( L, 2); | 259 | int hook_count = (int) lua_tointeger( L, 2); |
260 | lua_remove( L, 2); // argument is processed, remove it | 260 | lua_remove( L, 2); // argument is processed, remove it |
261 | if( hook_count < 1) | 261 | if( hook_count < 1) |
262 | { | 262 | { |
263 | return luaL_error( L, "hook count cannot be < 1"); | 263 | return luaL_error( L, "hook count cannot be < 1"); |
264 | } | 264 | } |
265 | lua_sethook( s->L, cancel_hook, op, hook_count); | 265 | lua_sethook( s->L, cancel_hook, op, hook_count); |
266 | } | 266 | } |
267 | 267 | ||
268 | if( lua_type( L, 2) == LUA_TNUMBER) | 268 | if( lua_type( L, 2) == LUA_TNUMBER) |
269 | { | 269 | { |
270 | secs = lua_tonumber( L, 2); | 270 | secs = lua_tonumber( L, 2); |
271 | lua_remove( L, 2); // argument is processed, remove it | 271 | lua_remove( L, 2); // argument is processed, remove it |
272 | if( secs < 0.0) | 272 | if( secs < 0.0) |
273 | { | 273 | { |
274 | return luaL_error( L, "cancel timeout cannot be < 0"); | 274 | return luaL_error( L, "cancel timeout cannot be < 0"); |
275 | } | 275 | } |
276 | } | 276 | } |
277 | 277 | ||
278 | { | 278 | { |
279 | bool_t force = lua_toboolean( L, 2); // FALSE if nothing there | 279 | bool_t force = lua_toboolean( L, 2); // FALSE if nothing there |
280 | double forcekill_timeout = luaL_optnumber( L, 3, 0.0); | 280 | double forcekill_timeout = luaL_optnumber( L, 3, 0.0); |
281 | 281 | ||
282 | switch( thread_cancel( L, s, op, secs, force, forcekill_timeout)) | 282 | switch( thread_cancel( L, s, op, secs, force, forcekill_timeout)) |
283 | { | 283 | { |
284 | case CR_Timeout: | 284 | case CR_Timeout: |
285 | lua_pushboolean( L, 0); | 285 | lua_pushboolean( L, 0); |
286 | lua_pushstring( L, "timeout"); | 286 | lua_pushstring( L, "timeout"); |
287 | return 2; | 287 | return 2; |
288 | 288 | ||
289 | case CR_Cancelled: | 289 | case CR_Cancelled: |
290 | lua_pushboolean( L, 1); | 290 | lua_pushboolean( L, 1); |
291 | push_thread_status( L, s); | 291 | push_thread_status( L, s); |
292 | return 2; | 292 | return 2; |
293 | 293 | ||
294 | case CR_Killed: | 294 | case CR_Killed: |
295 | lua_pushboolean( L, 1); | 295 | lua_pushboolean( L, 1); |
296 | push_thread_status( L, s); | 296 | push_thread_status( L, s); |
297 | return 2; | 297 | return 2; |
298 | } | 298 | } |
299 | } | 299 | } |
300 | // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value" | 300 | // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value" |
301 | return 0; | 301 | return 0; |
302 | } | 302 | } |