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 /src | |
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
Diffstat (limited to '')
-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 |
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/ |
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_ |