aboutsummaryrefslogtreecommitdiff
path: root/src/cancel.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cancel.c')
-rw-r--r--src/cancel.c334
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*/
56static inline enum e_cancel_request cancel_test( lua_State* L) 56static 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//
71LUAG_FUNC( cancel_test) 71LUAG_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
81static void cancel_hook( lua_State* L, lua_Debug* ar) 81static 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
115static cancel_result thread_cancel_soft( Lane* s, double secs_, bool_t wake_lindas_) 115static 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
133static cancel_result thread_cancel_hard( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_) 133static 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
178cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_) 178cancel_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
209static CancelOp which_op( lua_State* L, int idx_) 209static 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]])
251LUAG_FUNC( thread_cancel) 251LUAG_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}