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; |
