aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/keeper.cpp47
-rw-r--r--src/keeper.h7
-rw-r--r--src/linda.cpp190
-rw-r--r--src/linda.h70
4 files changed, 164 insertions, 150 deletions
diff --git a/src/keeper.cpp b/src/keeper.cpp
index c5bbb9d..c306d58 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -40,6 +40,7 @@
40#include "keeper.h" 40#include "keeper.h"
41 41
42#include "compat.h" 42#include "compat.h"
43#include "linda.h"
43#include "state.h" 44#include "state.h"
44#include "tools.h" 45#include "tools.h"
45#include "uniquekey.h" 46#include "uniquekey.h"
@@ -207,16 +208,17 @@ static void push_table(lua_State* L, int idx_)
207 208
208// ################################################################################################# 209// #################################################################################################
209 210
210int keeper_push_linda_storage(Universe* U, DestState L, void* ptr_, uintptr_t magic_) 211// only used by linda:dump() and linda:__towatch() for debugging purposes
212int keeper_push_linda_storage(Linda& linda_, DestState L)
211{ 213{
212 Keeper* const K{ which_keeper(U->keepers, magic_) }; 214 Keeper* const K{ linda_.whichKeeper() };
213 SourceState const KL{ K ? K->L : nullptr }; 215 SourceState const KL{ K ? K->L : nullptr };
214 if (KL == nullptr) 216 if (KL == nullptr)
215 return 0; 217 return 0;
216 STACK_GROW(KL, 4); // KEEPER MAIN 218 STACK_GROW(KL, 4); // KEEPER MAIN
217 STACK_CHECK_START_REL(KL, 0); 219 STACK_CHECK_START_REL(KL, 0);
218 FIFOS_KEY.pushValue(KL); // fifos 220 FIFOS_KEY.pushValue(KL); // fifos
219 lua_pushlightuserdata(KL, ptr_); // fifos ud 221 lua_pushlightuserdata(KL, &linda_); // fifos ud
220 lua_rawget(KL, -2); // fifos storage 222 lua_rawget(KL, -2); // fifos storage
221 lua_remove(KL, -2); // storage 223 lua_remove(KL, -2); // storage
222 if (!lua_istable(KL, -1)) 224 if (!lua_istable(KL, -1))
@@ -229,7 +231,7 @@ int keeper_push_linda_storage(Universe* U, DestState L, void* ptr_, uintptr_t ma
229 STACK_GROW(L, 5); 231 STACK_GROW(L, 5);
230 STACK_CHECK_START_REL(L, 0); 232 STACK_CHECK_START_REL(L, 0);
231 lua_newtable(L); // out 233 lua_newtable(L); // out
232 InterCopyContext c{ U, L, KL, {}, {}, {}, LookupMode::FromKeeper, {} }; 234 InterCopyContext c{ linda_.U, L, KL, {}, {}, {}, LookupMode::FromKeeper, {} };
233 lua_pushnil(KL); // storage nil 235 lua_pushnil(KL); // storage nil
234 while (lua_next(KL, -2)) // storage key fifo 236 while (lua_next(KL, -2)) // storage key fifo
235 { 237 {
@@ -748,37 +750,14 @@ void init_keepers(Universe* U, lua_State* L)
748 750
749// ################################################################################################# 751// #################################################################################################
750 752
751// should be called only when inside a keeper_acquire/keeper_release pair (see Linda::ProtectedCall) 753Keeper* Linda::acquireKeeper() const
752Keeper* which_keeper(Keepers* keepers_, uintptr_t magic_)
753{ 754{
754 int const nbKeepers{ keepers_->nb_keepers }; 755 int const nbKeepers{ U->keepers->nb_keepers };
755 if (nbKeepers)
756 {
757 unsigned int i = (unsigned int) ((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers);
758 return &keepers_->keeper_array[i];
759 }
760 return nullptr;
761}
762
763// #################################################################################################
764
765Keeper* keeper_acquire(Keepers* keepers_, uintptr_t magic_)
766{
767 int const nbKeepers{ keepers_->nb_keepers };
768 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) 756 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers)
769 if (nbKeepers) 757 if (nbKeepers)
770 { 758 {
771 /* 759 Keeper* const K{ &U->keepers->keeper_array[m_keeper_index] };
772 * Any hashing will do that maps pointers to 0..GNbKeepers-1
773 * consistently.
774 *
775 * Pointers are often aligned by 8 or so - ignore the low order bits
776 * have to cast to unsigned long to avoid compilation warnings about loss of data when converting pointer-to-integer
777 */
778 unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers);
779 Keeper* K = &keepers_->keeper_array[i];
780 K->m_mutex.lock(); 760 K->m_mutex.lock();
781 //++ K->count;
782 return K; 761 return K;
783 } 762 }
784 return nullptr; 763 return nullptr;
@@ -786,12 +765,12 @@ Keeper* keeper_acquire(Keepers* keepers_, uintptr_t magic_)
786 765
787// ################################################################################################# 766// #################################################################################################
788 767
789void keeper_release(Keeper* K) 768void Linda::releaseKeeper(Keeper* K_) const
790{ 769{
791 //-- K->count; 770 if (K_) // can be nullptr if we tried to acquire during shutdown
792 if (K)
793 { 771 {
794 K->m_mutex.unlock(); 772 assert(K_ == &U->keepers->keeper_array[m_keeper_index]);
773 K_->m_mutex.unlock();
795 } 774 }
796} 775}
797 776
diff --git a/src/keeper.h b/src/keeper.h
index ef86f94..4c79fd7 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -15,6 +15,7 @@ extern "C" {
15#include <mutex> 15#include <mutex>
16 16
17// forwards 17// forwards
18class Linda;
18enum class LookupMode; 19enum class LookupMode;
19class Universe; 20class Universe;
20 21
@@ -34,18 +35,14 @@ struct Keepers
34 Keeper keeper_array[1]; 35 Keeper keeper_array[1];
35}; 36};
36 37
37static constexpr uintptr_t KEEPER_MAGIC_SHIFT{ 3 };
38// crc64/we of string "NIL_SENTINEL" generated at http://www.nitrxgen.net/hashgen/ 38// crc64/we of string "NIL_SENTINEL" generated at http://www.nitrxgen.net/hashgen/
39static constexpr UniqueKey NIL_SENTINEL{ 0x7EAAFA003A1D11A1ull, "linda.null" }; 39static constexpr UniqueKey NIL_SENTINEL{ 0x7EAAFA003A1D11A1ull, "linda.null" };
40 40
41void init_keepers(Universe* U, lua_State* L); 41void init_keepers(Universe* U, lua_State* L);
42void close_keepers(Universe* U); 42void close_keepers(Universe* U);
43 43
44[[nodiscard]] Keeper* which_keeper(Keepers* keepers_, uintptr_t magic_);
45[[nodiscard]] Keeper* keeper_acquire(Keepers* keepers_, uintptr_t magic_);
46void keeper_release(Keeper* K_);
47void keeper_toggle_nil_sentinels(lua_State* L, int val_i_, LookupMode const mode_); 44void keeper_toggle_nil_sentinels(lua_State* L, int val_i_, LookupMode const mode_);
48[[nodiscard]] int keeper_push_linda_storage(Universe* U, DestState L, void* ptr_, uintptr_t magic_); 45[[nodiscard]] int keeper_push_linda_storage(Linda& linda_, DestState L);
49 46
50using keeper_api_t = lua_CFunction; 47using keeper_api_t = lua_CFunction;
51#define KEEPER_API(_op) keepercall_##_op 48#define KEEPER_API(_op) keepercall_##_op
diff --git a/src/linda.cpp b/src/linda.cpp
index a19b126..0a1ca3f 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -30,21 +30,22 @@ THE SOFTWARE.
30=============================================================================== 30===============================================================================
31*/ 31*/
32 32
33#include "linda.h"
34
33#include "compat.h" 35#include "compat.h"
34#include "deep.h"
35#include "keeper.h" 36#include "keeper.h"
36#include "lanes_private.h" 37#include "lanes_private.h"
37#include "threading.h" 38#include "threading.h"
38#include "tools.h" 39#include "tools.h"
39#include "universe.h" 40#include "universe.h"
40 41
41#include <array>
42#include <functional> 42#include <functional>
43#include <variant>
44 43
45// xxh64 of string "CANCEL_ERROR" generated at https://www.pelock.com/products/hash-calculator 44// xxh64 of string "CANCEL_ERROR" generated at https://www.pelock.com/products/hash-calculator
46static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull, "linda.batched" }; 45static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull, "linda.batched" };
47 46
47// #################################################################################################
48
48class LindaFactory : public DeepFactory 49class LindaFactory : public DeepFactory
49{ 50{
50 private: 51 private:
@@ -58,117 +59,84 @@ class LindaFactory : public DeepFactory
58static LindaFactory g_LindaFactory; 59static LindaFactory g_LindaFactory;
59 60
60// ################################################################################################# 61// #################################################################################################
62// #################################################################################################
61 63
62/* 64// Any hashing will do that maps pointers to [0..Universe::nb_keepers[ consistently.
63* Actual data is kept within a keeper state, which is hashed by the 'Linda' 65// Pointers are often aligned by 8 or so - ignore the low order bits
64* pointer (which is same to all userdatas pointing to it). 66// have to cast to unsigned long to avoid compilation warnings about loss of data when converting pointer-to-integer
65*/ 67static constexpr uintptr_t KEEPER_MAGIC_SHIFT{ 3 };
66class Linda : public DeepPrelude // Deep userdata MUST start with this header 68
69Linda::Linda(Universe* U_, LindaGroup group_, char const* name_, size_t len_)
70: DeepPrelude{ g_LindaFactory }
71, U{ U_ }
72, m_keeper_index{ (group_ ? group_ : static_cast<int>(std::bit_cast<uintptr_t>(this) >> KEEPER_MAGIC_SHIFT)) % U_->keepers->nb_keepers }
67{ 73{
68 private: 74 setName(name_, len_);
75}
69 76
70 static constexpr size_t kEmbeddedNameLength = 24; 77// #################################################################################################
71 using EmbeddedName = std::array<char, kEmbeddedNameLength>;
72 struct AllocatedName
73 {
74 size_t len{ 0 };
75 char* name{ nullptr };
76 };
77 // depending on the name length, it is either embedded inside the Linda, or allocated separately
78 std::variant<AllocatedName, EmbeddedName> m_name;
79
80 public:
81
82 std::condition_variable m_read_happened;
83 std::condition_variable m_write_happened;
84 Universe* const U; // the universe this linda belongs to
85 uintptr_t const group; // a group to control keeper allocation between lindas
86 CancelRequest simulate_cancel{ CancelRequest::None };
87
88 public:
89
90 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents
91 [[nodiscard]] static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internal_allocator.alloc(size_); }
92 // always embedded somewhere else or "in-place constructed" as a full userdata
93 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
94 static void operator delete(void* p_, Universe* U_) { U_->internal_allocator.free(p_, sizeof(Linda)); }
95 // this one is for us, to make sure memory is freed by the correct allocator
96 static void operator delete(void* p_) { static_cast<Linda*>(p_)->U->internal_allocator.free(p_, sizeof(Linda)); }
97
98 Linda(Universe* U_, uintptr_t group_, char const* name_, size_t len_)
99 : DeepPrelude{ g_LindaFactory }
100 , U{ U_ }
101 , group{ group_ << KEEPER_MAGIC_SHIFT }
102 {
103 setName(name_, len_);
104 }
105 78
106 ~Linda() 79Linda::~Linda()
80{
81 if (std::holds_alternative<AllocatedName>(m_name))
107 { 82 {
108 if (std::holds_alternative<AllocatedName>(m_name)) 83 AllocatedName& name = std::get<AllocatedName>(m_name);
109 { 84 U->internal_allocator.free(name.name, name.len);
110 AllocatedName& name = std::get<AllocatedName>(m_name);
111 U->internal_allocator.free(name.name, name.len);
112 }
113 } 85 }
86}
114 87
115 static int ProtectedCall(lua_State* L, lua_CFunction f_); 88// #################################################################################################
116
117 private :
118 89
119 void setName(char const* name_, size_t len_) 90void Linda::setName(char const* name_, size_t len_)
91{
92 // keep default
93 if (!name_ || len_ == 0)
120 { 94 {
121 // keep default 95 return;
122 if (!name_ || len_ == 0)
123 {
124 return;
125 }
126 ++len_; // don't forget terminating 0
127 if (len_ < kEmbeddedNameLength)
128 {
129 m_name.emplace<EmbeddedName>();
130 char* const name{ std::get<EmbeddedName>(m_name).data() };
131 memcpy(name, name_, len_);
132 }
133 else
134 {
135 AllocatedName& name = std::get<AllocatedName>(m_name);
136 name.name = static_cast<char*>(U->internal_allocator.alloc(len_));
137 name.len = len_;
138 memcpy(name.name, name_, len_);
139 }
140 } 96 }
97 ++len_; // don't forget terminating 0
98 if (len_ < kEmbeddedNameLength)
99 {
100 m_name.emplace<EmbeddedName>();
101 char* const name{ std::get<EmbeddedName>(m_name).data() };
102 memcpy(name, name_, len_);
103 }
104 else
105 {
106 AllocatedName& name = std::get<AllocatedName>(m_name);
107 name.name = static_cast<char*>(U->internal_allocator.alloc(len_));
108 name.len = len_;
109 memcpy(name.name, name_, len_);
110 }
111}
141 112
142 public: 113// #################################################################################################
143
144 uintptr_t hashSeed() const { return group ? group : std::bit_cast<uintptr_t>(this); }
145 114
146 char const* getName() const 115char const* Linda::getName() const
116{
117 if (std::holds_alternative<AllocatedName>(m_name))
147 { 118 {
148 if (std::holds_alternative<AllocatedName>(m_name)) 119 AllocatedName const& name = std::get<AllocatedName>(m_name);
149 { 120 return name.name;
150 AllocatedName const& name = std::get<AllocatedName>(m_name);
151 return name.name;
152 }
153 if (std::holds_alternative<EmbeddedName>(m_name))
154 {
155 char const* const name{ std::get<EmbeddedName>(m_name).data() };
156 return name;
157 }
158 return nullptr;
159 } 121 }
160}; 122 if (std::holds_alternative<EmbeddedName>(m_name))
123 {
124 char const* const name{ std::get<EmbeddedName>(m_name).data() };
125 return name;
126 }
127 return nullptr;
128}
161 129
162// ################################################################################################# 130// #################################################################################################
163 131
164template <bool OPT> 132template <bool OPT>
165[[nodiscard]] static inline Linda* ToLinda(lua_State* L, int idx_) 133[[nodiscard]] static inline Linda* ToLinda(lua_State* L_, int idx_)
166{ 134{
167 Linda* const linda{ static_cast<Linda*>(g_LindaFactory.toDeep(L, idx_)) }; 135 Linda* const linda{ static_cast<Linda*>(g_LindaFactory.toDeep(L_, idx_)) };
168 if constexpr (!OPT) 136 if constexpr (!OPT)
169 { 137 {
170 luaL_argcheck(L, linda != nullptr, idx_, "expecting a linda object"); 138 luaL_argcheck(L_, linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr
171 LUA_ASSERT(L, linda->U == universe_get(L)); 139 LUA_ASSERT(L_, linda->U == universe_get(L_));
172 } 140 }
173 return linda; 141 return linda;
174} 142}
@@ -213,7 +181,7 @@ int Linda::ProtectedCall(lua_State* L, lua_CFunction f_)
213 Linda* const linda{ ToLinda<false>(L, 1) }; 181 Linda* const linda{ ToLinda<false>(L, 1) };
214 182
215 // acquire the keeper 183 // acquire the keeper
216 Keeper* const K{ keeper_acquire(linda->U->keepers, linda->hashSeed()) }; 184 Keeper* const K{ linda->acquireKeeper() };
217 lua_State* const KL{ K ? K->L : nullptr }; 185 lua_State* const KL{ K ? K->L : nullptr };
218 if (KL == nullptr) 186 if (KL == nullptr)
219 return 0; 187 return 0;
@@ -229,7 +197,7 @@ int Linda::ProtectedCall(lua_State* L, lua_CFunction f_)
229 lua_settop(KL, 0); 197 lua_settop(KL, 0);
230 198
231 // release the keeper 199 // release the keeper
232 keeper_release(K); 200 linda->releaseKeeper(K);
233 201
234 // if there was an error, forward it 202 // if there was an error, forward it
235 if (rc != LUA_OK) 203 if (rc != LUA_OK)
@@ -306,7 +274,7 @@ LUAG_FUNC(linda_send)
306 KeeperCallResult pushed; 274 KeeperCallResult pushed;
307 { 275 {
308 Lane* const lane{ LANE_POINTER_REGKEY.readLightUserDataValue<Lane>(L) }; 276 Lane* const lane{ LANE_POINTER_REGKEY.readLightUserDataValue<Lane>(L) };
309 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 277 Keeper* const K{ linda->whichKeeper() };
310 KeeperState const KL{ K ? K->L : nullptr }; 278 KeeperState const KL{ K ? K->L : nullptr };
311 if (KL == nullptr) 279 if (KL == nullptr)
312 return 0; 280 return 0;
@@ -472,7 +440,7 @@ LUAG_FUNC(linda_receive)
472 } 440 }
473 441
474 Lane* const lane{ LANE_POINTER_REGKEY.readLightUserDataValue<Lane>(L) }; 442 Lane* const lane{ LANE_POINTER_REGKEY.readLightUserDataValue<Lane>(L) };
475 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 443 Keeper* const K{ linda->whichKeeper() };
476 KeeperState const KL{ K ? K->L : nullptr }; 444 KeeperState const KL{ K ? K->L : nullptr };
477 if (KL == nullptr) 445 if (KL == nullptr)
478 return 0; 446 return 0;
@@ -584,7 +552,7 @@ LUAG_FUNC(linda_set)
584 // make sure the key is of a valid type (throws an error if not the case) 552 // make sure the key is of a valid type (throws an error if not the case)
585 check_key_types(L, 2, 2); 553 check_key_types(L, 2, 2);
586 554
587 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 555 Keeper* const K{ linda->whichKeeper() };
588 KeeperCallResult pushed; 556 KeeperCallResult pushed;
589 if (linda->simulate_cancel == CancelRequest::None) 557 if (linda->simulate_cancel == CancelRequest::None)
590 { 558 {
@@ -639,7 +607,7 @@ LUAG_FUNC(linda_count)
639 // make sure the keys are of a valid type 607 // make sure the keys are of a valid type
640 check_key_types(L, 2, lua_gettop(L)); 608 check_key_types(L, 2, lua_gettop(L));
641 609
642 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 610 Keeper* const K{ linda->whichKeeper() };
643 KeeperCallResult const pushed{ keeper_call(linda->U, K->L, KEEPER_API(count), L, linda, 2) }; 611 KeeperCallResult const pushed{ keeper_call(linda->U, K->L, KEEPER_API(count), L, linda, 2) };
644 return OptionalValue(pushed, L, "tried to count an invalid key"); 612 return OptionalValue(pushed, L, "tried to count an invalid key");
645 }; 613 };
@@ -667,7 +635,7 @@ LUAG_FUNC(linda_get)
667 KeeperCallResult pushed; 635 KeeperCallResult pushed;
668 if (linda->simulate_cancel == CancelRequest::None) 636 if (linda->simulate_cancel == CancelRequest::None)
669 { 637 {
670 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 638 Keeper* const K{ linda->whichKeeper() };
671 pushed = keeper_call(linda->U, K->L, KEEPER_API(get), L, linda, 2); 639 pushed = keeper_call(linda->U, K->L, KEEPER_API(get), L, linda, 2);
672 if (pushed.value_or(0) > 0) 640 if (pushed.value_or(0) > 0)
673 { 641 {
@@ -709,7 +677,7 @@ LUAG_FUNC(linda_limit)
709 KeeperCallResult pushed; 677 KeeperCallResult pushed;
710 if (linda->simulate_cancel == CancelRequest::None) 678 if (linda->simulate_cancel == CancelRequest::None)
711 { 679 {
712 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 680 Keeper* const K{ linda->whichKeeper() };
713 pushed = keeper_call(linda->U, K->L, KEEPER_API(limit), L, linda, 2); 681 pushed = keeper_call(linda->U, K->L, KEEPER_API(limit), L, linda, 2);
714 LUA_ASSERT(L, pushed.has_value() && (pushed.value() == 0 || pushed.value() == 1)); // no error, optional boolean value saying if we should wake blocked writer threads 682 LUA_ASSERT(L, pushed.has_value() && (pushed.value() == 0 || pushed.value() == 1)); // no error, optional boolean value saying if we should wake blocked writer threads
715 if (pushed.value() == 1) 683 if (pushed.value() == 1)
@@ -863,7 +831,7 @@ LUAG_FUNC(linda_dump)
863 auto dump = [](lua_State* L) 831 auto dump = [](lua_State* L)
864 { 832 {
865 Linda* const linda{ ToLinda<false>(L, 1) }; 833 Linda* const linda{ ToLinda<false>(L, 1) };
866 return keeper_push_linda_storage(linda->U, DestState{ L }, linda, linda->hashSeed()); 834 return keeper_push_linda_storage(*linda, DestState{ L });
867 }; 835 };
868 return Linda::ProtectedCall(L, dump); 836 return Linda::ProtectedCall(L, dump);
869} 837}
@@ -871,13 +839,13 @@ LUAG_FUNC(linda_dump)
871// ################################################################################################# 839// #################################################################################################
872 840
873/* 841/*
874 * table = linda:dump() 842 * table/string = linda:__towatch()
875 * return a table listing all pending data inside the linda 843 * return a table listing all pending data inside the linda, or the stringified linda if empty
876 */ 844 */
877LUAG_FUNC(linda_towatch) 845LUAG_FUNC(linda_towatch)
878{ 846{
879 Linda* const linda{ ToLinda<false>(L, 1) }; 847 Linda* const linda{ ToLinda<false>(L, 1) };
880 int pushed{ keeper_push_linda_storage(linda->U, DestState{ L }, linda, linda->hashSeed()) }; 848 int pushed{ keeper_push_linda_storage(*linda, DestState{ L }) };
881 if (pushed == 0) 849 if (pushed == 0)
882 { 850 {
883 // if the linda is empty, don't return nil 851 // if the linda is empty, don't return nil
@@ -892,7 +860,7 @@ DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* L) const
892{ 860{
893 size_t name_len = 0; 861 size_t name_len = 0;
894 char const* linda_name = nullptr; 862 char const* linda_name = nullptr;
895 unsigned long linda_group = 0; 863 LindaGroup linda_group{ 0 };
896 // should have a string and/or a number of the stack as parameters (name and group) 864 // should have a string and/or a number of the stack as parameters (name and group)
897 switch (lua_gettop(L)) 865 switch (lua_gettop(L))
898 { 866 {
@@ -906,13 +874,13 @@ DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* L) const
906 } 874 }
907 else 875 else
908 { 876 {
909 linda_group = (unsigned long) lua_tointeger(L, -1); 877 linda_group = LindaGroup{ static_cast<int>(lua_tointeger(L, -1)) };
910 } 878 }
911 break; 879 break;
912 880
913 case 2: // 2 parameters, a name and group, in that order 881 case 2: // 2 parameters, a name and group, in that order
914 linda_name = lua_tolstring(L, -2, &name_len); 882 linda_name = lua_tolstring(L, -2, &name_len);
915 linda_group = (unsigned long) lua_tointeger(L, -1); 883 linda_group = LindaGroup{ static_cast<int>(lua_tointeger(L, -1)) };
916 break; 884 break;
917 } 885 }
918 886
@@ -932,7 +900,7 @@ void LindaFactory::deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const
932{ 900{
933 Linda* const linda{ static_cast<Linda*>(o_) }; 901 Linda* const linda{ static_cast<Linda*>(o_) };
934 LUA_ASSERT(L, linda); 902 LUA_ASSERT(L, linda);
935 Keeper* const myK{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 903 Keeper* const myK{ linda->whichKeeper() };
936 // if collected after the universe, keepers are already destroyed, and there is nothing to clear 904 // if collected after the universe, keepers are already destroyed, and there is nothing to clear
937 if (myK) 905 if (myK)
938 { 906 {
@@ -940,13 +908,13 @@ void LindaFactory::deleteDeepObjectInternal(lua_State* L, DeepPrelude* o_) const
940 // because we are already inside a protected area, and trying to do so would deadlock! 908 // because we are already inside a protected area, and trying to do so would deadlock!
941 bool const need_acquire_release{ myK->L != L }; 909 bool const need_acquire_release{ myK->L != L };
942 // Clean associated structures in the keeper state. 910 // Clean associated structures in the keeper state.
943 Keeper* const K{ need_acquire_release ? keeper_acquire(linda->U->keepers, linda->hashSeed()) : myK }; 911 Keeper* const K{ need_acquire_release ? linda->acquireKeeper() : myK };
944 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... 912 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex...
945 [[maybe_unused]] KeeperCallResult const result{ keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0) }; 913 [[maybe_unused]] KeeperCallResult const result{ keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0) };
946 LUA_ASSERT(L, result.has_value() && result.value() == 0); 914 LUA_ASSERT(L, result.has_value() && result.value() == 0);
947 if (need_acquire_release) 915 if (need_acquire_release)
948 { 916 {
949 keeper_release(K); 917 linda->releaseKeeper(K);
950 } 918 }
951 } 919 }
952 920
diff --git a/src/linda.h b/src/linda.h
new file mode 100644
index 0000000..559ef24
--- /dev/null
+++ b/src/linda.h
@@ -0,0 +1,70 @@
1#pragma once
2
3#include "cancel.h"
4#include "deep.h"
5#include "keeper.h"
6#include "universe.h"
7
8#include <array>
9#include <condition_variable>
10#include <variant>
11
12struct Keeper;
13
14// #################################################################################################
15
16using LindaGroup = Unique<int>;
17
18class Linda : public DeepPrelude // Deep userdata MUST start with this header
19{
20 private:
21
22 static constexpr size_t kEmbeddedNameLength = 24;
23 using EmbeddedName = std::array<char, kEmbeddedNameLength>;
24 struct AllocatedName
25 {
26 size_t len{ 0 };
27 char* name{ nullptr };
28 };
29 // depending on the name length, it is either embedded inside the Linda, or allocated separately
30 std::variant<AllocatedName, EmbeddedName> m_name;
31
32 public:
33
34 std::condition_variable m_read_happened;
35 std::condition_variable m_write_happened;
36 Universe* const U{ nullptr }; // the universe this linda belongs to
37 int const m_keeper_index{ -1 }; // the keeper associated to this linda
38 CancelRequest simulate_cancel{ CancelRequest::None };
39
40 public:
41
42 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents
43 [[nodiscard]] static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internal_allocator.alloc(size_); }
44 // always embedded somewhere else or "in-place constructed" as a full userdata
45 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
46 static void operator delete(void* p_, Universe* U_) { U_->internal_allocator.free(p_, sizeof(Linda)); }
47 // this one is for us, to make sure memory is freed by the correct allocator
48 static void operator delete(void* p_) { static_cast<Linda*>(p_)->U->internal_allocator.free(p_, sizeof(Linda)); }
49
50 ~Linda();
51 Linda(Universe* U_, LindaGroup group_, char const* name_, size_t len_);
52 Linda() = delete;
53 Linda(Linda const&) = delete;
54 Linda(Linda const&&) = delete;
55 Linda& operator=(Linda const&) = delete;
56 Linda& operator=(Linda const&&) = delete;
57
58 static int ProtectedCall(lua_State* L, lua_CFunction f_);
59
60 private :
61
62 void setName(char const* name_, size_t len_);
63
64 public:
65
66 [[nodiscard]] char const* getName() const;
67 [[nodiscard]] Keeper* whichKeeper() const { return U->keepers->nb_keepers ? &U->keepers->keeper_array[m_keeper_index] : nullptr; }
68 [[nodiscard]] Keeper* acquireKeeper() const;
69 void releaseKeeper(Keeper* keeper_) const;
70};