aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-04-05 08:49:48 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-04-05 08:51:09 +0200
commitc64f9dcd61c1ad7bef3dbf5b7647a2a2da23ac0f (patch)
tree349aa02b2367d11d252468af983d719f20a00a42 /src
parent2cb54d2ac028d648deab6d49a051f53a0bb67fb2 (diff)
downloadlanes-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.cpp54
-rw-r--r--src/keeper.h1
-rw-r--r--src/lanes.lua5
-rw-r--r--src/linda.cpp17
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)
735Keeper* which_keeper(Keepers* keepers_, uintptr_t magic_) 747Keeper* 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
25struct Keepers 25struct 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;