aboutsummaryrefslogtreecommitdiff
path: root/src/cancel.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cancel.c')
-rw-r--r--src/cancel.c252
1 files changed, 252 insertions, 0 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
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
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*/
55static 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//
70LUAG_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
80void 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
113static 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
131static 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
176cancel_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]])
205LUAG_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}