aboutsummaryrefslogtreecommitdiff
path: root/src/keeper.cpp
diff options
context:
space:
mode:
authorBenoit Germain <bnt.germain@gmail.com>2024-04-14 18:27:10 +0200
committerBenoit Germain <bnt.germain@gmail.com>2024-04-14 18:27:10 +0200
commit69d40c81d8343a1af7e0fe61fbf20a4cf5880c25 (patch)
treecbf7aa525868040820ce6743f1a30fbb59926407 /src/keeper.cpp
parent0d9c9bae120f92274e1c68f7abdebfcf2c24405d (diff)
parent00970610dc8fbd00a11d3b69e4702933a592ce9f (diff)
downloadlanes-69d40c81d8343a1af7e0fe61fbf20a4cf5880c25.tar.gz
lanes-69d40c81d8343a1af7e0fe61fbf20a4cf5880c25.tar.bz2
lanes-69d40c81d8343a1af7e0fe61fbf20a4cf5880c25.zip
Merge branch 'master' of https://github.com/LuaLanes/lanes
Diffstat (limited to 'src/keeper.cpp')
-rw-r--r--src/keeper.cpp153
1 files changed, 95 insertions, 58 deletions
diff --git a/src/keeper.cpp b/src/keeper.cpp
index 244cb6a..f56c50c 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -14,7 +14,7 @@
14 --[[ 14 --[[
15 =============================================================================== 15 ===============================================================================
16 16
17 Copyright (C) 2011-2023 Benoit Germain <bnt.germain@gmail.com> 17 Copyright (C) 2011-2024 Benoit Germain <bnt.germain@gmail.com>
18 18
19 Permission is hereby granted, free of charge, to any person obtaining a copy 19 Permission is hereby granted, free of charge, to any person obtaining a copy
20 of this software and associated documentation files (the "Software"), to deal 20 of this software and associated documentation files (the "Software"), to deal
@@ -61,12 +61,12 @@ class keeper_fifo
61 int limit{ -1 }; 61 int limit{ -1 };
62 62
63 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents 63 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents
64 static void* operator new([[maybe_unused]] size_t size_, lua_State* L) noexcept { return lua_newuserdatauv<keeper_fifo>(L, 1); } 64 [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, lua_State* L) noexcept { return lua_newuserdatauv<keeper_fifo>(L, 1); }
65 // always embedded somewhere else or "in-place constructed" as a full userdata 65 // always embedded somewhere else or "in-place constructed" as a full userdata
66 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception 66 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
67 static void operator delete([[maybe_unused]] void* p_, lua_State* L) { ASSERT_L(!"should never be called") }; 67 static void operator delete([[maybe_unused]] void* p_, lua_State* L) { ASSERT_L(!"should never be called") };
68 68
69 static keeper_fifo* getPtr(lua_State* L, int idx_) 69 [[nodiscard]] static keeper_fifo* getPtr(lua_State* L, int idx_)
70 { 70 {
71 return lua_tofulluserdata<keeper_fifo>(L, idx_); 71 return lua_tofulluserdata<keeper_fifo>(L, idx_);
72 } 72 }
@@ -77,7 +77,7 @@ static constexpr int CONTENTS_TABLE{ 1 };
77// ################################################################################################## 77// ##################################################################################################
78 78
79// replaces the fifo ud by its uservalue on the stack 79// replaces the fifo ud by its uservalue on the stack
80static keeper_fifo* prepare_fifo_access(lua_State* L, int idx_) 80[[nodiscard]] static keeper_fifo* prepare_fifo_access(lua_State* L, int idx_)
81{ 81{
82 keeper_fifo* const fifo{ keeper_fifo::getPtr(L, idx_) }; 82 keeper_fifo* const fifo{ keeper_fifo::getPtr(L, idx_) };
83 if (fifo != nullptr) 83 if (fifo != nullptr)
@@ -95,7 +95,7 @@ static keeper_fifo* prepare_fifo_access(lua_State* L, int idx_)
95 95
96// in: nothing 96// in: nothing
97// out: { first = 1, count = 0, limit = -1} 97// out: { first = 1, count = 0, limit = -1}
98static keeper_fifo* fifo_new(lua_State* L) 98[[nodiscard]] static keeper_fifo* fifo_new(lua_State* L)
99{ 99{
100 STACK_GROW(L, 2); 100 STACK_GROW(L, 2);
101 STACK_CHECK_START_REL(L, 0); 101 STACK_CHECK_START_REL(L, 0);
@@ -207,52 +207,52 @@ static void push_table(lua_State* L, int idx_)
207 207
208// ################################################################################################## 208// ##################################################################################################
209 209
210int keeper_push_linda_storage(Universe* U, lua_State* L, void* ptr_, uintptr_t magic_) 210int keeper_push_linda_storage(Universe* U, Dest L, void* ptr_, uintptr_t magic_)
211{ 211{
212 Keeper* const K{ which_keeper(U->keepers, magic_) }; 212 Keeper* const K{ which_keeper(U->keepers, magic_) };
213 lua_State* const KL{ K ? K->L : nullptr }; 213 Source const KL{ K ? K->L : nullptr };
214 if (KL == nullptr) 214 if (KL == nullptr)
215 return 0; 215 return 0;
216 STACK_GROW(KL, 4); 216 STACK_GROW(KL, 4); // KEEPER MAIN
217 STACK_CHECK_START_REL(KL, 0); 217 STACK_CHECK_START_REL(KL, 0);
218 FIFOS_KEY.pushValue(KL); // fifos 218 FIFOS_KEY.pushValue(KL); // fifos
219 lua_pushlightuserdata(KL, ptr_); // fifos ud 219 lua_pushlightuserdata(KL, ptr_); // fifos ud
220 lua_rawget(KL, -2); // fifos storage 220 lua_rawget(KL, -2); // fifos storage
221 lua_remove(KL, -2); // storage 221 lua_remove(KL, -2); // storage
222 if (!lua_istable(KL, -1)) 222 if (!lua_istable(KL, -1))
223 { 223 {
224 lua_pop(KL, 1); // 224 lua_pop(KL, 1); //
225 STACK_CHECK(KL, 0); 225 STACK_CHECK(KL, 0);
226 return 0; 226 return 0;
227 } 227 }
228 // move data from keeper to destination state KEEPER MAIN 228 // move data from keeper to destination state
229 lua_pushnil(KL); // storage nil 229 lua_pushnil(KL); // storage nil
230 STACK_GROW(L, 5); 230 STACK_GROW(L, 5);
231 STACK_CHECK_START_REL(L, 0); 231 STACK_CHECK_START_REL(L, 0);
232 lua_newtable(L); // out 232 lua_newtable(L); // out
233 while (lua_next(KL, -2)) // storage key fifo 233 while (lua_next(KL, -2)) // storage key fifo
234 { 234 {
235 keeper_fifo* fifo = prepare_fifo_access(KL, -1); // storage key fifotbl 235 keeper_fifo* fifo = prepare_fifo_access(KL, -1); // storage key fifotbl
236 lua_pushvalue(KL, -2); // storage key fifotbl key 236 lua_pushvalue(KL, -2); // storage key fifotbl key
237 luaG_inter_move(U, KL, L, 1, LookupMode::FromKeeper); // storage key fifotbl // out key 237 std::ignore = luaG_inter_move(U, KL, L, 1, LookupMode::FromKeeper); // storage key fifotbl // out key
238 STACK_CHECK(L, 2); 238 STACK_CHECK(L, 2);
239 lua_newtable(L); // out key keyout 239 lua_newtable(L); // out key keyout
240 luaG_inter_move(U, KL, L, 1, LookupMode::FromKeeper); // storage key // out key keyout fifotbl 240 std::ignore = luaG_inter_move(U, KL, L, 1, LookupMode::FromKeeper); // storage key // out key keyout fifotbl
241 lua_pushinteger(L, fifo->first); // out key keyout fifotbl first 241 lua_pushinteger(L, fifo->first); // out key keyout fifotbl first
242 STACK_CHECK(L, 5); 242 STACK_CHECK(L, 5);
243 lua_setfield(L, -3, "first"); // out key keyout fifotbl 243 lua_setfield(L, -3, "first"); // out key keyout fifotbl
244 lua_pushinteger(L, fifo->count); // out key keyout fifobtl count 244 lua_pushinteger(L, fifo->count); // out key keyout fifobtl count
245 STACK_CHECK(L, 5); 245 STACK_CHECK(L, 5);
246 lua_setfield(L, -3, "count"); // out key keyout fifotbl 246 lua_setfield(L, -3, "count"); // out key keyout fifotbl
247 lua_pushinteger(L, fifo->limit); // out key keyout fifotbl limit 247 lua_pushinteger(L, fifo->limit); // out key keyout fifotbl limit
248 STACK_CHECK(L, 5); 248 STACK_CHECK(L, 5);
249 lua_setfield(L, -3, "limit"); // out key keyout fifotbl 249 lua_setfield(L, -3, "limit"); // out key keyout fifotbl
250 lua_setfield(L, -2, "fifo"); // out key keyout 250 lua_setfield(L, -2, "fifo"); // out key keyout
251 lua_rawset(L, -3); // out 251 lua_rawset(L, -3); // out
252 STACK_CHECK(L, 1); 252 STACK_CHECK(L, 1);
253 } 253 }
254 STACK_CHECK(L, 1); 254 STACK_CHECK(L, 1);
255 lua_pop(KL, 1); // 255 lua_pop(KL, 1); //
256 STACK_CHECK(KL, 0); 256 STACK_CHECK(KL, 0);
257 return 1; 257 return 1;
258} 258}
@@ -287,7 +287,7 @@ int keepercall_send(lua_State* L)
287 if( lua_isnil(L, -1)) 287 if( lua_isnil(L, -1))
288 { 288 {
289 lua_pop(L, 1); // ud key ... fifos 289 lua_pop(L, 1); // ud key ... fifos
290 fifo_new(L); // ud key ... fifos fifo 290 std::ignore = fifo_new(L); // ud key ... fifos fifo
291 lua_pushvalue(L, 2); // ud key ... fifos fifo key 291 lua_pushvalue(L, 2); // ud key ... fifos fifo key
292 lua_pushvalue(L, -2); // ud key ... fifos fifo key fifo 292 lua_pushvalue(L, -2); // ud key ... fifos fifo key fifo
293 lua_rawset(L, -4); // ud key ... fifos fifo 293 lua_rawset(L, -4); // ud key ... fifos fifo
@@ -412,7 +412,7 @@ int keepercall_limit(lua_State* L)
412 // set the new limit 412 // set the new limit
413 fifo->limit = limit; 413 fifo->limit = limit;
414 // return 0 or 1 value 414 // return 0 or 1 value
415 return lua_gettop( L); 415 return lua_gettop(L);
416} 416}
417 417
418// ################################################################################################## 418// ##################################################################################################
@@ -465,7 +465,7 @@ int keepercall_set(lua_State* L)
465 { // fifos key [val [, ...]] nil 465 { // fifos key [val [, ...]] nil
466 // no need to wake writers in that case, because a writer can't wait on an inexistent key 466 // no need to wake writers in that case, because a writer can't wait on an inexistent key
467 lua_pop(L, 1); // fifos key [val [, ...]] 467 lua_pop(L, 1); // fifos key [val [, ...]]
468 fifo_new(L); // fifos key [val [, ...]] fifo 468 std::ignore = fifo_new(L); // fifos key [val [, ...]] fifo
469 lua_pushvalue(L, 2); // fifos key [val [, ...]] fifo key 469 lua_pushvalue(L, 2); // fifos key [val [, ...]] fifo key
470 lua_pushvalue(L, -2); // fifos key [val [, ...]] fifo key fifo 470 lua_pushvalue(L, -2); // fifos key [val [, ...]] fifo key fifo
471 lua_rawset(L, 1); // fifos key [val [, ...]] fifo 471 lua_rawset(L, 1); // fifos key [val [, ...]] fifo
@@ -485,7 +485,7 @@ int keepercall_set(lua_State* L)
485 lua_insert(L, 3); // fifos key fifotbl [val [, ...]] 485 lua_insert(L, 3); // fifos key fifotbl [val [, ...]]
486 fifo_push(L, fifo, count); // fifos key fifotbl 486 fifo_push(L, fifo, count); // fifos key fifotbl
487 } 487 }
488 return should_wake_writers ? (lua_pushboolean( L, 1), 1) : 0; 488 return should_wake_writers ? (lua_pushboolean(L, 1), 1) : 0;
489} 489}
490 490
491// ################################################################################################## 491// ##################################################################################################
@@ -627,7 +627,7 @@ void close_keepers(Universe* U)
627 } 627 }
628 for (int i = 0; i < nbKeepers; ++i) 628 for (int i = 0; i < nbKeepers; ++i)
629 { 629 {
630 MUTEX_FREE(&U->keepers->keeper_array[i].keeper_cs); 630 U->keepers->keeper_array[i].~Keeper();
631 } 631 }
632 // free the keeper bookkeeping structure 632 // free the keeper bookkeeping structure
633 U->internal_allocator.free(U->keepers, sizeof(Keepers) + (nbKeepers - 1) * sizeof(Keeper)); 633 U->internal_allocator.free(U->keepers, sizeof(Keepers) + (nbKeepers - 1) * sizeof(Keeper));
@@ -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 luaL_error(L, "Bad number of keepers (%d)", nb_keepers); // doesn't return
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 {
@@ -665,10 +671,16 @@ void init_keepers(Universe* U, lua_State* L)
665 U->keepers = static_cast<Keepers*>(U->internal_allocator.alloc(bytes)); 671 U->keepers = static_cast<Keepers*>(U->internal_allocator.alloc(bytes));
666 if (U->keepers == nullptr) 672 if (U->keepers == nullptr)
667 { 673 {
668 std::ignore = luaL_error(L, "init_keepers() failed while creating keeper array; out of memory"); 674 luaL_error(L, "init_keepers() failed while creating keeper array; out of memory"); // doesn't return
669 } 675 }
670 memset(U->keepers, 0, bytes); 676 U->keepers->Keepers::Keepers();
677 U->keepers->gc_threshold = keepers_gc_threshold;
671 U->keepers->nb_keepers = nb_keepers; 678 U->keepers->nb_keepers = nb_keepers;
679
680 for (int i = 0; i < nb_keepers; ++i)
681 {
682 U->keepers->keeper_array[i].Keeper::Keeper();
683 }
672 } 684 }
673 for (int i = 0; i < nb_keepers; ++i) // keepersUD 685 for (int i = 0; i < nb_keepers; ++i) // keepersUD
674 { 686 {
@@ -676,14 +688,15 @@ void init_keepers(Universe* U, lua_State* L)
676 lua_State* const K{ create_state(U, L) }; 688 lua_State* const K{ create_state(U, L) };
677 if (K == nullptr) 689 if (K == nullptr)
678 { 690 {
679 std::ignore = luaL_error(L, "init_keepers() failed while creating keeper states; out of memory"); 691 luaL_error(L, "init_keepers() failed while creating keeper states; out of memory"); // doesn't return
680 } 692 }
681 693
682 U->keepers->keeper_array[i].L = K; 694 U->keepers->keeper_array[i].L = K;
683 // we can trigger a GC from inside keeper_call(), where a keeper is acquired 695
684 // from there, GC can collect a linda, which would acquire the keeper again, and deadlock the thread. 696 if (U->keepers->gc_threshold >= 0)
685 // therefore, we need a recursive mutex. 697 {
686 MUTEX_RECURSIVE_INIT(&U->keepers->keeper_array[i].keeper_cs); 698 lua_gc(K, LUA_GCSTOP, 0);
699 }
687 700
688 STACK_CHECK_START_ABS(K, 0); 701 STACK_CHECK_START_ABS(K, 0);
689 702
@@ -704,7 +717,7 @@ void init_keepers(Universe* U, lua_State* L)
704 if (!lua_isnil(L, -1)) 717 if (!lua_isnil(L, -1))
705 { 718 {
706 // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately 719 // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately
707 if (luaG_inter_copy_package(U, L, K, -1, LookupMode::ToKeeper)) 720 if (luaG_inter_copy_package(U, Source{ L }, Dest{ K }, -1, LookupMode::ToKeeper) != InterCopyResult::Success)
708 { 721 {
709 // if something went wrong, the error message is at the top of the stack 722 // if something went wrong, the error message is at the top of the stack
710 lua_remove(L, -2); // error_msg 723 lua_remove(L, -2); // error_msg
@@ -735,8 +748,12 @@ void init_keepers(Universe* U, lua_State* L)
735Keeper* which_keeper(Keepers* keepers_, uintptr_t magic_) 748Keeper* which_keeper(Keepers* keepers_, uintptr_t magic_)
736{ 749{
737 int const nbKeepers{ keepers_->nb_keepers }; 750 int const nbKeepers{ keepers_->nb_keepers };
738 unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers); 751 if (nbKeepers)
739 return &keepers_->keeper_array[i]; 752 {
753 unsigned int i = (unsigned int) ((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers);
754 return &keepers_->keeper_array[i];
755 }
756 return nullptr;
740} 757}
741 758
742// ################################################################################################## 759// ##################################################################################################
@@ -745,11 +762,7 @@ Keeper* keeper_acquire(Keepers* keepers_, uintptr_t magic_)
745{ 762{
746 int const nbKeepers{ keepers_->nb_keepers }; 763 int const nbKeepers{ keepers_->nb_keepers };
747 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) 764 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers)
748 if( nbKeepers == 0) 765 if (nbKeepers)
749 {
750 return nullptr;
751 }
752 else
753 { 766 {
754 /* 767 /*
755 * Any hashing will do that maps pointers to 0..GNbKeepers-1 768 * Any hashing will do that maps pointers to 0..GNbKeepers-1
@@ -760,11 +773,11 @@ Keeper* keeper_acquire(Keepers* keepers_, uintptr_t magic_)
760 */ 773 */
761 unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers); 774 unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers);
762 Keeper* K = &keepers_->keeper_array[i]; 775 Keeper* K = &keepers_->keeper_array[i];
763 776 K->m_mutex.lock();
764 MUTEX_LOCK( &K->keeper_cs);
765 //++ K->count; 777 //++ K->count;
766 return K; 778 return K;
767 } 779 }
780 return nullptr;
768} 781}
769 782
770// ################################################################################################## 783// ##################################################################################################
@@ -774,7 +787,7 @@ void keeper_release(Keeper* K)
774 //-- K->count; 787 //-- K->count;
775 if (K) 788 if (K)
776 { 789 {
777 MUTEX_UNLOCK(&K->keeper_cs); 790 K->m_mutex.unlock();
778 } 791 }
779} 792}
780 793
@@ -827,21 +840,45 @@ int keeper_call(Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, voi
827 840
828 lua_pushlightuserdata(K, linda); 841 lua_pushlightuserdata(K, linda);
829 842
830 if ((args == 0) || luaG_inter_copy(U, L, K, args, LookupMode::ToKeeper) == 0) // L->K 843 if ((args == 0) || luaG_inter_copy(U, Source{ L }, Dest{ K }, args, LookupMode::ToKeeper) == InterCopyResult::Success) // L->K
831 { 844 {
832 lua_call(K, 1 + args, LUA_MULTRET); 845 lua_call(K, 1 + args, LUA_MULTRET);
833
834 retvals = lua_gettop(K) - Ktos; 846 retvals = lua_gettop(K) - Ktos;
835 // note that this can raise a luaL_error while the keeper state (and its mutex) is acquired 847 // note that this can raise a luaL_error while the keeper state (and its mutex) is acquired
836 // this may interrupt a lane, causing the destruction of the underlying OS thread 848 // this may interrupt a lane, causing the destruction of the underlying OS thread
837 // after this, another lane making use of this keeper can get an error code from the mutex-locking function 849 // after this, another lane making use of this keeper can get an error code from the mutex-locking function
838 // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) 850 // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread)
839 if ((retvals > 0) && luaG_inter_move(U, K, L, retvals, LookupMode::FromKeeper) != 0) // K->L 851 if ((retvals > 0) && luaG_inter_move(U, Source{ K }, Dest{ L }, retvals, LookupMode::FromKeeper) != InterCopyResult::Success) // K->L
840 { 852 {
841 retvals = -1; 853 retvals = -1;
842 } 854 }
843 } 855 }
844 // whatever happens, restore the stack to where it was at the origin 856 // whatever happens, restore the stack to where it was at the origin
845 lua_settop(K, Ktos); 857 lua_settop(K, Ktos);
858
859 // 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
860 if (func_ != KEEPER_API(clear)) [[unlikely]]
861 {
862 // since keeper state GC is stopped, let's run a step once in a while if required
863 int const gc_threshold{ U->keepers->gc_threshold };
864 if (gc_threshold == 0) [[unlikely]]
865 {
866 lua_gc(K, LUA_GCSTEP, 0);
867 }
868 else if (gc_threshold > 0) [[likely]]
869 {
870 int const gc_usage{ lua_gc(K, LUA_GCCOUNT, 0) };
871 if (gc_usage >= gc_threshold)
872 {
873 lua_gc(K, LUA_GCCOLLECT, 0);
874 int const gc_usage_after{ lua_gc(K, LUA_GCCOUNT, 0) };
875 if (gc_usage_after > gc_threshold) [[unlikely]]
876 {
877 luaL_error(L, "Keeper GC threshold is too low, need at least %d", gc_usage_after);
878 }
879 }
880 }
881 }
882
846 return retvals; 883 return retvals;
847} 884}