aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES4
-rw-r--r--Makefile4
-rw-r--r--docs/index.html47
-rw-r--r--lanes-3.11-1.rockspec66
-rw-r--r--lanes-3.14.0-0.rockspec (renamed from lanes-3.13.0-0.rockspec)6
-rw-r--r--src/Makefile2
-rw-r--r--src/cancel.c117
-rw-r--r--src/cancel.h15
-rw-r--r--src/lanes.c67
-rw-r--r--src/lanes.h2
-rw-r--r--src/lanes.lua17
-rw-r--r--src/lanes_private.h3
-rw-r--r--tests/basic.lua10
-rw-r--r--tests/cancel.lua162
-rw-r--r--tests/fifo.lua32
-rw-r--r--tests/timer.lua2
16 files changed, 305 insertions, 251 deletions
diff --git a/CHANGES b/CHANGES
index a6ed56b..2b381bd 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,9 @@
1CHANGES: 1CHANGES:
2 2
3CHANGE 146: BGe 26-Apr-19
4 * lane:cancel() rework (see doc).
5 * opt.cancelstep is gone, hook is installed by lane:cancel() if requested
6
3CHANGE 145: BGe 28-Nov-18 7CHANGE 145: BGe 28-Nov-18
4 * more code refacto 8 * more code refacto
5 * don't test __lanesignore for POD types (-> slightly faster when trasnfering lots of data) 9 * don't test __lanesignore for POD types (-> slightly faster when trasnfering lots of data)
diff --git a/Makefile b/Makefile
index fc44fac..7d2cf17 100644
--- a/Makefile
+++ b/Makefile
@@ -76,6 +76,7 @@ test:
76 $(MAKE) irayo_recursive 76 $(MAKE) irayo_recursive
77 $(MAKE) irayo_closure 77 $(MAKE) irayo_closure
78 $(MAKE) basic 78 $(MAKE) basic
79 $(MAKE) cancel
79 $(MAKE) fifo 80 $(MAKE) fifo
80 $(MAKE) keeper 81 $(MAKE) keeper
81 $(MAKE) timer 82 $(MAKE) timer
@@ -94,6 +95,9 @@ test:
94basic: tests/basic.lua $(_TARGET_SO) 95basic: tests/basic.lua $(_TARGET_SO)
95 $(_PREFIX) $(LUA) $< 96 $(_PREFIX) $(LUA) $<
96 97
98cancel: tests/cancel.lua $(_TARGET_SO)
99 $(_PREFIX) $(LUA) $<
100
97# 101#
98# This tries to show out a bug which happens in lane cleanup (multicore CPU's only) 102# This tries to show out a bug which happens in lane cleanup (multicore CPU's only)
99# 103#
diff --git a/docs/index.html b/docs/index.html
index db8e7a0..0b19223 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -64,13 +64,13 @@
64 <font size="-1"> 64 <font size="-1">
65 <p> 65 <p>
66 <br/> 66 <br/>
67 <i>Copyright &copy; 2007-18 Asko Kauppi, Benoit Germain. All rights reserved.</i> 67 <i>Copyright &copy; 2007-19 Asko Kauppi, Benoit Germain. All rights reserved.</i>
68 <br/> 68 <br/>
69 Lua Lanes is published under the same <a href="http://en.wikipedia.org/wiki/MIT_License">MIT license</a> as Lua 5.1, 5.2, and 5.3. 69 Lua Lanes is published under the same <a href="http://en.wikipedia.org/wiki/MIT_License">MIT license</a> as Lua 5.1, 5.2, 5.3 and 5.4.
70 </p> 70 </p>
71 71
72 <p> 72 <p>
73 This document was revised on 15-Nov-18, and applies to version <tt>3.13.0</tt>. 73 This document was revised on 26-Apr-19, and applies to version <tt>3.14.0</tt>.
74 </p> 74 </p>
75 </font> 75 </font>
76 </center> 76 </center>
@@ -605,16 +605,6 @@
605 </tr> 605 </tr>
606 <tr valign=top> 606 <tr valign=top>
607 <td> 607 <td>
608 <code>.cancelstep</code>
609 </td>
610 <td>integer >= 1/<tt>true</tt></td>
611 <td>
612 By default, lanes are only cancellable when they <u>enter</u> a pending <tt>:receive()</tt> or <tt>:send()</tt> call. With this option, one can set <a href="#cancelling">cancellation</a> check to occur every <tt>N</tt> Lua statements through the line hook facility. The value <tt>true</tt> uses a default value (100).
613 It is also possible to manually test for cancel requests with <tt>cancel_test()</tt>.
614 </td>
615 </tr>
616 <tr valign=top>
617 <td>
618 <code>.globals</code> 608 <code>.globals</code>
619 </td> 609 </td>
620 <td>table</td> 610 <td>table</td>
@@ -949,32 +939,37 @@
949<h2 id="cancelling">Cancelling</h2> 939<h2 id="cancelling">Cancelling</h2>
950 940
951<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> 941<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre>
952 bool[,reason] = lane_h:cancel( [positive_timeout_secs=0.0] [, force_kill_bool = false] [, forcekill_timeout=0.0]) 942 bool[,reason] = lane_h:cancel( "soft" [, timeout] [, wake_bool])
953 bool[,reason] = lane_h:cancel( negative_timeout_secs [, wake_bool = false]) 943 bool[,reason] = lane_h:cancel( "hard" [, timeout] [, force [, forcekill_timeout]])
944 bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, force [, forcekill_timeout]])
954</pre></td></tr></table> 945</pre></td></tr></table>
955 946
956<p> 947<p>
957 <tt>cancel()</tt> sends a cancellation request to the lane.<br/> 948 <tt>cancel()</tt> sends a cancellation request to the lane.<br/>
958 First argument is a timeout, that defaults to 0 if not specified. (Starting with version 3.6.3) signification of the following arguments differ depending on whether the timeout is negative or not. 949 First argument is a <tt>mode</tt> can be one of <tt>"hard"</tt>, <tt>"soft"</tt>, <tt>"count"</tt>, <tt>"line"</tt>, <tt>"call"</tt>, <tt>"ret"</tt>.
950 If <tt>mode</tt> is not specified, it defaults to <tt>"hard"</tt>.
959</p> 951</p>
960<p> 952<p>
961 If <tt>timeout_secs</tt> is negative (aka "soft cancel"), cancellation will only cause <tt>cancel_test()</tt> to return <tt>true</tt>, so that the lane can cleanup manually (the actual value is irrelevant). 953 If <tt>mode</tt> is <tt>"soft"</tt>, cancellation will only cause <tt>cancel_test()</tt> to return <tt>true</tt>, so that the lane can cleanup manually.<br/>
962 If <tt>wake_bool</tt> is <tt>true</tt>, the lane is also signalled so that execution returns from any pending linda operation. Linda operations detecting the cancellation request return <tt>lanes.cancel_error</tt>. 954 If <tt>wake_bool</tt> is <tt>true</tt>, the lane is also signalled so that execution returns from any pending linda operation. Linda operations detecting the cancellation request return <tt>lanes.cancel_error</tt>.
963</p> 955</p>
964<p> 956<p>
965 If <tt>timeout_secs</tt> is positive (aka "hard cancel"), waits for the request to be processed, or a timeout to occur. Linda operations detecting the cancellation request will raise a special cancellation error (meaning they won't return in that case). 957 If <tt>mode</tt> is <tt>"hard"</tt>, waits for the request to be processed, or a timeout to occur. Linda operations detecting the cancellation request will raise a special cancellation error (meaning they won't return in that case).<br/>
958 <tt>timeout</tt> defaults to 0 if not specified.
959</p>
960<p>
961 Other values of <tt>mode</tt> will asynchronously install the corresponding hook, then behave as <tt>"hard"</tt>.
962</p>
963<p>
966 If <tt>force_kill_bool</tt> is <tt>true</tt>, <tt>forcekill_timeout</tt> can be set to tell how long lanes will wait for the OS thread to terminate before raising an error. Windows threads always terminate immediately, but it might not always be the case with some pthread implementations. 964 If <tt>force_kill_bool</tt> is <tt>true</tt>, <tt>forcekill_timeout</tt> can be set to tell how long lanes will wait for the OS thread to terminate before raising an error. Windows threads always terminate immediately, but it might not always be the case with some pthread implementations.
967 Returns <tt>true</tt> if soft cancelling, or the lane was already done (in <tt>"done"</tt>, <tt>"error"</tt> or <tt>"cancelled"</tt> status), or the cancellation was fruitful within <tt>timeout_secs</tt> timeout period.
968</p> 965</p>
969
970<p> 966<p>
971 If the lane is still running after the timeout expired and <tt>force_kill</tt> is <tt>true</tt>, the OS thread running the lane is forcefully killed. This means no GC, probable OS resource leaks (thread stack, locks, DLL notifications), and should generally be the last resort. 967 Returns <tt>true, lane_h.status</tt> if lane was already done (in <tt>"done"</tt>, <tt>"error"</tt> or <tt>"cancelled"</tt> status), or the cancellation was fruitful within <tt>timeout_secs</tt> timeout period.<br/>
968 Returns <tt>false, "timeout"</tt> otherwise.
972</p> 969</p>
973
974<p> 970<p>
975 Starting with v3.3.0, if <tt>cancel()</tt> returns false, it also returns either <tt>"timeout"</tt> or <tt>"killed"</tt> as second return value. 971 If the lane is still running after the timeout expired and <tt>force_kill</tt> is <tt>true</tt>, the OS thread running the lane is forcefully killed. This means no GC, probable OS resource leaks (thread stack, locks, DLL notifications), and should generally be the last resort.
976</p> 972</p>
977
978<p> 973<p>
979 Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt>or <tt>send()</tt> call is awakened. 974 Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt>or <tt>send()</tt> call is awakened.
980 <br/> 975 <br/>
@@ -1732,7 +1727,9 @@ int luaD_new_clonable( lua_State* L)
1732<h2 id="changes">Change log</h2> 1727<h2 id="changes">Change log</h2>
1733 1728
1734<p> 1729<p>
1735 See CHANGES. 1730 v3.14.0: lane:cancel() rework: opt.cancelstep is gone, hook is installed by lane:cancel() if requested.
1731
1732 For older stuff see CHANGES.
1736</p> 1733</p>
1737 1734
1738<!-- footnotes +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> 1735<!-- footnotes +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
diff --git a/lanes-3.11-1.rockspec b/lanes-3.11-1.rockspec
deleted file mode 100644
index fde5878..0000000
--- a/lanes-3.11-1.rockspec
+++ /dev/null
@@ -1,66 +0,0 @@
1--
2-- Lanes rockspec
3--
4-- Ref:
5-- <http://luarocks.org/en/Rockspec_format>
6--
7
8package = "Lanes"
9
10version = "3.11-1"
11
12source= {
13 url= "git://github.com/LuaLanes/lanes.git",
14 branch= "v3.11"
15}
16
17description = {
18 summary= "Multithreading support for Lua",
19 detailed= [[
20 Lua Lanes is a portable, message passing multithreading library
21 providing the possibility to run multiple Lua states in parallel.
22 ]],
23 license= "MIT/X11",
24 homepage="https://github.com/LuaLanes/lanes",
25 maintainer="Benoit Germain <bnt.germain@gmail.com>"
26}
27
28-- Q: What is the difference of "windows" and "win32"? Seems there is none;
29-- so should we list either one or both?
30--
31supported_platforms= { "win32",
32 "macosx",
33 "linux",
34 "freebsd", -- TBD: not tested
35 "msys", -- TBD: not supported by LuaRocks 1.0 (or is it?)
36}
37
38dependencies= {
39 "lua >= 5.1", -- builds with either 5.1, 5.2 and 5.3
40}
41
42build = {
43 type = "builtin",
44 platforms =
45 {
46 linux =
47 {
48 modules =
49 {
50 ["lanes.core"] =
51 {
52 libraries = "pthread"
53 },
54 }
55 }
56 },
57 modules =
58 {
59 ["lanes.core"] =
60 {
61 sources = { "src/compat.c", "src/deep.c", "src/lanes.c", "src/keeper.c", "src/tools.c", "src/threading.c"},
62 incdirs = { "src"},
63 },
64 lanes = "src/lanes.lua"
65 }
66}
diff --git a/lanes-3.13.0-0.rockspec b/lanes-3.14.0-0.rockspec
index dec529e..8d56b80 100644
--- a/lanes-3.13.0-0.rockspec
+++ b/lanes-3.14.0-0.rockspec
@@ -7,11 +7,11 @@
7 7
8package = "Lanes" 8package = "Lanes"
9 9
10version = "3.13.0-0" 10version = "3.14.0-0"
11 11
12source= { 12source= {
13 url= "git://github.com/LuaLanes/lanes.git", 13 url= "git://github.com/LuaLanes/lanes.git",
14 branch= "v3.13.0" 14 branch= "v3.14.0"
15} 15}
16 16
17description = { 17description = {
@@ -58,7 +58,7 @@ build = {
58 { 58 {
59 ["lanes.core"] = 59 ["lanes.core"] =
60 { 60 {
61 sources = { "src/compat.c", "src/deep.c", "src/lanes.c", "src/linda.c", "src/keeper.c", "src/tools.c", "src/threading.c", "src/universe.c"}, 61 sources = { "src/cancel.c", "src/compat.c", "src/deep.c", "src/lanes.c", "src/linda.c", "src/keeper.c", "src/tools.c", "src/threading.c", "src/universe.c"},
62 incdirs = { "src"}, 62 incdirs = { "src"},
63 }, 63 },
64 lanes = "src/lanes.lua" 64 lanes = "src/lanes.lua"
diff --git a/src/Makefile b/src/Makefile
index aff06ba..9dc75a1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -7,7 +7,7 @@
7 7
8MODULE=lanes 8MODULE=lanes
9 9
10SRC=lanes.c compat.c threading.c tools.c linda.c deep.c keeper.c universe.c 10SRC=lanes.c cancel.c compat.c threading.c tools.c linda.c deep.c keeper.c universe.c
11 11
12OBJ=$(SRC:.c=.o) 12OBJ=$(SRC:.c=.o)
13 13
diff --git a/src/cancel.c b/src/cancel.c
index 11e100d..8ce00c8 100644
--- a/src/cancel.c
+++ b/src/cancel.c
@@ -77,12 +77,13 @@ LUAG_FUNC( cancel_test)
77// ################################################################################################ 77// ################################################################################################
78// ################################################################################################ 78// ################################################################################################
79 79
80void cancel_hook( lua_State* L, lua_Debug* ar) 80static void cancel_hook( lua_State* L, lua_Debug* ar)
81{ 81{
82 (void)ar; 82 (void)ar;
83 DEBUGSPEW_CODE( fprintf( stderr, "cancel_hook\n")); 83 DEBUGSPEW_CODE( fprintf( stderr, "cancel_hook\n"));
84 if( cancel_test( L) != CANCEL_NONE) 84 if( cancel_test( L) != CANCEL_NONE)
85 { 85 {
86 lua_sethook( L, NULL, 0, 0);
86 cancel_error( L); 87 cancel_error( L);
87 } 88 }
88} 89}
@@ -110,10 +111,10 @@ void cancel_hook( lua_State* L, lua_Debug* ar)
110 111
111// ################################################################################################ 112// ################################################################################################
112 113
113static cancel_result thread_cancel_soft( Lane* s, bool_t wake_lindas_) 114static cancel_result thread_cancel_soft( Lane* s, double secs_, bool_t wake_lindas_)
114{ 115{
115 s->cancel_request = CANCEL_SOFT; // it's now signaled to stop 116 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 // 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 if( wake_lindas_) // wake the thread so that execution returns from any pending linda operation if desired
118 { 119 {
119 SIGNAL_T *waiting_on = s->waiting_on; 120 SIGNAL_T *waiting_on = s->waiting_on;
@@ -122,8 +123,8 @@ static cancel_result thread_cancel_soft( Lane* s, bool_t wake_lindas_)
122 SIGNAL_ALL( waiting_on); 123 SIGNAL_ALL( waiting_on);
123 } 124 }
124 } 125 }
125 // say we succeeded though 126
126 return CR_Cancelled; 127 return THREAD_WAIT( &s->thread, secs_, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout;
127} 128}
128 129
129// ################################################################################################ 130// ################################################################################################
@@ -163,9 +164,9 @@ static cancel_result thread_cancel_hard( lua_State* L, Lane* s, double secs_, bo
163 (void) waitkill_timeout_; // unused 164 (void) waitkill_timeout_; // unused
164 (void) L; // unused 165 (void) L; // unused
165#endif // THREADAPI == THREADAPI_PTHREAD 166#endif // THREADAPI == THREADAPI_PTHREAD
166 s->mstatus = KILLED; // mark 'gc' to wait for it 167 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 // 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 // because we need to know if we can lua_close() the Lua State or not.
169 result = CR_Killed; 170 result = CR_Killed;
170 } 171 }
171 return result; 172 return result;
@@ -173,7 +174,7 @@ static cancel_result thread_cancel_hard( lua_State* L, Lane* s, double secs_, bo
173 174
174// ################################################################################################ 175// ################################################################################################
175 176
176cancel_result thread_cancel( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_) 177cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_)
177{ 178{
178 // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here 179 // 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 // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN)
@@ -190,9 +191,9 @@ cancel_result thread_cancel( lua_State* L, Lane* s, double secs_, bool_t force_,
190 191
191 // signal the linda the wake up the thread so that it can react to the cancel query 192 // 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 // let us hope we never land here with a pointer on a linda that has been destroyed...
193 if( secs_ < 0.0) 194 if( op_ == CO_Soft)
194 { 195 {
195 return thread_cancel_soft( s, force_); 196 return thread_cancel_soft( s, secs_, force_);
196 } 197 }
197 198
198 return thread_cancel_hard( L, s, secs_, force_, waitkill_timeout_); 199 return thread_cancel_hard( L, s, secs_, force_, waitkill_timeout_);
@@ -201,49 +202,97 @@ cancel_result thread_cancel( lua_State* L, Lane* s, double secs_, bool_t force_,
201// ################################################################################################ 202// ################################################################################################
202// ################################################################################################ 203// ################################################################################################
203 204
204// lane_h:cancel( [timeout] [, force [, forcekill_timeout]]) 205// > 0: the mask
206// = 0: soft
207// < 0: hard
208static CancelOp which_op( lua_State* L, int idx_)
209{
210 if( lua_type( L, idx_) == LUA_TSTRING)
211 {
212 CancelOp op = CO_Invalid;
213 char const* str = lua_tostring( L, idx_);
214 if( strcmp( str, "soft") == 0)
215 {
216 op = CO_Soft;
217 }
218 else if( strcmp( str, "count") == 0)
219 {
220 op = CO_Count;
221 }
222 else if( strcmp( str, "line") == 0)
223 {
224 op = CO_Line;
225 }
226 else if( strcmp( str, "call") == 0)
227 {
228 op = CO_Call;
229 }
230 else if( strcmp( str, "ret") == 0)
231 {
232 op = CO_Ret;
233 }
234 else if( strcmp( str, "hard") == 0)
235 {
236 op = CO_Hard;
237 }
238 lua_remove( L, idx_); // argument is processed, remove it
239 if( op == CO_Invalid)
240 {
241 luaL_error( L, "invalid hook option %s", str);
242 }
243 return op;
244 }
245 return CO_Hard;
246}
247// ################################################################################################
248
249// bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, force [, forcekill_timeout]])
205LUAG_FUNC( thread_cancel) 250LUAG_FUNC( thread_cancel)
206{ 251{
207 Lane* s = lua_toLane( L, 1); 252 Lane* s = lua_toLane( L, 1);
208 double secs = 0.0; 253 double secs = 0.0;
209 int force_i = 2; 254 CancelOp op = which_op( L, 2); // this removes the op string from the stack
210 int forcekill_timeout_i = 3;
211 255
212 if( lua_isnumber( L, 2)) 256 if( op > 0) // hook is requested
213 { 257 {
214 secs = lua_tonumber( L, 2); 258 int hook_count = (int) lua_tointeger( L, 2);
215 if( secs < 0.0 && lua_gettop( L) > 3) 259 lua_remove( L, 2); // argument is processed, remove it
260 if( hook_count < 1)
216 { 261 {
217 return luaL_error( L, "can't force_kill a soft cancel"); 262 return luaL_error( L, "hook count cannot be < 1");
218 } 263 }
219 // negative timeout and force flag means we want to wake linda-waiting threads 264 lua_sethook( s->L, cancel_hook, op, hook_count);
220 ++ force_i;
221 ++ forcekill_timeout_i;
222 } 265 }
223 else if( lua_isnil( L, 2)) 266
267 if( lua_type( L, 2) == LUA_TNUMBER)
224 { 268 {
225 ++ force_i; 269 secs = lua_tonumber( L, 2);
226 ++ forcekill_timeout_i; 270 lua_remove( L, 2); // argument is processed, remove it
271 if( secs < 0.0)
272 {
273 return luaL_error( L, "cancel timeout cannot be < 0");
274 }
227 } 275 }
228 276
229 { 277 {
230 bool_t force = lua_toboolean( L, force_i); // FALSE if nothing there 278 bool_t force = lua_toboolean( L, 2); // FALSE if nothing there
231 double forcekill_timeout = luaL_optnumber( L, forcekill_timeout_i, 0.0); 279 double forcekill_timeout = luaL_optnumber( L, 3, 0.0);
232 280
233 switch( thread_cancel( L, s, secs, force, forcekill_timeout)) 281 switch( thread_cancel( L, s, op, secs, force, forcekill_timeout))
234 { 282 {
235 case CR_Timeout: 283 case CR_Timeout:
236 lua_pushboolean( L, 0); 284 lua_pushboolean( L, 0);
237 lua_pushstring( L, "timeout"); 285 lua_pushstring( L, "timeout");
238 return 2; 286 return 2;
239 287
240 case CR_Cancelled: 288 case CR_Cancelled:
241 lua_pushboolean( L, 1); 289 lua_pushboolean( L, 1);
242 return 1; 290 push_thread_status( L, s);
291 return 2;
243 292
244 case CR_Killed: 293 case CR_Killed:
245 lua_pushboolean( L, 0); 294 lua_pushboolean( L, 1);
246 lua_pushstring( L, "killed"); 295 push_thread_status( L, s);
247 return 2; 296 return 2;
248 } 297 }
249 } 298 }
diff --git a/src/cancel.h b/src/cancel.h
index 3112809..e7656ac 100644
--- a/src/cancel.h
+++ b/src/cancel.h
@@ -29,15 +29,24 @@ typedef enum
29 CR_Killed 29 CR_Killed
30} cancel_result; 30} cancel_result;
31 31
32typedef enum
33{
34 CO_Invalid = -2,
35 CO_Hard = -1,
36 CO_Soft = 0,
37 CO_Count = LUA_MASKCOUNT,
38 CO_Line = LUA_MASKLINE,
39 CO_Call = LUA_MASKCALL,
40 CO_Ret = LUA_MASKRET,
41} CancelOp;
42
32// crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/ 43// crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/
33static DECLARE_CONST_UNIQUE_KEY(CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel 44static DECLARE_CONST_UNIQUE_KEY(CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel
34 45
35// crc64/we of string "CANCEL_TEST_KEY" generated at http://www.nitrxgen.net/hashgen/ 46// crc64/we of string "CANCEL_TEST_KEY" generated at http://www.nitrxgen.net/hashgen/
36static DECLARE_CONST_UNIQUE_KEY(CANCEL_TEST_KEY, 0xe66f5960c57d133a); // used as registry key 47static DECLARE_CONST_UNIQUE_KEY(CANCEL_TEST_KEY, 0xe66f5960c57d133a); // used as registry key
37 48
38void cancel_hook( lua_State* L, lua_Debug* ar); 49cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_);
39
40cancel_result thread_cancel( lua_State* L, Lane* s, double secs_, bool_t force_, double waitkill_timeout_);
41 50
42static inline int cancel_error( lua_State* L) 51static inline int cancel_error( lua_State* L)
43{ 52{
diff --git a/src/lanes.c b/src/lanes.c
index cbac6da..8f159a9 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -452,7 +452,7 @@ static int selfdestruct_gc( lua_State* L)
452 while( s != SELFDESTRUCT_END) 452 while( s != SELFDESTRUCT_END)
453 { 453 {
454 // attempt a regular unforced hard cancel with a small timeout 454 // attempt a regular unforced hard cancel with a small timeout
455 bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( L, s, 0.0001, FALSE, 0.0); 455 bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( L, s, CO_Hard, 0.0001, FALSE, 0.0);
456 // if we failed, and we know the thread is waiting on a linda 456 // if we failed, and we know the thread is waiting on a linda
457 if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL) 457 if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL)
458 { 458 {
@@ -1027,7 +1027,6 @@ static DECLARE_CONST_UNIQUE_KEY( GCCB_KEY, 0xcfb1f046ef074e88);
1027//--- 1027//---
1028// lane_ud = lane_new( function 1028// lane_ud = lane_new( function
1029// , [libs_str] 1029// , [libs_str]
1030// , [cancelstep_uint=0]
1031// , [priority_int=0] 1030// , [priority_int=0]
1032// , [globals_tbl] 1031// , [globals_tbl]
1033// , [package_tbl] 1032// , [package_tbl]
@@ -1044,14 +1043,13 @@ LUAG_FUNC( lane_new)
1044 Lane** ud; 1043 Lane** ud;
1045 1044
1046 char const* libs_str = lua_tostring( L, 2); 1045 char const* libs_str = lua_tostring( L, 2);
1047 uint_t cancelstep_idx = luaG_optunsigned( L, 3, 0); 1046 int const priority = (int) luaL_optinteger( L, 3, 0);
1048 int const priority = (int) luaL_optinteger( L, 4, 0); 1047 uint_t globals_idx = lua_isnoneornil( L, 4) ? 0 : 4;
1049 uint_t globals_idx = lua_isnoneornil( L, 5) ? 0 : 5; 1048 uint_t package_idx = lua_isnoneornil( L, 5) ? 0 : 5;
1050 uint_t package_idx = lua_isnoneornil( L, 6) ? 0 : 6; 1049 uint_t required_idx = lua_isnoneornil( L, 6) ? 0 : 6;
1051 uint_t required_idx = lua_isnoneornil( L, 7) ? 0 : 7; 1050 uint_t gc_cb_idx = lua_isnoneornil( L, 7) ? 0 : 7;
1052 uint_t gc_cb_idx = lua_isnoneornil( L, 8) ? 0 : 8; 1051
1053 1052#define FIXED_ARGS 7
1054#define FIXED_ARGS 8
1055 int const nargs = lua_gettop(L) - FIXED_ARGS; 1053 int const nargs = lua_gettop(L) - FIXED_ARGS;
1056 Universe* U = universe_get( L); 1054 Universe* U = universe_get( L);
1057 ASSERT_L( nargs >= 0); 1055 ASSERT_L( nargs >= 0);
@@ -1074,7 +1072,7 @@ LUAG_FUNC( lane_new)
1074 STACK_GROW( L2, nargs + 3); // 1072 STACK_GROW( L2, nargs + 3); //
1075 STACK_CHECK( L2, 0); 1073 STACK_CHECK( L2, 0);
1076 1074
1077 STACK_GROW( L, 3); // func libs cancelstep priority globals package required gc_cb [... args ...] 1075 STACK_GROW( L, 3); // func libs priority globals package required gc_cb [... args ...]
1078 STACK_CHECK( L, 0); 1076 STACK_CHECK( L, 0);
1079 1077
1080 // give a default "Lua" name to the thread to see VM name in Decoda debugger 1078 // give a default "Lua" name to the thread to see VM name in Decoda debugger
@@ -1103,8 +1101,8 @@ LUAG_FUNC( lane_new)
1103 return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required_idx)); 1101 return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required_idx));
1104 } 1102 }
1105 1103
1106 lua_pushnil( L); // func libs cancelstep priority globals package required gc_cb [... args ...] nil 1104 lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil
1107 while( lua_next( L, required_idx) != 0) // func libs cancelstep priority globals package required gc_cb [... args ...] n "modname" 1105 while( lua_next( L, required_idx) != 0) // func libs priority globals package required gc_cb [... args ...] n "modname"
1108 { 1106 {
1109 if( lua_type( L, -1) != LUA_TSTRING || lua_type( L, -2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) 1107 if( lua_type( L, -1) != LUA_TSTRING || lua_type( L, -2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired)
1110 { 1108 {
@@ -1130,7 +1128,7 @@ LUAG_FUNC( lane_new)
1130 if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode 1128 if( lua_pcall( L2, 1, 1, 0) != LUA_OK) // ret/errcode
1131 { 1129 {
1132 // propagate error to main state if any 1130 // propagate error to main state if any
1133 luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // func libs cancelstep priority globals package required gc_cb [... args ...] n "modname" error 1131 luaG_inter_move( U, L2, L, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] n "modname" error
1134 return lua_error( L); 1132 return lua_error( L);
1135 } 1133 }
1136 // after requiring the module, register the functions it exported in our name<->function database 1134 // after requiring the module, register the functions it exported in our name<->function database
@@ -1138,9 +1136,9 @@ LUAG_FUNC( lane_new)
1138 lua_pop( L2, 1); // 1136 lua_pop( L2, 1); //
1139 } 1137 }
1140 } 1138 }
1141 lua_pop( L, 1); // func libs cancelstep priority globals package required gc_cb [... args ...] n 1139 lua_pop( L, 1); // func libs priority globals package required gc_cb [... args ...] n
1142 ++ nbRequired; 1140 ++ nbRequired;
1143 } // func libs cancelstep priority globals package required gc_cb [... args ...] 1141 } // func libs priority globals package required gc_cb [... args ...]
1144 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1142 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1145 } 1143 }
1146 STACK_MID( L, 0); 1144 STACK_MID( L, 0);
@@ -1158,16 +1156,16 @@ LUAG_FUNC( lane_new)
1158 } 1156 }
1159 1157
1160 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1158 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1161 lua_pushnil( L); // func libs cancelstep priority globals package required gc_cb [... args ...] nil 1159 lua_pushnil( L); // func libs priority globals package required gc_cb [... args ...] nil
1162 // Lua 5.2 wants us to push the globals table on the stack 1160 // Lua 5.2 wants us to push the globals table on the stack
1163 lua_pushglobaltable( L2); // _G 1161 lua_pushglobaltable( L2); // _G
1164 while( lua_next( L, globals_idx)) // func libs cancelstep priority globals package required gc_cb [... args ...] k v 1162 while( lua_next( L, globals_idx)) // func libs priority globals package required gc_cb [... args ...] k v
1165 { 1163 {
1166 luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // _G k v 1164 luaG_inter_copy( U, L, L2, 2, eLM_LaneBody); // _G k v
1167 // assign it in L2's globals table 1165 // assign it in L2's globals table
1168 lua_rawset( L2, -3); // _G 1166 lua_rawset( L2, -3); // _G
1169 lua_pop( L, 1); // func libs cancelstep priority globals package required gc_cb [... args ...] k 1167 lua_pop( L, 1); // func libs priority globals package required gc_cb [... args ...] k
1170 } // func libs cancelstep priority globals package required gc_cb [... args ...] 1168 } // func libs priority globals package required gc_cb [... args ...]
1171 lua_pop( L2, 1); // 1169 lua_pop( L2, 1); //
1172 1170
1173 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1171 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
@@ -1181,8 +1179,8 @@ LUAG_FUNC( lane_new)
1181 int res; 1179 int res;
1182 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END)); 1180 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END));
1183 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1181 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1184 lua_pushvalue( L, 1); // func libs cancelstep priority globals package required gc_cb [... args ...] func 1182 lua_pushvalue( L, 1); // func libs priority globals package required gc_cb [... args ...] func
1185 res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs cancelstep priority globals package required gc_cb [... args ...] // func 1183 res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func
1186 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1184 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1187 if( res != 0) 1185 if( res != 0)
1188 { 1186 {
@@ -1207,7 +1205,7 @@ LUAG_FUNC( lane_new)
1207 int res; 1205 int res;
1208 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END)); 1206 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END));
1209 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1207 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1210 res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs cancelstep priority globals package required gc_cb // func [... args ...] 1208 res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...]
1211 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1209 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1212 if( res != 0) 1210 if( res != 0)
1213 { 1211 {
@@ -1222,7 +1220,7 @@ LUAG_FUNC( lane_new)
1222 // 's' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) 1220 // 's' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread)
1223 // 1221 //
1224 // a Lane full userdata needs a single uservalue 1222 // a Lane full userdata needs a single uservalue
1225 ud = lua_newuserdatauv( L, sizeof( Lane*), 1); // func libs cancelstep priority globals package required gc_cb lane 1223 ud = lua_newuserdatauv( L, sizeof( Lane*), 1); // func libs priority globals package required gc_cb lane
1226 s = *ud = (Lane*) malloc( sizeof( Lane)); 1224 s = *ud = (Lane*) malloc( sizeof( Lane));
1227 if( s == NULL) 1225 if( s == NULL)
1228 { 1226 {
@@ -1252,8 +1250,8 @@ LUAG_FUNC( lane_new)
1252 1250
1253 // Set metatable for the userdata 1251 // Set metatable for the userdata
1254 // 1252 //
1255 lua_pushvalue( L, lua_upvalueindex( 1)); // func libs cancelstep priority globals package required gc_cb lane mt 1253 lua_pushvalue( L, lua_upvalueindex( 1)); // func libs priority globals package required gc_cb lane mt
1256 lua_setmetatable( L, -2); // func libs cancelstep priority globals package required gc_cb lane 1254 lua_setmetatable( L, -2); // func libs priority globals package required gc_cb lane
1257 STACK_MID( L, 1); 1255 STACK_MID( L, 1);
1258 1256
1259 // Create uservalue for the userdata 1257 // Create uservalue for the userdata
@@ -1263,21 +1261,16 @@ LUAG_FUNC( lane_new)
1263 // Store the gc_cb callback in the uservalue 1261 // Store the gc_cb callback in the uservalue
1264 if( gc_cb_idx > 0) 1262 if( gc_cb_idx > 0)
1265 { 1263 {
1266 push_unique_key( L, GCCB_KEY); // func libs cancelstep priority globals package required gc_cb lane uv k 1264 push_unique_key( L, GCCB_KEY); // func libs priority globals package required gc_cb lane uv k
1267 lua_pushvalue( L, gc_cb_idx); // func libs cancelstep priority globals package required gc_cb lane uv k gc_cb 1265 lua_pushvalue( L, gc_cb_idx); // func libs priority globals package required gc_cb lane uv k gc_cb
1268 lua_rawset( L, -3); // func libs cancelstep priority globals package required gc_cb lane uv 1266 lua_rawset( L, -3); // func libs priority globals package required gc_cb lane uv
1269 } 1267 }
1270 1268
1271 lua_setiuservalue( L, -2, 1); // func libs cancelstep priority globals package required gc_cb lane 1269 lua_setiuservalue( L, -2, 1); // func libs priority globals package required gc_cb lane
1272 1270
1273 // Store 's' in the lane's registry, for 'cancel_test()' (even if 'cs'==0 we still do cancel tests at pending send/receive). 1271 // Store 's' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive).
1274 REGISTRY_SET( L2, CANCEL_TEST_KEY, lua_pushlightuserdata( L2, s)); // func [... args ...] 1272 REGISTRY_SET( L2, CANCEL_TEST_KEY, lua_pushlightuserdata( L2, s)); // func [... args ...]
1275 1273
1276 if( cancelstep_idx)
1277 {
1278 lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cancelstep_idx);
1279 }
1280
1281 STACK_END( L, 1); 1274 STACK_END( L, 1);
1282 STACK_END( L2, 1 + nargs); 1275 STACK_END( L2, 1 + nargs);
1283 1276
@@ -1402,7 +1395,7 @@ static char const * thread_status_string( Lane* s)
1402 return str; 1395 return str;
1403} 1396}
1404 1397
1405static int push_thread_status( lua_State* L, Lane* s) 1398int push_thread_status( lua_State* L, Lane* s)
1406{ 1399{
1407 char const* const str = thread_status_string( s); 1400 char const* const str = thread_status_string( s);
1408 ASSERT_L( str); 1401 ASSERT_L( str);
diff --git a/src/lanes.h b/src/lanes.h
index de60d6d..da0dd26 100644
--- a/src/lanes.h
+++ b/src/lanes.h
@@ -11,7 +11,7 @@
11#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 11#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
12 12
13#define LANES_VERSION_MAJOR 3 13#define LANES_VERSION_MAJOR 3
14#define LANES_VERSION_MINOR 13 14#define LANES_VERSION_MINOR 14
15#define LANES_VERSION_PATCH 0 15#define LANES_VERSION_PATCH 0
16 16
17#define LANES_MIN_VERSION_REQUIRED(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR>MAJOR) || (LANES_VERSION_MAJOR==MAJOR && (LANES_VERSION_MINOR>MINOR || (LANES_VERSION_MINOR==MINOR && LANES_VERSION_PATCH>=PATCH)))) 17#define LANES_MIN_VERSION_REQUIRED(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR>MAJOR) || (LANES_VERSION_MAJOR==MAJOR && (LANES_VERSION_MINOR>MINOR || (LANES_VERSION_MINOR==MINOR && LANES_VERSION_PATCH>=PATCH))))
diff --git a/src/lanes.lua b/src/lanes.lua
index ba9da81..4d6deac 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -146,7 +146,7 @@ lanes.configure = function( settings_)
146 author= "Asko Kauppi <akauppi@gmail.com>, Benoit Germain <bnt.germain@gmail.com>", 146 author= "Asko Kauppi <akauppi@gmail.com>, Benoit Germain <bnt.germain@gmail.com>",
147 description= "Running multiple Lua states in parallel", 147 description= "Running multiple Lua states in parallel",
148 license= "MIT/X11", 148 license= "MIT/X11",
149 copyright= "Copyright (c) 2007-10, Asko Kauppi; (c) 2011-18, Benoit Germain", 149 copyright= "Copyright (c) 2007-10, Asko Kauppi; (c) 2011-19, Benoit Germain",
150 version = assert( core.version) 150 version = assert( core.version)
151 } 151 }
152 152
@@ -198,13 +198,6 @@ lanes.configure = function( settings_)
198 -- 198 --
199 -- 'opt': .priority: int (-3..+3) smaller is lower priority (0 = default) 199 -- 'opt': .priority: int (-3..+3) smaller is lower priority (0 = default)
200 -- 200 --
201 -- .cancelstep: bool | uint
202 -- false: cancellation check only at pending Linda operations
203 -- (send/receive) so no runtime performance penalty (default)
204 -- true: adequate cancellation check (same as 100)
205 -- >0: cancellation check every x Lua lines (small number= faster
206 -- reaction but more performance overhead)
207 --
208 -- .globals: table of globals to set for a new thread (passed by value) 201 -- .globals: table of globals to set for a new thread (passed by value)
209 -- 202 --
210 -- .required: table of packages to require 203 -- .required: table of packages to require
@@ -246,10 +239,6 @@ lanes.configure = function( settings_)
246 local tv = type( v_) 239 local tv = type( v_)
247 return (tv == "number") and v_ or raise_option_error( "priority", tv, v_) 240 return (tv == "number") and v_ or raise_option_error( "priority", tv, v_)
248 end, 241 end,
249 cancelstep = function( v_)
250 local tv = type( v_)
251 return (tv == "number") and v_ or (v_ == true) and 100 or (v_ == false) and 0 or raise_option_error( "cancelstep", tv, v_)
252 end,
253 globals = function( v_) 242 globals = function( v_)
254 local tv = type( v_) 243 local tv = type( v_)
255 return (tv == "table") and v_ or raise_option_error( "globals", tv, v_) 244 return (tv == "table") and v_ or raise_option_error( "globals", tv, v_)
@@ -334,10 +323,10 @@ lanes.configure = function( settings_)
334 end 323 end
335 end 324 end
336 325
337 local cancelstep, priority, globals, package, required, gc_cb = opt.cancelstep, opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb 326 local priority, globals, package, required, gc_cb = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb
338 return function( ...) 327 return function( ...)
339 -- must pass functions args last else they will be truncated to the first one 328 -- must pass functions args last else they will be truncated to the first one
340 return core_lane_new( func, libs, cancelstep, priority, globals, package, required, gc_cb, ...) 329 return core_lane_new( func, libs, priority, globals, package, required, gc_cb, ...)
341 end 330 end
342 end -- gen() 331 end -- gen()
343 332
diff --git a/src/lanes_private.h b/src/lanes_private.h
index ac05129..1a15969 100644
--- a/src/lanes_private.h
+++ b/src/lanes_private.h
@@ -89,4 +89,7 @@ static inline Lane* get_lane_from_registry( lua_State* L)
89 return s; 89 return s;
90} 90}
91 91
92int push_thread_status( lua_State* L, Lane* s);
93
94
92#endif // __lanes_private_h__ \ No newline at end of file 95#endif // __lanes_private_h__ \ No newline at end of file
diff --git a/tests/basic.lua b/tests/basic.lua
index 020fe78..6fcfd53 100644
--- a/tests/basic.lua
+++ b/tests/basic.lua
@@ -114,7 +114,7 @@ collectgarbage()
114 114
115PRINT( "\n\n", "---=== Tasking (cancelling) ===---", "\n\n") 115PRINT( "\n\n", "---=== Tasking (cancelling) ===---", "\n\n")
116 116
117local task_launch2= lanes_gen( "", { cancelstep=100, globals={hey=true}, gc_cb = gc_cb}, task ) 117local task_launch2= lanes_gen( "", { globals={hey=true}, gc_cb = gc_cb}, task )
118 118
119local N=999999999 119local N=999999999
120local lane9= task_launch2(1,N,1) -- huuuuuuge... 120local lane9= task_launch2(1,N,1) -- huuuuuuge...
@@ -138,7 +138,7 @@ if st=="done" then
138end 138end
139assert( st=="running" ) 139assert( st=="running" )
140 140
141lane9:cancel() 141lane9:cancel( "count", 100) -- 0 timeout, 100 instructions count hook
142 142
143local t0= os.time() 143local t0= os.time()
144while os.time()-t0 < 5 do 144while os.time()-t0 < 5 do
@@ -166,7 +166,7 @@ end
166local wait_send_lane = lanes.gen( "*", wait_send)() 166local wait_send_lane = lanes.gen( "*", wait_send)()
167repeat until wait_send_lane.status == "waiting" 167repeat until wait_send_lane.status == "waiting"
168print "wait_send_lane is waiting" 168print "wait_send_lane is waiting"
169wait_send_lane:cancel() 169wait_send_lane:cancel() -- hard cancel, 0 timeout
170repeat until wait_send_lane.status == "cancelled" 170repeat until wait_send_lane.status == "cancelled"
171print "wait_send_lane is cancelled" 171print "wait_send_lane is cancelled"
172--################################################]] 172--################################################]]
@@ -179,7 +179,7 @@ end
179local wait_receive_lane = lanes.gen( "*", wait_receive)() 179local wait_receive_lane = lanes.gen( "*", wait_receive)()
180repeat until wait_receive_lane.status == "waiting" 180repeat until wait_receive_lane.status == "waiting"
181print "wait_receive_lane is waiting" 181print "wait_receive_lane is waiting"
182wait_receive_lane:cancel() 182wait_receive_lane:cancel() -- hard cancel, 0 timeout
183repeat until wait_receive_lane.status == "cancelled" 183repeat until wait_receive_lane.status == "cancelled"
184print "wait_receive_lane is cancelled" 184print "wait_receive_lane is cancelled"
185--################################################]] 185--################################################]]
@@ -192,7 +192,7 @@ end
192local wait_receive_batched_lane = lanes.gen( "*", wait_receive_batched)() 192local wait_receive_batched_lane = lanes.gen( "*", wait_receive_batched)()
193repeat until wait_receive_batched_lane.status == "waiting" 193repeat until wait_receive_batched_lane.status == "waiting"
194print "wait_receive_batched_lane is waiting" 194print "wait_receive_batched_lane is waiting"
195wait_receive_batched_lane:cancel() 195wait_receive_batched_lane:cancel() -- hard cancel, 0 timeout
196repeat until wait_receive_batched_lane.status == "cancelled" 196repeat until wait_receive_batched_lane.status == "cancelled"
197print "wait_receive_batched_lane is cancelled" 197print "wait_receive_batched_lane is cancelled"
198--################################################]] 198--################################################]]
diff --git a/tests/cancel.lua b/tests/cancel.lua
index 6429487..0d9d143 100644
--- a/tests/cancel.lua
+++ b/tests/cancel.lua
@@ -27,9 +27,29 @@ linda:set( "lock")
27linda:limit( "atomic", -1) 27linda:limit( "atomic", -1)
28linda:set( "atomic") 28linda:set( "atomic")
29 29
30-- a numeric value to read
31linda:set( "val", 33.0)
32
33print "test OK"
30--#################################################################### 34--####################################################################
31 35
32local laneBody = function( timeout_) 36local waitCancellation = function( h, expected_status)
37 local l = lanes.linda()
38 if expected_status ~= "running" then
39 repeat
40 -- print( "lane status:", h.status)
41 l:receive( 0.1, "yeah") -- wait a bit
42 until h.status ~= "running"
43 end
44 print( "lane status:", h.status)
45 assert( h.status == expected_status, h.status .. " ~= " .. expected_status)
46 print "test OK"
47end
48
49local laneBody = function( mode_, payload_)
50 local name = "laneBody("..tostring(mode_)..","..tostring(payload_)..")"
51 set_debug_threadname( name)
52
33 set_finalizer( function( err, stk) 53 set_finalizer( function( err, stk)
34 if err == lanes.cancel_error then 54 if err == lanes.cancel_error then
35 -- note that we don't get the cancel_error when running wrapped inside a protected call if it doesn't rethrow it 55 -- note that we don't get the cancel_error when running wrapped inside a protected call if it doesn't rethrow it
@@ -41,14 +61,39 @@ local laneBody = function( timeout_)
41 end 61 end
42 end) 62 end)
43 63
44 print( " entering lane with " .. tostring( timeout_) .. " timeout") 64 print( " entering " , name)
45 repeat 65 repeat
46 -- block-wait to be hard-cancelled 66 if mode_ == "receive" then
47 print " lane calling receive()" 67 -- linda mode
48 local key, val = linda:receive( timeout_, "boob") 68 io.stdout:write( " lane calling receive() ... ")
49 print( " receive() -> ", lanes.cancel_error == key and "cancel_error" or tostring( key), tostring( val)) 69 local key, val = linda:receive( payload_, "boob")
70 print( lanes.cancel_error == key and "cancel_error" or tostring( key), tostring( val))
71 if key == lanes.cancel_error then
72 break -- gracefully abort loop
73 end
74 elseif mode_ == "get" then
75 -- busy wait mode getting data from the linda
76 io.stdout:write( " lane busy waiting ... ")
77 for i = 1, payload_ do
78 -- force a non-jitable call
79 local a = linda:get( "val")
80 a = a * 2
81 end
82 print( "again?")
83 elseif mode_ == "busy" then
84 -- busy wait mode in pure Lua code
85 io.stdout:write( " lane busy waiting ... ")
86 local a = linda:get( "val")
87 for i = 1, payload_ do
88 a = a * 2
89 a = math.sin( a) * math.sin( a) + math.cos( a) * math.cos( a) -- aka 1
90 end
91 print( "again?")
92 else
93 error "no mode: raise an error"
94 end
50 until cancel_test() -- soft cancel self test 95 until cancel_test() -- soft cancel self test
51 print " shutting down after breaking out of loop" 96 print " lane shutting down after breaking out of loop"
52end 97end
53 98
54local protectedBody = function( ...) 99local protectedBody = function( ...)
@@ -61,7 +106,8 @@ local protectedBody = function( ...)
61 -- Lua 5.1 doesn't pass additional xpcall arguments to the called function 106 -- Lua 5.1 doesn't pass additional xpcall arguments to the called function
62 -- therefore we need to create a closure that has no arguments but pulls everything from its upvalue 107 -- therefore we need to create a closure that has no arguments but pulls everything from its upvalue
63 local params = {...} 108 local params = {...}
64 local paramLessClosure = function() laneBody(table.unpack( params)) end 109 local unpack = table.unpack or unpack -- unpack for 5.1, table.unpack for 5.2+
110 local paramLessClosure = function() laneBody(unpack( params)) end
65 local status, message = xpcall( paramLessClosure, errorHandler) 111 local status, message = xpcall( paramLessClosure, errorHandler)
66 if status == false then 112 if status == false then
67 print( " error handler rethrowing '" .. (ce == message and "cancel_error"or tostring( message)) .. "'") 113 print( " error handler rethrowing '" .. (ce == message and "cancel_error"or tostring( message)) .. "'")
@@ -71,66 +117,92 @@ local protectedBody = function( ...)
71end 117end
72 118
73--#################################################################### 119--####################################################################
120--####################################################################
121
122print "\n\n####################################################################\nbegin linda cancel test\n"
123h = lanes.gen( "*", laneBody)( "receive", nil) -- start an infinite wait on the linda
124
125print "wait 1s"
126linda:receive( 1, "yeah")
127
128-- linda cancel: linda:receive() returns cancel_error immediately
129linda:cancel( "both")
130
131-- wait until cancellation is effective.
132waitCancellation( h, "done")
133
134-- reset the linda so that the other tests work
135linda:cancel( "none")
74 136
75print "####################################################################\nbegin soft cancel test\n" 137print "\n\n####################################################################\nbegin soft cancel test\n"
76h = lanes.gen("*", protectedBody)( 0.666) 138h = lanes.gen( "*", protectedBody)( "receive") -- start an infinite wait on the linda
77print "wait 3s"
78linda:receive( 3, "yeah")
79 139
80-- soft cancel 140print "wait 1s"
81print "soft cancel with awakening" 141linda:receive( 1, "yeah")
82h:cancel( -1, true)
83 142
84-- wait 10s: the lane will interrupt its loop and print the exit message 143-- soft cancel, no awakening of waiting linda operations, should timeout
144local a, b = h:cancel( "soft", 1, false)
145-- cancellation should fail as the lane is still waiting on its linda
146assert( a == false and b == "timeout")
147waitCancellation( h, "waiting")
148
149-- soft cancel, this time awakens waiting linda operations, which returns cancel_error immediately, no timeout.
150h:cancel( "soft", true)
151
152-- wait until cancellation is effective. the lane will interrupt its loop and print the exit message
153waitCancellation( h, "done")
154
155-- do return end
156
157print "\n\n####################################################################\nbegin hook cancel test\n"
158h = lanes.gen( "*", protectedBody)( "get", 300000)
85print "wait 2s" 159print "wait 2s"
86linda:receive( 2, "yeah") 160linda:receive( 2, "yeah")
87 161
88--#################################################################### 162-- count hook cancel after 3 instructions
163h:cancel( "count", 300, 5.0)
164
165-- wait until cancellation is effective. the lane will interrupt its loop and print the exit message
166waitCancellation( h, "cancelled")
89 167
90print "\n\n####################################################################\nbegin hard cancel test\n" 168print "\n\n####################################################################\nbegin hard cancel test\n"
91h = lanes.gen("*", protectedBody)() 169h = lanes.gen( "*", protectedBody)( "receive", nil) -- infinite timeout
92 170
93-- wait 3s before cancelling the lane 171-- wait 2s before cancelling the lane
94print "wait 3s" 172print "wait 2s"
95linda:receive( 3, "yeah") 173linda:receive( 2, "yeah")
96 174
97-- hard cancel and wait 10s: the lane will be interrupted from inside its current linda:receive() and won't return from it 175-- hard cancel: the lane will be interrupted from inside its current linda:receive() and won't return from it
98print "hard cancel (always awakens)"
99h:cancel() 176h:cancel()
100 177
101print "wait 5s" 178-- wait until cancellation is effective. the lane will be stopped by the linda operation throwing an error
102linda:receive( 5, "yeah") 179waitCancellation( h, "cancelled")
103
104--####################################################################
105 180
106print "\n\n####################################################################\nbegin hard cancel test with unprotected lane body\n" 181print "\n\n####################################################################\nbegin hard cancel test with unprotected lane body\n"
107h = lanes.gen("*", laneBody)() 182h = lanes.gen( "*", laneBody)( "receive", nil)
108 183
109-- wait 3s before cancelling the lane 184-- wait 2s before cancelling the lane
110print "wait 3s" 185print "wait 2s"
111linda:receive( 3, "yeah") 186linda:receive( 2, "yeah")
112 187
113-- hard cancel: the lane will be interrupted from inside its current linda:receive() and won't return from it 188-- hard cancel: the lane will be interrupted from inside its current linda:receive() and won't return from it
114print "hard cancel (always awakens)"
115h:cancel() 189h:cancel()
116 190
117print "wait 5s" 191-- wait until cancellation is effective. the lane will be stopped by the linda operation throwing an error
118linda:receive( 5, "yeah") 192waitCancellation( h, "cancelled")
119 193
120--#################################################################### 194print "\n\n####################################################################\nbegin kill cancel test\n"
121print "\n\n####################################################################\nbegin linda cancel test\n" 195h = lanes.gen( "*", laneBody)( "busy", 50000000) -- start a pure Lua busy loop lane
122h = lanes.gen("*", laneBody)()
123 196
124-- wait 3s before cancelling the lane 197-- wait 1/3s before cancelling the lane, before the busy loop can finish
125print "wait 3s" 198print "wait 0.3s"
126linda:receive( 3, "yeah") 199linda:receive( 0.3, "yeah")
127 200
128-- linda cancel: the lane will be interrupted from inside its current linda:receive() and won't return from it 201-- hard cancel with kill: the lane thread will be forcefully terminated
129print "linda cancel (always awakens the lane)" 202h:cancel( true)
130linda:cancel( "both")
131 203
132print "wait 5s" 204-- wait until cancellation is effective. the lane will be stopped by the linda operation throwing an error
133linda:receive( 5, "yeah") 205waitCancellation( h, "killed")
134 206
135--#################################################################### 207--####################################################################
136 208
diff --git a/tests/fifo.lua b/tests/fifo.lua
index 47db4c9..bef60d5 100644
--- a/tests/fifo.lua
+++ b/tests/fifo.lua
@@ -6,11 +6,11 @@
6 6
7local lanes = require "lanes".configure{shutdown_timeout=3,with_timers=true} 7local lanes = require "lanes".configure{shutdown_timeout=3,with_timers=true}
8 8
9local linda= lanes.linda( "atom") 9local linda = lanes.linda( "atom")
10local atomic_inc= lanes.genatomic( linda, "FIFO_n" ) 10local atomic_inc= lanes.genatomic( linda, "FIFO_n")
11 11
12assert( atomic_inc()==1 ) 12assert( atomic_inc()==1)
13assert( atomic_inc()==2 ) 13assert( atomic_inc()==2)
14 14
15local function FIFO() 15local function FIFO()
16 local my_channel= "FIFO"..atomic_inc() 16 local my_channel= "FIFO"..atomic_inc()
@@ -18,32 +18,32 @@ local function FIFO()
18 return { 18 return {
19 -- Giving explicit 'nil' timeout allows numbers to be used as 'my_channel' 19 -- Giving explicit 'nil' timeout allows numbers to be used as 'my_channel'
20 -- 20 --
21 send= function(self, ...) 21 send = function(self, ...)
22 linda:send( nil, my_channel, ... ) 22 linda:send( nil, my_channel, ...)
23 end, 23 end,
24 receive = function(self, timeout) 24 receive = function(self, timeout)
25 return linda:receive( timeout, my_channel ) 25 return linda:receive( timeout, my_channel)
26 end 26 end
27 } 27 }
28end 28end
29 29
30local A= FIFO() 30local A = FIFO()
31local B= FIFO() 31local B = FIFO()
32 32
33print "Sending to A.." 33print "Sending to A.."
34A:send( 1,2,3,4,5 ) 34A:send( 1,2,3,4,5)
35 35
36print "Sending to B.." 36print "Sending to B.."
37B:send( 'a','b','c' ) 37B:send( 'a','b','c')
38 38
39print "Reading A.." 39print "Reading A.."
40print( A:receive( 1.0 ) ) 40print( A:receive( 1.0))
41 41
42print "Reading B.." 42print "Reading B.."
43print( B:receive( 2.0 ) ) 43print( B:receive( 2.0))
44 44
45-- Note: A and B can be passed between threads, or used as upvalues 45-- Note: A and B can be passed between threads, or used as upvalues
46-- by multiple threads (other parts will be copied but the 'linda' 46-- by multiple threads (other parts will be copied but the 'linda'
47-- handle is shared userdata and will thus point to the single place) 47-- handle is shared userdata and will thus point to the single place)
48lanes.timer_lane:cancel() 48lanes.timer_lane:cancel() -- hard cancel, 0 timeout
49lanes.timer_lane:join() \ No newline at end of file 49lanes.timer_lane:join() \ No newline at end of file
diff --git a/tests/timer.lua b/tests/timer.lua
index 805d85c..ec23cee 100644
--- a/tests/timer.lua
+++ b/tests/timer.lua
@@ -100,5 +100,5 @@ PRINT "...making sure no ticks are coming..."
100local k,v= linda:receive( 10, T1,T2 ) -- should not get any 100local k,v= linda:receive( 10, T1,T2 ) -- should not get any
101assert(v==nil) 101assert(v==nil)
102 102
103lanes.timer_lane:cancel() 103lanes.timer_lane:cancel() -- hard cancel, 0 timeout
104print (lanes.timer_lane[1], lanes.timer_lane[2]) \ No newline at end of file 104print (lanes.timer_lane[1], lanes.timer_lane[2]) \ No newline at end of file