diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-04-05 08:49:48 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-04-05 08:51:09 +0200 |
commit | c64f9dcd61c1ad7bef3dbf5b7647a2a2da23ac0f (patch) | |
tree | 349aa02b2367d11d252468af983d719f20a00a42 /src | |
parent | 2cb54d2ac028d648deab6d49a051f53a0bb67fb2 (diff) | |
download | lanes-c64f9dcd61c1ad7bef3dbf5b7647a2a2da23ac0f.tar.gz lanes-c64f9dcd61c1ad7bef3dbf5b7647a2a2da23ac0f.tar.bz2 lanes-c64f9dcd61c1ad7bef3dbf5b7647a2a2da23ac0f.zip |
Enable manual control of GC inside keeper states
Diffstat (limited to 'src')
-rw-r--r-- | src/keeper.cpp | 54 | ||||
-rw-r--r-- | src/keeper.h | 1 | ||||
-rw-r--r-- | src/lanes.lua | 5 | ||||
-rw-r--r-- | src/linda.cpp | 17 |
4 files changed, 64 insertions, 13 deletions
diff --git a/src/keeper.cpp b/src/keeper.cpp index 244cb6a..937d190 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp | |||
@@ -652,12 +652,18 @@ void init_keepers(Universe* U, lua_State* L) | |||
652 | { | 652 | { |
653 | STACK_CHECK_START_REL(L, 0); // L K | 653 | STACK_CHECK_START_REL(L, 0); // L K |
654 | lua_getfield(L, 1, "nb_keepers"); // nb_keepers | 654 | lua_getfield(L, 1, "nb_keepers"); // nb_keepers |
655 | int nb_keepers{ static_cast<int>(lua_tointeger(L, -1)) }; | 655 | int const nb_keepers{ static_cast<int>(lua_tointeger(L, -1)) }; |
656 | lua_pop(L, 1); // | 656 | lua_pop(L, 1); // |
657 | if (nb_keepers < 1) | 657 | if (nb_keepers < 1) |
658 | { | 658 | { |
659 | std::ignore = luaL_error(L, "Bad number of keepers (%d)", nb_keepers); | 659 | std::ignore = luaL_error(L, "Bad number of keepers (%d)", nb_keepers); |
660 | } | 660 | } |
661 | STACK_CHECK(L, 0); | ||
662 | |||
663 | lua_getfield(L, 1, "keepers_gc_threshold"); // keepers_gc_threshold | ||
664 | int const keepers_gc_threshold{ static_cast<int>(lua_tointeger(L, -1)) }; | ||
665 | lua_pop(L, 1); // | ||
666 | STACK_CHECK(L, 0); | ||
661 | 667 | ||
662 | // Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states | 668 | // Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states |
663 | { | 669 | { |
@@ -668,6 +674,7 @@ void init_keepers(Universe* U, lua_State* L) | |||
668 | std::ignore = luaL_error(L, "init_keepers() failed while creating keeper array; out of memory"); | 674 | std::ignore = luaL_error(L, "init_keepers() failed while creating keeper array; out of memory"); |
669 | } | 675 | } |
670 | memset(U->keepers, 0, bytes); | 676 | memset(U->keepers, 0, bytes); |
677 | U->keepers->gc_threshold = keepers_gc_threshold; | ||
671 | U->keepers->nb_keepers = nb_keepers; | 678 | U->keepers->nb_keepers = nb_keepers; |
672 | } | 679 | } |
673 | for (int i = 0; i < nb_keepers; ++i) // keepersUD | 680 | for (int i = 0; i < nb_keepers; ++i) // keepersUD |
@@ -685,6 +692,11 @@ void init_keepers(Universe* U, lua_State* L) | |||
685 | // therefore, we need a recursive mutex. | 692 | // therefore, we need a recursive mutex. |
686 | MUTEX_RECURSIVE_INIT(&U->keepers->keeper_array[i].keeper_cs); | 693 | MUTEX_RECURSIVE_INIT(&U->keepers->keeper_array[i].keeper_cs); |
687 | 694 | ||
695 | if (U->keepers->gc_threshold >= 0) | ||
696 | { | ||
697 | lua_gc(K, LUA_GCSTOP, 0); | ||
698 | } | ||
699 | |||
688 | STACK_CHECK_START_ABS(K, 0); | 700 | STACK_CHECK_START_ABS(K, 0); |
689 | 701 | ||
690 | // copy the universe pointer in the keeper itself | 702 | // copy the universe pointer in the keeper itself |
@@ -735,8 +747,12 @@ void init_keepers(Universe* U, lua_State* L) | |||
735 | Keeper* which_keeper(Keepers* keepers_, uintptr_t magic_) | 747 | Keeper* which_keeper(Keepers* keepers_, uintptr_t magic_) |
736 | { | 748 | { |
737 | int const nbKeepers{ keepers_->nb_keepers }; | 749 | int const nbKeepers{ keepers_->nb_keepers }; |
738 | unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers); | 750 | if (nbKeepers) |
739 | return &keepers_->keeper_array[i]; | 751 | { |
752 | unsigned int i = (unsigned int) ((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers); | ||
753 | return &keepers_->keeper_array[i]; | ||
754 | } | ||
755 | return nullptr; | ||
740 | } | 756 | } |
741 | 757 | ||
742 | // ################################################################################################## | 758 | // ################################################################################################## |
@@ -745,11 +761,7 @@ Keeper* keeper_acquire(Keepers* keepers_, uintptr_t magic_) | |||
745 | { | 761 | { |
746 | int const nbKeepers{ keepers_->nb_keepers }; | 762 | int const nbKeepers{ keepers_->nb_keepers }; |
747 | // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) | 763 | // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) |
748 | if( nbKeepers == 0) | 764 | if (nbKeepers) |
749 | { | ||
750 | return nullptr; | ||
751 | } | ||
752 | else | ||
753 | { | 765 | { |
754 | /* | 766 | /* |
755 | * Any hashing will do that maps pointers to 0..GNbKeepers-1 | 767 | * Any hashing will do that maps pointers to 0..GNbKeepers-1 |
@@ -765,6 +777,7 @@ Keeper* keeper_acquire(Keepers* keepers_, uintptr_t magic_) | |||
765 | //++ K->count; | 777 | //++ K->count; |
766 | return K; | 778 | return K; |
767 | } | 779 | } |
780 | return nullptr; | ||
768 | } | 781 | } |
769 | 782 | ||
770 | // ################################################################################################## | 783 | // ################################################################################################## |
@@ -843,5 +856,30 @@ int keeper_call(Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, voi | |||
843 | } | 856 | } |
844 | // whatever happens, restore the stack to where it was at the origin | 857 | // whatever happens, restore the stack to where it was at the origin |
845 | lua_settop(K, Ktos); | 858 | lua_settop(K, Ktos); |
859 | |||
860 | // 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 | ||
861 | if (func_ != KEEPER_API(clear)) [[unlikely]] | ||
862 | { | ||
863 | // since keeper state GC is stopped, let's run a step once in a while if required | ||
864 | int const gc_threshold{ U->keepers->gc_threshold }; | ||
865 | if (gc_threshold == 0) [[unlikely]] | ||
866 | { | ||
867 | lua_gc(K, LUA_GCSTEP, 0); | ||
868 | } | ||
869 | else if (gc_threshold > 0) [[likely]] | ||
870 | { | ||
871 | int const gc_usage{ lua_gc(K, LUA_GCCOUNT, 0) }; | ||
872 | if (gc_usage >= gc_threshold) | ||
873 | { | ||
874 | lua_gc(K, LUA_GCCOLLECT, 0); | ||
875 | int const gc_usage_after{ lua_gc(K, LUA_GCCOUNT, 0) }; | ||
876 | if (gc_usage_after > gc_threshold) [[unlikely]] | ||
877 | { | ||
878 | luaL_error(L, "Keeper GC threshold is too low, need at least %d", gc_usage_after); | ||
879 | } | ||
880 | } | ||
881 | } | ||
882 | } | ||
883 | |||
846 | return retvals; | 884 | return retvals; |
847 | } | 885 | } |
diff --git a/src/keeper.h b/src/keeper.h index e081bea..f7e3951 100644 --- a/src/keeper.h +++ b/src/keeper.h | |||
@@ -24,6 +24,7 @@ struct Keeper | |||
24 | 24 | ||
25 | struct Keepers | 25 | struct Keepers |
26 | { | 26 | { |
27 | int gc_threshold{ 0 }; | ||
27 | int nb_keepers; | 28 | int nb_keepers; |
28 | Keeper keeper_array[1]; | 29 | Keeper keeper_array[1]; |
29 | }; | 30 | }; |
diff --git a/src/lanes.lua b/src/lanes.lua index b4c0070..6af286a 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 |
diff --git a/src/linda.cpp b/src/linda.cpp index 37a74b0..5ee4768 100644 --- a/src/linda.cpp +++ b/src/linda.cpp | |||
@@ -885,15 +885,22 @@ static void* linda_id( lua_State* L, DeepOp op_) | |||
885 | { | 885 | { |
886 | Linda* const linda{ lua_tolightuserdata<Linda>(L, 1) }; | 886 | Linda* const linda{ lua_tolightuserdata<Linda>(L, 1) }; |
887 | ASSERT_L(linda); | 887 | ASSERT_L(linda); |
888 | 888 | Keeper* const myK{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | |
889 | // Clean associated structures in the keeper state. | 889 | // if collected after the universe, keepers are already destroyed, and there is nothing to clear |
890 | Keeper* const K{ keeper_acquire(linda->U->keepers, linda->hashSeed()) }; | 890 | if (myK) |
891 | if (K && K->L) // can be nullptr if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) | ||
892 | { | 891 | { |
892 | // if collected from my own keeper, we can't acquire/release it | ||
893 | // because we are already inside a protected area, and trying to do so would deadlock! | ||
894 | bool const need_acquire_release{ myK->L != L }; | ||
895 | // Clean associated structures in the keeper state. | ||
896 | Keeper* const K{ need_acquire_release ? keeper_acquire(linda->U->keepers, linda->hashSeed()) : myK }; | ||
893 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... | 897 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... |
894 | keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0); | 898 | keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0); |
899 | if (need_acquire_release) | ||
900 | { | ||
901 | keeper_release(K); | ||
902 | } | ||
895 | } | 903 | } |
896 | keeper_release(K); | ||
897 | 904 | ||
898 | delete linda; // operator delete overload ensures things go as expected | 905 | delete linda; // operator delete overload ensures things go as expected |
899 | return nullptr; | 906 | return nullptr; |