aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-04-11 15:14:52 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-04-11 15:14:52 +0200
commitadaa36dbec1ce9aaafd61873b9d3d898a8c240cf (patch)
tree4c81e8f5983c3d696a636e2cc433ce7c0a9c3dd8
parent1d310e6ecb6e156598337612f16573d9cd284f5e (diff)
downloadlanes-adaa36dbec1ce9aaafd61873b9d3d898a8c240cf.tar.gz
lanes-adaa36dbec1ce9aaafd61873b9d3d898a8c240cf.tar.bz2
lanes-adaa36dbec1ce9aaafd61873b9d3d898a8c240cf.zip
Bring all interesting fixes from the C++ implementation back into the C implementation
-rw-r--r--CHANGES7
-rw-r--r--Makefile32
-rw-r--r--docs/index.html24
-rw-r--r--lanes-3.17.0-0.rockspec (renamed from lanes-3.16.3-0.rockspec)4
-rw-r--r--src/cancel.h3
-rw-r--r--src/compat.c20
-rw-r--r--src/compat.h1
-rw-r--r--src/deep.c13
-rw-r--r--src/keeper.c83
-rw-r--r--src/keeper.h9
-rw-r--r--src/lanes.c115
-rw-r--r--src/lanes.h4
-rw-r--r--src/lanes.lua449
-rw-r--r--src/lanes_private.h10
-rw-r--r--src/linda.c115
-rw-r--r--src/macros_and_utils.h7
-rw-r--r--src/platform.h1
-rw-r--r--src/state.c2
-rw-r--r--src/tools.c50
-rw-r--r--src/tools.h16
-rw-r--r--src/uniquekey.h4
-rw-r--r--tests/basic.lua14
-rw-r--r--tests/cancel.lua212
-rw-r--r--tests/errhangtest.lua15
-rw-r--r--tests/fifo.lua42
-rw-r--r--tests/keeper.lua63
-rw-r--r--tests/linda_perf.lua165
-rw-r--r--tests/protect_allocator.lua3
-rw-r--r--tests/timer.lua12
29 files changed, 880 insertions, 615 deletions
diff --git a/CHANGES b/CHANGES
index d93f5b4..34bd2d9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,12 @@
1CHANGES: 1CHANGES:
2 2
3CHANGE 160: BGe 11-Apr-24
4 * add manual control over GC behavior in keeper states
5 * update a bunch of test scripts
6 * minor internal fixes
7 * probably the last C implementation update, unless a critical bug creeps up
8 * internal version bumped to 3.17.0
9
3CHANGE 159: BGe 19-Mar-24 10CHANGE 159: BGe 19-Mar-24
4 * fix small internal issue with when hitting timeout on thread kill during thread_cancel() in pthread implementation 11 * fix small internal issue with when hitting timeout on thread kill during thread_cancel() in pthread implementation
5 12
diff --git a/Makefile b/Makefile
index 1e9f70e..08888e4 100644
--- a/Makefile
+++ b/Makefile
@@ -72,25 +72,25 @@ rock:
72#--- Testing --- 72#--- Testing ---
73# 73#
74test: 74test:
75 $(MAKE) errhangtest 75 $(MAKE) atexit
76 $(MAKE) irayo_recursive 76 $(MAKE) atomic
77 $(MAKE) irayo_closure
78 $(MAKE) basic 77 $(MAKE) basic
79 $(MAKE) cancel 78 $(MAKE) cancel
80 $(MAKE) fifo
81 $(MAKE) keeper
82 $(MAKE) timer
83 $(MAKE) atomic
84 $(MAKE) cyclic 79 $(MAKE) cyclic
85 $(MAKE) objects 80 $(MAKE) errhangtest
86 $(MAKE) fibonacci 81 $(MAKE) fibonacci
87 $(MAKE) recursive 82 $(MAKE) fifo
88 $(MAKE) func_is_string 83 $(MAKE) func_is_string
89 $(MAKE) atexit 84 $(MAKE) irayo_closure
85 $(MAKE) irayo_recursive
86 $(MAKE) keeper
90 $(MAKE) linda_perf 87 $(MAKE) linda_perf
91 $(MAKE) rupval 88 $(MAKE) objects
92 $(MAKE) package 89 $(MAKE) package
93 $(MAKE) pingpong 90 $(MAKE) pingpong
91 $(MAKE) recursive
92 $(MAKE) rupval
93 $(MAKE) timer
94 94
95basic: tests/basic.lua $(_TARGET_SO) 95basic: tests/basic.lua $(_TARGET_SO)
96 $(_PREFIX) $(LUA) $< 96 $(_PREFIX) $(LUA) $<
@@ -104,8 +104,8 @@ cancel: tests/cancel.lua $(_TARGET_SO)
104REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end" 104REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end"
105repetitive: $(_TARGET_SO) 105repetitive: $(_TARGET_SO)
106 for i in 1 2 3 4 5 6 7 8 9 10 a b c d e f g h i j k l m n o p q r s t u v w x y z; \ 106 for i in 1 2 3 4 5 6 7 8 9 10 a b c d e f g h i j k l m n o p q r s t u v w x y z; \
107 do $(_PREFIX) $(LUA) $(REP_ARGS); \ 107 do $(_PREFIX) $(LUA) $(REP_ARGS); \
108 done 108 done
109 109
110repetitive1: $(_TARGET_SO) 110repetitive1: $(_TARGET_SO)
111 $(_PREFIX) $(LUA) $(REP_ARGS) 111 $(_PREFIX) $(LUA) $(REP_ARGS)
@@ -236,9 +236,9 @@ else
236 -rm -rf $(MODULE)-$(VERSION) 236 -rm -rf $(MODULE)-$(VERSION)
237 mkdir $(MODULE)-$(VERSION) 237 mkdir $(MODULE)-$(VERSION)
238 tar c * --exclude=.svn --exclude=.DS_Store --exclude="_*" \ 238 tar c * --exclude=.svn --exclude=.DS_Store --exclude="_*" \
239 --exclude="*.tgz" --exclude="*.rockspec" \ 239 --exclude="*.tgz" --exclude="*.rockspec" \
240 --exclude=lanes.dev --exclude="$(MODULE)-*" --exclude=xcode \ 240 --exclude=lanes.dev --exclude="$(MODULE)-*" --exclude=xcode \
241 --exclude="*.obj" --exclude="*.dll" --exclude=timeit.dat \ 241 --exclude="*.obj" --exclude="*.dll" --exclude=timeit.dat \
242 | (cd $(MODULE)-$(VERSION) && tar x) 242 | (cd $(MODULE)-$(VERSION) && tar x)
243 tar czvf $(MODULE)-$(VERSION).tgz $(MODULE)-$(VERSION) 243 tar czvf $(MODULE)-$(VERSION).tgz $(MODULE)-$(VERSION)
244 rm -rf $(MODULE)-$(VERSION) 244 rm -rf $(MODULE)-$(VERSION)
diff --git a/docs/index.html b/docs/index.html
index aed022a..da94898 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -70,7 +70,7 @@
70 </p> 70 </p>
71 71
72 <p> 72 <p>
73 This document was revised on 23-Feb-24, and applies to version <tt>3.16.3</tt>. 73 This document was revised on 11-Apr-24, and applies to version <tt>3.17.0</tt>.
74 </p> 74 </p>
75 </font> 75 </font>
76 </center> 76 </center>
@@ -291,6 +291,19 @@
291 </tr> 291 </tr>
292 292
293 <tr valign=top> 293 <tr valign=top>
294 <td id="keepers_gc_threshold">
295 <code>.keepers_gc_threshold</code>
296 </td>
297 <td>integer</td>
298 <td>
299 (Since v3.17.0)<br/>
300 If &lt;0, GC runs automatically. This is the default.<br />
301 If 0, GC runs after *every* keeper operation.<br />
302 If &gt;0, Keepers run GC manually with <tt>lua_gc(LUA_GCCOLLECT)</tt> whenever memory usage reported by <tt>lua_gc(LUA_GCCOUNT)</tt> reaches this threshold. Check is made after every keeper operation (see <a href="#lindas">below</a>). If memory usage remains above threshold after the GC cycle, an error is raised.
303 </td>
304 </tr>
305
306 <tr valign=top>
294 <td id="with_timers"> 307 <td id="with_timers">
295 <code>.with_timers</code> 308 <code>.with_timers</code>
296 </td> 309 </td>
@@ -351,7 +364,6 @@
351 </table> 364 </table>
352 The contents will be used to create the state with <tt>lua_newstate( allocF, allocUD)</tt>. 365 The contents will be used to create the state with <tt>lua_newstate( allocF, allocUD)</tt>.
353 This option is mostly useful for embedders that want to provide different allocators to each lane, for example to have each one work in a different memory pool thus preventing the need for the allocator itself to be threadsafe. 366 This option is mostly useful for embedders that want to provide different allocators to each lane, for example to have each one work in a different memory pool thus preventing the need for the allocator itself to be threadsafe.
354 Note however that linda deep proxy are allocated with the allocator from the master state, because they are not tied to a particular state.
355 </td> 367 </td>
356 </tr> 368 </tr>
357 369
@@ -364,7 +376,7 @@
364 </td> 376 </td>
365 <td> 377 <td>
366 (Since v3.16.1)<br /> 378 (Since v3.16.1)<br />
367 Controls which allocator is used for Lanest internal allocations (for keeper and deep userdata management). 379 Controls which allocator is used for Lanes internal allocations (for keeper, linda and lane management).
368 If <tt>"libc"</tt>, Lanes uses <tt>realloc</tt> and <tt>free</tt>.<br /> 380 If <tt>"libc"</tt>, Lanes uses <tt>realloc</tt> and <tt>free</tt>.<br />
369 If <tt>"allocator"</tt>, Lanes uses whatever was obtained from the <tt>"allocator"</tt> setting.<br /> 381 If <tt>"allocator"</tt>, Lanes uses whatever was obtained from the <tt>"allocator"</tt> setting.<br />
370 This option is mostly useful for embedders that want control all memory allocations, but have issues when Lanes tries to use the Lua State allocator for internal purposes (especially with LuaJIT). 382 This option is mostly useful for embedders that want control all memory allocations, but have issues when Lanes tries to use the Lua State allocator for internal purposes (especially with LuaJIT).
@@ -911,7 +923,7 @@
911 <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> 923 <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%">
912 <tr> 924 <tr>
913 <td> 925 <td>
914 <pre> {{name = "name", status = "status", ...}|nil = lanes.threads()</pre> 926 <pre> {name = "name", status = "status", ...}|nil = lanes.threads()</pre>
915 </td> 927 </td>
916 </tr> 928 </tr>
917 </table> 929 </table>
@@ -1617,8 +1629,8 @@ events to a common Linda, but... :).</font>
1617<h3 id="clonable_userdata">Clonable full userdata in your own apps</h3> 1629<h3 id="clonable_userdata">Clonable full userdata in your own apps</h3>
1618<p> 1630<p>
1619 Starting with version 3.13.0, a new way of passing full userdata across lanes uses a new <tt>__lanesclone</tt> metamethod. 1631 Starting with version 3.13.0, a new way of passing full userdata across lanes uses a new <tt>__lanesclone</tt> metamethod.
1620 When a deep userdata is cloned, Lanes calls <tt>__lanesclone</tt> once, in the context of the source lane.</br> 1632 When a deep userdata is cloned, Lanes calls <tt>__lanesclone</tt> once, in the context of the source lane.<br/>
1621 The call receives the clone and original as light userdata, plus the actual userdata size, as in <tt>clone:__lanesclone(original,size)</tt>, and should perform the actual cloning.</br> 1633 The call receives the clone and original as light userdata, plus the actual userdata size, as in <tt>clone:__lanesclone(original,size)</tt>, and should perform the actual cloning.<br/>
1622 A typical implementation would look like (BEWARE, THIS CHANGED WITH VERSION 3.16.0): 1634 A typical implementation would look like (BEWARE, THIS CHANGED WITH VERSION 3.16.0):
1623<table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> 1635<table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre>
1624static int clonable_lanesclone( lua_State* L) 1636static int clonable_lanesclone( lua_State* L)
diff --git a/lanes-3.16.3-0.rockspec b/lanes-3.17.0-0.rockspec
index e223a19..69ee515 100644
--- a/lanes-3.16.3-0.rockspec
+++ b/lanes-3.17.0-0.rockspec
@@ -7,11 +7,11 @@
7 7
8package = "Lanes" 8package = "Lanes"
9 9
10version = "3.16.3-0" 10version = "3.17.0-0"
11 11
12source= { 12source= {
13 url= "git+https://github.com/LuaLanes/lanes.git", 13 url= "git+https://github.com/LuaLanes/lanes.git",
14 branch= "v3.16.3" 14 branch= "v3.17.0"
15} 15}
16 16
17description = { 17description = {
diff --git a/src/cancel.h b/src/cancel.h
index c7c5433..b25d9f9 100644
--- a/src/cancel.h
+++ b/src/cancel.h
@@ -43,9 +43,6 @@ typedef enum
43// 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/
44static DECLARE_CONST_UNIQUE_KEY(CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel 44static DECLARE_CONST_UNIQUE_KEY(CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel
45 45
46// crc64/we of string "CANCEL_TEST_KEY" generated at http://www.nitrxgen.net/hashgen/
47static DECLARE_CONST_UNIQUE_KEY(CANCEL_TEST_KEY, 0xe66f5960c57d133a); // used as registry key
48
49cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_); 46cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_);
50 47
51static inline int cancel_error( lua_State* L) 48static inline int cancel_error( lua_State* L)
diff --git a/src/compat.c b/src/compat.c
index 19159a9..bc39d4c 100644
--- a/src/compat.c
+++ b/src/compat.c
@@ -1,6 +1,6 @@
1/* 1/*
2 * ############################################################################################### 2 * ###############################################################################################
3 * ######################################### Lua 5.1/5.2 ######################################### 3 * ####################################### Lua 5.1/5.2/5.3 #######################################
4 * ############################################################################################### 4 * ###############################################################################################
5 */ 5 */
6#include "compat.h" 6#include "compat.h"
@@ -9,7 +9,11 @@
9/* 9/*
10** Copied from Lua 5.2 loadlib.c 10** Copied from Lua 5.2 loadlib.c
11*/ 11*/
12// ################################################################################################
13// ################################################################################################
12#if LUA_VERSION_NUM == 501 14#if LUA_VERSION_NUM == 501
15// ################################################################################################
16// ################################################################################################
13static int luaL_getsubtable (lua_State *L, int idx, const char *fname) 17static int luaL_getsubtable (lua_State *L, int idx, const char *fname)
14{ 18{
15 lua_getfield(L, idx, fname); 19 lua_getfield(L, idx, fname);
@@ -26,6 +30,8 @@ static int luaL_getsubtable (lua_State *L, int idx, const char *fname)
26 } 30 }
27} 31}
28 32
33// ################################################################################################
34
29void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb) 35void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb)
30{ 36{
31 lua_pushcfunction(L, openf); 37 lua_pushcfunction(L, openf);
@@ -43,7 +49,11 @@ void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int
43} 49}
44#endif // LUA_VERSION_NUM 50#endif // LUA_VERSION_NUM
45 51
52// ################################################################################################
53// ################################################################################################
46#if LUA_VERSION_NUM < 504 54#if LUA_VERSION_NUM < 504
55// ################################################################################################
56// ################################################################################################
47 57
48void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue) 58void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue)
49{ 59{
@@ -51,8 +61,12 @@ void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue)
51 return lua_newuserdata( L, sz); 61 return lua_newuserdata( L, sz);
52} 62}
53 63
64// ################################################################################################
65
66// push on stack uservalue #n of full userdata at idx
54int lua_getiuservalue( lua_State* L, int idx, int n) 67int lua_getiuservalue( lua_State* L, int idx, int n)
55{ 68{
69 // full userdata can have only 1 uservalue before 5.4
56 if( n > 1) 70 if( n > 1)
57 { 71 {
58 lua_pushnil( L); 72 lua_pushnil( L);
@@ -76,6 +90,10 @@ int lua_getiuservalue( lua_State* L, int idx, int n)
76 return lua_type( L, -1); 90 return lua_type( L, -1);
77} 91}
78 92
93// ################################################################################################
94
95// Pops a value from the stack and sets it as the new n-th user value associated to the full userdata at the given index.
96// Returns 0 if the userdata does not have that value.
79int lua_setiuservalue( lua_State* L, int idx, int n) 97int lua_setiuservalue( lua_State* L, int idx, int n)
80{ 98{
81 if( n > 1 99 if( n > 1
diff --git a/src/compat.h b/src/compat.h
index e44f827..fbcbee1 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -90,6 +90,7 @@ int lua_setiuservalue( lua_State* L, int idx, int n);
90#define luaG_registerlibfuncs( L, _funcs) luaL_setfuncs( L, _funcs, 0) 90#define luaG_registerlibfuncs( L, _funcs) luaL_setfuncs( L, _funcs, 0)
91#define lua504_dump lua_dump 91#define lua504_dump lua_dump
92#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) 92#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
93#define LUA_ERRGCMM 666 // doesn't exist in Lua 5.4, we don't care about the actual value
93 94
94#endif // LUA_VERSION_NUM == 504 95#endif // LUA_VERSION_NUM == 504
95 96
diff --git a/src/deep.c b/src/deep.c
index 9496477..a1f078a 100644
--- a/src/deep.c
+++ b/src/deep.c
@@ -236,7 +236,7 @@ char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, in
236 *proxy = prelude; 236 *proxy = prelude;
237 237
238 // Get/create metatable for 'idfunc' (in this state) 238 // Get/create metatable for 'idfunc' (in this state)
239 lua_pushlightuserdata( L, (void*)(ptrdiff_t)(prelude->idfunc)); // DPC proxy idfunc 239 lua_pushlightuserdata( L, (void*)(uintptr_t)(prelude->idfunc)); // DPC proxy idfunc
240 get_deep_lookup( L); // DPC proxy metatable? 240 get_deep_lookup( L); // DPC proxy metatable?
241 241
242 if( lua_isnil( L, -1)) // // No metatable yet. 242 if( lua_isnil( L, -1)) // // No metatable yet.
@@ -278,7 +278,7 @@ char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, in
278 278
279 // Memorize for later rounds 279 // Memorize for later rounds
280 lua_pushvalue( L, -1); // DPC proxy metatable metatable 280 lua_pushvalue( L, -1); // DPC proxy metatable metatable
281 lua_pushlightuserdata( L, (void*)(ptrdiff_t)(prelude->idfunc)); // DPC proxy metatable metatable idfunc 281 lua_pushlightuserdata( L, (void*)(uintptr_t)(prelude->idfunc)); // DPC proxy metatable metatable idfunc
282 set_deep_lookup( L); // DPC proxy metatable 282 set_deep_lookup( L); // DPC proxy metatable
283 283
284 // 2 - cause the target state to require the module that exported the idfunc 284 // 2 - cause the target state to require the module that exported the idfunc
@@ -473,15 +473,18 @@ bool_t copydeep( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, ui
473 lua_pop( L, 1); // ... u [uv]* 473 lua_pop( L, 1); // ... u [uv]*
474 STACK_MID( L, nuv); 474 STACK_MID( L, nuv);
475 475
476 errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, i), nuv, mode_); // u 476 errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, i), nuv, mode_); // u
477 477
478 // transfer all uservalues of the source in the destination 478 // transfer all uservalues of the source in the destination
479 { 479 {
480 int const clone_i = lua_gettop( L2); 480 int const clone_i = lua_gettop( L2);
481 while( nuv) 481 while( nuv)
482 { 482 {
483 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // u uv 483 if(!inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_)) // u uv
484 lua_pop( L, 1); // ... u [uv]* 484 {
485 return luaL_error(L, "Cannot copy upvalue type '%s'", luaL_typename(L, -1));
486 }
487 lua_pop( L, 1); // ... u [uv]*
485 // this pops the value from the stack 488 // this pops the value from the stack
486 lua_setiuservalue( L2, clone_i, nuv); // u 489 lua_setiuservalue( L2, clone_i, nuv); // u
487 -- nuv; 490 -- nuv;
diff --git a/src/keeper.c b/src/keeper.c
index 8aa734a..a1505b7 100644
--- a/src/keeper.c
+++ b/src/keeper.c
@@ -122,7 +122,7 @@ static void fifo_push( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
122static void fifo_peek( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) 122static void fifo_peek( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
123{ 123{
124 lua_Integer i; 124 lua_Integer i;
125 STACK_GROW( L, count_); 125 STACK_GROW( L, (int) count_);
126 for( i = 0; i < count_; ++ i) 126 for( i = 0; i < count_; ++ i)
127 { 127 {
128 lua_rawgeti( L, 1, (int)( fifo_->first + i)); 128 lua_rawgeti( L, 1, (int)( fifo_->first + i));
@@ -136,7 +136,7 @@ static void fifo_pop( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
136 int const fifo_idx = lua_gettop( L); // ... fifo 136 int const fifo_idx = lua_gettop( L); // ... fifo
137 int i; 137 int i;
138 // each iteration pushes a value on the stack! 138 // each iteration pushes a value on the stack!
139 STACK_GROW( L, count_ + 2); 139 STACK_GROW( L, (int) count_ + 2);
140 // skip first item, we will push it last 140 // skip first item, we will push it last
141 for( i = 1; i < count_; ++ i) 141 for( i = 1; i < count_; ++ i)
142 { 142 {
@@ -169,7 +169,7 @@ static void fifo_pop( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
169static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465); 169static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465);
170static void push_table( lua_State* L, int idx_) 170static void push_table( lua_State* L, int idx_)
171{ 171{
172 STACK_GROW( L, 4); 172 STACK_GROW( L, 5);
173 STACK_CHECK( L, 0); 173 STACK_CHECK( L, 0);
174 idx_ = lua_absindex( L, idx_); 174 idx_ = lua_absindex( L, idx_);
175 REGISTRY_GET( L, FIFOS_KEY); // ud fifos 175 REGISTRY_GET( L, FIFOS_KEY); // ud fifos
@@ -189,7 +189,7 @@ static void push_table( lua_State* L, int idx_)
189 STACK_END( L, 1); 189 STACK_END( L, 1);
190} 190}
191 191
192int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_) 192int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, uintptr_t magic_)
193{ 193{
194 Keeper* const K = which_keeper( U->keepers, magic_); 194 Keeper* const K = which_keeper( U->keepers, magic_);
195 lua_State* const KL = K ? K->L : NULL; 195 lua_State* const KL = K ? K->L : NULL;
@@ -557,7 +557,7 @@ int keepercall_count( lua_State* L)
557 { 557 {
558 lua_pop( L, 1); // out fifos keys 558 lua_pop( L, 1); // out fifos keys
559 } 559 }
560 } 560 } // all keys are exhausted // out fifos
561 lua_pop( L, 1); // out 561 lua_pop( L, 1); // out
562 } 562 }
563 ASSERT_L( lua_gettop( L) == 1); 563 ASSERT_L( lua_gettop( L) == 1);
@@ -633,6 +633,7 @@ void init_keepers( Universe* U, lua_State* L)
633{ 633{
634 int i; 634 int i;
635 int nb_keepers; 635 int nb_keepers;
636 int keepers_gc_threshold;
636 637
637 STACK_CHECK( L, 0); // L K 638 STACK_CHECK( L, 0); // L K
638 lua_getfield( L, 1, "nb_keepers"); // nb_keepers 639 lua_getfield( L, 1, "nb_keepers"); // nb_keepers
@@ -642,6 +643,12 @@ void init_keepers( Universe* U, lua_State* L)
642 { 643 {
643 (void) luaL_error( L, "Bad number of keepers (%d)", nb_keepers); 644 (void) luaL_error( L, "Bad number of keepers (%d)", nb_keepers);
644 } 645 }
646 STACK_MID(L, 0);
647
648 lua_getfield(L, 1, "keepers_gc_threshold"); // keepers_gc_threshold
649 keepers_gc_threshold = (int) lua_tointeger(L, -1);
650 lua_pop(L, 1); //
651 STACK_MID(L, 0);
645 652
646 // Keepers contains an array of 1 s_Keeper, adjust for the actual number of keeper states 653 // Keepers contains an array of 1 s_Keeper, adjust for the actual number of keeper states
647 { 654 {
@@ -656,6 +663,7 @@ void init_keepers( Universe* U, lua_State* L)
656 return; 663 return;
657 } 664 }
658 memset( U->keepers, 0, bytes); 665 memset( U->keepers, 0, bytes);
666 U->keepers->gc_threshold = keepers_gc_threshold;
659 U->keepers->nb_keepers = nb_keepers; 667 U->keepers->nb_keepers = nb_keepers;
660 } 668 }
661 for( i = 0; i < nb_keepers; ++ i) // keepersUD 669 for( i = 0; i < nb_keepers; ++ i) // keepersUD
@@ -669,10 +677,12 @@ void init_keepers( Universe* U, lua_State* L)
669 } 677 }
670 678
671 U->keepers->keeper_array[i].L = K; 679 U->keepers->keeper_array[i].L = K;
672 // we can trigger a GC from inside keeper_call(), where a keeper is acquired 680 MUTEX_INIT( &U->keepers->keeper_array[i].keeper_cs);
673 // from there, GC can collect a linda, which would acquire the keeper again, and deadlock the thread. 681
674 // therefore, we need a recursive mutex. 682 if (U->keepers->gc_threshold >= 0)
675 MUTEX_RECURSIVE_INIT( &U->keepers->keeper_array[i].keeper_cs); 683 {
684 lua_gc(K, LUA_GCSTOP, 0);
685 }
676 686
677 STACK_CHECK( K, 0); 687 STACK_CHECK( K, 0);
678 688
@@ -693,7 +703,7 @@ void init_keepers( Universe* U, lua_State* L)
693 if( !lua_isnil( L, -1)) 703 if( !lua_isnil( L, -1))
694 { 704 {
695 // when copying with mode eLM_ToKeeper, error message is pushed at the top of the stack, not raised immediately 705 // when copying with mode eLM_ToKeeper, error message is pushed at the top of the stack, not raised immediately
696 if( luaG_inter_copy_package( U, L, K, -1, eLM_ToKeeper)) 706 if( luaG_inter_copy_package( U, L, K, -1, eLM_ToKeeper) != eICR_Success)
697 { 707 {
698 // if something went wrong, the error message is at the top of the stack 708 // if something went wrong, the error message is at the top of the stack
699 lua_remove( L, -2); // error_msg 709 lua_remove( L, -2); // error_msg
@@ -721,22 +731,22 @@ void init_keepers( Universe* U, lua_State* L)
721} 731}
722 732
723// should be called only when inside a keeper_acquire/keeper_release pair (see linda_protected_call) 733// should be called only when inside a keeper_acquire/keeper_release pair (see linda_protected_call)
724Keeper* which_keeper(Keepers* keepers_, ptrdiff_t magic_) 734Keeper* which_keeper(Keepers* keepers_, uintptr_t magic_)
725{ 735{
726 int const nbKeepers = keepers_->nb_keepers; 736 int const nbKeepers = keepers_->nb_keepers;
727 unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers); 737 if (nbKeepers)
728 return &keepers_->keeper_array[i]; 738 {
739 unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers);
740 return &keepers_->keeper_array[i];
741 }
742 return NULL;
729} 743}
730 744
731Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_) 745Keeper* keeper_acquire( Keepers* keepers_, uintptr_t magic_)
732{ 746{
733 int const nbKeepers = keepers_->nb_keepers; 747 int const nbKeepers = keepers_->nb_keepers;
734 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) 748 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers)
735 if( nbKeepers == 0) 749 if( nbKeepers)
736 {
737 return NULL;
738 }
739 else
740 { 750 {
741 /* 751 /*
742 * Any hashing will do that maps pointers to 0..GNbKeepers-1 752 * Any hashing will do that maps pointers to 0..GNbKeepers-1
@@ -752,12 +762,13 @@ Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_)
752 //++ K->count; 762 //++ K->count;
753 return K; 763 return K;
754 } 764 }
765 return NULL;
755} 766}
756 767
757void keeper_release( Keeper* K) 768void keeper_release( Keeper* K_)
758{ 769{
759 //-- K->count; 770 //-- K->count;
760 if( K) MUTEX_UNLOCK( &K->keeper_cs); 771 if( K_) MUTEX_UNLOCK( &K_->keeper_cs);
761} 772}
762 773
763void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_) 774void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_)
@@ -805,7 +816,7 @@ int keeper_call( Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, vo
805 816
806 lua_pushlightuserdata( K, linda); 817 lua_pushlightuserdata( K, linda);
807 818
808 if( (args == 0) || luaG_inter_copy( U, L, K, args, eLM_ToKeeper) == 0) // L->K 819 if( (args == 0) || luaG_inter_copy( U, L, K, args, eLM_ToKeeper) == eICR_Success) // L->K
809 { 820 {
810 lua_call( K, 1 + args, LUA_MULTRET); 821 lua_call( K, 1 + args, LUA_MULTRET);
811 822
@@ -814,12 +825,38 @@ int keeper_call( Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, vo
814 // this may interrupt a lane, causing the destruction of the underlying OS thread 825 // this may interrupt a lane, causing the destruction of the underlying OS thread
815 // after this, another lane making use of this keeper can get an error code from the mutex-locking function 826 // after this, another lane making use of this keeper can get an error code from the mutex-locking function
816 // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) 827 // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread)
817 if( (retvals > 0) && luaG_inter_move( U, K, L, retvals, eLM_FromKeeper) != 0) // K->L 828 if( (retvals > 0) && luaG_inter_move( U, K, L, retvals, eLM_FromKeeper) != eICR_Success) // K->L
818 { 829 {
819 retvals = -1; 830 retvals = -1;
820 } 831 }
821 } 832 }
822 // whatever happens, restore the stack to where it was at the origin 833 // whatever happens, restore the stack to where it was at the origin
823 lua_settop( K, Ktos); 834 lua_settop( K, Ktos);
835
836
837 // don't do this for this particular function, as it is only called during Linda destruction, and we don't want to raise an error, ever
838 if (func_ != KEEPER_API(clear))
839 {
840 // since keeper state GC is stopped, let's run a step once in a while if required
841 int const gc_threshold = U->keepers->gc_threshold;
842 if (gc_threshold == 0)
843 {
844 lua_gc(K, LUA_GCSTEP, 0);
845 }
846 else if (gc_threshold > 0)
847 {
848 int const gc_usage = lua_gc(K, LUA_GCCOUNT, 0);
849 if (gc_usage >= gc_threshold)
850 {
851 lua_gc(K, LUA_GCCOLLECT, 0);
852 int const gc_usage_after = lua_gc(K, LUA_GCCOUNT, 0);
853 if (gc_usage_after > gc_threshold)
854 {
855 luaL_error(L, "Keeper GC threshold is too low, need at least %d", gc_usage_after);
856 }
857 }
858 }
859 }
860
824 return retvals; 861 return retvals;
825} 862}
diff --git a/src/keeper.h b/src/keeper.h
index d30aa36..7c55809 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -21,6 +21,7 @@ typedef struct s_Keeper Keeper;
21 21
22struct s_Keepers 22struct s_Keepers
23{ 23{
24 int gc_threshold;
24 int nb_keepers; 25 int nb_keepers;
25 Keeper keeper_array[1]; 26 Keeper keeper_array[1];
26}; 27};
@@ -29,12 +30,12 @@ typedef struct s_Keepers Keepers;
29void init_keepers( Universe* U, lua_State* L); 30void init_keepers( Universe* U, lua_State* L);
30void close_keepers( Universe* U); 31void close_keepers( Universe* U);
31 32
32Keeper* which_keeper( Keepers* keepers_, ptrdiff_t magic_); 33Keeper* which_keeper( Keepers* keepers_, uintptr_t magic_);
33Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_); 34Keeper* keeper_acquire( Keepers* keepers_, uintptr_t magic_);
34#define KEEPER_MAGIC_SHIFT 3 35#define KEEPER_MAGIC_SHIFT 3
35void keeper_release( Keeper* K); 36void keeper_release( Keeper* K_);
36void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_); 37void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_);
37int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_); 38int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, uintptr_t magic_);
38 39
39// crc64/we of string "NIL_SENTINEL" generated at http://www.nitrxgen.net/hashgen/ 40// crc64/we of string "NIL_SENTINEL" generated at http://www.nitrxgen.net/hashgen/
40static DECLARE_CONST_UNIQUE_KEY( NIL_SENTINEL, 0x7eaafa003a1d11a1); 41static DECLARE_CONST_UNIQUE_KEY( NIL_SENTINEL, 0x7eaafa003a1d11a1);
diff --git a/src/lanes.c b/src/lanes.c
index 332a1b8..ca2b53a 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -442,7 +442,7 @@ static bool_t selfdestruct_remove( Lane* s)
442/* 442/*
443* Process end; cancel any still free-running threads 443* Process end; cancel any still free-running threads
444*/ 444*/
445static int selfdestruct_gc( lua_State* L) 445static int universe_gc( lua_State* L)
446{ 446{
447 Universe* U = (Universe*) lua_touserdata( L, 1); 447 Universe* U = (Universe*) lua_touserdata( L, 1);
448 448
@@ -456,7 +456,7 @@ static int selfdestruct_gc( lua_State* L)
456 while( s != SELFDESTRUCT_END) 456 while( s != SELFDESTRUCT_END)
457 { 457 {
458 // attempt a regular unforced hard cancel with a small timeout 458 // attempt a regular unforced hard cancel with a small timeout
459 bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( L, s, CO_Hard, 0.0001, FALSE, 0.0); 459 bool_t cancelled = THREAD_ISNULL( s->thread) || (thread_cancel( L, s, CO_Hard, 0.0001, FALSE, 0.0) != CR_Timeout);
460 // if we failed, and we know the thread is waiting on a linda 460 // if we failed, and we know the thread is waiting on a linda
461 if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL) 461 if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL)
462 { 462 {
@@ -609,7 +609,7 @@ static int selfdestruct_gc( lua_State* L)
609// 609//
610LUAG_FUNC( set_singlethreaded) 610LUAG_FUNC( set_singlethreaded)
611{ 611{
612 uint_t cores = luaG_optunsigned( L, 1, 1); 612 lua_Integer cores = luaG_optunsigned( L, 1, 1);
613 (void) cores; // prevent "unused" warning 613 (void) cores; // prevent "unused" warning
614 614
615#ifdef PLATFORM_OSX 615#ifdef PLATFORM_OSX
@@ -653,24 +653,16 @@ static DECLARE_CONST_UNIQUE_KEY( EXTENDED_STACKTRACE_REGKEY, 0x2357c69a7c92c936)
653 653
654LUAG_FUNC( set_error_reporting) 654LUAG_FUNC( set_error_reporting)
655{ 655{
656 bool_t equal; 656 luaL_checktype(L, 1, LUA_TSTRING);
657 luaL_checktype( L, 1, LUA_TSTRING); 657 char const* mode = lua_tostring(L, 1);
658 lua_pushliteral( L, "extended"); 658 bool_t const extended = (strcmp(mode, "extended") == 0);
659 equal = lua_rawequal( L, -1, 1); 659 bool_t const basic = (strcmp(mode, "basic") == 0);
660 lua_pop( L, 1); 660 if (!extended && !basic)
661 if( equal)
662 { 661 {
663 goto done; 662 return luaL_error(L, "unsupported error reporting model %s", mode);
664 } 663 }
665 lua_pushliteral( L, "basic"); 664
666 equal = !lua_rawequal( L, -1, 1); 665 REGISTRY_SET( L, EXTENDED_STACKTRACE_REGKEY, lua_pushboolean( L, extended ? 1 : 0));
667 lua_pop( L, 1);
668 if( equal)
669 {
670 return luaL_error( L, "unsupported error reporting model");
671 }
672done:
673 REGISTRY_SET( L, EXTENDED_STACKTRACE_REGKEY, lua_pushboolean( L, equal));
674 return 0; 666 return 0;
675} 667}
676 668
@@ -788,7 +780,8 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_)
788 780
789LUAG_FUNC( set_debug_threadname) 781LUAG_FUNC( set_debug_threadname)
790{ 782{
791 DECLARE_CONST_UNIQUE_KEY( hidden_regkey, LG_set_debug_threadname); 783 // fnv164 of string "debug_threadname" generated at https://www.pelock.com/products/hash-calculator
784 static DECLARE_CONST_UNIQUE_KEY( hidden_regkey, 0x79C0669AAAE04440);
792 // C s_lane structure is a light userdata upvalue 785 // C s_lane structure is a light userdata upvalue
793 Lane* s = lua_touserdata( L, lua_upvalueindex( 1)); 786 Lane* s = lua_touserdata( L, lua_upvalueindex( 1));
794 luaL_checktype( L, -1, LUA_TSTRING); // "name" 787 luaL_checktype( L, -1, LUA_TSTRING); // "name"
@@ -1049,10 +1042,10 @@ LUAG_FUNC( lane_new)
1049 char const* libs_str = lua_tostring( L, 2); 1042 char const* libs_str = lua_tostring( L, 2);
1050 bool_t const have_priority = !lua_isnoneornil( L, 3); 1043 bool_t const have_priority = !lua_isnoneornil( L, 3);
1051 int const priority = have_priority ? (int) lua_tointeger( L, 3) : THREAD_PRIO_DEFAULT; 1044 int const priority = have_priority ? (int) lua_tointeger( L, 3) : THREAD_PRIO_DEFAULT;
1052 uint_t const globals_idx = lua_isnoneornil( L, 4) ? 0 : 4; 1045 int const globals_idx = lua_isnoneornil( L, 4) ? 0 : 4;
1053 uint_t const package_idx = lua_isnoneornil( L, 5) ? 0 : 5; 1046 int const package_idx = lua_isnoneornil( L, 5) ? 0 : 5;
1054 uint_t const required_idx = lua_isnoneornil( L, 6) ? 0 : 6; 1047 int const required_idx = lua_isnoneornil( L, 6) ? 0 : 6;
1055 uint_t const gc_cb_idx = lua_isnoneornil( L, 7) ? 0 : 7; 1048 int const gc_cb_idx = lua_isnoneornil( L, 7) ? 0 : 7;
1056 1049
1057#define FIXED_ARGS 7 1050#define FIXED_ARGS 7
1058 int const nargs = lua_gettop(L) - FIXED_ARGS; 1051 int const nargs = lua_gettop(L) - FIXED_ARGS;
@@ -1090,7 +1083,8 @@ LUAG_FUNC( lane_new)
1090 if( package_idx != 0) 1083 if( package_idx != 0)
1091 { 1084 {
1092 // when copying with mode eLM_LaneBody, should raise an error in case of problem, not leave it one the stack 1085 // when copying with mode eLM_LaneBody, should raise an error in case of problem, not leave it one the stack
1093 (void) luaG_inter_copy_package( U, L, L2, package_idx, eLM_LaneBody); 1086 InterCopyResult const ret = luaG_inter_copy_package( U, L, L2, package_idx, eLM_LaneBody);
1087 ASSERT_L(ret == eICR_Success); // either all went well, or we should not even get here
1094 } 1088 }
1095 1089
1096 // modules to require in the target lane *before* the function is transfered! 1090 // modules to require in the target lane *before* the function is transfered!
@@ -1179,25 +1173,32 @@ LUAG_FUNC( lane_new)
1179 STACK_MID( L2, 0); 1173 STACK_MID( L2, 0);
1180 1174
1181 // Lane main function 1175 // Lane main function
1182 if( lua_type( L, 1) == LUA_TFUNCTION)
1183 { 1176 {
1184 int res; 1177 int const func_type = lua_type(L, 1);
1185 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END)); 1178 if (func_type == LUA_TFUNCTION)
1186 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1187 lua_pushvalue( L, 1); // func libs priority globals package required gc_cb [... args ...] func
1188 res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func
1189 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1190 if( res != 0)
1191 { 1179 {
1192 return luaL_error( L, "tried to copy unsupported types"); 1180 InterCopyResult res;
1181 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END));
1182 DEBUGSPEW_CODE(++U->debugspew_indent_depth);
1183 lua_pushvalue(L, 1); // func libs priority globals package required gc_cb [... args ...] func
1184 res = luaG_inter_move(U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func
1185 DEBUGSPEW_CODE(--U->debugspew_indent_depth);
1186 if (res != eICR_Success)
1187 {
1188 return luaL_error(L, "tried to copy unsupported types");
1189 }
1193 } 1190 }
1194 } 1191 else if (func_type == LUA_TSTRING)
1195 else if( lua_type( L, 1) == LUA_TSTRING)
1196 {
1197 // compile the string
1198 if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) // func
1199 { 1192 {
1200 return luaL_error( L, "error when parsing lane function code"); 1193 // compile the string
1194 if (luaL_loadstring(L2, lua_tostring(L, 1)) != 0) // func
1195 {
1196 return luaL_error(L, "error when parsing lane function code");
1197 }
1198 }
1199 else
1200 {
1201 luaL_error(L, "Expected function, got %s", lua_typename(L, func_type)); // doesn't return
1201 } 1202 }
1202 } 1203 }
1203 STACK_MID( L, 0); 1204 STACK_MID( L, 0);
@@ -1207,12 +1208,12 @@ LUAG_FUNC( lane_new)
1207 // revive arguments 1208 // revive arguments
1208 if( nargs > 0) 1209 if( nargs > 0)
1209 { 1210 {
1210 int res; 1211 InterCopyResult res;
1211 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END)); 1212 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END));
1212 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1213 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1213 res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...] 1214 res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...]
1214 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1215 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1215 if( res != 0) 1216 if( res != eICR_Success)
1216 { 1217 {
1217 return luaL_error( L, "tried to copy unsupported types"); 1218 return luaL_error( L, "tried to copy unsupported types");
1218 } 1219 }
@@ -1277,7 +1278,7 @@ LUAG_FUNC( lane_new)
1277 lua_setiuservalue( L, -2, 1); // func libs priority globals package required gc_cb lane 1278 lua_setiuservalue( L, -2, 1); // func libs priority globals package required gc_cb lane
1278 1279
1279 // Store 's' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). 1280 // Store 's' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive).
1280 REGISTRY_SET( L2, CANCEL_TEST_KEY, lua_pushlightuserdata( L2, s)); // func [... args ...] 1281 REGISTRY_SET( L2, LANE_POINTER_REGKEY, lua_pushlightuserdata( L2, s)); // func [... args ...]
1281 1282
1282 STACK_END( L, 1); 1283 STACK_END( L, 1);
1283 STACK_END( L2, 1 + nargs); 1284 STACK_END( L2, 1 + nargs);
@@ -1457,8 +1458,8 @@ LUAG_FUNC( thread_join)
1457 { 1458 {
1458 case DONE: 1459 case DONE:
1459 { 1460 {
1460 uint_t n = lua_gettop( L2); // whole L2 stack 1461 int n = lua_gettop( L2); // whole L2 stack
1461 if( (n > 0) && (luaG_inter_move( U, L2, L, n, eLM_LaneBody) != 0)) 1462 if( (n > 0) && (luaG_inter_move( U, L2, L, n, eLM_LaneBody) != eICR_Success))
1462 { 1463 {
1463 return luaL_error( L, "tried to copy unsupported types"); 1464 return luaL_error( L, "tried to copy unsupported types");
1464 } 1465 }
@@ -1472,7 +1473,7 @@ LUAG_FUNC( thread_join)
1472 STACK_GROW( L, 3); 1473 STACK_GROW( L, 3);
1473 lua_pushnil( L); 1474 lua_pushnil( L);
1474 // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ... 1475 // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ...
1475 if( luaG_inter_move( U, L2, L, n, eLM_LaneBody) != 0) // nil "err" [trace] 1476 if( luaG_inter_move( U, L2, L, n, eLM_LaneBody) != eICR_Success) // nil "err" [trace]
1476 { 1477 {
1477 return luaL_error( L, "tried to copy unsupported types: %s", lua_tostring( L, -n)); 1478 return luaL_error( L, "tried to copy unsupported types: %s", lua_tostring( L, -n));
1478 } 1479 }
@@ -1874,7 +1875,7 @@ LUAG_FUNC( configure)
1874 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1875 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1875 lua_newtable( L); // settings universe mt 1876 lua_newtable( L); // settings universe mt
1876 lua_getfield( L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout 1877 lua_getfield( L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout
1877 lua_pushcclosure( L, selfdestruct_gc, 1); // settings universe mt selfdestruct_gc 1878 lua_pushcclosure( L, universe_gc, 1); // settings universe mt universe_gc
1878 lua_setfield( L, -2, "__gc"); // settings universe mt 1879 lua_setfield( L, -2, "__gc"); // settings universe mt
1879 lua_setmetatable( L, -2); // settings universe 1880 lua_setmetatable( L, -2); // settings universe
1880 lua_pop( L, 1); // settings 1881 lua_pop( L, 1); // settings
@@ -2051,16 +2052,20 @@ static void EnableCrashingOnCrashes( void)
2051 const DWORD EXCEPTION_SWALLOWING = 0x1; 2052 const DWORD EXCEPTION_SWALLOWING = 0x1;
2052 2053
2053 HMODULE kernel32 = LoadLibraryA("kernel32.dll"); 2054 HMODULE kernel32 = LoadLibraryA("kernel32.dll");
2054 tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy"); 2055 if (kernel32)
2055 tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
2056 if( pGetPolicy && pSetPolicy)
2057 { 2056 {
2058 DWORD dwFlags; 2057 tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
2059 if( pGetPolicy( &dwFlags)) 2058 tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
2059 if( pGetPolicy && pSetPolicy)
2060 { 2060 {
2061 // Turn off the filter 2061 DWORD dwFlags;
2062 pSetPolicy( dwFlags & ~EXCEPTION_SWALLOWING); 2062 if( pGetPolicy( &dwFlags))
2063 {
2064 // Turn off the filter
2065 pSetPolicy( dwFlags & ~EXCEPTION_SWALLOWING);
2066 }
2063 } 2067 }
2068 FreeLibrary(kernel32);
2064 } 2069 }
2065 //typedef void (* SignalHandlerPointer)( int); 2070 //typedef void (* SignalHandlerPointer)( int);
2066 /*SignalHandlerPointer previousHandler =*/ signal( SIGABRT, signal_handler); 2071 /*SignalHandlerPointer previousHandler =*/ signal( SIGABRT, signal_handler);
@@ -2072,7 +2077,7 @@ static void EnableCrashingOnCrashes( void)
2072 while( !s_ecoc_go_ahead) { Sleep(1); } // changes threads 2077 while( !s_ecoc_go_ahead) { Sleep(1); } // changes threads
2073 } 2078 }
2074} 2079}
2075#endif // PLATFORM_WIN32 2080#endif // PLATFORM_WIN32 && !defined NDEBUG
2076 2081
2077int LANES_API luaopen_lanes_core( lua_State* L) 2082int LANES_API luaopen_lanes_core( lua_State* L)
2078{ 2083{
diff --git a/src/lanes.h b/src/lanes.h
index 7e1a2e5..62b9ea9 100644
--- a/src/lanes.h
+++ b/src/lanes.h
@@ -11,8 +11,8 @@
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 16 14#define LANES_VERSION_MINOR 17
15#define LANES_VERSION_PATCH 3 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))))
18#define LANES_VERSION_LESS_THAN(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR<MAJOR) || (LANES_VERSION_MAJOR==MAJOR && (LANES_VERSION_MINOR<MINOR || (LANES_VERSION_MINOR==MINOR && LANES_VERSION_PATCH<PATCH)))) 18#define LANES_VERSION_LESS_THAN(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 b4c0070..49900f9 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -70,6 +70,7 @@ lanes.configure = function( settings_)
70 local default_params = 70 local default_params =
71 { 71 {
72 nb_keepers = 1, 72 nb_keepers = 1,
73 keepers_gc_threshold = -1,
73 on_state_create = nil, 74 on_state_create = nil,
74 shutdown_timeout = 0.25, 75 shutdown_timeout = 0.25,
75 with_timers = true, 76 with_timers = true,
@@ -91,6 +92,10 @@ lanes.configure = function( settings_)
91 -- nb_keepers should be a number > 0 92 -- nb_keepers should be a number > 0
92 return type( val_) == "number" and val_ > 0 93 return type( val_) == "number" and val_ > 0
93 end, 94 end,
95 keepers_gc_threshold = function( val_)
96 -- keepers_gc_threshold should be a number
97 return type( val_) == "number"
98 end,
94 with_timers = boolean_param_checker, 99 with_timers = boolean_param_checker,
95 allocator = function( val_) 100 allocator = function( val_)
96 -- can be nil, "protected", or a function 101 -- can be nil, "protected", or a function
@@ -363,261 +368,263 @@ lanes.configure = function( settings_)
363 368
364 if settings.with_timers ~= false then 369 if settings.with_timers ~= false then
365 370
366 -- 371 --
367 -- On first 'require "lanes"', a timer lane is spawned that will maintain 372 -- On first 'require "lanes"', a timer lane is spawned that will maintain
368 -- timer tables and sleep in between the timer events. All interaction with 373 -- timer tables and sleep in between the timer events. All interaction with
369 -- the timer lane happens via a 'timer_gateway' Linda, which is common to 374 -- the timer lane happens via a 'timer_gateway' Linda, which is common to
370 -- all that 'require "lanes"'. 375 -- all that 'require "lanes"'.
371 -- 376 --
372 -- Linda protocol to timer lane: 377 -- Linda protocol to timer lane:
373 -- 378 --
374 -- TGW_KEY: linda_h, key, [wakeup_at_secs], [repeat_secs] 379 -- TGW_KEY: linda_h, key, [wakeup_at_secs], [repeat_secs]
375 -- 380 --
376 local TGW_KEY= "(timer control)" -- the key does not matter, a 'weird' key may help debugging 381 local TGW_KEY= "(timer control)" -- the key does not matter, a 'weird' key may help debugging
377 local TGW_QUERY, TGW_REPLY = "(timer query)", "(timer reply)" 382 local TGW_QUERY, TGW_REPLY = "(timer query)", "(timer reply)"
378 local first_time_key= "first time" 383 local first_time_key= "first time"
379
380 local first_time = timer_gateway:get( first_time_key) == nil
381 timer_gateway:set( first_time_key, true)
382 384
383 -- 385 local first_time = timer_gateway:get( first_time_key) == nil
384 -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally 386 timer_gateway:set( first_time_key, true)
385 -- has 'table' always declared)
386 --
387 if first_time then
388 387
389 local now_secs = core.now_secs 388 local now_secs = core.now_secs
390 assert( type( now_secs) == "function") 389 local wakeup_conv = core.wakeup_conv
391 ----- 390
392 -- Snore loop (run as a lane on the background)
393 --
394 -- High priority, to get trustworthy timings.
395 -- 391 --
396 -- We let the timer lane be a "free running" thread; no handle to it 392 -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally
397 -- remains. 393 -- has 'table' always declared)
398 -- 394 --
399 local timer_body = function() 395 if first_time then
400 set_debug_threadname( "LanesTimer") 396
401 -- 397 assert( type( now_secs) == "function")
402 -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h, 398 -----
403 -- [key]= { wakeup_secs [,period_secs] } [, ...] }, 399 -- Snore loop (run as a lane on the background)
404 -- }
405 --
406 -- Collection of all running timers, indexed with linda's & key.
407 -- 400 --
408 -- Note that we need to use the deep lightuserdata identifiers, instead 401 -- High priority, to get trustworthy timings.
409 -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple
410 -- entries for the same timer.
411 -- 402 --
412 -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but 403 -- We let the timer lane be a "free running" thread; no handle to it
413 -- also important to keep the Linda alive, even if all outside world threw 404 -- remains.
414 -- away pointers to it (which would ruin uniqueness of the deep pointer).
415 -- Now we're safe.
416 -- 405 --
417 local collection = {} 406 local timer_body = function()
418 local table_insert = assert( table.insert) 407 set_debug_threadname( "LanesTimer")
419 408 --
420 local get_timers = function() 409 -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h,
421 local r = {} 410 -- [key]= { wakeup_secs [,period_secs] } [, ...] },
422 for deep, t in pairs( collection) do 411 -- }
423 -- WR( tostring( deep)) 412 --
424 local l = t[deep] 413 -- Collection of all running timers, indexed with linda's & key.
425 for key, timer_data in pairs( t) do 414 --
426 if key ~= deep then 415 -- Note that we need to use the deep lightuserdata identifiers, instead
427 table_insert( r, {l, key, timer_data}) 416 -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple
417 -- entries for the same timer.
418 --
419 -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but
420 -- also important to keep the Linda alive, even if all outside world threw
421 -- away pointers to it (which would ruin uniqueness of the deep pointer).
422 -- Now we're safe.
423 --
424 local collection = {}
425 local table_insert = assert( table.insert)
426
427 local get_timers = function()
428 local r = {}
429 for deep, t in pairs( collection) do
430 -- WR( tostring( deep))
431 local l = t[deep]
432 for key, timer_data in pairs( t) do
433 if key ~= deep then
434 table_insert( r, {l, key, timer_data})
435 end
428 end 436 end
429 end 437 end
430 end 438 return r
431 return r 439 end -- get_timers()
432 end -- get_timers()
433
434 --
435 -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] )
436 --
437 local set_timer = function( linda, key, wakeup_at, period)
438 assert( wakeup_at == nil or wakeup_at > 0.0)
439 assert( period == nil or period > 0.0)
440
441 local linda_deep = linda:deep()
442 assert( linda_deep)
443 440
444 -- Find or make a lookup for this timer
445 -- 441 --
446 local t1 = collection[linda_deep] 442 -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] )
447 if not t1 then 443 --
448 t1 = { [linda_deep] = linda} -- proxy to use the Linda 444 local set_timer = function( linda, key, wakeup_at, period)
449 collection[linda_deep] = t1 445 assert( wakeup_at == nil or wakeup_at > 0.0)
450 end 446 assert( period == nil or period > 0.0)
451 447
452 if wakeup_at == nil then 448 local linda_deep = linda:deep()
453 -- Clear the timer 449 assert( linda_deep)
454 --
455 t1[key]= nil
456 450
457 -- Remove empty tables from collection; speeds timer checks and 451 -- Find or make a lookup for this timer
458 -- lets our 'safety reference' proxy be gc:ed as well.
459 -- 452 --
460 local empty = true 453 local t1 = collection[linda_deep]
461 for k, _ in pairs( t1) do 454 if not t1 then
462 if k ~= linda_deep then 455 t1 = { [linda_deep] = linda} -- proxy to use the Linda
463 empty = false 456 collection[linda_deep] = t1
464 break
465 end
466 end
467 if empty then
468 collection[linda_deep] = nil
469 end 457 end
470 458
471 -- Note: any unread timer value is left at 'linda[key]' intensionally; 459 if wakeup_at == nil then
472 -- clearing a timer just stops it. 460 -- Clear the timer
473 else 461 --
474 -- New timer or changing the timings 462 t1[key]= nil
475 --
476 local t2 = t1[key]
477 if not t2 then
478 t2= {}
479 t1[key]= t2
480 end
481 463
482 t2[1] = wakeup_at 464 -- Remove empty tables from collection; speeds timer checks and
483 t2[2] = period -- can be 'nil' 465 -- lets our 'safety reference' proxy be gc:ed as well.
484 end 466 --
485 end -- set_timer() 467 local empty = true
468 for k, _ in pairs( t1) do
469 if k ~= linda_deep then
470 empty = false
471 break
472 end
473 end
474 if empty then
475 collection[linda_deep] = nil
476 end
486 477
487 ----- 478 -- Note: any unread timer value is left at 'linda[key]' intensionally;
488 -- [next_wakeup_at]= check_timers() 479 -- clearing a timer just stops it.
489 -- Check timers, and wake up the ones expired (if any) 480 else
490 -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none). 481 -- New timer or changing the timings
491 local check_timers = function()
492 local now = now_secs()
493 local next_wakeup
494
495 for linda_deep,t1 in pairs(collection) do
496 for key,t2 in pairs(t1) do
497 -- 482 --
498 if key==linda_deep then 483 local t2 = t1[key]
499 -- no 'continue' in Lua :/ 484 if not t2 then
500 else 485 t2= {}
501 -- 't2': { wakeup_at_secs [,period_secs] } 486 t1[key]= t2
487 end
488
489 t2[1] = wakeup_at
490 t2[2] = period -- can be 'nil'
491 end
492 end -- set_timer()
493
494 -----
495 -- [next_wakeup_at]= check_timers()
496 -- Check timers, and wake up the ones expired (if any)
497 -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none).
498 local check_timers = function()
499 local now = now_secs()
500 local next_wakeup
501
502 for linda_deep,t1 in pairs(collection) do
503 for key,t2 in pairs(t1) do
502 -- 504 --
503 local wakeup_at= t2[1] 505 if key==linda_deep then
504 local period= t2[2] -- may be 'nil' 506 -- no 'continue' in Lua :/
505 507 else
506 if wakeup_at <= now then 508 -- 't2': { wakeup_at_secs [,period_secs] }
507 local linda= t1[linda_deep] 509 --
508 assert(linda) 510 local wakeup_at= t2[1]
509 511 local period= t2[2] -- may be 'nil'
510 linda:set( key, now ) 512
511 513 if wakeup_at <= now then
512 -- 'pairs()' allows the values to be modified (and even 514 local linda= t1[linda_deep]
513 -- removed) as far as keys are not touched 515 assert(linda)
514 516
515 if not period then 517 linda:set( key, now )
516 -- one-time timer; gone 518
517 -- 519 -- 'pairs()' allows the values to be modified (and even
518 t1[key]= nil 520 -- removed) as far as keys are not touched
519 wakeup_at= nil -- no 'continue' in Lua :/ 521
520 else 522 if not period then
521 -- repeating timer; find next wakeup (may jump multiple repeats) 523 -- one-time timer; gone
522 -- 524 --
523 repeat 525 t1[key]= nil
524 wakeup_at= wakeup_at+period 526 wakeup_at= nil -- no 'continue' in Lua :/
525 until wakeup_at > now 527 else
526 528 -- repeating timer; find next wakeup (may jump multiple repeats)
527 t2[1]= wakeup_at 529 --
530 repeat
531 wakeup_at= wakeup_at+period
532 until wakeup_at > now
533
534 t2[1]= wakeup_at
535 end
528 end 536 end
529 end
530 537
531 if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then 538 if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then
532 next_wakeup= wakeup_at 539 next_wakeup= wakeup_at
540 end
533 end 541 end
542 end -- t2 loop
543 end -- t1 loop
544
545 return next_wakeup -- may be 'nil'
546 end -- check_timers()
547
548 local timer_gateway_batched = timer_gateway.batched
549 set_finalizer( function( err, stk)
550 if err and type( err) ~= "userdata" then
551 WR( "LanesTimer error: "..tostring(err))
552 --elseif type( err) == "userdata" then
553 -- WR( "LanesTimer after cancel" )
554 --else
555 -- WR("LanesTimer finalized")
556 end
557 end)
558 while true do
559 local next_wakeup = check_timers()
560
561 -- Sleep until next timer to wake up, or a set/clear command
562 --
563 local secs
564 if next_wakeup then
565 secs = next_wakeup - now_secs()
566 if secs < 0 then secs = 0 end
567 end
568 local key, what = timer_gateway:receive( secs, TGW_KEY, TGW_QUERY)
569
570 if key == TGW_KEY then
571 assert( getmetatable( what) == "Linda") -- 'what' should be a linda on which the client sets a timer
572 local _, key, wakeup_at, period = timer_gateway:receive( 0, timer_gateway_batched, TGW_KEY, 3)
573 assert( key)
574 set_timer( what, key, wakeup_at, period and period > 0 and period or nil)
575 elseif key == TGW_QUERY then
576 if what == "get_timers" then
577 timer_gateway:send( TGW_REPLY, get_timers())
578 else
579 timer_gateway:send( TGW_REPLY, "unknown query " .. what)
534 end 580 end
535 end -- t2 loop 581 --elseif secs == nil then -- got no value while block-waiting?
536 end -- t1 loop 582 -- WR( "timer lane: no linda, aborted?")
537 583 end
538 return next_wakeup -- may be 'nil'
539 end -- check_timers()
540
541 local timer_gateway_batched = timer_gateway.batched
542 set_finalizer( function( err, stk)
543 if err and type( err) ~= "userdata" then
544 WR( "LanesTimer error: "..tostring(err))
545 --elseif type( err) == "userdata" then
546 -- WR( "LanesTimer after cancel" )
547 --else
548 -- WR("LanesTimer finalized")
549 end 584 end
550 end) 585 end -- timer_body()
551 while true do 586 timer_lane = gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility...
552 local next_wakeup = check_timers() 587 end -- first_time
553 588
554 -- Sleep until next timer to wake up, or a set/clear command 589 -----
590 -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] )
591 --
592 -- PUBLIC LANES API
593 timer = function( linda, key, a, period )
594 if getmetatable( linda) ~= "Linda" then
595 error "expecting a Linda"
596 end
597 if a == 0.0 then
598 -- Caller expects to get current time stamp in Linda, on return
599 -- (like the timer had expired instantly); it would be good to set this
600 -- as late as possible (to give most current time) but also we want it
601 -- to precede any possible timers that might start striking.
555 -- 602 --
556 local secs 603 linda:set( key, now_secs())
557 if next_wakeup then 604
558 secs = next_wakeup - now_secs() 605 if not period or period==0.0 then
559 if secs < 0 then secs = 0 end 606 timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer
560 end 607 return -- nothing more to do
561 local key, what = timer_gateway:receive( secs, TGW_KEY, TGW_QUERY)
562
563 if key == TGW_KEY then
564 assert( getmetatable( what) == "Linda") -- 'what' should be a linda on which the client sets a timer
565 local _, key, wakeup_at, period = timer_gateway:receive( 0, timer_gateway_batched, TGW_KEY, 3)
566 assert( key)
567 set_timer( what, key, wakeup_at, period and period > 0 and period or nil)
568 elseif key == TGW_QUERY then
569 if what == "get_timers" then
570 timer_gateway:send( TGW_REPLY, get_timers())
571 else
572 timer_gateway:send( TGW_REPLY, "unknown query " .. what)
573 end
574 --elseif secs == nil then -- got no value while block-waiting?
575 -- WR( "timer lane: no linda, aborted?")
576 end 608 end
609 a= period
577 end 610 end
578 end -- timer_body()
579 timer_lane = gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility...
580 end -- first_time
581 611
582 ----- 612 local wakeup_at= type(a)=="table" and wakeup_conv(a) -- given point of time
583 -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) 613 or (a and now_secs()+a or nil)
584 -- 614 -- queue to timer
585 -- PUBLIC LANES API
586 timer = function( linda, key, a, period )
587 if getmetatable( linda) ~= "Linda" then
588 error "expecting a Linda"
589 end
590 if a == 0.0 then
591 -- Caller expects to get current time stamp in Linda, on return
592 -- (like the timer had expired instantly); it would be good to set this
593 -- as late as possible (to give most current time) but also we want it
594 -- to precede any possible timers that might start striking.
595 -- 615 --
596 linda:set( key, core.now_secs()) 616 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period )
617 end -- timer()
597 618
598 if not period or period==0.0 then 619 -----
599 timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer 620 -- {[{linda, slot, when, period}[,...]]} = timers()
600 return -- nothing more to do
601 end
602 a= period
603 end
604
605 local wakeup_at= type(a)=="table" and core.wakeup_conv(a) -- given point of time
606 or (a and core.now_secs()+a or nil)
607 -- queue to timer
608 -- 621 --
609 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) 622 -- PUBLIC LANES API
610 end 623 timers = function()
611 624 timer_gateway:send( TGW_QUERY, "get_timers")
612 ----- 625 local _, r = timer_gateway:receive( TGW_REPLY)
613 -- {[{linda, slot, when, period}[,...]]} = timers() 626 return r
614 -- 627 end -- timers()
615 -- PUBLIC LANES API
616 timers = function()
617 timer_gateway:send( TGW_QUERY, "get_timers")
618 local _, r = timer_gateway:receive( TGW_REPLY)
619 return r
620 end
621 628
622 end -- settings.with_timers 629 end -- settings.with_timers
623 630
diff --git a/src/lanes_private.h b/src/lanes_private.h
index 6717fe0..8143216 100644
--- a/src/lanes_private.h
+++ b/src/lanes_private.h
@@ -72,18 +72,24 @@ struct s_Lane
72}; 72};
73typedef struct s_Lane Lane; 73typedef struct s_Lane Lane;
74 74
75// xxh64 of string "LANE_POINTER_REGKEY" generated at https://www.pelock.com/products/hash-calculator
76static DECLARE_CONST_UNIQUE_KEY( LANE_POINTER_REGKEY, 0xB3022205633743BC); // used as registry key
77
75// To allow free-running threads (longer lifespan than the handle's) 78// To allow free-running threads (longer lifespan than the handle's)
76// 'Lane' are malloc/free'd and the handle only carries a pointer. 79// 'Lane' are malloc/free'd and the handle only carries a pointer.
77// This is not deep userdata since the handle's not portable among lanes. 80// This is not deep userdata since the handle's not portable among lanes.
78// 81//
79#define lua_toLane( L, i) (*((Lane**) luaL_checkudata( L, i, "Lane"))) 82inline Lane* lua_toLane(lua_State* L, int i_)
83{
84 return *(Lane**)(luaL_checkudata(L, i_, "Lane"));
85}
80 86
81static inline Lane* get_lane_from_registry( lua_State* L) 87static inline Lane* get_lane_from_registry( lua_State* L)
82{ 88{
83 Lane* s; 89 Lane* s;
84 STACK_GROW( L, 1); 90 STACK_GROW( L, 1);
85 STACK_CHECK( L, 0); 91 STACK_CHECK( L, 0);
86 REGISTRY_GET( L, CANCEL_TEST_KEY); 92 REGISTRY_GET( L, LANE_POINTER_REGKEY);
87 s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil 93 s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil
88 lua_pop( L, 1); 94 lua_pop( L, 1);
89 STACK_END( L, 0); 95 STACK_END( L, 0);
diff --git a/src/linda.c b/src/linda.c
index 8b59790..2128520 100644
--- a/src/linda.c
+++ b/src/linda.c
@@ -52,11 +52,11 @@ struct s_Linda
52 SIGNAL_T read_happened; 52 SIGNAL_T read_happened;
53 SIGNAL_T write_happened; 53 SIGNAL_T write_happened;
54 Universe* U; // the universe this linda belongs to 54 Universe* U; // the universe this linda belongs to
55 ptrdiff_t group; // a group to control keeper allocation between lindas 55 uintptr_t group; // a group to control keeper allocation between lindas
56 enum e_cancel_request simulate_cancel; 56 enum e_cancel_request simulate_cancel;
57 char name[1]; 57 char name[1];
58}; 58};
59#define LINDA_KEEPER_HASHSEED( linda) (linda->group ? linda->group : (ptrdiff_t)linda) 59#define LINDA_KEEPER_HASHSEED( linda) (linda->group ? linda->group : (uintptr_t)linda)
60 60
61static void* linda_id( lua_State*, DeepOp); 61static void* linda_id( lua_State*, DeepOp);
62 62
@@ -125,7 +125,7 @@ LUAG_FUNC( linda_send)
125 enum e_cancel_request cancel = CANCEL_NONE; 125 enum e_cancel_request cancel = CANCEL_NONE;
126 int pushed; 126 int pushed;
127 time_d timeout = -1.0; 127 time_d timeout = -1.0;
128 uint_t key_i = 2; // index of first key, if timeout not there 128 int key_i = 2; // index of first key, if timeout not there
129 bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided 129 bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided
130 130
131 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion 131 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion
@@ -151,7 +151,7 @@ LUAG_FUNC( linda_send)
151 STACK_GROW( L, 1); 151 STACK_GROW( L, 1);
152 152
153 // make sure there is something to send 153 // make sure there is something to send
154 if( (uint_t)lua_gettop( L) == key_i) 154 if( lua_gettop( L) == key_i)
155 { 155 {
156 if( as_nil_sentinel) 156 if( as_nil_sentinel)
157 { 157 {
@@ -270,16 +270,17 @@ LUAG_FUNC( linda_send)
270 * returns the actual consumed values, or nil if there weren't enough values to consume 270 * returns the actual consumed values, or nil if there weren't enough values to consume
271 * 271 *
272 */ 272 */
273#define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" 273 // xxh64 of string "BATCH_SENTINEL" generated at https://www.pelock.com/products/hash-calculator
274DECLARE_CONST_UNIQUE_KEY(BATCH_SENTINEL, 0x2DDFEE0968C62AA7);
274LUAG_FUNC( linda_receive) 275LUAG_FUNC( linda_receive)
275{ 276{
276 struct s_Linda* linda = lua_toLinda( L, 1); 277 struct s_Linda* linda = lua_toLinda( L, 1);
277 int pushed, expected_pushed_min, expected_pushed_max; 278 int pushed, expected_pushed_min, expected_pushed_max;
278 enum e_cancel_request cancel = CANCEL_NONE; 279 enum e_cancel_request cancel = CANCEL_NONE;
279 keeper_api_t keeper_receive; 280 keeper_api_t selected_keeper_receive;
280 281
281 time_d timeout = -1.0; 282 time_d timeout = -1.0;
282 uint_t key_i = 2; 283 int key_i = 2;
283 284
284 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion 285 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion
285 { 286 {
@@ -294,7 +295,7 @@ LUAG_FUNC( linda_receive)
294 // are we in batched mode? 295 // are we in batched mode?
295 { 296 {
296 int is_batched; 297 int is_batched;
297 lua_pushliteral( L, BATCH_SENTINEL); 298 push_unique_key( L, BATCH_SENTINEL);
298 is_batched = lua501_equal( L, key_i, -1); 299 is_batched = lua501_equal( L, key_i, -1);
299 lua_pop( L, 1); 300 lua_pop( L, 1);
300 if( is_batched) 301 if( is_batched)
@@ -304,7 +305,7 @@ LUAG_FUNC( linda_receive)
304 // make sure the keys are of a valid type 305 // make sure the keys are of a valid type
305 check_key_types( L, key_i, key_i); 306 check_key_types( L, key_i, key_i);
306 // receive multiple values from a single slot 307 // receive multiple values from a single slot
307 keeper_receive = KEEPER_API( receive_batched); 308 selected_keeper_receive = KEEPER_API( receive_batched);
308 // we expect a user-defined amount of return value 309 // we expect a user-defined amount of return value
309 expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1); 310 expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1);
310 expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); 311 expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min);
@@ -321,7 +322,7 @@ LUAG_FUNC( linda_receive)
321 // make sure the keys are of a valid type 322 // make sure the keys are of a valid type
322 check_key_types( L, key_i, lua_gettop( L)); 323 check_key_types( L, key_i, lua_gettop( L));
323 // receive a single value, checking multiple slots 324 // receive a single value, checking multiple slots
324 keeper_receive = KEEPER_API( receive); 325 selected_keeper_receive = KEEPER_API( receive);
325 // we expect a single (value, key) pair of returned values 326 // we expect a single (value, key) pair of returned values
326 expected_pushed_min = expected_pushed_max = 2; 327 expected_pushed_min = expected_pushed_max = 2;
327 } 328 }
@@ -347,7 +348,7 @@ LUAG_FUNC( linda_receive)
347 } 348 }
348 349
349 // all arguments of receive() but the first are passed to the keeper's receive function 350 // all arguments of receive() but the first are passed to the keeper's receive function
350 pushed = keeper_call( linda->U, K->L, keeper_receive, L, linda, key_i); 351 pushed = keeper_call( linda->U, K->L, selected_keeper_receive, L, linda, key_i);
351 if( pushed < 0) 352 if( pushed < 0)
352 { 353 {
353 break; 354 break;
@@ -511,29 +512,27 @@ LUAG_FUNC( linda_get)
511 512
512 // make sure the key is of a valid type (throws an error if not the case) 513 // make sure the key is of a valid type (throws an error if not the case)
513 check_key_types( L, 2, 2); 514 check_key_types( L, 2, 2);
514 {
515 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
516 515
517 if( linda->simulate_cancel == CANCEL_NONE) 516 if( linda->simulate_cancel == CANCEL_NONE)
518 { 517 {
519 pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2); 518 Keeper* const K = which_keeper(linda->U->keepers, LINDA_KEEPER_HASHSEED(linda));
520 if( pushed > 0) 519 pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2);
521 { 520 if( pushed > 0)
522 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper);
523 }
524 }
525 else // linda is cancelled
526 {
527 // do nothing and return lanes.cancel_error
528 push_unique_key( L, CANCEL_ERROR);
529 pushed = 1;
530 }
531 // an error can be raised if we attempt to read an unregistered function
532 if( pushed < 0)
533 { 521 {
534 return luaL_error( L, "tried to copy unsupported types"); 522 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper);
535 } 523 }
536 } 524 }
525 else // linda is cancelled
526 {
527 // do nothing and return lanes.cancel_error
528 push_unique_key( L, CANCEL_ERROR);
529 pushed = 1;
530 }
531 // an error can be raised if we attempt to read an unregistered function
532 if( pushed < 0)
533 {
534 return luaL_error( L, "tried to copy unsupported types");
535 }
537 536
538 return pushed; 537 return pushed;
539} 538}
@@ -557,26 +556,23 @@ LUAG_FUNC( linda_limit)
557 // make sure the key is of a valid type 556 // make sure the key is of a valid type
558 check_key_types( L, 2, 2); 557 check_key_types( L, 2, 2);
559 558
559 if( linda->simulate_cancel == CANCEL_NONE)
560 { 560 {
561 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 561 Keeper* const K = which_keeper(linda->U->keepers, LINDA_KEEPER_HASHSEED(linda));
562 562 pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2);
563 if( linda->simulate_cancel == CANCEL_NONE) 563 ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads
564 { 564 if( pushed == 1)
565 pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2);
566 ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads
567 if( pushed == 1)
568 {
569 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1);
570 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area
571 }
572 }
573 else // linda is cancelled
574 { 565 {
575 // do nothing and return lanes.cancel_error 566 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1);
576 push_unique_key( L, CANCEL_ERROR); 567 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area
577 pushed = 1;
578 } 568 }
579 } 569 }
570 else // linda is cancelled
571 {
572 // do nothing and return lanes.cancel_error
573 push_unique_key( L, CANCEL_ERROR);
574 pushed = 1;
575 }
580 // propagate pushed boolean if any 576 // propagate pushed boolean if any
581 return pushed; 577 return pushed;
582} 578}
@@ -762,6 +758,7 @@ static void* linda_id( lua_State* L, DeepOp op_)
762 { 758 {
763 case eDO_new: 759 case eDO_new:
764 { 760 {
761 Universe* const U = universe_get(L);
765 struct s_Linda* s; 762 struct s_Linda* s;
766 size_t name_len = 0; 763 size_t name_len = 0;
767 char const* linda_name = NULL; 764 char const* linda_name = NULL;
@@ -795,7 +792,6 @@ static void* linda_id( lua_State* L, DeepOp op_)
795 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda 792 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda
796 */ 793 */
797 { 794 {
798 Universe* const U = universe_get(L);
799 AllocatorDefinition* const allocD = &U->internal_allocator; 795 AllocatorDefinition* const allocD = &U->internal_allocator;
800 s = (struct s_Linda*) allocD->allocF(allocD->allocUD, NULL, 0, sizeof(struct s_Linda) + name_len); // terminating 0 is already included 796 s = (struct s_Linda*) allocD->allocF(allocD->allocUD, NULL, 0, sizeof(struct s_Linda) + name_len); // terminating 0 is already included
801 } 797 }
@@ -804,7 +800,7 @@ static void* linda_id( lua_State* L, DeepOp op_)
804 s->prelude.magic.value = DEEP_VERSION.value; 800 s->prelude.magic.value = DEEP_VERSION.value;
805 SIGNAL_INIT( &s->read_happened); 801 SIGNAL_INIT( &s->read_happened);
806 SIGNAL_INIT( &s->write_happened); 802 SIGNAL_INIT( &s->write_happened);
807 s->U = universe_get( L); 803 s->U = U;
808 s->simulate_cancel = CANCEL_NONE; 804 s->simulate_cancel = CANCEL_NONE;
809 s->group = linda_group << KEEPER_MAGIC_SHIFT; 805 s->group = linda_group << KEEPER_MAGIC_SHIFT;
810 s->name[0] = 0; 806 s->name[0] = 0;
@@ -815,25 +811,32 @@ static void* linda_id( lua_State* L, DeepOp op_)
815 811
816 case eDO_delete: 812 case eDO_delete:
817 { 813 {
818 Keeper* K; 814 Keeper* myK;
819 struct s_Linda* linda = lua_touserdata( L, 1); 815 struct s_Linda* linda = lua_touserdata( L, 1);
820 ASSERT_L( linda); 816 ASSERT_L( linda);
821 817
822 // Clean associated structures in the keeper state. 818 // Clean associated structures in the keeper state.
823 K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 819 myK = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
824 if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) 820 if (myK)
825 { 821 {
822 // if collected from my own keeper, we can't acquire/release it
823 // because we are already inside a protected area, and trying to do so would deadlock!
824 bool_t const need_acquire_release = (myK->L != L);
825 // Clean associated structures in the keeper state.
826 Keeper* const K = need_acquire_release ? keeper_acquire(linda->U->keepers, LINDA_KEEPER_HASHSEED(linda)) : myK;
826 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... 827 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex...
827 keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); 828 keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0);
829 if(need_acquire_release)
830 {
831 keeper_release(K);
832 }
828 } 833 }
829 keeper_release( K);
830 834
831 // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right? 835 // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right?
832 SIGNAL_FREE( &linda->read_happened); 836 SIGNAL_FREE( &linda->read_happened);
833 SIGNAL_FREE( &linda->write_happened); 837 SIGNAL_FREE( &linda->write_happened);
834 { 838 {
835 Universe* const U = universe_get(L); 839 AllocatorDefinition* const allocD = &linda->U->internal_allocator;
836 AllocatorDefinition* const allocD = &U->internal_allocator;
837 (void) allocD->allocF(allocD->allocUD, linda, sizeof(struct s_Linda) + strlen(linda->name), 0); 840 (void) allocD->allocF(allocD->allocUD, linda, sizeof(struct s_Linda) + strlen(linda->name), 0);
838 } 841 }
839 return NULL; 842 return NULL;
@@ -901,7 +904,7 @@ static void* linda_id( lua_State* L, DeepOp op_)
901 lua_setfield( L, -2, "dump"); 904 lua_setfield( L, -2, "dump");
902 905
903 // some constants 906 // some constants
904 lua_pushliteral( L, BATCH_SENTINEL); 907 push_unique_key( L, BATCH_SENTINEL);
905 lua_setfield( L, -2, "batched"); 908 lua_setfield( L, -2, "batched");
906 909
907 push_unique_key( L, NIL_SENTINEL); 910 push_unique_key( L, NIL_SENTINEL);
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h
index 05a46b5..e184476 100644
--- a/src/macros_and_utils.h
+++ b/src/macros_and_utils.h
@@ -6,6 +6,7 @@
6 6
7#include "lua.h" 7#include "lua.h"
8#include "lualib.h" 8#include "lualib.h"
9#include "lauxlib.h"
9 10
10 // M$ compiler doesn't support 'inline' keyword in C files... 11 // M$ compiler doesn't support 'inline' keyword in C files...
11#if defined( _MSC_VER) 12#if defined( _MSC_VER)
@@ -81,7 +82,11 @@ extern char const* debugspew_indent;
81 82
82#define ASSERT_L(c) _ASSERT_L(L,c) 83#define ASSERT_L(c) _ASSERT_L(L,c)
83 84
84#define STACK_GROW( L, n) do { if (!lua_checkstack(L,(int)(n))) luaL_error( L, "Cannot grow stack!" ); } while( 0) 85inline void STACK_GROW(lua_State * L, int n_)
86{
87 if (!lua_checkstack(L, n_))
88 luaL_error(L, "Cannot grow stack!");
89}
85 90
86// non-string keyed registry access 91// non-string keyed registry access
87#define REGISTRY_SET( L, key_, value_) \ 92#define REGISTRY_SET( L, key_, value_) \
diff --git a/src/platform.h b/src/platform.h
index da5264e..2f71c07 100644
--- a/src/platform.h
+++ b/src/platform.h
@@ -7,6 +7,7 @@
7 #define PLATFORM_XBOX 7 #define PLATFORM_XBOX
8#elif (defined _WIN32) 8#elif (defined _WIN32)
9 #define PLATFORM_WIN32 9 #define PLATFORM_WIN32
10 #define NOMINMAX
10#elif (defined __linux__) 11#elif (defined __linux__)
11 #define PLATFORM_LINUX 12 #define PLATFORM_LINUX
12#elif (defined __APPLE__) && (defined __MACH__) 13#elif (defined __APPLE__) && (defined __MACH__)
diff --git a/src/state.c b/src/state.c
index 21ca397..32e5b47 100644
--- a/src/state.c
+++ b/src/state.c
@@ -205,7 +205,7 @@ static void copy_one_time_settings( Universe* U, lua_State* L, lua_State* L2)
205 205
206 REGISTRY_GET( L, CONFIG_REGKEY); // config 206 REGISTRY_GET( L, CONFIG_REGKEY); // config
207 // copy settings from from source to destination registry 207 // copy settings from from source to destination registry
208 if( luaG_inter_move( U, L, L2, 1, eLM_LaneBody) < 0) // // config 208 if( luaG_inter_move( U, L, L2, 1, eLM_LaneBody) != eICR_Success) // // config
209 { 209 {
210 (void) luaL_error( L, "failed to copy settings when loading lanes.core"); 210 (void) luaL_error( L, "failed to copy settings when loading lanes.core");
211 } 211 }
diff --git a/src/tools.c b/src/tools.c
index 80e0f71..c43d8a2 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -242,8 +242,14 @@ void initialize_allocator_function( Universe* U, lua_State* L)
242 U->internal_allocator.allocF = libc_lua_Alloc; 242 U->internal_allocator.allocF = libc_lua_Alloc;
243 U->internal_allocator.allocUD = NULL; 243 U->internal_allocator.allocUD = NULL;
244 } 244 }
245 else if (U->provide_allocator == luaG_provide_protected_allocator)
246 {
247 // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case.
248 U->internal_allocator.allocF = lua_getallocf(L, &U->internal_allocator.allocUD);
249 }
245 else 250 else
246 { 251 {
252 // no protection required, just use whatever we have as-is.
247 U->internal_allocator = U->protected_allocator.definition; 253 U->internal_allocator = U->protected_allocator.definition;
248 } 254 }
249 } 255 }
@@ -844,8 +850,8 @@ static bool_t lookup_table( lua_State* L2, lua_State* L, uint_t i, LookupMode mo
844 */ 850 */
845static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) 851static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i)
846{ 852{
847 bool_t not_found_in_cache; // L2 853 bool_t not_found_in_cache; // L2
848 DECLARE_CONST_UNIQUE_KEY( p, lua_topointer( L, i)); 854 void const* p = lua_topointer( L, i);
849 855
850 ASSERT_L( L2_cache_i != 0); 856 ASSERT_L( L2_cache_i != 0);
851 STACK_GROW( L2, 3); 857 STACK_GROW( L2, 3);
@@ -854,17 +860,17 @@ static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L,
854 // We don't need to use the from state ('L') in ID since the life span 860 // We don't need to use the from state ('L') in ID since the life span
855 // is only for the duration of a copy (both states are locked). 861 // is only for the duration of a copy (both states are locked).
856 // push a light userdata uniquely representing the table 862 // push a light userdata uniquely representing the table
857 push_unique_key( L2, p); // ... p 863 lua_pushlightuserdata( L2, (void*) p); // ... p
858 864
859 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); 865 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1));
860 866
861 lua_rawget( L2, L2_cache_i); // ... {cached|nil} 867 lua_rawget( L2, L2_cache_i); // ... {cached|nil}
862 not_found_in_cache = lua_isnil( L2, -1); 868 not_found_in_cache = lua_isnil( L2, -1);
863 if( not_found_in_cache) 869 if( not_found_in_cache)
864 { 870 {
865 lua_pop( L2, 1); // ... 871 lua_pop( L2, 1); // ...
866 lua_newtable( L2); // ... {} 872 lua_newtable( L2); // ... {}
867 push_unique_key( L2, p); // ... {} p 873 lua_pushlightuserdata( L2, (void*) p); // ... {} p
868 lua_pushvalue( L2, -2); // ... {} p {} 874 lua_pushvalue( L2, -2); // ... {} p {}
869 lua_rawset( L2, L2_cache_i); // ... {} 875 lua_rawset( L2, L2_cache_i); // ... {}
870 } 876 }
@@ -1446,7 +1452,7 @@ static void inter_copy_keyvaluepair( Universe* U, lua_State* L2, uint_t L2_cache
1446 uint_t key_i = val_i - 1; 1452 uint_t key_i = val_i - 1;
1447 1453
1448 // Only basic key types are copied over; others ignored 1454 // Only basic key types are copied over; others ignored
1449 if( inter_copy_one( U, L2, 0 /*key*/, L, key_i, VT_KEY, mode_, upName_)) 1455 if( inter_copy_one( U, L2, L2_cache_i, L, key_i, VT_KEY, mode_, upName_))
1450 { 1456 {
1451 char* valPath = (char*) upName_; 1457 char* valPath = (char*) upName_;
1452 if( U->verboseErrors) 1458 if( U->verboseErrors)
@@ -1596,7 +1602,10 @@ static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_Stat
1596 // assign uservalues 1602 // assign uservalues
1597 while( uvi > 0) 1603 while( uvi > 0)
1598 { 1604 {
1599 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // ... u uv 1605 if(!inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_)) // ... u uv
1606 {
1607 (void) luaL_error(L, "Cannot copy upvalue type '%s'", luaL_typename(L, -1));
1608 }
1600 lua_pop( L, 1); // ... mt __lanesclone [uv]* 1609 lua_pop( L, 1); // ... mt __lanesclone [uv]*
1601 // this pops the value from the stack 1610 // this pops the value from the stack
1602 lua_setiuservalue( L2, -2, uvi); // ... u 1611 lua_setiuservalue( L2, -2, uvi); // ... u
@@ -1730,7 +1739,10 @@ static bool_t inter_copy_function( Universe* U, lua_State* L2, uint_t L2_cache_i
1730 // transfer and assign uservalues 1739 // transfer and assign uservalues
1731 while( uvi > 0) 1740 while( uvi > 0)
1732 { 1741 {
1733 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), vt, mode_, upName_); // ... mt u uv 1742 if(!inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), vt, mode_, upName_)) // ... mt u uv
1743 {
1744 (void) luaL_error(L, "Cannot copy upvalue type '%s'", luaL_typename(L, -1));
1745 }
1734 lua_pop( L, 1); // ... u [uv]* 1746 lua_pop( L, 1); // ... u [uv]*
1735 // this pops the value from the stack 1747 // this pops the value from the stack
1736 lua_setiuservalue( L2, -2, uvi); // ... mt u 1748 lua_setiuservalue( L2, -2, uvi); // ... mt u
@@ -1957,7 +1969,7 @@ bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State*
1957* 1969*
1958* Note: Parameters are in this order ('L' = from first) to be same as 'lua_xmove'. 1970* Note: Parameters are in this order ('L' = from first) to be same as 'lua_xmove'.
1959*/ 1971*/
1960int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_) 1972InterCopyResult luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_)
1961{ 1973{
1962 uint_t top_L = lua_gettop( L); // ... {}n 1974 uint_t top_L = lua_gettop( L); // ... {}n
1963 uint_t top_L2 = lua_gettop( L2); // ... 1975 uint_t top_L2 = lua_gettop( L2); // ...
@@ -1974,7 +1986,7 @@ int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupM
1974 // requesting to copy more than is available? 1986 // requesting to copy more than is available?
1975 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END)); 1987 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END));
1976 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1988 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1977 return -1; 1989 return eICR_NotEnoughValues;
1978 } 1990 }
1979 1991
1980 STACK_CHECK( L2, 0); 1992 STACK_CHECK( L2, 0);
@@ -2010,24 +2022,24 @@ int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupM
2010 // Remove the cache table. Persistent caching would cause i.e. multiple 2022 // Remove the cache table. Persistent caching would cause i.e. multiple
2011 // messages passed in the same table to use the same table also in receiving end. 2023 // messages passed in the same table to use the same table also in receiving end.
2012 lua_remove( L2, top_L2 + 1); 2024 lua_remove( L2, top_L2 + 1);
2013 return 0; 2025 return eICR_Success;
2014 } 2026 }
2015 2027
2016 // error -> pop everything from the target state stack 2028 // error -> pop everything from the target state stack
2017 lua_settop( L2, top_L2); 2029 lua_settop( L2, top_L2);
2018 STACK_END( L2, 0); 2030 STACK_END( L2, 0);
2019 return -2; 2031 return eICR_Error;
2020} 2032}
2021 2033
2022 2034
2023int luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_) 2035InterCopyResult luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_)
2024{ 2036{
2025 int ret = luaG_inter_copy( U, L, L2, n, mode_); 2037 InterCopyResult ret = luaG_inter_copy( U, L, L2, n, mode_);
2026 lua_pop( L, (int) n); 2038 lua_pop( L, (int) n);
2027 return ret; 2039 return ret;
2028} 2040}
2029 2041
2030int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_) 2042InterCopyResult luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_)
2031{ 2043{
2032 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_inter_copy_package()\n" INDENT_END)); 2044 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_inter_copy_package()\n" INDENT_END));
2033 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 2045 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
@@ -2040,7 +2052,11 @@ int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int packa
2040 lua_pushfstring( L, "expected package as table, got %s", luaL_typename( L, package_idx_)); 2052 lua_pushfstring( L, "expected package as table, got %s", luaL_typename( L, package_idx_));
2041 STACK_MID( L, 1); 2053 STACK_MID( L, 1);
2042 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later 2054 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
2043 return ( mode_ == eLM_LaneBody) ? lua_error( L) : 1; 2055 if (mode_ == eLM_LaneBody)
2056 {
2057 lua_error(L); // doesn't return
2058 }
2059 return eICR_Error;
2044 } 2060 }
2045 lua_getglobal( L2, "package"); 2061 lua_getglobal( L2, "package");
2046 if( !lua_isnil( L2, -1)) // package library not loaded: do nothing 2062 if( !lua_isnil( L2, -1)) // package library not loaded: do nothing
@@ -2076,5 +2092,5 @@ int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int packa
2076 STACK_END( L2, 0); 2092 STACK_END( L2, 0);
2077 STACK_END( L, 0); 2093 STACK_END( L, 0);
2078 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 2094 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
2079 return 0; 2095 return eICR_Success;
2080} 2096}
diff --git a/src/tools.h b/src/tools.h
index a0893e4..6c08734 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -31,14 +31,22 @@ enum e_vt
31 VT_KEY, 31 VT_KEY,
32 VT_METATABLE 32 VT_METATABLE
33}; 33};
34
35enum eInterCopyResult
36{
37 eICR_Success,
38 eICR_NotEnoughValues,
39 eICR_Error
40};
41typedef enum eInterCopyResult InterCopyResult;
42
34bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_); 43bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_);
35 44
36// ################################################################################################ 45// ################################################################################################
37 46
38int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_); 47InterCopyResult luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_);
39 48InterCopyResult luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_);
40int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_); 49InterCopyResult luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_);
41int luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_);
42 50
43int luaG_nameof( lua_State* L); 51int luaG_nameof( lua_State* L);
44 52
diff --git a/src/uniquekey.h b/src/uniquekey.h
index 015fbf2..7162753 100644
--- a/src/uniquekey.h
+++ b/src/uniquekey.h
@@ -11,9 +11,9 @@ struct s_UniqueKey
11typedef struct s_UniqueKey UniqueKey; 11typedef struct s_UniqueKey UniqueKey;
12 12
13#if LUAJIT_FLAVOR() == 64 // building against LuaJIT headers for 64 bits, light userdata is restricted to 47 significant bits, because LuaJIT uses the other bits for internal optimizations 13#if LUAJIT_FLAVOR() == 64 // building against LuaJIT headers for 64 bits, light userdata is restricted to 47 significant bits, because LuaJIT uses the other bits for internal optimizations
14#define MAKE_UNIQUE_KEY( p_) ((void*)((ptrdiff_t)(p_) & 0x7fffffffffffull)) 14#define MAKE_UNIQUE_KEY( p_) ((void*)((uintptr_t)(p_) & 0x7fffffffffffull))
15#else // LUAJIT_FLAVOR() 15#else // LUAJIT_FLAVOR()
16#define MAKE_UNIQUE_KEY( p_) ((void*)(ptrdiff_t)(p_)) 16#define MAKE_UNIQUE_KEY( p_) ((void*)(uintptr_t)(p_))
17#endif // LUAJIT_FLAVOR() 17#endif // LUAJIT_FLAVOR()
18 18
19#define DECLARE_UNIQUE_KEY( name_) UniqueKey name_ 19#define DECLARE_UNIQUE_KEY( name_) UniqueKey name_
diff --git a/tests/basic.lua b/tests/basic.lua
index 385e22f..4b4fae6 100644
--- a/tests/basic.lua
+++ b/tests/basic.lua
@@ -154,7 +154,7 @@ PRINT(" "..st)
154assert( st == "cancelled" ) 154assert( st == "cancelled" )
155 155
156-- cancellation of lanes waiting on a linda 156-- cancellation of lanes waiting on a linda
157local limited = lanes.linda() 157local limited = lanes.linda("limited")
158limited:limit( "key", 1) 158limited:limit( "key", 1)
159-- [[################################################ 159-- [[################################################
160limited:send( "key", "hello") -- saturate linda 160limited:send( "key", "hello") -- saturate linda
@@ -234,7 +234,7 @@ local chunk= function( linda )
234 WR( "Lane ends!\n" ) 234 WR( "Lane ends!\n" )
235end 235end
236 236
237local linda= lanes_linda() 237local linda= lanes_linda("communications")
238assert( type(linda) == "userdata" ) 238assert( type(linda) == "userdata" )
239 -- 239 --
240 -- ["->"] master -> slave 240 -- ["->"] master -> slave
@@ -278,10 +278,12 @@ local complex_table = RECEIVE(); WR( type(complex_table).." received\n" )
278assert( complex_table[1] == complex_table[3] and complex_table[2] == complex_table[4]) 278assert( complex_table[1] == complex_table[3] and complex_table[2] == complex_table[4])
279WR( table.concat( {complex_table[1][1],complex_table[2][1],complex_table[3][1],complex_table[4][1]},", ")) 279WR( table.concat( {complex_table[1][1],complex_table[2][1],complex_table[3][1],complex_table[4][1]},", "))
280 280
281WR("collectgarbage")
281t = nil 282t = nil
282collectgarbage() 283collectgarbage()
283-- wait 284-- wait
284linda: receive( 1, "wait") 285WR("waiting 1s")
286linda:receive( 1, "wait")
285 287
286--############################################################## 288--##############################################################
287--############################################################## 289--##############################################################
@@ -336,6 +338,7 @@ for _, t in ipairs( stdlib_naming_tests) do
336 assert( f(t[1])[1] ) 338 assert( f(t[1])[1] )
337end 339end
338 340
341WR("collectgarbage")
339collectgarbage() 342collectgarbage()
340 343
341--############################################################## 344--##############################################################
@@ -361,12 +364,13 @@ local tc= lanes_gen( "io", {gc_cb = gc_cb},
361 end 364 end
362) 365)
363 366
364local linda= lanes_linda() 367local linda= lanes_linda("criss cross")
365 368
366local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms 369local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms
367 370
368local _= a[1],b[1] -- waits until they are both ready 371local _= a[1],b[1] -- waits until they are both ready
369 372
373WR("collectgarbage")
370a, b = nil 374a, b = nil
371collectgarbage() 375collectgarbage()
372 376
@@ -408,7 +412,7 @@ local function chunk2( linda )
408 linda:send( "up", function() return ":)" end, "ok2" ) 412 linda:send( "up", function() return ":)" end, "ok2" )
409end 413end
410 414
411local linda= lanes.linda() 415local linda= lanes.linda("linda")
412local t2= lanes_gen( "debug,string,io", {gc_cb = gc_cb}, chunk2 )(linda) -- prepare & launch 416local t2= lanes_gen( "debug,string,io", {gc_cb = gc_cb}, chunk2 )(linda) -- prepare & launch
413linda:send( "down", function(linda) linda:send( "up", "ready!" ) end, 417linda:send( "down", function(linda) linda:send( "up", "ready!" ) end,
414 "ok" ) 418 "ok" )
diff --git a/tests/cancel.lua b/tests/cancel.lua
index 0d9d143..4e57184 100644
--- a/tests/cancel.lua
+++ b/tests/cancel.lua
@@ -1,36 +1,47 @@
1local lanes = require "lanes" .configure{ with_timers = false} 1local which_tests, remaining_tests = {}, {}
2 2for k,v in ipairs{...} do
3local linda = lanes.linda() 3 print("got arg:", type(v), tostring(v))
4 which_tests[v] = true
5 remaining_tests[v] = true
6end
4 7
5--#################################################################### 8--####################################################################
6print "\n\n####################################################################\nbegin genlock & genatomic cancel test\n"
7
8-- get a lock and a atomic operator
9local lock = lanes.genlock( linda, "lock", 1)
10local atomic = lanes.genatomic( linda, "atomic")
11
12-- check that cancelled lindas give cancel_error as they should
13linda:cancel()
14assert( linda:get( "empty") == lanes.cancel_error)
15assert( lanes.genlock( linda, "any", 1) == lanes.cancel_error)
16assert( lanes.genatomic( linda, "any") == lanes.cancel_error)
17
18-- check that lock and atomic functions return cancel_error if the linda was cancelled
19assert( lock( 1) == lanes.cancel_error)
20assert( lock( -1) == lanes.cancel_error)
21assert( atomic( 1) == lanes.cancel_error)
22
23-- reset the linda so that the other tests work
24linda:cancel( "none")
25linda:limit( "lock", -1)
26linda:set( "lock")
27linda:limit( "atomic", -1)
28linda:set( "atomic")
29 9
10local lanes = require "lanes" .configure{ with_timers = false}
11
12local linda = lanes.linda()
30-- a numeric value to read 13-- a numeric value to read
31linda:set( "val", 33.0) 14linda:set( "val", 33.0)
32 15
33print "test OK" 16--####################################################################
17if not next(which_tests) or which_tests.genlock then
18 remaining_tests.genlock = nil
19 print "\n\n####################################################################\nbegin genlock & genatomic cancel test\n"
20
21 -- get a lock and a atomic operator
22 local lock = lanes.genlock( linda, "lock", 1)
23 local atomic = lanes.genatomic( linda, "atomic")
24
25 -- check that cancelled lindas give cancel_error as they should
26 linda:cancel()
27 assert( linda:get( "empty") == lanes.cancel_error)
28 assert( lanes.genlock( linda, "any", 1) == lanes.cancel_error)
29 assert( lanes.genatomic( linda, "any") == lanes.cancel_error)
30
31 -- check that lock and atomic functions return cancel_error if the linda was cancelled
32 assert( lock( 1) == lanes.cancel_error)
33 assert( lock( -1) == lanes.cancel_error)
34 assert( atomic( 1) == lanes.cancel_error)
35
36 -- reset the linda so that the other tests work
37 linda:cancel( "none")
38 linda:limit( "lock", -1)
39 linda:set( "lock")
40 linda:limit( "atomic", -1)
41 linda:set( "atomic")
42
43 print "test OK"
44end
34--#################################################################### 45--####################################################################
35 46
36local waitCancellation = function( h, expected_status) 47local waitCancellation = function( h, expected_status)
@@ -119,92 +130,115 @@ end
119--#################################################################### 130--####################################################################
120--#################################################################### 131--####################################################################
121 132
122print "\n\n####################################################################\nbegin linda cancel test\n" 133if not next(which_tests) or which_tests.linda then
123h = lanes.gen( "*", laneBody)( "receive", nil) -- start an infinite wait on the linda 134 remaining_tests.linda = nil
124 135 print "\n\n####################################################################\nbegin linda cancel test\n"
125print "wait 1s" 136 h = lanes.gen( "*", laneBody)( "receive", nil) -- start an infinite wait on the linda
126linda:receive( 1, "yeah")
127
128-- linda cancel: linda:receive() returns cancel_error immediately
129linda:cancel( "both")
130 137
131-- wait until cancellation is effective. 138 print "wait 1s"
132waitCancellation( h, "done") 139 linda:receive( 1, "yeah")
133 140
134-- reset the linda so that the other tests work 141 -- linda cancel: linda:receive() returns cancel_error immediately
135linda:cancel( "none") 142 print "cancelling"
143 linda:cancel( "both")
136 144
137print "\n\n####################################################################\nbegin soft cancel test\n" 145 -- wait until cancellation is effective.
138h = lanes.gen( "*", protectedBody)( "receive") -- start an infinite wait on the linda 146 waitCancellation( h, "done")
139 147
140print "wait 1s" 148 -- reset the linda so that the other tests work
141linda:receive( 1, "yeah") 149 linda:cancel( "none")
150end
142 151
143-- soft cancel, no awakening of waiting linda operations, should timeout 152if not next(which_tests) or which_tests.soft then
144local a, b = h:cancel( "soft", 1, false) 153 remaining_tests.soft = nil
145-- cancellation should fail as the lane is still waiting on its linda 154 print "\n\n####################################################################\nbegin soft cancel test\n"
146assert( a == false and b == "timeout") 155 h = lanes.gen( "*", protectedBody)( "receive") -- start an infinite wait on the linda
147waitCancellation( h, "waiting")
148 156
149-- soft cancel, this time awakens waiting linda operations, which returns cancel_error immediately, no timeout. 157 print "wait 1s"
150h:cancel( "soft", true) 158 linda:receive( 1, "yeah")
151 159
152-- wait until cancellation is effective. the lane will interrupt its loop and print the exit message 160 -- soft cancel, no awakening of waiting linda operations, should timeout
153waitCancellation( h, "done") 161 local a, b = h:cancel( "soft", 1, false)
162 -- cancellation should fail as the lane is still waiting on its linda
163 assert( a == false and b == "timeout")
164 waitCancellation( h, "waiting")
154 165
155-- do return end 166 -- soft cancel, this time awakens waiting linda operations, which returns cancel_error immediately, no timeout.
167 print "cancelling"
168 h:cancel( "soft", true)
156 169
157print "\n\n####################################################################\nbegin hook cancel test\n" 170 -- wait until cancellation is effective. the lane will interrupt its loop and print the exit message
158h = lanes.gen( "*", protectedBody)( "get", 300000) 171 waitCancellation( h, "done")
159print "wait 2s" 172end
160linda:receive( 2, "yeah")
161 173
162-- count hook cancel after 3 instructions 174if not next(which_tests) or which_tests.hook then
163h:cancel( "count", 300, 5.0) 175 remaining_tests.hook = nil
176 print "\n\n####################################################################\nbegin hook cancel test\n"
177 h = lanes.gen( "*", protectedBody)( "get", 300000)
178 print "wait 2s"
179 linda:receive( 2, "yeah")
164 180
165-- wait until cancellation is effective. the lane will interrupt its loop and print the exit message 181 -- count hook cancel after some instruction instructions
166waitCancellation( h, "cancelled") 182 print "cancelling"
183 h:cancel( "line", 300, 5.0)
167 184
168print "\n\n####################################################################\nbegin hard cancel test\n" 185 -- wait until cancellation is effective. the lane will interrupt its loop and print the exit message
169h = lanes.gen( "*", protectedBody)( "receive", nil) -- infinite timeout 186 waitCancellation( h, "cancelled")
187end
170 188
171-- wait 2s before cancelling the lane 189if not next(which_tests) or which_tests.hard then
172print "wait 2s" 190 remaining_tests.hard = nil
173linda:receive( 2, "yeah") 191 print "\n\n####################################################################\nbegin hard cancel test\n"
192 h = lanes.gen( "*", protectedBody)( "receive", nil) -- infinite timeout
174 193
175-- hard cancel: the lane will be interrupted from inside its current linda:receive() and won't return from it 194 -- wait 2s before cancelling the lane
176h:cancel() 195 print "wait 2s"
196 linda:receive( 2, "yeah")
177 197
178-- wait until cancellation is effective. the lane will be stopped by the linda operation throwing an error 198 -- hard cancel: the lane will be interrupted from inside its current linda:receive() and won't return from it
179waitCancellation( h, "cancelled") 199 print "cancelling"
200 h:cancel()
180 201
181print "\n\n####################################################################\nbegin hard cancel test with unprotected lane body\n" 202 -- wait until cancellation is effective. the lane will be stopped by the linda operation throwing an error
182h = lanes.gen( "*", laneBody)( "receive", nil) 203 waitCancellation( h, "cancelled")
204end
183 205
184-- wait 2s before cancelling the lane 206if not next(which_tests) or which_tests.hard_unprotected then
185print "wait 2s" 207 remaining_tests.hard_unprotected = nil
186linda:receive( 2, "yeah") 208 print "\n\n####################################################################\nbegin hard cancel test with unprotected lane body\n"
209 h = lanes.gen( "*", laneBody)( "receive", nil)
187 210
188-- hard cancel: the lane will be interrupted from inside its current linda:receive() and won't return from it 211 -- wait 2s before cancelling the lane
189h:cancel() 212 print "wait 2s"
213 linda:receive( 2, "yeah")
190 214
191-- wait until cancellation is effective. the lane will be stopped by the linda operation throwing an error 215 -- hard cancel: the lane will be interrupted from inside its current linda:receive() and won't return from it
192waitCancellation( h, "cancelled") 216 print "cancelling"
217 h:cancel()
193 218
194print "\n\n####################################################################\nbegin kill cancel test\n" 219 -- wait until cancellation is effective. the lane will be stopped by the linda operation throwing an error
195h = lanes.gen( "*", laneBody)( "busy", 50000000) -- start a pure Lua busy loop lane 220 waitCancellation( h, "cancelled")
221end
196 222
197-- wait 1/3s before cancelling the lane, before the busy loop can finish 223if not next(which_tests) or which_tests.kill then
198print "wait 0.3s" 224 remaining_tests.kill = nil
199linda:receive( 0.3, "yeah") 225 print "\n\n####################################################################\nbegin kill cancel test\n"
226 h = lanes.gen( "*", laneBody)( "busy", 50000000) -- start a pure Lua busy loop lane
200 227
201-- hard cancel with kill: the lane thread will be forcefully terminated 228 -- wait 1/3s before cancelling the lane, before the busy loop can finish
202h:cancel( true) 229 print "wait 0.3s"
230 linda:receive( 0.3, "yeah")
203 231
204-- wait until cancellation is effective. the lane will be stopped by the linda operation throwing an error 232 -- hard cancel with kill: the lane thread will be forcefully terminated. kill timeout is pthread-specific
205waitCancellation( h, "killed") 233 print "cancelling"
234 h:cancel( true, 1.0)
206 235
236 -- wait until cancellation is effective. the lane will be stopped by the linda operation throwing an error
237 waitCancellation( h, "killed")
238end
207--#################################################################### 239--####################################################################
208 240
209print "\ndone" 241local unknown_test, val = next(remaining_tests)
242assert(not unknown_test, tostring(unknown_test) .. " test is unknown")
210 243
244print "\nTHE END"
diff --git a/tests/errhangtest.lua b/tests/errhangtest.lua
index 7286fa5..d26dcef 100644
--- a/tests/errhangtest.lua
+++ b/tests/errhangtest.lua
@@ -4,10 +4,19 @@ local linda = lanes.linda()
4 4
5local coro = coroutine.create(function() end) 5local coro = coroutine.create(function() end)
6 6
7local fun = function() print "fun" end
8local t_in = { [fun] = fun, fun = fun }
9
10-- send a string
11print( pcall(linda.send,linda, 'test', "oh boy"))
12-- send a table that contains a function
13print( pcall(linda.send,linda, 'test', t_in))
7-- we are not allowed to send coroutines through a lanes 14-- we are not allowed to send coroutines through a lanes
8-- however, this should raise an error, not hang the program... 15-- however, this should raise an error, not hang the program...
9print( pcall(linda.send,linda, 'test', "oh boy"))
10print( pcall(linda.send,linda, 'test', coro)) 16print( pcall(linda.send,linda, 'test', coro))
11k,res = linda:receive('test') 17k,str = linda:receive('test') -- read the contents successfully sent
12print( res) 18print( str) -- "oh boy"
19k,t_out = linda:receive('test') -- read the contents successfully sent
20t_out.fun() -- "fun"
13-- linda:send( 'test', coro) 21-- linda:send( 'test', coro)
22print "SUCCESS" \ No newline at end of file
diff --git a/tests/fifo.lua b/tests/fifo.lua
index bef60d5..498f540 100644
--- a/tests/fifo.lua
+++ b/tests/fifo.lua
@@ -6,24 +6,27 @@
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 atomic_linda = lanes.linda( "atom")
10local atomic_inc= lanes.genatomic( linda, "FIFO_n") 10local atomic_inc= lanes.genatomic( atomic_linda, "FIFO_n")
11
12local fifo_linda = lanes.linda( "fifo")
11 13
12assert( atomic_inc()==1) 14assert( atomic_inc()==1)
13assert( atomic_inc()==2) 15assert( atomic_inc()==2)
14 16
15local function FIFO() 17local function FIFO()
16 local my_channel= "FIFO"..atomic_inc() 18 local my_channel= "FIFO_"..atomic_inc()
17 19
18 return { 20 return {
19 -- Giving explicit 'nil' timeout allows numbers to be used as 'my_channel' 21 -- Giving explicit 'nil' timeout allows numbers to be used as 'my_channel'
20 -- 22 --
21 send = function(self, ...) 23 send = function(self, ...)
22 linda:send( nil, my_channel, ...) 24 fifo_linda:send( nil, my_channel, ...)
23 end, 25 end,
24 receive = function(self, timeout) 26 receive = function(self, timeout)
25 return linda:receive( timeout, my_channel) 27 return fifo_linda:receive( timeout, my_channel)
26 end 28 end,
29 channel = my_channel
27 } 30 }
28end 31end
29 32
@@ -36,11 +39,38 @@ A:send( 1,2,3,4,5)
36print "Sending to B.." 39print "Sending to B.."
37B:send( 'a','b','c') 40B:send( 'a','b','c')
38 41
42print "Dumping linda stats.. [1]" -- count everything
43for key,count in pairs(fifo_linda:count()) do
44 print("channel " .. key .. " contains " .. count .. " entries.")
45 -- print(i, key_count[1], key_count[2])
46end
47print "Dumping linda stats.. [2]" -- query count for known channels one at a time
48print("channel " .. A.channel .. " contains " .. fifo_linda:count(A.channel) .. " entries.")
49print("channel " .. B.channel .. " contains " .. fifo_linda:count(B.channel) .. " entries.")
50print "Dumping linda stats.. [3]" -- query counts for a predefined list of keys
51for key,count in pairs(fifo_linda:count(A.channel, B.channel)) do
52 print("channel " .. key .. " contains " .. count .. " entries.")
53 -- print(i, key_count[1], key_count[2])
54end
55print "Dumping linda stats.. [4]" -- count everything
56for key,contents in pairs(fifo_linda:dump()) do
57 print("channel " .. key .. ": limit=".. contents.limit, " first=" .. contents.first, " count=" .. contents.count)
58 for k,v in pairs(contents.fifo) do
59 print("[".. k.."] = " .. v)
60 end
61end
62
39print "Reading A.." 63print "Reading A.."
40print( A:receive( 1.0)) 64print( A:receive( 1.0))
65print( A:receive( 1.0))
66print( A:receive( 1.0))
67print( A:receive( 1.0))
68print( A:receive( 1.0))
41 69
42print "Reading B.." 70print "Reading B.."
43print( B:receive( 2.0)) 71print( B:receive( 2.0))
72print( B:receive( 2.0))
73print( B:receive( 2.0))
44 74
45-- Note: A and B can be passed between threads, or used as upvalues 75-- 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' 76-- by multiple threads (other parts will be copied but the 'linda'
diff --git a/tests/keeper.lua b/tests/keeper.lua
index f8c915d..3333938 100644
--- a/tests/keeper.lua
+++ b/tests/keeper.lua
@@ -4,7 +4,34 @@
4-- Test program for Lua Lanes 4-- Test program for Lua Lanes
5-- 5--
6 6
7local lanes = require "lanes".configure{ with_timers = false, nb_keepers = 200} 7local lanes = require "lanes".configure{ with_timers = false, nb_keepers = 1, keepers_gc_threshold = 500}
8
9do
10 print "Linda names test:"
11 local unnamedLinda = lanes.linda()
12 local unnamedLinda2 = lanes.linda("")
13 local veeeerrrryyyylooongNamedLinda= lanes.linda( "veeeerrrryyyylooongNamedLinda", 1)
14 print(unnamedLinda, unnamedLinda2, veeeerrrryyyylooongNamedLinda)
15 print "GC deadlock test start"
16 -- store a linda in another linda (-> in a keeper)
17 unnamedLinda:set("here", lanes.linda("temporary linda"))
18 -- repeatedly add and remove stuff in the linda so that a GC happens during the keeper operation
19 for i = 1, 1000 do
20 for j = 1, 1000 do -- send 1000 tables
21 unnamedLinda:send("here", {"a", "table", "with", "some", "stuff"})
22 end
23 unnamedLinda:set("here") -- clear everything
24 end
25end
26print "collecting garbage"
27collectgarbage()
28print "GC deadlock test done"
29
30local print_id = 0
31local PRINT = function(...)
32 print_id = print_id + 1
33 print("main", print_id .. ".", ...)
34end
8 35
9local function keeper(linda) 36local function keeper(linda)
10 local mt= { 37 local mt= {
@@ -25,23 +52,49 @@ local A= keeper( lindaA )
25local lindaB= lanes.linda( "B", 2) 52local lindaB= lanes.linda( "B", 2)
26local B= keeper( lindaB ) 53local B= keeper( lindaB )
27 54
55local lindaC= lanes.linda( "C", 3)
56local C= keeper( lindaC )
57print("Created", lindaA, lindaB, lindaC)
58
28A.some= 1 59A.some= 1
29print( A.some ) 60PRINT("A.some == " .. A.some )
30assert( A.some==1 ) 61assert( A.some==1 )
31 62
32B.some= "hoo" 63B.some= "hoo"
64PRINT("B.some == " .. B.some )
33assert( B.some=="hoo" ) 65assert( B.some=="hoo" )
34assert( A.some==1 ) 66assert( A.some==1 )
67assert( C.some==nil )
35 68
36function lane() 69function lane()
70 local print_id = 0
71 local PRINT = function(...)
72 print_id = print_id + 1
73 print("lane", print_id .. ".", ...)
74 end
75
37 local a= keeper(lindaA) 76 local a= keeper(lindaA)
38 print( a.some ) 77 PRINT("a.some == " .. a.some )
39 assert( a.some==1 ) 78 assert( a.some==1 )
40 a.some= 2 79 a.some= 2
80 assert( a.some==2 )
81 PRINT("a.some == " .. a.some )
82
83 local c = keeper(lindaC)
84 assert( c.some==nil )
85 PRINT("c.some == " .. tostring(c.some))
86 c.some= 3
87 assert( c.some==3 )
88 PRINT("c.some == " .. c.some)
41end 89end
42 90
91PRINT("lane started")
43local h= lanes.gen( "io", lane )() 92local h= lanes.gen( "io", lane )()
44h:join() 93PRINT("lane joined:", h:join())
45 94
46print( A.some ) -- 2 95PRINT("A.some = " .. A.some )
47assert( A.some==2 ) 96assert( A.some==2 )
97PRINT("C.some = " .. C.some )
98assert( C.some==3 )
99lindaC:set("some")
100assert( C.some==nil ) \ No newline at end of file
diff --git a/tests/linda_perf.lua b/tests/linda_perf.lua
index a170b01..61b8f05 100644
--- a/tests/linda_perf.lua
+++ b/tests/linda_perf.lua
@@ -1,38 +1,62 @@
1local lanes = require "lanes" 1local lanes = require "lanes"
2lanes.configure{ with_timers = false} 2lanes.configure{ with_timers = false, keepers_gc_threshold=30000 }
3
4-- set TEST1, PREFILL1, FILL1, TEST2, PREFILL2, FILL2 from the command line
3 5
4-- Lua 5.1/5.2 compatibility 6-- Lua 5.1/5.2 compatibility
5local table_unpack = unpack or table.unpack 7local table_unpack = unpack or table.unpack
6 8
9local finalizer = function(err, stk)
10 if err == lanes.cancel_error then
11 -- note that we don't get the cancel_error when running wrapped inside a protected call if it doesn't rethrow it
12 print(" laneBody after cancel" )
13 elseif err then
14 print(" laneBody error: "..tostring(err))
15 else
16 print(" laneBody finalized")
17 end
18end
19
20--##################################################################################################
21
7-- this lane eats items in the linda one by one 22-- this lane eats items in the linda one by one
8local eater = function( l, loop) 23local eater = function( l, loop)
24 set_finalizer(finalizer)
9 -- wait for start signal 25 -- wait for start signal
10 l:receive( "go") 26 l:receive( "go")
11 -- eat data one by one 27 -- eat data one by one
12 for i = 1, loop do 28 for i = 1, loop do
13 local val, key = l:receive( "key") 29 local key, val = l:receive( "key")
14 --print( val) 30 -- print("eater:", val)
15 end 31 end
16 -- print "loop is over" 32 -- print "loop is over"
17 key, val = l:receive( "done") 33 key, val = l:receive( "done")
18 -- print( val) 34 print("eater: done ("..val..")")
19end 35end
20 36
37--##################################################################################################
38
21-- this lane eats items in the linda in batches 39-- this lane eats items in the linda in batches
22local batched = function( l, loop, batch) 40local gobbler = function( l, loop, batch)
41 set_finalizer(finalizer)
23 -- wait for start signal 42 -- wait for start signal
24 l:receive( "go") 43 l:receive( "go")
25 -- eat data in batches 44 -- eat data in batches
26 for i = 1, loop/batch do 45 for i = 1, loop/batch do
27 l:receive( l.batched, "key", batch) 46 l:receive( l.batched, "key", batch)
47 -- print("gobbler:", batch)
28 end 48 end
29 print "loop is over" 49 print "loop is over"
30 key, val = l:receive( "done") 50 key, val = l:receive( "done")
31 print( val) 51 print("gobbler: done ("..val..")")
32end 52end
33 53
54--##################################################################################################
55
34local lane_eater_gen = lanes.gen( "*", {priority = 3}, eater) 56local lane_eater_gen = lanes.gen( "*", {priority = 3}, eater)
35local lane_batched_gen = lanes.gen( "*", {priority = 3}, batched) 57local lane_gobbler_gen = lanes.gen( "*", {priority = 3}, gobbler)
58
59--##################################################################################################
36 60
37-- main thread writes data while a lane reads it 61-- main thread writes data while a lane reads it
38local function ziva( preloop, loop, batch) 62local function ziva( preloop, loop, batch)
@@ -46,7 +70,7 @@ local function ziva( preloop, loop, batch)
46 print( "stored " .. l:count( "key") .. " items in the linda before starting consumer lane") 70 print( "stored " .. l:count( "key") .. " items in the linda before starting consumer lane")
47 if batch > 0 then 71 if batch > 0 then
48 if l.batched then 72 if l.batched then
49 lane = lane_batched_gen( l, top, batch) 73 lane = lane_gobbler_gen( l, top, batch)
50 else 74 else
51 print "no batch support in this version of Lanes" 75 print "no batch support in this version of Lanes"
52 lane = lane_eater_gen( l, top) 76 lane = lane_eater_gen( l, top)
@@ -63,7 +87,9 @@ local function ziva( preloop, loop, batch)
63 for i = 1, batch do 87 for i = 1, batch do
64 table.insert( batch_values, i) 88 table.insert( batch_values, i)
65 end 89 end
90 local batch_send_log = "main: sending "..batch.." values"
66 local batch_send = function() 91 local batch_send = function()
92 -- print(batch_send_log)
67 l:send( "key", table_unpack( batch_values)) 93 l:send( "key", table_unpack( batch_values))
68 end 94 end
69 if loop > preloop then 95 if loop > preloop then
@@ -76,57 +102,35 @@ local function ziva( preloop, loop, batch)
76 return lanes.now_secs() - t1 102 return lanes.now_secs() - t1
77end 103end
78 104
105--##################################################################################################
106
107TEST1 = TEST1 or 1000
108PREFILL1 = PREFILL1 or 10000
109FILL1 = FILL1 or 2000000
110
79local tests1 = 111local tests1 =
80{ 112{
81 { 10000, 2000000, 0}, 113 { PREFILL1, FILL1, 0},
82 { 10000, 2000000, 1}, 114 { PREFILL1, FILL1, 1},
83 { 10000, 2000000, 2}, 115 { PREFILL1, FILL1, 2},
84 { 10000, 2000000, 3}, 116 { PREFILL1, FILL1, 3},
85 { 10000, 2000000, 5}, 117 { PREFILL1, FILL1, 5},
86 { 10000, 2000000, 8}, 118 { PREFILL1, FILL1, 8},
87 { 10000, 2000000, 13}, 119 { PREFILL1, FILL1, 13},
88 { 10000, 2000000, 21}, 120 { PREFILL1, FILL1, 21},
89 { 10000, 2000000, 44}, 121 { PREFILL1, FILL1, 44},
122 { PREFILL1, FILL1, 65},
90} 123}
91print "############################################\ntests #1" 124print "############################################ tests #1"
92for k, v in pairs( tests1) do 125for i, v in ipairs( tests1) do
126 if i > TEST1 then break end
93 local pre, loop, batch = v[1], v[2], v[3] 127 local pre, loop, batch = v[1], v[2], v[3]
94 print( "testing", pre, loop, batch) 128 print("-------------------------------------------------\n")
95 print( pre, loop, batch, "duration = " .. ziva( pre, loop, batch) .. "\n") 129 print("START", "prefill="..pre, "fill="..loop, "batch="..batch)
130 print("DURATION = " .. ziva( pre, loop, batch) .. "\n")
96end 131end
97 132
98--[[ 133--##################################################################################################
99 V 2.1.0:
100 ziva( 20000, 0) -> 4s ziva( 10000, 20000) -> 3s
101 ziva( 30000, 0) -> 8s ziva( 20000, 30000) -> 7s
102 ziva( 40000, 0) -> 15s ziva( 30000, 40000) -> 15s
103 ziva( 50000, 0) -> 24s ziva( 40000, 50000) -> 23s
104 ziva( 60000, 0) -> 34s ziva( 50000, 60000) -> 33s
105
106 SIMPLIFIED:
107 ziva( 20000, 0) -> 4s ziva( 10000, 20000) -> 3s
108 ziva( 30000, 0) -> 9s ziva( 20000, 30000) -> 8s
109 ziva( 40000, 0) -> 15s ziva( 30000, 40000) -> 15s
110 ziva( 50000, 0) -> 25s ziva( 40000, 50000) -> 24s
111 ziva( 60000, 0) -> 35s ziva( 50000, 60000) -> 35s
112
113 FIFO:
114 ziva( 2000000, 0) -> 9s ziva( 1000000, 2000000) -> 33s
115 ziva( 3000000, 0) -> 14s ziva( 2000000, 3000000) -> 40s
116 ziva( 4000000, 0) -> 20s ziva( 3000000, 4000000) -> 27s
117 ziva( 5000000, 0) -> 24s ziva( 4000000, 5000000) -> 42s
118 ziva( 6000000, 0) -> 29s ziva( 5000000, 6000000) -> 55s
119
120 FIFO BATCHED:
121 ziva( 4000000, 0, 1) -> 20s
122 ziva( 4000000, 0, 2) -> 11s
123 ziva( 4000000, 0, 3) -> 7s
124 ziva( 4000000, 0, 5) -> 5s
125 ziva( 4000000, 0, 8) -> 3s
126 ziva( 4000000, 0, 13) -> 3s
127 ziva( 4000000, 0, 21) -> 3s
128 ziva( 4000000, 0, 44) -> 2s
129]]
130 134
131-- sequential write/read (no parallelization involved) 135-- sequential write/read (no parallelization involved)
132local function ziva2( preloop, loop, batch) 136local function ziva2( preloop, loop, batch)
@@ -159,7 +163,7 @@ local function ziva2( preloop, loop, batch)
159 for i = 1, preloop, step do 163 for i = 1, preloop, step do
160 batch_send() 164 batch_send()
161 end 165 end
162 print( "stored " .. (l:count( "key") or 0) .. " items in the linda before starting consumer lane") 166 print( "stored " .. (l:count( "key") or 0) .. " items in the linda before starting the alternating reads and writes")
163 -- loop that alternatively sends and reads data off the linda 167 -- loop that alternatively sends and reads data off the linda
164 if loop > preloop then 168 if loop > preloop then
165 for i = preloop + 1, loop, step do 169 for i = preloop + 1, loop, step do
@@ -169,40 +173,39 @@ local function ziva2( preloop, loop, batch)
169 end 173 end
170 -- here, we have preloop elements still waiting inside the linda 174 -- here, we have preloop elements still waiting inside the linda
171 for i = 1, preloop, step do 175 for i = 1, preloop, step do
172 batch_read() 176 batch_read()
173 end 177 end
174 return lanes.now_secs() - t1 178 return lanes.now_secs() - t1
175end 179end
176 180
181--##################################################################################################
182
183TEST2 = TEST2 or 1000
184PREFILL2 = PREFILL2 or 0
185FILL2 = FILL2 or 4000000
186
177local tests2 = 187local tests2 =
178{ 188{
179 -- prefill, then consume everything 189 { PREFILL2, FILL2},
180 --[[ 190 { PREFILL2, FILL2, 1},
181 { 4000000, 0}, 191 { PREFILL2, FILL2, 2},
182 { 4000000, 0, 1}, 192 { PREFILL2, FILL2, 3},
183 { 4000000, 0, 2}, 193 { PREFILL2, FILL2, 5},
184 { 4000000, 0, 3}, 194 { PREFILL2, FILL2, 8},
185 { 4000000, 0, 5}, 195 { PREFILL2, FILL2, 13},
186 { 4000000, 0, 8}, 196 { PREFILL2, FILL2, 21},
187 { 4000000, 0, 13}, 197 { PREFILL2, FILL2, 44},
188 { 4000000, 0, 21}, 198 { PREFILL2, FILL2, 65},
189 { 4000000, 0, 44},
190 --]]
191 -- alternatively fill and consume
192 { 0, 4000000},
193 { 0, 4000000, 1},
194 { 0, 4000000, 2},
195 { 0, 4000000, 3},
196 { 0, 4000000, 5},
197 { 0, 4000000, 8},
198 { 0, 4000000, 13},
199 { 0, 4000000, 21},
200 { 0, 4000000, 44},
201} 199}
202 200
203print "\n############################################\ntests #2" 201print "############################################ tests #2"
204for k, v in pairs( tests2) do 202for i, v in ipairs( tests2) do
203 if i > TEST2 then break end
205 local pre, loop, batch = v[1], v[2], v[3] 204 local pre, loop, batch = v[1], v[2], v[3]
206 print( "testing", pre, loop, batch) 205 print("-------------------------------------------------\n")
207 print( pre, loop, batch, "duration = " .. ziva2( pre, loop, batch) .. "\n") 206 print("START", "prefill="..pre, "fill="..loop, "batch="..(batch or "no"))
207 print("DURATION = " .. ziva2( pre, loop, batch) .. "\n")
208end 208end
209
210print "############################################"
211print "THE END" \ No newline at end of file
diff --git a/tests/protect_allocator.lua b/tests/protect_allocator.lua
index 593261e..5cbb1d8 100644
--- a/tests/protect_allocator.lua
+++ b/tests/protect_allocator.lua
@@ -46,5 +46,8 @@ for i = 1, COUNT do
46end 46end
47 47
48-- wait for completion 48-- wait for completion
49print "wait for completion"
49linda:receive( linda.batched, "key", COUNT) 50linda:receive( linda.batched, "key", COUNT)
51print "waiting a bit more ..."
52linda:receive( 1, "foo")
50print "SUCCESS" 53print "SUCCESS"
diff --git a/tests/timer.lua b/tests/timer.lua
index ec23cee..73ecb93 100644
--- a/tests/timer.lua
+++ b/tests/timer.lua
@@ -18,13 +18,14 @@ end
18 18
19local T1= "1s" -- these keys can be anything... 19local T1= "1s" -- these keys can be anything...
20local T2= "5s" 20local T2= "5s"
21local PING_DURATION = 20
21 22
22local step= {} 23local step= {}
23 24
24lanes.timer( linda, T1, 1.0, 1.0 ) 25lanes.timer( linda, T1, 1.0, 1.0 )
25step[T1]= 1.0 26step[T1]= 1.0
26 27
27PRINT( "\n*** Timers every second (not synced to wall clock) ***\n" ) 28PRINT( "\n*** Starting 1s Timer (not synced to wall clock) ***\n" )
28 29
29local v_first 30local v_first
30local v_last= {} -- { [channel]= num } 31local v_last= {} -- { [channel]= num }
@@ -46,14 +47,15 @@ while true do
46 -- do not make measurements, first round is not 5secs due to wall clock adjustment 47 -- do not make measurements, first round is not 5secs due to wall clock adjustment
47 T2_first_round= false 48 T2_first_round= false
48 else 49 else
49 assert( math.abs(v-v_last[channel]- step[channel]) < 0.02 ) 50 local dt = math.abs(v-v_last[channel]- step[channel])
51 assert( dt < 0.02, "channel " .. channel .. " is late: " .. dt)
50 end 52 end
51 end 53 end
52 54
53 if not v_first then 55 if not v_first then
54 v_first= v 56 v_first= v
55 elseif v-v_first > 3.0 and (not step[T2]) then 57 elseif v-v_first > 3.0 and (not step[T2]) then
56 PRINT( "\n*** Adding timers every 5 second (synced to wall clock) ***\n" ) 58 PRINT( "\n*** Starting 5s timer (synced to wall clock) ***\n" )
57 59
58 -- The first event can be in the past (just cut seconds down to 5s) 60 -- The first event can be in the past (just cut seconds down to 5s)
59 -- 61 --
@@ -63,7 +65,7 @@ while true do
63 lanes.timer( linda, T2, date, 5.0 ) 65 lanes.timer( linda, T2, date, 5.0 )
64 step[T2]= 5.0 66 step[T2]= 5.0
65 67
66 elseif v-v_first > 10 then -- exit condition 68 elseif v-v_first > PING_DURATION then -- exit condition
67 break 69 break
68 end 70 end
69 v_last[channel]= v 71 v_last[channel]= v
@@ -80,7 +82,7 @@ PRINT( "\n*** Listing timers ***\n" )
80local r = lanes.timers() -- list of {linda, key, {}} 82local r = lanes.timers() -- list of {linda, key, {}}
81for _,t in ipairs( r) do 83for _,t in ipairs( r) do
82 local linda, key, timer = t[1], t[2], t[3] 84 local linda, key, timer = t[1], t[2], t[3]
83 print( tostring( linda), key, timer[1], timer[2]) 85 print( tostring( linda), key, timer[1], timer[2])
84end 86end
85 87
86 88