aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-04-11 15:14:52 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-04-11 15:14:52 +0200
commitadaa36dbec1ce9aaafd61873b9d3d898a8c240cf (patch)
tree4c81e8f5983c3d696a636e2cc433ce7c0a9c3dd8 /src
parent1d310e6ecb6e156598337612f16573d9cd284f5e (diff)
downloadlanes-adaa36dbec1ce9aaafd61873b9d3d898a8c240cf.tar.gz
lanes-adaa36dbec1ce9aaafd61873b9d3d898a8c240cf.tar.bz2
lanes-adaa36dbec1ce9aaafd61873b9d3d898a8c240cf.zip
Bring all interesting fixes from the C++ implementation back into the C implementation
Diffstat (limited to '')
-rw-r--r--src/cancel.h3
-rw-r--r--src/compat.c20
-rw-r--r--src/compat.h1
-rw-r--r--src/deep.c13
-rw-r--r--src/keeper.c83
-rw-r--r--src/keeper.h9
-rw-r--r--src/lanes.c115
-rw-r--r--src/lanes.h4
-rw-r--r--src/lanes.lua449
-rw-r--r--src/lanes_private.h10
-rw-r--r--src/linda.c115
-rw-r--r--src/macros_and_utils.h7
-rw-r--r--src/platform.h1
-rw-r--r--src/state.c2
-rw-r--r--src/tools.c50
-rw-r--r--src/tools.h16
-rw-r--r--src/uniquekey.h4
17 files changed, 505 insertions, 397 deletions
diff --git a/src/cancel.h b/src/cancel.h
index c7c5433..b25d9f9 100644
--- a/src/cancel.h
+++ b/src/cancel.h
@@ -43,9 +43,6 @@ typedef enum
43// crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/ 43// crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/
44static DECLARE_CONST_UNIQUE_KEY(CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel 44static DECLARE_CONST_UNIQUE_KEY(CANCEL_ERROR, 0xe97d41626cc97577); // 'cancel_error' sentinel
45 45
46// crc64/we of string "CANCEL_TEST_KEY" generated at http://www.nitrxgen.net/hashgen/
47static DECLARE_CONST_UNIQUE_KEY(CANCEL_TEST_KEY, 0xe66f5960c57d133a); // used as registry key
48
49cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_); 46cancel_result thread_cancel( lua_State* L, Lane* s, CancelOp op_, double secs_, bool_t force_, double waitkill_timeout_);
50 47
51static inline int cancel_error( lua_State* L) 48static inline int cancel_error( lua_State* L)
diff --git a/src/compat.c b/src/compat.c
index 19159a9..bc39d4c 100644
--- a/src/compat.c
+++ b/src/compat.c
@@ -1,6 +1,6 @@
1/* 1/*
2 * ############################################################################################### 2 * ###############################################################################################
3 * ######################################### Lua 5.1/5.2 ######################################### 3 * ####################################### Lua 5.1/5.2/5.3 #######################################
4 * ############################################################################################### 4 * ###############################################################################################
5 */ 5 */
6#include "compat.h" 6#include "compat.h"
@@ -9,7 +9,11 @@
9/* 9/*
10** Copied from Lua 5.2 loadlib.c 10** Copied from Lua 5.2 loadlib.c
11*/ 11*/
12// ################################################################################################
13// ################################################################################################
12#if LUA_VERSION_NUM == 501 14#if LUA_VERSION_NUM == 501
15// ################################################################################################
16// ################################################################################################
13static int luaL_getsubtable (lua_State *L, int idx, const char *fname) 17static int luaL_getsubtable (lua_State *L, int idx, const char *fname)
14{ 18{
15 lua_getfield(L, idx, fname); 19 lua_getfield(L, idx, fname);
@@ -26,6 +30,8 @@ static int luaL_getsubtable (lua_State *L, int idx, const char *fname)
26 } 30 }
27} 31}
28 32
33// ################################################################################################
34
29void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb) 35void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb)
30{ 36{
31 lua_pushcfunction(L, openf); 37 lua_pushcfunction(L, openf);
@@ -43,7 +49,11 @@ void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int
43} 49}
44#endif // LUA_VERSION_NUM 50#endif // LUA_VERSION_NUM
45 51
52// ################################################################################################
53// ################################################################################################
46#if LUA_VERSION_NUM < 504 54#if LUA_VERSION_NUM < 504
55// ################################################################################################
56// ################################################################################################
47 57
48void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue) 58void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue)
49{ 59{
@@ -51,8 +61,12 @@ void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue)
51 return lua_newuserdata( L, sz); 61 return lua_newuserdata( L, sz);
52} 62}
53 63
64// ################################################################################################
65
66// push on stack uservalue #n of full userdata at idx
54int lua_getiuservalue( lua_State* L, int idx, int n) 67int lua_getiuservalue( lua_State* L, int idx, int n)
55{ 68{
69 // full userdata can have only 1 uservalue before 5.4
56 if( n > 1) 70 if( n > 1)
57 { 71 {
58 lua_pushnil( L); 72 lua_pushnil( L);
@@ -76,6 +90,10 @@ int lua_getiuservalue( lua_State* L, int idx, int n)
76 return lua_type( L, -1); 90 return lua_type( L, -1);
77} 91}
78 92
93// ################################################################################################
94
95// Pops a value from the stack and sets it as the new n-th user value associated to the full userdata at the given index.
96// Returns 0 if the userdata does not have that value.
79int lua_setiuservalue( lua_State* L, int idx, int n) 97int lua_setiuservalue( lua_State* L, int idx, int n)
80{ 98{
81 if( n > 1 99 if( n > 1
diff --git a/src/compat.h b/src/compat.h
index e44f827..fbcbee1 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -90,6 +90,7 @@ int lua_setiuservalue( lua_State* L, int idx, int n);
90#define luaG_registerlibfuncs( L, _funcs) luaL_setfuncs( L, _funcs, 0) 90#define luaG_registerlibfuncs( L, _funcs) luaL_setfuncs( L, _funcs, 0)
91#define lua504_dump lua_dump 91#define lua504_dump lua_dump
92#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) 92#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
93#define LUA_ERRGCMM 666 // doesn't exist in Lua 5.4, we don't care about the actual value
93 94
94#endif // LUA_VERSION_NUM == 504 95#endif // LUA_VERSION_NUM == 504
95 96
diff --git a/src/deep.c b/src/deep.c
index 9496477..a1f078a 100644
--- a/src/deep.c
+++ b/src/deep.c
@@ -236,7 +236,7 @@ char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, in
236 *proxy = prelude; 236 *proxy = prelude;
237 237
238 // Get/create metatable for 'idfunc' (in this state) 238 // Get/create metatable for 'idfunc' (in this state)
239 lua_pushlightuserdata( L, (void*)(ptrdiff_t)(prelude->idfunc)); // DPC proxy idfunc 239 lua_pushlightuserdata( L, (void*)(uintptr_t)(prelude->idfunc)); // DPC proxy idfunc
240 get_deep_lookup( L); // DPC proxy metatable? 240 get_deep_lookup( L); // DPC proxy metatable?
241 241
242 if( lua_isnil( L, -1)) // // No metatable yet. 242 if( lua_isnil( L, -1)) // // No metatable yet.
@@ -278,7 +278,7 @@ char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, in
278 278
279 // Memorize for later rounds 279 // Memorize for later rounds
280 lua_pushvalue( L, -1); // DPC proxy metatable metatable 280 lua_pushvalue( L, -1); // DPC proxy metatable metatable
281 lua_pushlightuserdata( L, (void*)(ptrdiff_t)(prelude->idfunc)); // DPC proxy metatable metatable idfunc 281 lua_pushlightuserdata( L, (void*)(uintptr_t)(prelude->idfunc)); // DPC proxy metatable metatable idfunc
282 set_deep_lookup( L); // DPC proxy metatable 282 set_deep_lookup( L); // DPC proxy metatable
283 283
284 // 2 - cause the target state to require the module that exported the idfunc 284 // 2 - cause the target state to require the module that exported the idfunc
@@ -473,15 +473,18 @@ bool_t copydeep( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, ui
473 lua_pop( L, 1); // ... u [uv]* 473 lua_pop( L, 1); // ... u [uv]*
474 STACK_MID( L, nuv); 474 STACK_MID( L, nuv);
475 475
476 errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, i), nuv, mode_); // u 476 errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, i), nuv, mode_); // u
477 477
478 // transfer all uservalues of the source in the destination 478 // transfer all uservalues of the source in the destination
479 { 479 {
480 int const clone_i = lua_gettop( L2); 480 int const clone_i = lua_gettop( L2);
481 while( nuv) 481 while( nuv)
482 { 482 {
483 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // u uv 483 if(!inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_)) // u uv
484 lua_pop( L, 1); // ... u [uv]* 484 {
485 return luaL_error(L, "Cannot copy upvalue type '%s'", luaL_typename(L, -1));
486 }
487 lua_pop( L, 1); // ... u [uv]*
485 // this pops the value from the stack 488 // this pops the value from the stack
486 lua_setiuservalue( L2, clone_i, nuv); // u 489 lua_setiuservalue( L2, clone_i, nuv); // u
487 -- nuv; 490 -- nuv;
diff --git a/src/keeper.c b/src/keeper.c
index 8aa734a..a1505b7 100644
--- a/src/keeper.c
+++ b/src/keeper.c
@@ -122,7 +122,7 @@ static void fifo_push( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
122static void fifo_peek( lua_State* L, keeper_fifo* fifo_, lua_Integer count_) 122static void fifo_peek( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
123{ 123{
124 lua_Integer i; 124 lua_Integer i;
125 STACK_GROW( L, count_); 125 STACK_GROW( L, (int) count_);
126 for( i = 0; i < count_; ++ i) 126 for( i = 0; i < count_; ++ i)
127 { 127 {
128 lua_rawgeti( L, 1, (int)( fifo_->first + i)); 128 lua_rawgeti( L, 1, (int)( fifo_->first + i));
@@ -136,7 +136,7 @@ static void fifo_pop( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
136 int const fifo_idx = lua_gettop( L); // ... fifo 136 int const fifo_idx = lua_gettop( L); // ... fifo
137 int i; 137 int i;
138 // each iteration pushes a value on the stack! 138 // each iteration pushes a value on the stack!
139 STACK_GROW( L, count_ + 2); 139 STACK_GROW( L, (int) count_ + 2);
140 // skip first item, we will push it last 140 // skip first item, we will push it last
141 for( i = 1; i < count_; ++ i) 141 for( i = 1; i < count_; ++ i)
142 { 142 {
@@ -169,7 +169,7 @@ static void fifo_pop( lua_State* L, keeper_fifo* fifo_, lua_Integer count_)
169static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465); 169static DECLARE_CONST_UNIQUE_KEY( FIFOS_KEY, 0xdce50bbc351cd465);
170static void push_table( lua_State* L, int idx_) 170static void push_table( lua_State* L, int idx_)
171{ 171{
172 STACK_GROW( L, 4); 172 STACK_GROW( L, 5);
173 STACK_CHECK( L, 0); 173 STACK_CHECK( L, 0);
174 idx_ = lua_absindex( L, idx_); 174 idx_ = lua_absindex( L, idx_);
175 REGISTRY_GET( L, FIFOS_KEY); // ud fifos 175 REGISTRY_GET( L, FIFOS_KEY); // ud fifos
@@ -189,7 +189,7 @@ static void push_table( lua_State* L, int idx_)
189 STACK_END( L, 1); 189 STACK_END( L, 1);
190} 190}
191 191
192int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_) 192int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, uintptr_t magic_)
193{ 193{
194 Keeper* const K = which_keeper( U->keepers, magic_); 194 Keeper* const K = which_keeper( U->keepers, magic_);
195 lua_State* const KL = K ? K->L : NULL; 195 lua_State* const KL = K ? K->L : NULL;
@@ -557,7 +557,7 @@ int keepercall_count( lua_State* L)
557 { 557 {
558 lua_pop( L, 1); // out fifos keys 558 lua_pop( L, 1); // out fifos keys
559 } 559 }
560 } 560 } // all keys are exhausted // out fifos
561 lua_pop( L, 1); // out 561 lua_pop( L, 1); // out
562 } 562 }
563 ASSERT_L( lua_gettop( L) == 1); 563 ASSERT_L( lua_gettop( L) == 1);
@@ -633,6 +633,7 @@ void init_keepers( Universe* U, lua_State* L)
633{ 633{
634 int i; 634 int i;
635 int nb_keepers; 635 int nb_keepers;
636 int keepers_gc_threshold;
636 637
637 STACK_CHECK( L, 0); // L K 638 STACK_CHECK( L, 0); // L K
638 lua_getfield( L, 1, "nb_keepers"); // nb_keepers 639 lua_getfield( L, 1, "nb_keepers"); // nb_keepers
@@ -642,6 +643,12 @@ void init_keepers( Universe* U, lua_State* L)
642 { 643 {
643 (void) luaL_error( L, "Bad number of keepers (%d)", nb_keepers); 644 (void) luaL_error( L, "Bad number of keepers (%d)", nb_keepers);
644 } 645 }
646 STACK_MID(L, 0);
647
648 lua_getfield(L, 1, "keepers_gc_threshold"); // keepers_gc_threshold
649 keepers_gc_threshold = (int) lua_tointeger(L, -1);
650 lua_pop(L, 1); //
651 STACK_MID(L, 0);
645 652
646 // Keepers contains an array of 1 s_Keeper, adjust for the actual number of keeper states 653 // Keepers contains an array of 1 s_Keeper, adjust for the actual number of keeper states
647 { 654 {
@@ -656,6 +663,7 @@ void init_keepers( Universe* U, lua_State* L)
656 return; 663 return;
657 } 664 }
658 memset( U->keepers, 0, bytes); 665 memset( U->keepers, 0, bytes);
666 U->keepers->gc_threshold = keepers_gc_threshold;
659 U->keepers->nb_keepers = nb_keepers; 667 U->keepers->nb_keepers = nb_keepers;
660 } 668 }
661 for( i = 0; i < nb_keepers; ++ i) // keepersUD 669 for( i = 0; i < nb_keepers; ++ i) // keepersUD
@@ -669,10 +677,12 @@ void init_keepers( Universe* U, lua_State* L)
669 } 677 }
670 678
671 U->keepers->keeper_array[i].L = K; 679 U->keepers->keeper_array[i].L = K;
672 // we can trigger a GC from inside keeper_call(), where a keeper is acquired 680 MUTEX_INIT( &U->keepers->keeper_array[i].keeper_cs);
673 // from there, GC can collect a linda, which would acquire the keeper again, and deadlock the thread. 681
674 // therefore, we need a recursive mutex. 682 if (U->keepers->gc_threshold >= 0)
675 MUTEX_RECURSIVE_INIT( &U->keepers->keeper_array[i].keeper_cs); 683 {
684 lua_gc(K, LUA_GCSTOP, 0);
685 }
676 686
677 STACK_CHECK( K, 0); 687 STACK_CHECK( K, 0);
678 688
@@ -693,7 +703,7 @@ void init_keepers( Universe* U, lua_State* L)
693 if( !lua_isnil( L, -1)) 703 if( !lua_isnil( L, -1))
694 { 704 {
695 // when copying with mode eLM_ToKeeper, error message is pushed at the top of the stack, not raised immediately 705 // when copying with mode eLM_ToKeeper, error message is pushed at the top of the stack, not raised immediately
696 if( luaG_inter_copy_package( U, L, K, -1, eLM_ToKeeper)) 706 if( luaG_inter_copy_package( U, L, K, -1, eLM_ToKeeper) != eICR_Success)
697 { 707 {
698 // if something went wrong, the error message is at the top of the stack 708 // if something went wrong, the error message is at the top of the stack
699 lua_remove( L, -2); // error_msg 709 lua_remove( L, -2); // error_msg
@@ -721,22 +731,22 @@ void init_keepers( Universe* U, lua_State* L)
721} 731}
722 732
723// should be called only when inside a keeper_acquire/keeper_release pair (see linda_protected_call) 733// should be called only when inside a keeper_acquire/keeper_release pair (see linda_protected_call)
724Keeper* which_keeper(Keepers* keepers_, ptrdiff_t magic_) 734Keeper* which_keeper(Keepers* keepers_, uintptr_t magic_)
725{ 735{
726 int const nbKeepers = keepers_->nb_keepers; 736 int const nbKeepers = keepers_->nb_keepers;
727 unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers); 737 if (nbKeepers)
728 return &keepers_->keeper_array[i]; 738 {
739 unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers);
740 return &keepers_->keeper_array[i];
741 }
742 return NULL;
729} 743}
730 744
731Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_) 745Keeper* keeper_acquire( Keepers* keepers_, uintptr_t magic_)
732{ 746{
733 int const nbKeepers = keepers_->nb_keepers; 747 int const nbKeepers = keepers_->nb_keepers;
734 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) 748 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers)
735 if( nbKeepers == 0) 749 if( nbKeepers)
736 {
737 return NULL;
738 }
739 else
740 { 750 {
741 /* 751 /*
742 * Any hashing will do that maps pointers to 0..GNbKeepers-1 752 * Any hashing will do that maps pointers to 0..GNbKeepers-1
@@ -752,12 +762,13 @@ Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_)
752 //++ K->count; 762 //++ K->count;
753 return K; 763 return K;
754 } 764 }
765 return NULL;
755} 766}
756 767
757void keeper_release( Keeper* K) 768void keeper_release( Keeper* K_)
758{ 769{
759 //-- K->count; 770 //-- K->count;
760 if( K) MUTEX_UNLOCK( &K->keeper_cs); 771 if( K_) MUTEX_UNLOCK( &K_->keeper_cs);
761} 772}
762 773
763void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_) 774void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_)
@@ -805,7 +816,7 @@ int keeper_call( Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, vo
805 816
806 lua_pushlightuserdata( K, linda); 817 lua_pushlightuserdata( K, linda);
807 818
808 if( (args == 0) || luaG_inter_copy( U, L, K, args, eLM_ToKeeper) == 0) // L->K 819 if( (args == 0) || luaG_inter_copy( U, L, K, args, eLM_ToKeeper) == eICR_Success) // L->K
809 { 820 {
810 lua_call( K, 1 + args, LUA_MULTRET); 821 lua_call( K, 1 + args, LUA_MULTRET);
811 822
@@ -814,12 +825,38 @@ int keeper_call( Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, vo
814 // this may interrupt a lane, causing the destruction of the underlying OS thread 825 // this may interrupt a lane, causing the destruction of the underlying OS thread
815 // after this, another lane making use of this keeper can get an error code from the mutex-locking function 826 // after this, another lane making use of this keeper can get an error code from the mutex-locking function
816 // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) 827 // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread)
817 if( (retvals > 0) && luaG_inter_move( U, K, L, retvals, eLM_FromKeeper) != 0) // K->L 828 if( (retvals > 0) && luaG_inter_move( U, K, L, retvals, eLM_FromKeeper) != eICR_Success) // K->L
818 { 829 {
819 retvals = -1; 830 retvals = -1;
820 } 831 }
821 } 832 }
822 // whatever happens, restore the stack to where it was at the origin 833 // whatever happens, restore the stack to where it was at the origin
823 lua_settop( K, Ktos); 834 lua_settop( K, Ktos);
835
836
837 // don't do this for this particular function, as it is only called during Linda destruction, and we don't want to raise an error, ever
838 if (func_ != KEEPER_API(clear))
839 {
840 // since keeper state GC is stopped, let's run a step once in a while if required
841 int const gc_threshold = U->keepers->gc_threshold;
842 if (gc_threshold == 0)
843 {
844 lua_gc(K, LUA_GCSTEP, 0);
845 }
846 else if (gc_threshold > 0)
847 {
848 int const gc_usage = lua_gc(K, LUA_GCCOUNT, 0);
849 if (gc_usage >= gc_threshold)
850 {
851 lua_gc(K, LUA_GCCOLLECT, 0);
852 int const gc_usage_after = lua_gc(K, LUA_GCCOUNT, 0);
853 if (gc_usage_after > gc_threshold)
854 {
855 luaL_error(L, "Keeper GC threshold is too low, need at least %d", gc_usage_after);
856 }
857 }
858 }
859 }
860
824 return retvals; 861 return retvals;
825} 862}
diff --git a/src/keeper.h b/src/keeper.h
index d30aa36..7c55809 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -21,6 +21,7 @@ typedef struct s_Keeper Keeper;
21 21
22struct s_Keepers 22struct s_Keepers
23{ 23{
24 int gc_threshold;
24 int nb_keepers; 25 int nb_keepers;
25 Keeper keeper_array[1]; 26 Keeper keeper_array[1];
26}; 27};
@@ -29,12 +30,12 @@ typedef struct s_Keepers Keepers;
29void init_keepers( Universe* U, lua_State* L); 30void init_keepers( Universe* U, lua_State* L);
30void close_keepers( Universe* U); 31void close_keepers( Universe* U);
31 32
32Keeper* which_keeper( Keepers* keepers_, ptrdiff_t magic_); 33Keeper* which_keeper( Keepers* keepers_, uintptr_t magic_);
33Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_); 34Keeper* keeper_acquire( Keepers* keepers_, uintptr_t magic_);
34#define KEEPER_MAGIC_SHIFT 3 35#define KEEPER_MAGIC_SHIFT 3
35void keeper_release( Keeper* K); 36void keeper_release( Keeper* K_);
36void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_); 37void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_);
37int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_); 38int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, uintptr_t magic_);
38 39
39// crc64/we of string "NIL_SENTINEL" generated at http://www.nitrxgen.net/hashgen/ 40// crc64/we of string "NIL_SENTINEL" generated at http://www.nitrxgen.net/hashgen/
40static DECLARE_CONST_UNIQUE_KEY( NIL_SENTINEL, 0x7eaafa003a1d11a1); 41static DECLARE_CONST_UNIQUE_KEY( NIL_SENTINEL, 0x7eaafa003a1d11a1);
diff --git a/src/lanes.c b/src/lanes.c
index 332a1b8..ca2b53a 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -442,7 +442,7 @@ static bool_t selfdestruct_remove( Lane* s)
442/* 442/*
443* Process end; cancel any still free-running threads 443* Process end; cancel any still free-running threads
444*/ 444*/
445static int selfdestruct_gc( lua_State* L) 445static int universe_gc( lua_State* L)
446{ 446{
447 Universe* U = (Universe*) lua_touserdata( L, 1); 447 Universe* U = (Universe*) lua_touserdata( L, 1);
448 448
@@ -456,7 +456,7 @@ static int selfdestruct_gc( lua_State* L)
456 while( s != SELFDESTRUCT_END) 456 while( s != SELFDESTRUCT_END)
457 { 457 {
458 // attempt a regular unforced hard cancel with a small timeout 458 // attempt a regular unforced hard cancel with a small timeout
459 bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( L, s, CO_Hard, 0.0001, FALSE, 0.0); 459 bool_t cancelled = THREAD_ISNULL( s->thread) || (thread_cancel( L, s, CO_Hard, 0.0001, FALSE, 0.0) != CR_Timeout);
460 // if we failed, and we know the thread is waiting on a linda 460 // if we failed, and we know the thread is waiting on a linda
461 if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL) 461 if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL)
462 { 462 {
@@ -609,7 +609,7 @@ static int selfdestruct_gc( lua_State* L)
609// 609//
610LUAG_FUNC( set_singlethreaded) 610LUAG_FUNC( set_singlethreaded)
611{ 611{
612 uint_t cores = luaG_optunsigned( L, 1, 1); 612 lua_Integer cores = luaG_optunsigned( L, 1, 1);
613 (void) cores; // prevent "unused" warning 613 (void) cores; // prevent "unused" warning
614 614
615#ifdef PLATFORM_OSX 615#ifdef PLATFORM_OSX
@@ -653,24 +653,16 @@ static DECLARE_CONST_UNIQUE_KEY( EXTENDED_STACKTRACE_REGKEY, 0x2357c69a7c92c936)
653 653
654LUAG_FUNC( set_error_reporting) 654LUAG_FUNC( set_error_reporting)
655{ 655{
656 bool_t equal; 656 luaL_checktype(L, 1, LUA_TSTRING);
657 luaL_checktype( L, 1, LUA_TSTRING); 657 char const* mode = lua_tostring(L, 1);
658 lua_pushliteral( L, "extended"); 658 bool_t const extended = (strcmp(mode, "extended") == 0);
659 equal = lua_rawequal( L, -1, 1); 659 bool_t const basic = (strcmp(mode, "basic") == 0);
660 lua_pop( L, 1); 660 if (!extended && !basic)
661 if( equal)
662 { 661 {
663 goto done; 662 return luaL_error(L, "unsupported error reporting model %s", mode);
664 } 663 }
665 lua_pushliteral( L, "basic"); 664
666 equal = !lua_rawequal( L, -1, 1); 665 REGISTRY_SET( L, EXTENDED_STACKTRACE_REGKEY, lua_pushboolean( L, extended ? 1 : 0));
667 lua_pop( L, 1);
668 if( equal)
669 {
670 return luaL_error( L, "unsupported error reporting model");
671 }
672done:
673 REGISTRY_SET( L, EXTENDED_STACKTRACE_REGKEY, lua_pushboolean( L, equal));
674 return 0; 666 return 0;
675} 667}
676 668
@@ -788,7 +780,8 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_)
788 780
789LUAG_FUNC( set_debug_threadname) 781LUAG_FUNC( set_debug_threadname)
790{ 782{
791 DECLARE_CONST_UNIQUE_KEY( hidden_regkey, LG_set_debug_threadname); 783 // fnv164 of string "debug_threadname" generated at https://www.pelock.com/products/hash-calculator
784 static DECLARE_CONST_UNIQUE_KEY( hidden_regkey, 0x79C0669AAAE04440);
792 // C s_lane structure is a light userdata upvalue 785 // C s_lane structure is a light userdata upvalue
793 Lane* s = lua_touserdata( L, lua_upvalueindex( 1)); 786 Lane* s = lua_touserdata( L, lua_upvalueindex( 1));
794 luaL_checktype( L, -1, LUA_TSTRING); // "name" 787 luaL_checktype( L, -1, LUA_TSTRING); // "name"
@@ -1049,10 +1042,10 @@ LUAG_FUNC( lane_new)
1049 char const* libs_str = lua_tostring( L, 2); 1042 char const* libs_str = lua_tostring( L, 2);
1050 bool_t const have_priority = !lua_isnoneornil( L, 3); 1043 bool_t const have_priority = !lua_isnoneornil( L, 3);
1051 int const priority = have_priority ? (int) lua_tointeger( L, 3) : THREAD_PRIO_DEFAULT; 1044 int const priority = have_priority ? (int) lua_tointeger( L, 3) : THREAD_PRIO_DEFAULT;
1052 uint_t const globals_idx = lua_isnoneornil( L, 4) ? 0 : 4; 1045 int const globals_idx = lua_isnoneornil( L, 4) ? 0 : 4;
1053 uint_t const package_idx = lua_isnoneornil( L, 5) ? 0 : 5; 1046 int const package_idx = lua_isnoneornil( L, 5) ? 0 : 5;
1054 uint_t const required_idx = lua_isnoneornil( L, 6) ? 0 : 6; 1047 int const required_idx = lua_isnoneornil( L, 6) ? 0 : 6;
1055 uint_t const gc_cb_idx = lua_isnoneornil( L, 7) ? 0 : 7; 1048 int const gc_cb_idx = lua_isnoneornil( L, 7) ? 0 : 7;
1056 1049
1057#define FIXED_ARGS 7 1050#define FIXED_ARGS 7
1058 int const nargs = lua_gettop(L) - FIXED_ARGS; 1051 int const nargs = lua_gettop(L) - FIXED_ARGS;
@@ -1090,7 +1083,8 @@ LUAG_FUNC( lane_new)
1090 if( package_idx != 0) 1083 if( package_idx != 0)
1091 { 1084 {
1092 // when copying with mode eLM_LaneBody, should raise an error in case of problem, not leave it one the stack 1085 // when copying with mode eLM_LaneBody, should raise an error in case of problem, not leave it one the stack
1093 (void) luaG_inter_copy_package( U, L, L2, package_idx, eLM_LaneBody); 1086 InterCopyResult const ret = luaG_inter_copy_package( U, L, L2, package_idx, eLM_LaneBody);
1087 ASSERT_L(ret == eICR_Success); // either all went well, or we should not even get here
1094 } 1088 }
1095 1089
1096 // modules to require in the target lane *before* the function is transfered! 1090 // modules to require in the target lane *before* the function is transfered!
@@ -1179,25 +1173,32 @@ LUAG_FUNC( lane_new)
1179 STACK_MID( L2, 0); 1173 STACK_MID( L2, 0);
1180 1174
1181 // Lane main function 1175 // Lane main function
1182 if( lua_type( L, 1) == LUA_TFUNCTION)
1183 { 1176 {
1184 int res; 1177 int const func_type = lua_type(L, 1);
1185 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END)); 1178 if (func_type == LUA_TFUNCTION)
1186 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1187 lua_pushvalue( L, 1); // func libs priority globals package required gc_cb [... args ...] func
1188 res = luaG_inter_move( U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func
1189 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1190 if( res != 0)
1191 { 1179 {
1192 return luaL_error( L, "tried to copy unsupported types"); 1180 InterCopyResult res;
1181 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: transfer lane body\n" INDENT_END));
1182 DEBUGSPEW_CODE(++U->debugspew_indent_depth);
1183 lua_pushvalue(L, 1); // func libs priority globals package required gc_cb [... args ...] func
1184 res = luaG_inter_move(U, L, L2, 1, eLM_LaneBody); // func libs priority globals package required gc_cb [... args ...] // func
1185 DEBUGSPEW_CODE(--U->debugspew_indent_depth);
1186 if (res != eICR_Success)
1187 {
1188 return luaL_error(L, "tried to copy unsupported types");
1189 }
1193 } 1190 }
1194 } 1191 else if (func_type == LUA_TSTRING)
1195 else if( lua_type( L, 1) == LUA_TSTRING)
1196 {
1197 // compile the string
1198 if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) // func
1199 { 1192 {
1200 return luaL_error( L, "error when parsing lane function code"); 1193 // compile the string
1194 if (luaL_loadstring(L2, lua_tostring(L, 1)) != 0) // func
1195 {
1196 return luaL_error(L, "error when parsing lane function code");
1197 }
1198 }
1199 else
1200 {
1201 luaL_error(L, "Expected function, got %s", lua_typename(L, func_type)); // doesn't return
1201 } 1202 }
1202 } 1203 }
1203 STACK_MID( L, 0); 1204 STACK_MID( L, 0);
@@ -1207,12 +1208,12 @@ LUAG_FUNC( lane_new)
1207 // revive arguments 1208 // revive arguments
1208 if( nargs > 0) 1209 if( nargs > 0)
1209 { 1210 {
1210 int res; 1211 InterCopyResult res;
1211 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END)); 1212 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: transfer lane arguments\n" INDENT_END));
1212 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1213 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1213 res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...] 1214 res = luaG_inter_move( U, L, L2, nargs, eLM_LaneBody); // func libs priority globals package required gc_cb // func [... args ...]
1214 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1215 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1215 if( res != 0) 1216 if( res != eICR_Success)
1216 { 1217 {
1217 return luaL_error( L, "tried to copy unsupported types"); 1218 return luaL_error( L, "tried to copy unsupported types");
1218 } 1219 }
@@ -1277,7 +1278,7 @@ LUAG_FUNC( lane_new)
1277 lua_setiuservalue( L, -2, 1); // func libs priority globals package required gc_cb lane 1278 lua_setiuservalue( L, -2, 1); // func libs priority globals package required gc_cb lane
1278 1279
1279 // Store 's' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive). 1280 // Store 's' in the lane's registry, for 'cancel_test()' (we do cancel tests at pending send/receive).
1280 REGISTRY_SET( L2, CANCEL_TEST_KEY, lua_pushlightuserdata( L2, s)); // func [... args ...] 1281 REGISTRY_SET( L2, LANE_POINTER_REGKEY, lua_pushlightuserdata( L2, s)); // func [... args ...]
1281 1282
1282 STACK_END( L, 1); 1283 STACK_END( L, 1);
1283 STACK_END( L2, 1 + nargs); 1284 STACK_END( L2, 1 + nargs);
@@ -1457,8 +1458,8 @@ LUAG_FUNC( thread_join)
1457 { 1458 {
1458 case DONE: 1459 case DONE:
1459 { 1460 {
1460 uint_t n = lua_gettop( L2); // whole L2 stack 1461 int n = lua_gettop( L2); // whole L2 stack
1461 if( (n > 0) && (luaG_inter_move( U, L2, L, n, eLM_LaneBody) != 0)) 1462 if( (n > 0) && (luaG_inter_move( U, L2, L, n, eLM_LaneBody) != eICR_Success))
1462 { 1463 {
1463 return luaL_error( L, "tried to copy unsupported types"); 1464 return luaL_error( L, "tried to copy unsupported types");
1464 } 1465 }
@@ -1472,7 +1473,7 @@ LUAG_FUNC( thread_join)
1472 STACK_GROW( L, 3); 1473 STACK_GROW( L, 3);
1473 lua_pushnil( L); 1474 lua_pushnil( L);
1474 // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ... 1475 // even when ERROR_FULL_STACK, if the error is not LUA_ERRRUN, the handler wasn't called, and we only have 1 error message on the stack ...
1475 if( luaG_inter_move( U, L2, L, n, eLM_LaneBody) != 0) // nil "err" [trace] 1476 if( luaG_inter_move( U, L2, L, n, eLM_LaneBody) != eICR_Success) // nil "err" [trace]
1476 { 1477 {
1477 return luaL_error( L, "tried to copy unsupported types: %s", lua_tostring( L, -n)); 1478 return luaL_error( L, "tried to copy unsupported types: %s", lua_tostring( L, -n));
1478 } 1479 }
@@ -1874,7 +1875,7 @@ LUAG_FUNC( configure)
1874 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1875 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
1875 lua_newtable( L); // settings universe mt 1876 lua_newtable( L); // settings universe mt
1876 lua_getfield( L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout 1877 lua_getfield( L, 1, "shutdown_timeout"); // settings universe mt shutdown_timeout
1877 lua_pushcclosure( L, selfdestruct_gc, 1); // settings universe mt selfdestruct_gc 1878 lua_pushcclosure( L, universe_gc, 1); // settings universe mt universe_gc
1878 lua_setfield( L, -2, "__gc"); // settings universe mt 1879 lua_setfield( L, -2, "__gc"); // settings universe mt
1879 lua_setmetatable( L, -2); // settings universe 1880 lua_setmetatable( L, -2); // settings universe
1880 lua_pop( L, 1); // settings 1881 lua_pop( L, 1); // settings
@@ -2051,16 +2052,20 @@ static void EnableCrashingOnCrashes( void)
2051 const DWORD EXCEPTION_SWALLOWING = 0x1; 2052 const DWORD EXCEPTION_SWALLOWING = 0x1;
2052 2053
2053 HMODULE kernel32 = LoadLibraryA("kernel32.dll"); 2054 HMODULE kernel32 = LoadLibraryA("kernel32.dll");
2054 tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy"); 2055 if (kernel32)
2055 tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
2056 if( pGetPolicy && pSetPolicy)
2057 { 2056 {
2058 DWORD dwFlags; 2057 tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
2059 if( pGetPolicy( &dwFlags)) 2058 tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
2059 if( pGetPolicy && pSetPolicy)
2060 { 2060 {
2061 // Turn off the filter 2061 DWORD dwFlags;
2062 pSetPolicy( dwFlags & ~EXCEPTION_SWALLOWING); 2062 if( pGetPolicy( &dwFlags))
2063 {
2064 // Turn off the filter
2065 pSetPolicy( dwFlags & ~EXCEPTION_SWALLOWING);
2066 }
2063 } 2067 }
2068 FreeLibrary(kernel32);
2064 } 2069 }
2065 //typedef void (* SignalHandlerPointer)( int); 2070 //typedef void (* SignalHandlerPointer)( int);
2066 /*SignalHandlerPointer previousHandler =*/ signal( SIGABRT, signal_handler); 2071 /*SignalHandlerPointer previousHandler =*/ signal( SIGABRT, signal_handler);
@@ -2072,7 +2077,7 @@ static void EnableCrashingOnCrashes( void)
2072 while( !s_ecoc_go_ahead) { Sleep(1); } // changes threads 2077 while( !s_ecoc_go_ahead) { Sleep(1); } // changes threads
2073 } 2078 }
2074} 2079}
2075#endif // PLATFORM_WIN32 2080#endif // PLATFORM_WIN32 && !defined NDEBUG
2076 2081
2077int LANES_API luaopen_lanes_core( lua_State* L) 2082int LANES_API luaopen_lanes_core( lua_State* L)
2078{ 2083{
diff --git a/src/lanes.h b/src/lanes.h
index 7e1a2e5..62b9ea9 100644
--- a/src/lanes.h
+++ b/src/lanes.h
@@ -11,8 +11,8 @@
11#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 11#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
12 12
13#define LANES_VERSION_MAJOR 3 13#define LANES_VERSION_MAJOR 3
14#define LANES_VERSION_MINOR 16 14#define LANES_VERSION_MINOR 17
15#define LANES_VERSION_PATCH 3 15#define LANES_VERSION_PATCH 0
16 16
17#define LANES_MIN_VERSION_REQUIRED(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR>MAJOR) || (LANES_VERSION_MAJOR==MAJOR && (LANES_VERSION_MINOR>MINOR || (LANES_VERSION_MINOR==MINOR && LANES_VERSION_PATCH>=PATCH)))) 17#define LANES_MIN_VERSION_REQUIRED(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR>MAJOR) || (LANES_VERSION_MAJOR==MAJOR && (LANES_VERSION_MINOR>MINOR || (LANES_VERSION_MINOR==MINOR && LANES_VERSION_PATCH>=PATCH))))
18#define LANES_VERSION_LESS_THAN(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR<MAJOR) || (LANES_VERSION_MAJOR==MAJOR && (LANES_VERSION_MINOR<MINOR || (LANES_VERSION_MINOR==MINOR && LANES_VERSION_PATCH<PATCH)))) 18#define LANES_VERSION_LESS_THAN(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR<MAJOR) || (LANES_VERSION_MAJOR==MAJOR && (LANES_VERSION_MINOR<MINOR || (LANES_VERSION_MINOR==MINOR && LANES_VERSION_PATCH<PATCH))))
diff --git a/src/lanes.lua b/src/lanes.lua
index b4c0070..49900f9 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -70,6 +70,7 @@ lanes.configure = function( settings_)
70 local default_params = 70 local default_params =
71 { 71 {
72 nb_keepers = 1, 72 nb_keepers = 1,
73 keepers_gc_threshold = -1,
73 on_state_create = nil, 74 on_state_create = nil,
74 shutdown_timeout = 0.25, 75 shutdown_timeout = 0.25,
75 with_timers = true, 76 with_timers = true,
@@ -91,6 +92,10 @@ lanes.configure = function( settings_)
91 -- nb_keepers should be a number > 0 92 -- nb_keepers should be a number > 0
92 return type( val_) == "number" and val_ > 0 93 return type( val_) == "number" and val_ > 0
93 end, 94 end,
95 keepers_gc_threshold = function( val_)
96 -- keepers_gc_threshold should be a number
97 return type( val_) == "number"
98 end,
94 with_timers = boolean_param_checker, 99 with_timers = boolean_param_checker,
95 allocator = function( val_) 100 allocator = function( val_)
96 -- can be nil, "protected", or a function 101 -- can be nil, "protected", or a function
@@ -363,261 +368,263 @@ lanes.configure = function( settings_)
363 368
364 if settings.with_timers ~= false then 369 if settings.with_timers ~= false then
365 370
366 -- 371 --
367 -- On first 'require "lanes"', a timer lane is spawned that will maintain 372 -- On first 'require "lanes"', a timer lane is spawned that will maintain
368 -- timer tables and sleep in between the timer events. All interaction with 373 -- timer tables and sleep in between the timer events. All interaction with
369 -- the timer lane happens via a 'timer_gateway' Linda, which is common to 374 -- the timer lane happens via a 'timer_gateway' Linda, which is common to
370 -- all that 'require "lanes"'. 375 -- all that 'require "lanes"'.
371 -- 376 --
372 -- Linda protocol to timer lane: 377 -- Linda protocol to timer lane:
373 -- 378 --
374 -- TGW_KEY: linda_h, key, [wakeup_at_secs], [repeat_secs] 379 -- TGW_KEY: linda_h, key, [wakeup_at_secs], [repeat_secs]
375 -- 380 --
376 local TGW_KEY= "(timer control)" -- the key does not matter, a 'weird' key may help debugging 381 local TGW_KEY= "(timer control)" -- the key does not matter, a 'weird' key may help debugging
377 local TGW_QUERY, TGW_REPLY = "(timer query)", "(timer reply)" 382 local TGW_QUERY, TGW_REPLY = "(timer query)", "(timer reply)"
378 local first_time_key= "first time" 383 local first_time_key= "first time"
379
380 local first_time = timer_gateway:get( first_time_key) == nil
381 timer_gateway:set( first_time_key, true)
382 384
383 -- 385 local first_time = timer_gateway:get( first_time_key) == nil
384 -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally 386 timer_gateway:set( first_time_key, true)
385 -- has 'table' always declared)
386 --
387 if first_time then
388 387
389 local now_secs = core.now_secs 388 local now_secs = core.now_secs
390 assert( type( now_secs) == "function") 389 local wakeup_conv = core.wakeup_conv
391 ----- 390
392 -- Snore loop (run as a lane on the background)
393 --
394 -- High priority, to get trustworthy timings.
395 -- 391 --
396 -- We let the timer lane be a "free running" thread; no handle to it 392 -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally
397 -- remains. 393 -- has 'table' always declared)
398 -- 394 --
399 local timer_body = function() 395 if first_time then
400 set_debug_threadname( "LanesTimer") 396
401 -- 397 assert( type( now_secs) == "function")
402 -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h, 398 -----
403 -- [key]= { wakeup_secs [,period_secs] } [, ...] }, 399 -- Snore loop (run as a lane on the background)
404 -- }
405 --
406 -- Collection of all running timers, indexed with linda's & key.
407 -- 400 --
408 -- Note that we need to use the deep lightuserdata identifiers, instead 401 -- High priority, to get trustworthy timings.
409 -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple
410 -- entries for the same timer.
411 -- 402 --
412 -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but 403 -- We let the timer lane be a "free running" thread; no handle to it
413 -- also important to keep the Linda alive, even if all outside world threw 404 -- remains.
414 -- away pointers to it (which would ruin uniqueness of the deep pointer).
415 -- Now we're safe.
416 -- 405 --
417 local collection = {} 406 local timer_body = function()
418 local table_insert = assert( table.insert) 407 set_debug_threadname( "LanesTimer")
419 408 --
420 local get_timers = function() 409 -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h,
421 local r = {} 410 -- [key]= { wakeup_secs [,period_secs] } [, ...] },
422 for deep, t in pairs( collection) do 411 -- }
423 -- WR( tostring( deep)) 412 --
424 local l = t[deep] 413 -- Collection of all running timers, indexed with linda's & key.
425 for key, timer_data in pairs( t) do 414 --
426 if key ~= deep then 415 -- Note that we need to use the deep lightuserdata identifiers, instead
427 table_insert( r, {l, key, timer_data}) 416 -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple
417 -- entries for the same timer.
418 --
419 -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but
420 -- also important to keep the Linda alive, even if all outside world threw
421 -- away pointers to it (which would ruin uniqueness of the deep pointer).
422 -- Now we're safe.
423 --
424 local collection = {}
425 local table_insert = assert( table.insert)
426
427 local get_timers = function()
428 local r = {}
429 for deep, t in pairs( collection) do
430 -- WR( tostring( deep))
431 local l = t[deep]
432 for key, timer_data in pairs( t) do
433 if key ~= deep then
434 table_insert( r, {l, key, timer_data})
435 end
428 end 436 end
429 end 437 end
430 end 438 return r
431 return r 439 end -- get_timers()
432 end -- get_timers()
433
434 --
435 -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] )
436 --
437 local set_timer = function( linda, key, wakeup_at, period)
438 assert( wakeup_at == nil or wakeup_at > 0.0)
439 assert( period == nil or period > 0.0)
440
441 local linda_deep = linda:deep()
442 assert( linda_deep)
443 440
444 -- Find or make a lookup for this timer
445 -- 441 --
446 local t1 = collection[linda_deep] 442 -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] )
447 if not t1 then 443 --
448 t1 = { [linda_deep] = linda} -- proxy to use the Linda 444 local set_timer = function( linda, key, wakeup_at, period)
449 collection[linda_deep] = t1 445 assert( wakeup_at == nil or wakeup_at > 0.0)
450 end 446 assert( period == nil or period > 0.0)
451 447
452 if wakeup_at == nil then 448 local linda_deep = linda:deep()
453 -- Clear the timer 449 assert( linda_deep)
454 --
455 t1[key]= nil
456 450
457 -- Remove empty tables from collection; speeds timer checks and 451 -- Find or make a lookup for this timer
458 -- lets our 'safety reference' proxy be gc:ed as well.
459 -- 452 --
460 local empty = true 453 local t1 = collection[linda_deep]
461 for k, _ in pairs( t1) do 454 if not t1 then
462 if k ~= linda_deep then 455 t1 = { [linda_deep] = linda} -- proxy to use the Linda
463 empty = false 456 collection[linda_deep] = t1
464 break
465 end
466 end
467 if empty then
468 collection[linda_deep] = nil
469 end 457 end
470 458
471 -- Note: any unread timer value is left at 'linda[key]' intensionally; 459 if wakeup_at == nil then
472 -- clearing a timer just stops it. 460 -- Clear the timer
473 else 461 --
474 -- New timer or changing the timings 462 t1[key]= nil
475 --
476 local t2 = t1[key]
477 if not t2 then
478 t2= {}
479 t1[key]= t2
480 end
481 463
482 t2[1] = wakeup_at 464 -- Remove empty tables from collection; speeds timer checks and
483 t2[2] = period -- can be 'nil' 465 -- lets our 'safety reference' proxy be gc:ed as well.
484 end 466 --
485 end -- set_timer() 467 local empty = true
468 for k, _ in pairs( t1) do
469 if k ~= linda_deep then
470 empty = false
471 break
472 end
473 end
474 if empty then
475 collection[linda_deep] = nil
476 end
486 477
487 ----- 478 -- Note: any unread timer value is left at 'linda[key]' intensionally;
488 -- [next_wakeup_at]= check_timers() 479 -- clearing a timer just stops it.
489 -- Check timers, and wake up the ones expired (if any) 480 else
490 -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none). 481 -- New timer or changing the timings
491 local check_timers = function()
492 local now = now_secs()
493 local next_wakeup
494
495 for linda_deep,t1 in pairs(collection) do
496 for key,t2 in pairs(t1) do
497 -- 482 --
498 if key==linda_deep then 483 local t2 = t1[key]
499 -- no 'continue' in Lua :/ 484 if not t2 then
500 else 485 t2= {}
501 -- 't2': { wakeup_at_secs [,period_secs] } 486 t1[key]= t2
487 end
488
489 t2[1] = wakeup_at
490 t2[2] = period -- can be 'nil'
491 end
492 end -- set_timer()
493
494 -----
495 -- [next_wakeup_at]= check_timers()
496 -- Check timers, and wake up the ones expired (if any)
497 -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none).
498 local check_timers = function()
499 local now = now_secs()
500 local next_wakeup
501
502 for linda_deep,t1 in pairs(collection) do
503 for key,t2 in pairs(t1) do
502 -- 504 --
503 local wakeup_at= t2[1] 505 if key==linda_deep then
504 local period= t2[2] -- may be 'nil' 506 -- no 'continue' in Lua :/
505 507 else
506 if wakeup_at <= now then 508 -- 't2': { wakeup_at_secs [,period_secs] }
507 local linda= t1[linda_deep] 509 --
508 assert(linda) 510 local wakeup_at= t2[1]
509 511 local period= t2[2] -- may be 'nil'
510 linda:set( key, now ) 512
511 513 if wakeup_at <= now then
512 -- 'pairs()' allows the values to be modified (and even 514 local linda= t1[linda_deep]
513 -- removed) as far as keys are not touched 515 assert(linda)
514 516
515 if not period then 517 linda:set( key, now )
516 -- one-time timer; gone 518
517 -- 519 -- 'pairs()' allows the values to be modified (and even
518 t1[key]= nil 520 -- removed) as far as keys are not touched
519 wakeup_at= nil -- no 'continue' in Lua :/ 521
520 else 522 if not period then
521 -- repeating timer; find next wakeup (may jump multiple repeats) 523 -- one-time timer; gone
522 -- 524 --
523 repeat 525 t1[key]= nil
524 wakeup_at= wakeup_at+period 526 wakeup_at= nil -- no 'continue' in Lua :/
525 until wakeup_at > now 527 else
526 528 -- repeating timer; find next wakeup (may jump multiple repeats)
527 t2[1]= wakeup_at 529 --
530 repeat
531 wakeup_at= wakeup_at+period
532 until wakeup_at > now
533
534 t2[1]= wakeup_at
535 end
528 end 536 end
529 end
530 537
531 if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then 538 if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then
532 next_wakeup= wakeup_at 539 next_wakeup= wakeup_at
540 end
533 end 541 end
542 end -- t2 loop
543 end -- t1 loop
544
545 return next_wakeup -- may be 'nil'
546 end -- check_timers()
547
548 local timer_gateway_batched = timer_gateway.batched
549 set_finalizer( function( err, stk)
550 if err and type( err) ~= "userdata" then
551 WR( "LanesTimer error: "..tostring(err))
552 --elseif type( err) == "userdata" then
553 -- WR( "LanesTimer after cancel" )
554 --else
555 -- WR("LanesTimer finalized")
556 end
557 end)
558 while true do
559 local next_wakeup = check_timers()
560
561 -- Sleep until next timer to wake up, or a set/clear command
562 --
563 local secs
564 if next_wakeup then
565 secs = next_wakeup - now_secs()
566 if secs < 0 then secs = 0 end
567 end
568 local key, what = timer_gateway:receive( secs, TGW_KEY, TGW_QUERY)
569
570 if key == TGW_KEY then
571 assert( getmetatable( what) == "Linda") -- 'what' should be a linda on which the client sets a timer
572 local _, key, wakeup_at, period = timer_gateway:receive( 0, timer_gateway_batched, TGW_KEY, 3)
573 assert( key)
574 set_timer( what, key, wakeup_at, period and period > 0 and period or nil)
575 elseif key == TGW_QUERY then
576 if what == "get_timers" then
577 timer_gateway:send( TGW_REPLY, get_timers())
578 else
579 timer_gateway:send( TGW_REPLY, "unknown query " .. what)
534 end 580 end
535 end -- t2 loop 581 --elseif secs == nil then -- got no value while block-waiting?
536 end -- t1 loop 582 -- WR( "timer lane: no linda, aborted?")
537 583 end
538 return next_wakeup -- may be 'nil'
539 end -- check_timers()
540
541 local timer_gateway_batched = timer_gateway.batched
542 set_finalizer( function( err, stk)
543 if err and type( err) ~= "userdata" then
544 WR( "LanesTimer error: "..tostring(err))
545 --elseif type( err) == "userdata" then
546 -- WR( "LanesTimer after cancel" )
547 --else
548 -- WR("LanesTimer finalized")
549 end 584 end
550 end) 585 end -- timer_body()
551 while true do 586 timer_lane = gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility...
552 local next_wakeup = check_timers() 587 end -- first_time
553 588
554 -- Sleep until next timer to wake up, or a set/clear command 589 -----
590 -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] )
591 --
592 -- PUBLIC LANES API
593 timer = function( linda, key, a, period )
594 if getmetatable( linda) ~= "Linda" then
595 error "expecting a Linda"
596 end
597 if a == 0.0 then
598 -- Caller expects to get current time stamp in Linda, on return
599 -- (like the timer had expired instantly); it would be good to set this
600 -- as late as possible (to give most current time) but also we want it
601 -- to precede any possible timers that might start striking.
555 -- 602 --
556 local secs 603 linda:set( key, now_secs())
557 if next_wakeup then 604
558 secs = next_wakeup - now_secs() 605 if not period or period==0.0 then
559 if secs < 0 then secs = 0 end 606 timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer
560 end 607 return -- nothing more to do
561 local key, what = timer_gateway:receive( secs, TGW_KEY, TGW_QUERY)
562
563 if key == TGW_KEY then
564 assert( getmetatable( what) == "Linda") -- 'what' should be a linda on which the client sets a timer
565 local _, key, wakeup_at, period = timer_gateway:receive( 0, timer_gateway_batched, TGW_KEY, 3)
566 assert( key)
567 set_timer( what, key, wakeup_at, period and period > 0 and period or nil)
568 elseif key == TGW_QUERY then
569 if what == "get_timers" then
570 timer_gateway:send( TGW_REPLY, get_timers())
571 else
572 timer_gateway:send( TGW_REPLY, "unknown query " .. what)
573 end
574 --elseif secs == nil then -- got no value while block-waiting?
575 -- WR( "timer lane: no linda, aborted?")
576 end 608 end
609 a= period
577 end 610 end
578 end -- timer_body()
579 timer_lane = gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility...
580 end -- first_time
581 611
582 ----- 612 local wakeup_at= type(a)=="table" and wakeup_conv(a) -- given point of time
583 -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) 613 or (a and now_secs()+a or nil)
584 -- 614 -- queue to timer
585 -- PUBLIC LANES API
586 timer = function( linda, key, a, period )
587 if getmetatable( linda) ~= "Linda" then
588 error "expecting a Linda"
589 end
590 if a == 0.0 then
591 -- Caller expects to get current time stamp in Linda, on return
592 -- (like the timer had expired instantly); it would be good to set this
593 -- as late as possible (to give most current time) but also we want it
594 -- to precede any possible timers that might start striking.
595 -- 615 --
596 linda:set( key, core.now_secs()) 616 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period )
617 end -- timer()
597 618
598 if not period or period==0.0 then 619 -----
599 timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer 620 -- {[{linda, slot, when, period}[,...]]} = timers()
600 return -- nothing more to do
601 end
602 a= period
603 end
604
605 local wakeup_at= type(a)=="table" and core.wakeup_conv(a) -- given point of time
606 or (a and core.now_secs()+a or nil)
607 -- queue to timer
608 -- 621 --
609 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) 622 -- PUBLIC LANES API
610 end 623 timers = function()
611 624 timer_gateway:send( TGW_QUERY, "get_timers")
612 ----- 625 local _, r = timer_gateway:receive( TGW_REPLY)
613 -- {[{linda, slot, when, period}[,...]]} = timers() 626 return r
614 -- 627 end -- timers()
615 -- PUBLIC LANES API
616 timers = function()
617 timer_gateway:send( TGW_QUERY, "get_timers")
618 local _, r = timer_gateway:receive( TGW_REPLY)
619 return r
620 end
621 628
622 end -- settings.with_timers 629 end -- settings.with_timers
623 630
diff --git a/src/lanes_private.h b/src/lanes_private.h
index 6717fe0..8143216 100644
--- a/src/lanes_private.h
+++ b/src/lanes_private.h
@@ -72,18 +72,24 @@ struct s_Lane
72}; 72};
73typedef struct s_Lane Lane; 73typedef struct s_Lane Lane;
74 74
75// xxh64 of string "LANE_POINTER_REGKEY" generated at https://www.pelock.com/products/hash-calculator
76static DECLARE_CONST_UNIQUE_KEY( LANE_POINTER_REGKEY, 0xB3022205633743BC); // used as registry key
77
75// To allow free-running threads (longer lifespan than the handle's) 78// To allow free-running threads (longer lifespan than the handle's)
76// 'Lane' are malloc/free'd and the handle only carries a pointer. 79// 'Lane' are malloc/free'd and the handle only carries a pointer.
77// This is not deep userdata since the handle's not portable among lanes. 80// This is not deep userdata since the handle's not portable among lanes.
78// 81//
79#define lua_toLane( L, i) (*((Lane**) luaL_checkudata( L, i, "Lane"))) 82inline Lane* lua_toLane(lua_State* L, int i_)
83{
84 return *(Lane**)(luaL_checkudata(L, i_, "Lane"));
85}
80 86
81static inline Lane* get_lane_from_registry( lua_State* L) 87static inline Lane* get_lane_from_registry( lua_State* L)
82{ 88{
83 Lane* s; 89 Lane* s;
84 STACK_GROW( L, 1); 90 STACK_GROW( L, 1);
85 STACK_CHECK( L, 0); 91 STACK_CHECK( L, 0);
86 REGISTRY_GET( L, CANCEL_TEST_KEY); 92 REGISTRY_GET( L, LANE_POINTER_REGKEY);
87 s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil 93 s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil
88 lua_pop( L, 1); 94 lua_pop( L, 1);
89 STACK_END( L, 0); 95 STACK_END( L, 0);
diff --git a/src/linda.c b/src/linda.c
index 8b59790..2128520 100644
--- a/src/linda.c
+++ b/src/linda.c
@@ -52,11 +52,11 @@ struct s_Linda
52 SIGNAL_T read_happened; 52 SIGNAL_T read_happened;
53 SIGNAL_T write_happened; 53 SIGNAL_T write_happened;
54 Universe* U; // the universe this linda belongs to 54 Universe* U; // the universe this linda belongs to
55 ptrdiff_t group; // a group to control keeper allocation between lindas 55 uintptr_t group; // a group to control keeper allocation between lindas
56 enum e_cancel_request simulate_cancel; 56 enum e_cancel_request simulate_cancel;
57 char name[1]; 57 char name[1];
58}; 58};
59#define LINDA_KEEPER_HASHSEED( linda) (linda->group ? linda->group : (ptrdiff_t)linda) 59#define LINDA_KEEPER_HASHSEED( linda) (linda->group ? linda->group : (uintptr_t)linda)
60 60
61static void* linda_id( lua_State*, DeepOp); 61static void* linda_id( lua_State*, DeepOp);
62 62
@@ -125,7 +125,7 @@ LUAG_FUNC( linda_send)
125 enum e_cancel_request cancel = CANCEL_NONE; 125 enum e_cancel_request cancel = CANCEL_NONE;
126 int pushed; 126 int pushed;
127 time_d timeout = -1.0; 127 time_d timeout = -1.0;
128 uint_t key_i = 2; // index of first key, if timeout not there 128 int key_i = 2; // index of first key, if timeout not there
129 bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided 129 bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided
130 130
131 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion 131 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion
@@ -151,7 +151,7 @@ LUAG_FUNC( linda_send)
151 STACK_GROW( L, 1); 151 STACK_GROW( L, 1);
152 152
153 // make sure there is something to send 153 // make sure there is something to send
154 if( (uint_t)lua_gettop( L) == key_i) 154 if( lua_gettop( L) == key_i)
155 { 155 {
156 if( as_nil_sentinel) 156 if( as_nil_sentinel)
157 { 157 {
@@ -270,16 +270,17 @@ LUAG_FUNC( linda_send)
270 * returns the actual consumed values, or nil if there weren't enough values to consume 270 * returns the actual consumed values, or nil if there weren't enough values to consume
271 * 271 *
272 */ 272 */
273#define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" 273 // xxh64 of string "BATCH_SENTINEL" generated at https://www.pelock.com/products/hash-calculator
274DECLARE_CONST_UNIQUE_KEY(BATCH_SENTINEL, 0x2DDFEE0968C62AA7);
274LUAG_FUNC( linda_receive) 275LUAG_FUNC( linda_receive)
275{ 276{
276 struct s_Linda* linda = lua_toLinda( L, 1); 277 struct s_Linda* linda = lua_toLinda( L, 1);
277 int pushed, expected_pushed_min, expected_pushed_max; 278 int pushed, expected_pushed_min, expected_pushed_max;
278 enum e_cancel_request cancel = CANCEL_NONE; 279 enum e_cancel_request cancel = CANCEL_NONE;
279 keeper_api_t keeper_receive; 280 keeper_api_t selected_keeper_receive;
280 281
281 time_d timeout = -1.0; 282 time_d timeout = -1.0;
282 uint_t key_i = 2; 283 int key_i = 2;
283 284
284 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion 285 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion
285 { 286 {
@@ -294,7 +295,7 @@ LUAG_FUNC( linda_receive)
294 // are we in batched mode? 295 // are we in batched mode?
295 { 296 {
296 int is_batched; 297 int is_batched;
297 lua_pushliteral( L, BATCH_SENTINEL); 298 push_unique_key( L, BATCH_SENTINEL);
298 is_batched = lua501_equal( L, key_i, -1); 299 is_batched = lua501_equal( L, key_i, -1);
299 lua_pop( L, 1); 300 lua_pop( L, 1);
300 if( is_batched) 301 if( is_batched)
@@ -304,7 +305,7 @@ LUAG_FUNC( linda_receive)
304 // make sure the keys are of a valid type 305 // make sure the keys are of a valid type
305 check_key_types( L, key_i, key_i); 306 check_key_types( L, key_i, key_i);
306 // receive multiple values from a single slot 307 // receive multiple values from a single slot
307 keeper_receive = KEEPER_API( receive_batched); 308 selected_keeper_receive = KEEPER_API( receive_batched);
308 // we expect a user-defined amount of return value 309 // we expect a user-defined amount of return value
309 expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1); 310 expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1);
310 expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); 311 expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min);
@@ -321,7 +322,7 @@ LUAG_FUNC( linda_receive)
321 // make sure the keys are of a valid type 322 // make sure the keys are of a valid type
322 check_key_types( L, key_i, lua_gettop( L)); 323 check_key_types( L, key_i, lua_gettop( L));
323 // receive a single value, checking multiple slots 324 // receive a single value, checking multiple slots
324 keeper_receive = KEEPER_API( receive); 325 selected_keeper_receive = KEEPER_API( receive);
325 // we expect a single (value, key) pair of returned values 326 // we expect a single (value, key) pair of returned values
326 expected_pushed_min = expected_pushed_max = 2; 327 expected_pushed_min = expected_pushed_max = 2;
327 } 328 }
@@ -347,7 +348,7 @@ LUAG_FUNC( linda_receive)
347 } 348 }
348 349
349 // all arguments of receive() but the first are passed to the keeper's receive function 350 // all arguments of receive() but the first are passed to the keeper's receive function
350 pushed = keeper_call( linda->U, K->L, keeper_receive, L, linda, key_i); 351 pushed = keeper_call( linda->U, K->L, selected_keeper_receive, L, linda, key_i);
351 if( pushed < 0) 352 if( pushed < 0)
352 { 353 {
353 break; 354 break;
@@ -511,29 +512,27 @@ LUAG_FUNC( linda_get)
511 512
512 // make sure the key is of a valid type (throws an error if not the case) 513 // make sure the key is of a valid type (throws an error if not the case)
513 check_key_types( L, 2, 2); 514 check_key_types( L, 2, 2);
514 {
515 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
516 515
517 if( linda->simulate_cancel == CANCEL_NONE) 516 if( linda->simulate_cancel == CANCEL_NONE)
518 { 517 {
519 pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2); 518 Keeper* const K = which_keeper(linda->U->keepers, LINDA_KEEPER_HASHSEED(linda));
520 if( pushed > 0) 519 pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2);
521 { 520 if( pushed > 0)
522 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper);
523 }
524 }
525 else // linda is cancelled
526 {
527 // do nothing and return lanes.cancel_error
528 push_unique_key( L, CANCEL_ERROR);
529 pushed = 1;
530 }
531 // an error can be raised if we attempt to read an unregistered function
532 if( pushed < 0)
533 { 521 {
534 return luaL_error( L, "tried to copy unsupported types"); 522 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper);
535 } 523 }
536 } 524 }
525 else // linda is cancelled
526 {
527 // do nothing and return lanes.cancel_error
528 push_unique_key( L, CANCEL_ERROR);
529 pushed = 1;
530 }
531 // an error can be raised if we attempt to read an unregistered function
532 if( pushed < 0)
533 {
534 return luaL_error( L, "tried to copy unsupported types");
535 }
537 536
538 return pushed; 537 return pushed;
539} 538}
@@ -557,26 +556,23 @@ LUAG_FUNC( linda_limit)
557 // make sure the key is of a valid type 556 // make sure the key is of a valid type
558 check_key_types( L, 2, 2); 557 check_key_types( L, 2, 2);
559 558
559 if( linda->simulate_cancel == CANCEL_NONE)
560 { 560 {
561 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 561 Keeper* const K = which_keeper(linda->U->keepers, LINDA_KEEPER_HASHSEED(linda));
562 562 pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2);
563 if( linda->simulate_cancel == CANCEL_NONE) 563 ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads
564 { 564 if( pushed == 1)
565 pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2);
566 ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads
567 if( pushed == 1)
568 {
569 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1);
570 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area
571 }
572 }
573 else // linda is cancelled
574 { 565 {
575 // do nothing and return lanes.cancel_error 566 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1);
576 push_unique_key( L, CANCEL_ERROR); 567 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area
577 pushed = 1;
578 } 568 }
579 } 569 }
570 else // linda is cancelled
571 {
572 // do nothing and return lanes.cancel_error
573 push_unique_key( L, CANCEL_ERROR);
574 pushed = 1;
575 }
580 // propagate pushed boolean if any 576 // propagate pushed boolean if any
581 return pushed; 577 return pushed;
582} 578}
@@ -762,6 +758,7 @@ static void* linda_id( lua_State* L, DeepOp op_)
762 { 758 {
763 case eDO_new: 759 case eDO_new:
764 { 760 {
761 Universe* const U = universe_get(L);
765 struct s_Linda* s; 762 struct s_Linda* s;
766 size_t name_len = 0; 763 size_t name_len = 0;
767 char const* linda_name = NULL; 764 char const* linda_name = NULL;
@@ -795,7 +792,6 @@ static void* linda_id( lua_State* L, DeepOp op_)
795 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda 792 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda
796 */ 793 */
797 { 794 {
798 Universe* const U = universe_get(L);
799 AllocatorDefinition* const allocD = &U->internal_allocator; 795 AllocatorDefinition* const allocD = &U->internal_allocator;
800 s = (struct s_Linda*) allocD->allocF(allocD->allocUD, NULL, 0, sizeof(struct s_Linda) + name_len); // terminating 0 is already included 796 s = (struct s_Linda*) allocD->allocF(allocD->allocUD, NULL, 0, sizeof(struct s_Linda) + name_len); // terminating 0 is already included
801 } 797 }
@@ -804,7 +800,7 @@ static void* linda_id( lua_State* L, DeepOp op_)
804 s->prelude.magic.value = DEEP_VERSION.value; 800 s->prelude.magic.value = DEEP_VERSION.value;
805 SIGNAL_INIT( &s->read_happened); 801 SIGNAL_INIT( &s->read_happened);
806 SIGNAL_INIT( &s->write_happened); 802 SIGNAL_INIT( &s->write_happened);
807 s->U = universe_get( L); 803 s->U = U;
808 s->simulate_cancel = CANCEL_NONE; 804 s->simulate_cancel = CANCEL_NONE;
809 s->group = linda_group << KEEPER_MAGIC_SHIFT; 805 s->group = linda_group << KEEPER_MAGIC_SHIFT;
810 s->name[0] = 0; 806 s->name[0] = 0;
@@ -815,25 +811,32 @@ static void* linda_id( lua_State* L, DeepOp op_)
815 811
816 case eDO_delete: 812 case eDO_delete:
817 { 813 {
818 Keeper* K; 814 Keeper* myK;
819 struct s_Linda* linda = lua_touserdata( L, 1); 815 struct s_Linda* linda = lua_touserdata( L, 1);
820 ASSERT_L( linda); 816 ASSERT_L( linda);
821 817
822 // Clean associated structures in the keeper state. 818 // Clean associated structures in the keeper state.
823 K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 819 myK = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
824 if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) 820 if (myK)
825 { 821 {
822 // if collected from my own keeper, we can't acquire/release it
823 // because we are already inside a protected area, and trying to do so would deadlock!
824 bool_t const need_acquire_release = (myK->L != L);
825 // Clean associated structures in the keeper state.
826 Keeper* const K = need_acquire_release ? keeper_acquire(linda->U->keepers, LINDA_KEEPER_HASHSEED(linda)) : myK;
826 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... 827 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex...
827 keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); 828 keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0);
829 if(need_acquire_release)
830 {
831 keeper_release(K);
832 }
828 } 833 }
829 keeper_release( K);
830 834
831 // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right? 835 // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right?
832 SIGNAL_FREE( &linda->read_happened); 836 SIGNAL_FREE( &linda->read_happened);
833 SIGNAL_FREE( &linda->write_happened); 837 SIGNAL_FREE( &linda->write_happened);
834 { 838 {
835 Universe* const U = universe_get(L); 839 AllocatorDefinition* const allocD = &linda->U->internal_allocator;
836 AllocatorDefinition* const allocD = &U->internal_allocator;
837 (void) allocD->allocF(allocD->allocUD, linda, sizeof(struct s_Linda) + strlen(linda->name), 0); 840 (void) allocD->allocF(allocD->allocUD, linda, sizeof(struct s_Linda) + strlen(linda->name), 0);
838 } 841 }
839 return NULL; 842 return NULL;
@@ -901,7 +904,7 @@ static void* linda_id( lua_State* L, DeepOp op_)
901 lua_setfield( L, -2, "dump"); 904 lua_setfield( L, -2, "dump");
902 905
903 // some constants 906 // some constants
904 lua_pushliteral( L, BATCH_SENTINEL); 907 push_unique_key( L, BATCH_SENTINEL);
905 lua_setfield( L, -2, "batched"); 908 lua_setfield( L, -2, "batched");
906 909
907 push_unique_key( L, NIL_SENTINEL); 910 push_unique_key( L, NIL_SENTINEL);
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h
index 05a46b5..e184476 100644
--- a/src/macros_and_utils.h
+++ b/src/macros_and_utils.h
@@ -6,6 +6,7 @@
6 6
7#include "lua.h" 7#include "lua.h"
8#include "lualib.h" 8#include "lualib.h"
9#include "lauxlib.h"
9 10
10 // M$ compiler doesn't support 'inline' keyword in C files... 11 // M$ compiler doesn't support 'inline' keyword in C files...
11#if defined( _MSC_VER) 12#if defined( _MSC_VER)
@@ -81,7 +82,11 @@ extern char const* debugspew_indent;
81 82
82#define ASSERT_L(c) _ASSERT_L(L,c) 83#define ASSERT_L(c) _ASSERT_L(L,c)
83 84
84#define STACK_GROW( L, n) do { if (!lua_checkstack(L,(int)(n))) luaL_error( L, "Cannot grow stack!" ); } while( 0) 85inline void STACK_GROW(lua_State * L, int n_)
86{
87 if (!lua_checkstack(L, n_))
88 luaL_error(L, "Cannot grow stack!");
89}
85 90
86// non-string keyed registry access 91// non-string keyed registry access
87#define REGISTRY_SET( L, key_, value_) \ 92#define REGISTRY_SET( L, key_, value_) \
diff --git a/src/platform.h b/src/platform.h
index da5264e..2f71c07 100644
--- a/src/platform.h
+++ b/src/platform.h
@@ -7,6 +7,7 @@
7 #define PLATFORM_XBOX 7 #define PLATFORM_XBOX
8#elif (defined _WIN32) 8#elif (defined _WIN32)
9 #define PLATFORM_WIN32 9 #define PLATFORM_WIN32
10 #define NOMINMAX
10#elif (defined __linux__) 11#elif (defined __linux__)
11 #define PLATFORM_LINUX 12 #define PLATFORM_LINUX
12#elif (defined __APPLE__) && (defined __MACH__) 13#elif (defined __APPLE__) && (defined __MACH__)
diff --git a/src/state.c b/src/state.c
index 21ca397..32e5b47 100644
--- a/src/state.c
+++ b/src/state.c
@@ -205,7 +205,7 @@ static void copy_one_time_settings( Universe* U, lua_State* L, lua_State* L2)
205 205
206 REGISTRY_GET( L, CONFIG_REGKEY); // config 206 REGISTRY_GET( L, CONFIG_REGKEY); // config
207 // copy settings from from source to destination registry 207 // copy settings from from source to destination registry
208 if( luaG_inter_move( U, L, L2, 1, eLM_LaneBody) < 0) // // config 208 if( luaG_inter_move( U, L, L2, 1, eLM_LaneBody) != eICR_Success) // // config
209 { 209 {
210 (void) luaL_error( L, "failed to copy settings when loading lanes.core"); 210 (void) luaL_error( L, "failed to copy settings when loading lanes.core");
211 } 211 }
diff --git a/src/tools.c b/src/tools.c
index 80e0f71..c43d8a2 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -242,8 +242,14 @@ void initialize_allocator_function( Universe* U, lua_State* L)
242 U->internal_allocator.allocF = libc_lua_Alloc; 242 U->internal_allocator.allocF = libc_lua_Alloc;
243 U->internal_allocator.allocUD = NULL; 243 U->internal_allocator.allocUD = NULL;
244 } 244 }
245 else if (U->provide_allocator == luaG_provide_protected_allocator)
246 {
247 // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case.
248 U->internal_allocator.allocF = lua_getallocf(L, &U->internal_allocator.allocUD);
249 }
245 else 250 else
246 { 251 {
252 // no protection required, just use whatever we have as-is.
247 U->internal_allocator = U->protected_allocator.definition; 253 U->internal_allocator = U->protected_allocator.definition;
248 } 254 }
249 } 255 }
@@ -844,8 +850,8 @@ static bool_t lookup_table( lua_State* L2, lua_State* L, uint_t i, LookupMode mo
844 */ 850 */
845static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) 851static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i)
846{ 852{
847 bool_t not_found_in_cache; // L2 853 bool_t not_found_in_cache; // L2
848 DECLARE_CONST_UNIQUE_KEY( p, lua_topointer( L, i)); 854 void const* p = lua_topointer( L, i);
849 855
850 ASSERT_L( L2_cache_i != 0); 856 ASSERT_L( L2_cache_i != 0);
851 STACK_GROW( L2, 3); 857 STACK_GROW( L2, 3);
@@ -854,17 +860,17 @@ static bool_t push_cached_table( lua_State* L2, uint_t L2_cache_i, lua_State* L,
854 // We don't need to use the from state ('L') in ID since the life span 860 // We don't need to use the from state ('L') in ID since the life span
855 // is only for the duration of a copy (both states are locked). 861 // is only for the duration of a copy (both states are locked).
856 // push a light userdata uniquely representing the table 862 // push a light userdata uniquely representing the table
857 push_unique_key( L2, p); // ... p 863 lua_pushlightuserdata( L2, (void*) p); // ... p
858 864
859 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); 865 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1));
860 866
861 lua_rawget( L2, L2_cache_i); // ... {cached|nil} 867 lua_rawget( L2, L2_cache_i); // ... {cached|nil}
862 not_found_in_cache = lua_isnil( L2, -1); 868 not_found_in_cache = lua_isnil( L2, -1);
863 if( not_found_in_cache) 869 if( not_found_in_cache)
864 { 870 {
865 lua_pop( L2, 1); // ... 871 lua_pop( L2, 1); // ...
866 lua_newtable( L2); // ... {} 872 lua_newtable( L2); // ... {}
867 push_unique_key( L2, p); // ... {} p 873 lua_pushlightuserdata( L2, (void*) p); // ... {} p
868 lua_pushvalue( L2, -2); // ... {} p {} 874 lua_pushvalue( L2, -2); // ... {} p {}
869 lua_rawset( L2, L2_cache_i); // ... {} 875 lua_rawset( L2, L2_cache_i); // ... {}
870 } 876 }
@@ -1446,7 +1452,7 @@ static void inter_copy_keyvaluepair( Universe* U, lua_State* L2, uint_t L2_cache
1446 uint_t key_i = val_i - 1; 1452 uint_t key_i = val_i - 1;
1447 1453
1448 // Only basic key types are copied over; others ignored 1454 // Only basic key types are copied over; others ignored
1449 if( inter_copy_one( U, L2, 0 /*key*/, L, key_i, VT_KEY, mode_, upName_)) 1455 if( inter_copy_one( U, L2, L2_cache_i, L, key_i, VT_KEY, mode_, upName_))
1450 { 1456 {
1451 char* valPath = (char*) upName_; 1457 char* valPath = (char*) upName_;
1452 if( U->verboseErrors) 1458 if( U->verboseErrors)
@@ -1596,7 +1602,10 @@ static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_Stat
1596 // assign uservalues 1602 // assign uservalues
1597 while( uvi > 0) 1603 while( uvi > 0)
1598 { 1604 {
1599 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // ... u uv 1605 if(!inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_)) // ... u uv
1606 {
1607 (void) luaL_error(L, "Cannot copy upvalue type '%s'", luaL_typename(L, -1));
1608 }
1600 lua_pop( L, 1); // ... mt __lanesclone [uv]* 1609 lua_pop( L, 1); // ... mt __lanesclone [uv]*
1601 // this pops the value from the stack 1610 // this pops the value from the stack
1602 lua_setiuservalue( L2, -2, uvi); // ... u 1611 lua_setiuservalue( L2, -2, uvi); // ... u
@@ -1730,7 +1739,10 @@ static bool_t inter_copy_function( Universe* U, lua_State* L2, uint_t L2_cache_i
1730 // transfer and assign uservalues 1739 // transfer and assign uservalues
1731 while( uvi > 0) 1740 while( uvi > 0)
1732 { 1741 {
1733 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), vt, mode_, upName_); // ... mt u uv 1742 if(!inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), vt, mode_, upName_)) // ... mt u uv
1743 {
1744 (void) luaL_error(L, "Cannot copy upvalue type '%s'", luaL_typename(L, -1));
1745 }
1734 lua_pop( L, 1); // ... u [uv]* 1746 lua_pop( L, 1); // ... u [uv]*
1735 // this pops the value from the stack 1747 // this pops the value from the stack
1736 lua_setiuservalue( L2, -2, uvi); // ... mt u 1748 lua_setiuservalue( L2, -2, uvi); // ... mt u
@@ -1957,7 +1969,7 @@ bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State*
1957* 1969*
1958* Note: Parameters are in this order ('L' = from first) to be same as 'lua_xmove'. 1970* Note: Parameters are in this order ('L' = from first) to be same as 'lua_xmove'.
1959*/ 1971*/
1960int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_) 1972InterCopyResult luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_)
1961{ 1973{
1962 uint_t top_L = lua_gettop( L); // ... {}n 1974 uint_t top_L = lua_gettop( L); // ... {}n
1963 uint_t top_L2 = lua_gettop( L2); // ... 1975 uint_t top_L2 = lua_gettop( L2); // ...
@@ -1974,7 +1986,7 @@ int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupM
1974 // requesting to copy more than is available? 1986 // requesting to copy more than is available?
1975 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END)); 1987 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END));
1976 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 1988 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
1977 return -1; 1989 return eICR_NotEnoughValues;
1978 } 1990 }
1979 1991
1980 STACK_CHECK( L2, 0); 1992 STACK_CHECK( L2, 0);
@@ -2010,24 +2022,24 @@ int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupM
2010 // Remove the cache table. Persistent caching would cause i.e. multiple 2022 // Remove the cache table. Persistent caching would cause i.e. multiple
2011 // messages passed in the same table to use the same table also in receiving end. 2023 // messages passed in the same table to use the same table also in receiving end.
2012 lua_remove( L2, top_L2 + 1); 2024 lua_remove( L2, top_L2 + 1);
2013 return 0; 2025 return eICR_Success;
2014 } 2026 }
2015 2027
2016 // error -> pop everything from the target state stack 2028 // error -> pop everything from the target state stack
2017 lua_settop( L2, top_L2); 2029 lua_settop( L2, top_L2);
2018 STACK_END( L2, 0); 2030 STACK_END( L2, 0);
2019 return -2; 2031 return eICR_Error;
2020} 2032}
2021 2033
2022 2034
2023int luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_) 2035InterCopyResult luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_)
2024{ 2036{
2025 int ret = luaG_inter_copy( U, L, L2, n, mode_); 2037 InterCopyResult ret = luaG_inter_copy( U, L, L2, n, mode_);
2026 lua_pop( L, (int) n); 2038 lua_pop( L, (int) n);
2027 return ret; 2039 return ret;
2028} 2040}
2029 2041
2030int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_) 2042InterCopyResult luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_)
2031{ 2043{
2032 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_inter_copy_package()\n" INDENT_END)); 2044 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_inter_copy_package()\n" INDENT_END));
2033 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 2045 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
@@ -2040,7 +2052,11 @@ int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int packa
2040 lua_pushfstring( L, "expected package as table, got %s", luaL_typename( L, package_idx_)); 2052 lua_pushfstring( L, "expected package as table, got %s", luaL_typename( L, package_idx_));
2041 STACK_MID( L, 1); 2053 STACK_MID( L, 1);
2042 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later 2054 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
2043 return ( mode_ == eLM_LaneBody) ? lua_error( L) : 1; 2055 if (mode_ == eLM_LaneBody)
2056 {
2057 lua_error(L); // doesn't return
2058 }
2059 return eICR_Error;
2044 } 2060 }
2045 lua_getglobal( L2, "package"); 2061 lua_getglobal( L2, "package");
2046 if( !lua_isnil( L2, -1)) // package library not loaded: do nothing 2062 if( !lua_isnil( L2, -1)) // package library not loaded: do nothing
@@ -2076,5 +2092,5 @@ int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int packa
2076 STACK_END( L2, 0); 2092 STACK_END( L2, 0);
2077 STACK_END( L, 0); 2093 STACK_END( L, 0);
2078 DEBUGSPEW_CODE( -- U->debugspew_indent_depth); 2094 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
2079 return 0; 2095 return eICR_Success;
2080} 2096}
diff --git a/src/tools.h b/src/tools.h
index a0893e4..6c08734 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -31,14 +31,22 @@ enum e_vt
31 VT_KEY, 31 VT_KEY,
32 VT_METATABLE 32 VT_METATABLE
33}; 33};
34
35enum eInterCopyResult
36{
37 eICR_Success,
38 eICR_NotEnoughValues,
39 eICR_Error
40};
41typedef enum eInterCopyResult InterCopyResult;
42
34bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_); 43bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_);
35 44
36// ################################################################################################ 45// ################################################################################################
37 46
38int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_); 47InterCopyResult luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_);
39 48InterCopyResult luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_);
40int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_); 49InterCopyResult luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_);
41int luaG_inter_move( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_);
42 50
43int luaG_nameof( lua_State* L); 51int luaG_nameof( lua_State* L);
44 52
diff --git a/src/uniquekey.h b/src/uniquekey.h
index 015fbf2..7162753 100644
--- a/src/uniquekey.h
+++ b/src/uniquekey.h
@@ -11,9 +11,9 @@ struct s_UniqueKey
11typedef struct s_UniqueKey UniqueKey; 11typedef struct s_UniqueKey UniqueKey;
12 12
13#if LUAJIT_FLAVOR() == 64 // building against LuaJIT headers for 64 bits, light userdata is restricted to 47 significant bits, because LuaJIT uses the other bits for internal optimizations 13#if LUAJIT_FLAVOR() == 64 // building against LuaJIT headers for 64 bits, light userdata is restricted to 47 significant bits, because LuaJIT uses the other bits for internal optimizations
14#define MAKE_UNIQUE_KEY( p_) ((void*)((ptrdiff_t)(p_) & 0x7fffffffffffull)) 14#define MAKE_UNIQUE_KEY( p_) ((void*)((uintptr_t)(p_) & 0x7fffffffffffull))
15#else // LUAJIT_FLAVOR() 15#else // LUAJIT_FLAVOR()
16#define MAKE_UNIQUE_KEY( p_) ((void*)(ptrdiff_t)(p_)) 16#define MAKE_UNIQUE_KEY( p_) ((void*)(uintptr_t)(p_))
17#endif // LUAJIT_FLAVOR() 17#endif // LUAJIT_FLAVOR()
18 18
19#define DECLARE_UNIQUE_KEY( name_) UniqueKey name_ 19#define DECLARE_UNIQUE_KEY( name_) UniqueKey name_