aboutsummaryrefslogtreecommitdiff
path: root/src/cancel.c
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-03-19 15:13:45 +0100
committerBenoit Germain <benoit.germain@ubisoft.com>2024-03-19 15:57:52 +0100
commit930bfb287de0a38746493463016d0e4cea153ac0 (patch)
tree2424f961d525e6b33cf4dda7c42cc2301e740a5f /src/cancel.c
parent37e9658f74f9421aaae5fe71f12eb2221f2d574a (diff)
downloadlanes-930bfb287de0a38746493463016d0e4cea153ac0.tar.gz
lanes-930bfb287de0a38746493463016d0e4cea153ac0.tar.bz2
lanes-930bfb287de0a38746493463016d0e4cea153ac0.zip
C++ migration: changed file extensions from .c to .cpp
Diffstat (limited to 'src/cancel.c')
-rw-r--r--src/cancel.c302
1 files changed, 0 insertions, 302 deletions
diff --git a/src/cancel.c b/src/cancel.c
deleted file mode 100644
index 0a5adb6..0000000
--- a/src/cancel.c
+++ /dev/null
@@ -1,302 +0,0 @@
1/*
2--
3-- CANCEL.C
4--
5-- Lane cancellation support
6--
7-- Author: Benoit Germain <bnt.germain@gmail.com>
8--
9--[[
10===============================================================================
11
12Copyright (C) 2011-2019 Benoit Germain <bnt.germain@gmail.com>
13
14Permission is hereby granted, free of charge, to any person obtaining a copy
15of this software and associated documentation files (the "Software"), to deal
16in the Software without restriction, including without limitation the rights
17to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18copies of the Software, and to permit persons to whom the Software is
19furnished to do so, subject to the following conditions:
20
21The above copyright notice and this permission notice shall be included in
22all copies or substantial portions of the Software.
23
24THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30THE SOFTWARE.
31
32===============================================================================
33]]--
34*/
35
36#include <assert.h>
37#include <string.h>
38
39#include "threading.h"
40#include "cancel.h"
41#include "tools.h"
42#include "lanes_private.h"
43
44// ################################################################################################
45// ################################################################################################
46
47/*
48* Check if the thread in question ('L') has been signalled for cancel.
49*
50* Called by cancellation hooks and/or pending Linda operations (because then
51* the check won't affect performance).
52*
53* Returns TRUE if any locks are to be exited, and 'cancel_error()' called,
54* to make execution of the lane end.
55*/
56static inline enum e_cancel_request cancel_test( lua_State* L)
57{
58 Lane* const s = get_lane_from_registry( L);
59 // 's' is NULL for the original main state (and no-one can cancel that)
60 return s ? s->cancel_request : CANCEL_NONE;
61}
62
63// ################################################################################################
64
65//---
66// bool = cancel_test()
67//
68// Available inside the global namespace of lanes
69// returns a boolean saying if a cancel request is pending
70//
71LUAG_FUNC( cancel_test)
72{
73 enum e_cancel_request test = cancel_test( L);
74 lua_pushboolean( L, test != CANCEL_NONE);
75 return 1;
76}
77
78// ################################################################################################
79// ################################################################################################
80
81static void cancel_hook( lua_State* L, lua_Debug* ar)
82{
83 (void)ar;
84 DEBUGSPEW_CODE( fprintf( stderr, "cancel_hook\n"));
85 if( cancel_test( L) != CANCEL_NONE)
86 {
87 lua_sethook( L, NULL, 0, 0);
88 cancel_error( L);
89 }
90}
91
92// ################################################################################################
93// ################################################################################################
94
95//---
96// = thread_cancel( lane_ud [,timeout_secs=0.0] [,force_kill_bool=false] )
97//
98// The originator thread asking us specifically to cancel the other thread.
99//
100// 'timeout': <0: wait forever, until the lane is finished
101// 0.0: just signal it to cancel, no time waited
102// >0: time to wait for the lane to detect cancellation
103//
104// 'force_kill': if true, and lane does not detect cancellation within timeout,
105// it is forcefully killed. Using this with 0.0 timeout means just kill
106// (unless the lane is already finished).
107//
108// Returns: true if the lane was already finished (DONE/ERROR_ST/CANCELLED) or if we
109// managed to cancel it.
110// false if the cancellation timed out, or a kill was needed.
111//
112
113// ################################################################################################
114
115static cancel_result thread_cancel_soft( Lane* s, double secs_, bool_t wake_lindas_)
116{
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
119 if( wake_lindas_) // wake the thread so that execution returns from any pending linda operation if desired
120 {
121 SIGNAL_T *waiting_on = s->waiting_on;
122 if( s->status == WAITING && waiting_on != NULL)
123 {
124 SIGNAL_ALL( waiting_on);
125 }
126 }
127
128 return THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout;
129}
130
131// ################################################################################################
132
133static cancel_result thread_cancel_hard( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_)
134{
135 cancel_result result;
136
137 s->cancel_request = CANCEL_HARD; // it's now signaled to stop
138 {
139 SIGNAL_T *waiting_on = s->waiting_on;
140 if( s->status == WAITING && waiting_on != NULL)
141 {
142 SIGNAL_ALL( waiting_on);
143 }
144 }
145
146 result = THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout;
147
148 if( (result == CR_Timeout) && force_)
149 {
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
152 // would be use of "cancellation cleanup handlers" that at least
153 // PThread seems to have).
154 //
155 THREAD_KILL( &s->thread);
156#if THREADAPI == THREADAPI_PTHREAD
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
159 result = THREAD_WAIT( &s->thread, waitkill_timeout_, &s->done_signal, &s->done_lock, &s->status);
160 if( result == CR_Timeout)
161 {
162 return luaL_error( L, "force-killed lane failed to terminate within %f second%s", waitkill_timeout_, waitkill_timeout_ > 1 ? "s" : "");
163 }
164#else
165 (void) waitkill_timeout_; // unused
166 (void) L; // unused
167#endif // THREADAPI == THREADAPI_PTHREAD
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
170 // because we need to know if we can lua_close() the Lua State or not.
171 result = CR_Killed;
172 }
173 return result;
174}
175
176// ################################################################################################
177
178cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_)
179{
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)
182 if( s->mstatus == KILLED)
183 {
184 return CR_Killed;
185 }
186
187 if( s->status >= DONE)
188 {
189 // say "ok" by default, including when lane is already done
190 return CR_Cancelled;
191 }
192
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...
195 if( op_ == CO_Soft)
196 {
197 return thread_cancel_soft( s, secs_, force_);
198 }
199
200 return thread_cancel_hard( L, s, secs_, force_, waitkill_timeout_);
201}
202
203// ################################################################################################
204// ################################################################################################
205
206// > 0: the mask
207// = 0: soft
208// < 0: hard
209static CancelOp which_op( lua_State* L, int idx_)
210{
211 if( lua_type( L, idx_) == LUA_TSTRING)
212 {
213 CancelOp op = CO_Invalid;
214 char const* str = lua_tostring( L, idx_);
215 if( strcmp( str, "soft") == 0)
216 {
217 op = CO_Soft;
218 }
219 else if( strcmp( str, "count") == 0)
220 {
221 op = CO_Count;
222 }
223 else if( strcmp( str, "line") == 0)
224 {
225 op = CO_Line;
226 }
227 else if( strcmp( str, "call") == 0)
228 {
229 op = CO_Call;
230 }
231 else if( strcmp( str, "ret") == 0)
232 {
233 op = CO_Ret;
234 }
235 else if( strcmp( str, "hard") == 0)
236 {
237 op = CO_Hard;
238 }
239 lua_remove( L, idx_); // argument is processed, remove it
240 if( op == CO_Invalid)
241 {
242 luaL_error( L, "invalid hook option %s", str);
243 }
244 return op;
245 }
246 return CO_Hard;
247}
248// ################################################################################################
249
250// bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, force [, forcekill_timeout]])
251LUAG_FUNC( thread_cancel)
252{
253 Lane* s = lua_toLane( L, 1);
254 double secs = 0.0;
255 CancelOp op = which_op( L, 2); // this removes the op string from the stack
256
257 if( op > 0) // hook is requested
258 {
259 int hook_count = (int) lua_tointeger( L, 2);
260 lua_remove( L, 2); // argument is processed, remove it
261 if( hook_count < 1)
262 {
263 return luaL_error( L, "hook count cannot be < 1");
264 }
265 lua_sethook( s->L, cancel_hook, op, hook_count);
266 }
267
268 if( lua_type( L, 2) == LUA_TNUMBER)
269 {
270 secs = lua_tonumber( L, 2);
271 lua_remove( L, 2); // argument is processed, remove it
272 if( secs < 0.0)
273 {
274 return luaL_error( L, "cancel timeout cannot be < 0");
275 }
276 }
277
278 {
279 bool_t force = lua_toboolean( L, 2); // FALSE if nothing there
280 double forcekill_timeout = luaL_optnumber( L, 3, 0.0);
281
282 switch( thread_cancel( L, s, op, secs, force, forcekill_timeout))
283 {
284 case CR_Timeout:
285 lua_pushboolean( L, 0);
286 lua_pushstring( L, "timeout");
287 return 2;
288
289 case CR_Cancelled:
290 lua_pushboolean( L, 1);
291 push_thread_status( L, s);
292 return 2;
293
294 case CR_Killed:
295 lua_pushboolean( L, 1);
296 push_thread_status( L, s);
297 return 2;
298 }
299 }
300 // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value"
301 return 0;
302}