diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-04-11 15:14:52 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-04-11 15:14:52 +0200 |
commit | adaa36dbec1ce9aaafd61873b9d3d898a8c240cf (patch) | |
tree | 4c81e8f5983c3d696a636e2cc433ce7c0a9c3dd8 | |
parent | 1d310e6ecb6e156598337612f16573d9cd284f5e (diff) | |
download | lanes-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-- | CHANGES | 7 | ||||
-rw-r--r-- | Makefile | 32 | ||||
-rw-r--r-- | docs/index.html | 24 | ||||
-rw-r--r-- | lanes-3.17.0-0.rockspec (renamed from lanes-3.16.3-0.rockspec) | 4 | ||||
-rw-r--r-- | src/cancel.h | 3 | ||||
-rw-r--r-- | src/compat.c | 20 | ||||
-rw-r--r-- | src/compat.h | 1 | ||||
-rw-r--r-- | src/deep.c | 13 | ||||
-rw-r--r-- | src/keeper.c | 83 | ||||
-rw-r--r-- | src/keeper.h | 9 | ||||
-rw-r--r-- | src/lanes.c | 115 | ||||
-rw-r--r-- | src/lanes.h | 4 | ||||
-rw-r--r-- | src/lanes.lua | 449 | ||||
-rw-r--r-- | src/lanes_private.h | 10 | ||||
-rw-r--r-- | src/linda.c | 115 | ||||
-rw-r--r-- | src/macros_and_utils.h | 7 | ||||
-rw-r--r-- | src/platform.h | 1 | ||||
-rw-r--r-- | src/state.c | 2 | ||||
-rw-r--r-- | src/tools.c | 50 | ||||
-rw-r--r-- | src/tools.h | 16 | ||||
-rw-r--r-- | src/uniquekey.h | 4 | ||||
-rw-r--r-- | tests/basic.lua | 14 | ||||
-rw-r--r-- | tests/cancel.lua | 212 | ||||
-rw-r--r-- | tests/errhangtest.lua | 15 | ||||
-rw-r--r-- | tests/fifo.lua | 42 | ||||
-rw-r--r-- | tests/keeper.lua | 63 | ||||
-rw-r--r-- | tests/linda_perf.lua | 165 | ||||
-rw-r--r-- | tests/protect_allocator.lua | 3 | ||||
-rw-r--r-- | tests/timer.lua | 12 |
29 files changed, 880 insertions, 615 deletions
@@ -1,5 +1,12 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 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 | |||
3 | CHANGE 159: BGe 19-Mar-24 | 10 | CHANGE 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 | ||
@@ -72,25 +72,25 @@ rock: | |||
72 | #--- Testing --- | 72 | #--- Testing --- |
73 | # | 73 | # |
74 | test: | 74 | test: |
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 | ||
95 | basic: tests/basic.lua $(_TARGET_SO) | 95 | basic: tests/basic.lua $(_TARGET_SO) |
96 | $(_PREFIX) $(LUA) $< | 96 | $(_PREFIX) $(LUA) $< |
@@ -104,8 +104,8 @@ cancel: tests/cancel.lua $(_TARGET_SO) | |||
104 | REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end" | 104 | REP_ARGS=-llanes -e "print'say aaa'; for i=1,10 do print(i) end" |
105 | repetitive: $(_TARGET_SO) | 105 | repetitive: $(_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 | ||
110 | repetitive1: $(_TARGET_SO) | 110 | repetitive1: $(_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 <0, GC runs automatically. This is the default.<br /> | ||
301 | If 0, GC runs after *every* keeper operation.<br /> | ||
302 | If >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> |
1624 | static int clonable_lanesclone( lua_State* L) | 1636 | static 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 | ||
8 | package = "Lanes" | 8 | package = "Lanes" |
9 | 9 | ||
10 | version = "3.16.3-0" | 10 | version = "3.17.0-0" |
11 | 11 | ||
12 | source= { | 12 | source= { |
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 | ||
17 | description = { | 17 | description = { |
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/ |
44 | static DECLARE_CONST_UNIQUE_KEY(CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel | 44 | static 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/ | ||
47 | static DECLARE_CONST_UNIQUE_KEY(CANCEL_TEST_KEY, 0xe66f5960c57d133a); // used as registry key | ||
48 | |||
49 | cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_); | 46 | cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_); |
50 | 47 | ||
51 | static inline int cancel_error( lua_State* L) | 48 | static 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 | // ################################################################################################ | ||
13 | static int luaL_getsubtable (lua_State *L, int idx, const char *fname) | 17 | static 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 | |||
29 | void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb) | 35 | void 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 | ||
48 | void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue) | 58 | void* 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 | ||
54 | int lua_getiuservalue( lua_State* L, int idx, int n) | 67 | int 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. | ||
79 | int lua_setiuservalue( lua_State* L, int idx, int n) | 97 | int 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 | ||
@@ -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_) | |||
122 | static void fifo_peek( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) | 122 | static 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_) | |||
169 | static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465); | 169 | static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465); |
170 | static void push_table( lua_State* L, int idx_) | 170 | static 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 | ||
192 | int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_) | 192 | int 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) |
724 | Keeper* which_keeper(Keepers* keepers_, ptrdiff_t magic_) | 734 | Keeper* 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 | ||
731 | Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_) | 745 | Keeper* 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 | ||
757 | void keeper_release( Keeper* K) | 768 | void 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 | ||
763 | void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_) | 774 | void 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 | ||
22 | struct s_Keepers | 22 | struct 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; | |||
29 | void init_keepers( Universe* U, lua_State* L); | 30 | void init_keepers( Universe* U, lua_State* L); |
30 | void close_keepers( Universe* U); | 31 | void close_keepers( Universe* U); |
31 | 32 | ||
32 | Keeper* which_keeper( Keepers* keepers_, ptrdiff_t magic_); | 33 | Keeper* which_keeper( Keepers* keepers_, uintptr_t magic_); |
33 | Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_); | 34 | Keeper* keeper_acquire( Keepers* keepers_, uintptr_t magic_); |
34 | #define KEEPER_MAGIC_SHIFT 3 | 35 | #define KEEPER_MAGIC_SHIFT 3 |
35 | void keeper_release( Keeper* K); | 36 | void keeper_release( Keeper* K_); |
36 | void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_); | 37 | void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_); |
37 | int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_); | 38 | int 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/ |
40 | static DECLARE_CONST_UNIQUE_KEY( NIL_SENTINEL, 0x7eaafa003a1d11a1); | 41 | static 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 | */ |
445 | static int selfdestruct_gc( lua_State* L) | 445 | static 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 | // |
610 | LUAG_FUNC( set_singlethreaded) | 610 | LUAG_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 | ||
654 | LUAG_FUNC( set_error_reporting) | 654 | LUAG_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 | } | ||
672 | done: | ||
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 | ||
789 | LUAG_FUNC( set_debug_threadname) | 781 | LUAG_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 | ||
2077 | int LANES_API luaopen_lanes_core( lua_State* L) | 2082 | int 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 | }; |
73 | typedef struct s_Lane Lane; | 73 | typedef struct s_Lane Lane; |
74 | 74 | ||
75 | // xxh64 of string "LANE_POINTER_REGKEY" generated at https://www.pelock.com/products/hash-calculator | ||
76 | static 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"))) | 82 | inline Lane* lua_toLane(lua_State* L, int i_) |
83 | { | ||
84 | return *(Lane**)(luaL_checkudata(L, i_, "Lane")); | ||
85 | } | ||
80 | 86 | ||
81 | static inline Lane* get_lane_from_registry( lua_State* L) | 87 | static 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 | ||
61 | static void* linda_id( lua_State*, DeepOp); | 61 | static 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 |
274 | DECLARE_CONST_UNIQUE_KEY(BATCH_SENTINEL, 0x2DDFEE0968C62AA7); | ||
274 | LUAG_FUNC( linda_receive) | 275 | LUAG_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) | 85 | inline 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 | */ |
845 | static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) | 851 | static 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 | */ |
1960 | int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_) | 1972 | InterCopyResult 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 | ||
2023 | int luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_) | 2035 | InterCopyResult 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 | ||
2030 | int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_) | 2042 | InterCopyResult 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 | |||
35 | enum eInterCopyResult | ||
36 | { | ||
37 | eICR_Success, | ||
38 | eICR_NotEnoughValues, | ||
39 | eICR_Error | ||
40 | }; | ||
41 | typedef enum eInterCopyResult InterCopyResult; | ||
42 | |||
34 | bool_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_); | 43 | bool_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 | ||
38 | int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_); | 47 | InterCopyResult luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_); |
39 | 48 | InterCopyResult luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_); | |
40 | int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_); | 49 | InterCopyResult luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_); |
41 | int luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_); | ||
42 | 50 | ||
43 | int luaG_nameof( lua_State* L); | 51 | int 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 | |||
11 | typedef struct s_UniqueKey UniqueKey; | 11 | typedef 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) | |||
154 | assert( st == "cancelled" ) | 154 | assert( st == "cancelled" ) |
155 | 155 | ||
156 | -- cancellation of lanes waiting on a linda | 156 | -- cancellation of lanes waiting on a linda |
157 | local limited = lanes.linda() | 157 | local limited = lanes.linda("limited") |
158 | limited:limit( "key", 1) | 158 | limited:limit( "key", 1) |
159 | -- [[################################################ | 159 | -- [[################################################ |
160 | limited:send( "key", "hello") -- saturate linda | 160 | limited:send( "key", "hello") -- saturate linda |
@@ -234,7 +234,7 @@ local chunk= function( linda ) | |||
234 | WR( "Lane ends!\n" ) | 234 | WR( "Lane ends!\n" ) |
235 | end | 235 | end |
236 | 236 | ||
237 | local linda= lanes_linda() | 237 | local linda= lanes_linda("communications") |
238 | assert( type(linda) == "userdata" ) | 238 | assert( 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" ) | |||
278 | assert( complex_table[1] == complex_table[3] and complex_table[2] == complex_table[4]) | 278 | assert( complex_table[1] == complex_table[3] and complex_table[2] == complex_table[4]) |
279 | WR( table.concat( {complex_table[1][1],complex_table[2][1],complex_table[3][1],complex_table[4][1]},", ")) | 279 | WR( table.concat( {complex_table[1][1],complex_table[2][1],complex_table[3][1],complex_table[4][1]},", ")) |
280 | 280 | ||
281 | WR("collectgarbage") | ||
281 | t = nil | 282 | t = nil |
282 | collectgarbage() | 283 | collectgarbage() |
283 | -- wait | 284 | -- wait |
284 | linda: receive( 1, "wait") | 285 | WR("waiting 1s") |
286 | linda: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] ) |
337 | end | 339 | end |
338 | 340 | ||
341 | WR("collectgarbage") | ||
339 | collectgarbage() | 342 | collectgarbage() |
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 | ||
364 | local linda= lanes_linda() | 367 | local linda= lanes_linda("criss cross") |
365 | 368 | ||
366 | local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms | 369 | local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms |
367 | 370 | ||
368 | local _= a[1],b[1] -- waits until they are both ready | 371 | local _= a[1],b[1] -- waits until they are both ready |
369 | 372 | ||
373 | WR("collectgarbage") | ||
370 | a, b = nil | 374 | a, b = nil |
371 | collectgarbage() | 375 | collectgarbage() |
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" ) |
409 | end | 413 | end |
410 | 414 | ||
411 | local linda= lanes.linda() | 415 | local linda= lanes.linda("linda") |
412 | local t2= lanes_gen( "debug,string,io", {gc_cb = gc_cb}, chunk2 )(linda) -- prepare & launch | 416 | local t2= lanes_gen( "debug,string,io", {gc_cb = gc_cb}, chunk2 )(linda) -- prepare & launch |
413 | linda:send( "down", function(linda) linda:send( "up", "ready!" ) end, | 417 | linda: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 @@ | |||
1 | local lanes = require "lanes" .configure{ with_timers = false} | 1 | local which_tests, remaining_tests = {}, {} |
2 | 2 | for k,v in ipairs{...} do | |
3 | local linda = lanes.linda() | 3 | print("got arg:", type(v), tostring(v)) |
4 | which_tests[v] = true | ||
5 | remaining_tests[v] = true | ||
6 | end | ||
4 | 7 | ||
5 | --#################################################################### | 8 | --#################################################################### |
6 | print "\n\n####################################################################\nbegin genlock & genatomic cancel test\n" | ||
7 | |||
8 | -- get a lock and a atomic operator | ||
9 | local lock = lanes.genlock( linda, "lock", 1) | ||
10 | local atomic = lanes.genatomic( linda, "atomic") | ||
11 | |||
12 | -- check that cancelled lindas give cancel_error as they should | ||
13 | linda:cancel() | ||
14 | assert( linda:get( "empty") == lanes.cancel_error) | ||
15 | assert( lanes.genlock( linda, "any", 1) == lanes.cancel_error) | ||
16 | assert( lanes.genatomic( linda, "any") == lanes.cancel_error) | ||
17 | |||
18 | -- check that lock and atomic functions return cancel_error if the linda was cancelled | ||
19 | assert( lock( 1) == lanes.cancel_error) | ||
20 | assert( lock( -1) == lanes.cancel_error) | ||
21 | assert( atomic( 1) == lanes.cancel_error) | ||
22 | |||
23 | -- reset the linda so that the other tests work | ||
24 | linda:cancel( "none") | ||
25 | linda:limit( "lock", -1) | ||
26 | linda:set( "lock") | ||
27 | linda:limit( "atomic", -1) | ||
28 | linda:set( "atomic") | ||
29 | 9 | ||
10 | local lanes = require "lanes" .configure{ with_timers = false} | ||
11 | |||
12 | local linda = lanes.linda() | ||
30 | -- a numeric value to read | 13 | -- a numeric value to read |
31 | linda:set( "val", 33.0) | 14 | linda:set( "val", 33.0) |
32 | 15 | ||
33 | print "test OK" | 16 | --#################################################################### |
17 | if 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" | ||
44 | end | ||
34 | --#################################################################### | 45 | --#################################################################### |
35 | 46 | ||
36 | local waitCancellation = function( h, expected_status) | 47 | local waitCancellation = function( h, expected_status) |
@@ -119,92 +130,115 @@ end | |||
119 | --#################################################################### | 130 | --#################################################################### |
120 | --#################################################################### | 131 | --#################################################################### |
121 | 132 | ||
122 | print "\n\n####################################################################\nbegin linda cancel test\n" | 133 | if not next(which_tests) or which_tests.linda then |
123 | h = 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" | |
125 | print "wait 1s" | 136 | h = lanes.gen( "*", laneBody)( "receive", nil) -- start an infinite wait on the linda |
126 | linda:receive( 1, "yeah") | ||
127 | |||
128 | -- linda cancel: linda:receive() returns cancel_error immediately | ||
129 | linda:cancel( "both") | ||
130 | 137 | ||
131 | -- wait until cancellation is effective. | 138 | print "wait 1s" |
132 | waitCancellation( 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 |
135 | linda:cancel( "none") | 142 | print "cancelling" |
143 | linda:cancel( "both") | ||
136 | 144 | ||
137 | print "\n\n####################################################################\nbegin soft cancel test\n" | 145 | -- wait until cancellation is effective. |
138 | h = lanes.gen( "*", protectedBody)( "receive") -- start an infinite wait on the linda | 146 | waitCancellation( h, "done") |
139 | 147 | ||
140 | print "wait 1s" | 148 | -- reset the linda so that the other tests work |
141 | linda:receive( 1, "yeah") | 149 | linda:cancel( "none") |
150 | end | ||
142 | 151 | ||
143 | -- soft cancel, no awakening of waiting linda operations, should timeout | 152 | if not next(which_tests) or which_tests.soft then |
144 | local 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" |
146 | assert( a == false and b == "timeout") | 155 | h = lanes.gen( "*", protectedBody)( "receive") -- start an infinite wait on the linda |
147 | waitCancellation( h, "waiting") | ||
148 | 156 | ||
149 | -- soft cancel, this time awakens waiting linda operations, which returns cancel_error immediately, no timeout. | 157 | print "wait 1s" |
150 | h: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 |
153 | waitCancellation( 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 | ||
157 | print "\n\n####################################################################\nbegin hook cancel test\n" | 170 | -- wait until cancellation is effective. the lane will interrupt its loop and print the exit message |
158 | h = lanes.gen( "*", protectedBody)( "get", 300000) | 171 | waitCancellation( h, "done") |
159 | print "wait 2s" | 172 | end |
160 | linda:receive( 2, "yeah") | ||
161 | 173 | ||
162 | -- count hook cancel after 3 instructions | 174 | if not next(which_tests) or which_tests.hook then |
163 | h: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 |
166 | waitCancellation( h, "cancelled") | 182 | print "cancelling" |
183 | h:cancel( "line", 300, 5.0) | ||
167 | 184 | ||
168 | print "\n\n####################################################################\nbegin hard cancel test\n" | 185 | -- wait until cancellation is effective. the lane will interrupt its loop and print the exit message |
169 | h = lanes.gen( "*", protectedBody)( "receive", nil) -- infinite timeout | 186 | waitCancellation( h, "cancelled") |
187 | end | ||
170 | 188 | ||
171 | -- wait 2s before cancelling the lane | 189 | if not next(which_tests) or which_tests.hard then |
172 | print "wait 2s" | 190 | remaining_tests.hard = nil |
173 | linda: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 |
176 | h: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 |
179 | waitCancellation( h, "cancelled") | 199 | print "cancelling" |
200 | h:cancel() | ||
180 | 201 | ||
181 | print "\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 |
182 | h = lanes.gen( "*", laneBody)( "receive", nil) | 203 | waitCancellation( h, "cancelled") |
204 | end | ||
183 | 205 | ||
184 | -- wait 2s before cancelling the lane | 206 | if not next(which_tests) or which_tests.hard_unprotected then |
185 | print "wait 2s" | 207 | remaining_tests.hard_unprotected = nil |
186 | linda: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 |
189 | h: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 |
192 | waitCancellation( h, "cancelled") | 216 | print "cancelling" |
217 | h:cancel() | ||
193 | 218 | ||
194 | print "\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 |
195 | h = lanes.gen( "*", laneBody)( "busy", 50000000) -- start a pure Lua busy loop lane | 220 | waitCancellation( h, "cancelled") |
221 | end | ||
196 | 222 | ||
197 | -- wait 1/3s before cancelling the lane, before the busy loop can finish | 223 | if not next(which_tests) or which_tests.kill then |
198 | print "wait 0.3s" | 224 | remaining_tests.kill = nil |
199 | linda: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 |
202 | h: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 |
205 | waitCancellation( 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") | ||
238 | end | ||
207 | --#################################################################### | 239 | --#################################################################### |
208 | 240 | ||
209 | print "\ndone" | 241 | local unknown_test, val = next(remaining_tests) |
242 | assert(not unknown_test, tostring(unknown_test) .. " test is unknown") | ||
210 | 243 | ||
244 | print "\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 | ||
5 | local coro = coroutine.create(function() end) | 5 | local coro = coroutine.create(function() end) |
6 | 6 | ||
7 | local fun = function() print "fun" end | ||
8 | local t_in = { [fun] = fun, fun = fun } | ||
9 | |||
10 | -- send a string | ||
11 | print( pcall(linda.send,linda, 'test', "oh boy")) | ||
12 | -- send a table that contains a function | ||
13 | print( 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... |
9 | print( pcall(linda.send,linda, 'test', "oh boy")) | ||
10 | print( pcall(linda.send,linda, 'test', coro)) | 16 | print( pcall(linda.send,linda, 'test', coro)) |
11 | k,res = linda:receive('test') | 17 | k,str = linda:receive('test') -- read the contents successfully sent |
12 | print( res) | 18 | print( str) -- "oh boy" |
19 | k,t_out = linda:receive('test') -- read the contents successfully sent | ||
20 | t_out.fun() -- "fun" | ||
13 | -- linda:send( 'test', coro) | 21 | -- linda:send( 'test', coro) |
22 | print "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 | ||
7 | local lanes = require "lanes".configure{shutdown_timeout=3,with_timers=true} | 7 | local lanes = require "lanes".configure{shutdown_timeout=3,with_timers=true} |
8 | 8 | ||
9 | local linda = lanes.linda( "atom") | 9 | local atomic_linda = lanes.linda( "atom") |
10 | local atomic_inc= lanes.genatomic( linda, "FIFO_n") | 10 | local atomic_inc= lanes.genatomic( atomic_linda, "FIFO_n") |
11 | |||
12 | local fifo_linda = lanes.linda( "fifo") | ||
11 | 13 | ||
12 | assert( atomic_inc()==1) | 14 | assert( atomic_inc()==1) |
13 | assert( atomic_inc()==2) | 15 | assert( atomic_inc()==2) |
14 | 16 | ||
15 | local function FIFO() | 17 | local 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 | } |
28 | end | 31 | end |
29 | 32 | ||
@@ -36,11 +39,38 @@ A:send( 1,2,3,4,5) | |||
36 | print "Sending to B.." | 39 | print "Sending to B.." |
37 | B:send( 'a','b','c') | 40 | B:send( 'a','b','c') |
38 | 41 | ||
42 | print "Dumping linda stats.. [1]" -- count everything | ||
43 | for key,count in pairs(fifo_linda:count()) do | ||
44 | print("channel " .. key .. " contains " .. count .. " entries.") | ||
45 | -- print(i, key_count[1], key_count[2]) | ||
46 | end | ||
47 | print "Dumping linda stats.. [2]" -- query count for known channels one at a time | ||
48 | print("channel " .. A.channel .. " contains " .. fifo_linda:count(A.channel) .. " entries.") | ||
49 | print("channel " .. B.channel .. " contains " .. fifo_linda:count(B.channel) .. " entries.") | ||
50 | print "Dumping linda stats.. [3]" -- query counts for a predefined list of keys | ||
51 | for 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]) | ||
54 | end | ||
55 | print "Dumping linda stats.. [4]" -- count everything | ||
56 | for 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 | ||
61 | end | ||
62 | |||
39 | print "Reading A.." | 63 | print "Reading A.." |
40 | print( A:receive( 1.0)) | 64 | print( A:receive( 1.0)) |
65 | print( A:receive( 1.0)) | ||
66 | print( A:receive( 1.0)) | ||
67 | print( A:receive( 1.0)) | ||
68 | print( A:receive( 1.0)) | ||
41 | 69 | ||
42 | print "Reading B.." | 70 | print "Reading B.." |
43 | print( B:receive( 2.0)) | 71 | print( B:receive( 2.0)) |
72 | print( B:receive( 2.0)) | ||
73 | print( 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 | ||
7 | local lanes = require "lanes".configure{ with_timers = false, nb_keepers = 200} | 7 | local lanes = require "lanes".configure{ with_timers = false, nb_keepers = 1, keepers_gc_threshold = 500} |
8 | |||
9 | do | ||
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 | ||
25 | end | ||
26 | print "collecting garbage" | ||
27 | collectgarbage() | ||
28 | print "GC deadlock test done" | ||
29 | |||
30 | local print_id = 0 | ||
31 | local PRINT = function(...) | ||
32 | print_id = print_id + 1 | ||
33 | print("main", print_id .. ".", ...) | ||
34 | end | ||
8 | 35 | ||
9 | local function keeper(linda) | 36 | local function keeper(linda) |
10 | local mt= { | 37 | local mt= { |
@@ -25,23 +52,49 @@ local A= keeper( lindaA ) | |||
25 | local lindaB= lanes.linda( "B", 2) | 52 | local lindaB= lanes.linda( "B", 2) |
26 | local B= keeper( lindaB ) | 53 | local B= keeper( lindaB ) |
27 | 54 | ||
55 | local lindaC= lanes.linda( "C", 3) | ||
56 | local C= keeper( lindaC ) | ||
57 | print("Created", lindaA, lindaB, lindaC) | ||
58 | |||
28 | A.some= 1 | 59 | A.some= 1 |
29 | print( A.some ) | 60 | PRINT("A.some == " .. A.some ) |
30 | assert( A.some==1 ) | 61 | assert( A.some==1 ) |
31 | 62 | ||
32 | B.some= "hoo" | 63 | B.some= "hoo" |
64 | PRINT("B.some == " .. B.some ) | ||
33 | assert( B.some=="hoo" ) | 65 | assert( B.some=="hoo" ) |
34 | assert( A.some==1 ) | 66 | assert( A.some==1 ) |
67 | assert( C.some==nil ) | ||
35 | 68 | ||
36 | function lane() | 69 | function 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) | ||
41 | end | 89 | end |
42 | 90 | ||
91 | PRINT("lane started") | ||
43 | local h= lanes.gen( "io", lane )() | 92 | local h= lanes.gen( "io", lane )() |
44 | h:join() | 93 | PRINT("lane joined:", h:join()) |
45 | 94 | ||
46 | print( A.some ) -- 2 | 95 | PRINT("A.some = " .. A.some ) |
47 | assert( A.some==2 ) | 96 | assert( A.some==2 ) |
97 | PRINT("C.some = " .. C.some ) | ||
98 | assert( C.some==3 ) | ||
99 | lindaC:set("some") | ||
100 | assert( 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 @@ | |||
1 | local lanes = require "lanes" | 1 | local lanes = require "lanes" |
2 | lanes.configure{ with_timers = false} | 2 | lanes.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 |
5 | local table_unpack = unpack or table.unpack | 7 | local table_unpack = unpack or table.unpack |
6 | 8 | ||
9 | local 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 | ||
18 | end | ||
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 |
8 | local eater = function( l, loop) | 23 | local 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..")") |
19 | end | 35 | end |
20 | 36 | ||
37 | --################################################################################################## | ||
38 | |||
21 | -- this lane eats items in the linda in batches | 39 | -- this lane eats items in the linda in batches |
22 | local batched = function( l, loop, batch) | 40 | local 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..")") |
32 | end | 52 | end |
33 | 53 | ||
54 | --################################################################################################## | ||
55 | |||
34 | local lane_eater_gen = lanes.gen( "*", {priority = 3}, eater) | 56 | local lane_eater_gen = lanes.gen( "*", {priority = 3}, eater) |
35 | local lane_batched_gen = lanes.gen( "*", {priority = 3}, batched) | 57 | local 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 |
38 | local function ziva( preloop, loop, batch) | 62 | local 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 |
77 | end | 103 | end |
78 | 104 | ||
105 | --################################################################################################## | ||
106 | |||
107 | TEST1 = TEST1 or 1000 | ||
108 | PREFILL1 = PREFILL1 or 10000 | ||
109 | FILL1 = FILL1 or 2000000 | ||
110 | |||
79 | local tests1 = | 111 | local 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 | } |
91 | print "############################################\ntests #1" | 124 | print "############################################ tests #1" |
92 | for k, v in pairs( tests1) do | 125 | for 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") | ||
96 | end | 131 | end |
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) |
132 | local function ziva2( preloop, loop, batch) | 136 | local 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 |
175 | end | 179 | end |
176 | 180 | ||
181 | --################################################################################################## | ||
182 | |||
183 | TEST2 = TEST2 or 1000 | ||
184 | PREFILL2 = PREFILL2 or 0 | ||
185 | FILL2 = FILL2 or 4000000 | ||
186 | |||
177 | local tests2 = | 187 | local 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 | ||
203 | print "\n############################################\ntests #2" | 201 | print "############################################ tests #2" |
204 | for k, v in pairs( tests2) do | 202 | for 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") | ||
208 | end | 208 | end |
209 | |||
210 | print "############################################" | ||
211 | print "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 | |||
46 | end | 46 | end |
47 | 47 | ||
48 | -- wait for completion | 48 | -- wait for completion |
49 | print "wait for completion" | ||
49 | linda:receive( linda.batched, "key", COUNT) | 50 | linda:receive( linda.batched, "key", COUNT) |
51 | print "waiting a bit more ..." | ||
52 | linda:receive( 1, "foo") | ||
50 | print "SUCCESS" | 53 | print "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 | ||
19 | local T1= "1s" -- these keys can be anything... | 19 | local T1= "1s" -- these keys can be anything... |
20 | local T2= "5s" | 20 | local T2= "5s" |
21 | local PING_DURATION = 20 | ||
21 | 22 | ||
22 | local step= {} | 23 | local step= {} |
23 | 24 | ||
24 | lanes.timer( linda, T1, 1.0, 1.0 ) | 25 | lanes.timer( linda, T1, 1.0, 1.0 ) |
25 | step[T1]= 1.0 | 26 | step[T1]= 1.0 |
26 | 27 | ||
27 | PRINT( "\n*** Timers every second (not synced to wall clock) ***\n" ) | 28 | PRINT( "\n*** Starting 1s Timer (not synced to wall clock) ***\n" ) |
28 | 29 | ||
29 | local v_first | 30 | local v_first |
30 | local v_last= {} -- { [channel]= num } | 31 | local 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" ) | |||
80 | local r = lanes.timers() -- list of {linda, key, {}} | 82 | local r = lanes.timers() -- list of {linda, key, {}} |
81 | for _,t in ipairs( r) do | 83 | for _,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]) |
84 | end | 86 | end |
85 | 87 | ||
86 | 88 | ||