diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-03-19 15:13:45 +0100 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-03-19 15:57:52 +0100 |
commit | 930bfb287de0a38746493463016d0e4cea153ac0 (patch) | |
tree | 2424f961d525e6b33cf4dda7c42cc2301e740a5f /src/cancel.c | |
parent | 37e9658f74f9421aaae5fe71f12eb2221f2d574a (diff) | |
download | lanes-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.c | 302 |
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 | |||
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 | #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 | */ | ||
56 | static 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 | // | ||
71 | LUAG_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 | |||
81 | static 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 | |||
115 | static 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 | |||
133 | static 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 | |||
178 | cancel_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 | ||
209 | static 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]]) | ||
251 | LUAG_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 | } | ||