diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/keeper.cpp | 47 | ||||
-rw-r--r-- | src/keeper.h | 7 | ||||
-rw-r--r-- | src/linda.cpp | 190 | ||||
-rw-r--r-- | src/linda.h | 70 |
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 | ||
210 | int 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 |
212 | int 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) | 753 | Keeper* Linda::acquireKeeper() const |
752 | Keeper* 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 | |||
765 | Keeper* 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 | ||
789 | void keeper_release(Keeper* K) | 768 | void 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 |
18 | class Linda; | ||
18 | enum class LookupMode; | 19 | enum class LookupMode; |
19 | class Universe; | 20 | class Universe; |
20 | 21 | ||
@@ -34,18 +35,14 @@ struct Keepers | |||
34 | Keeper keeper_array[1]; | 35 | Keeper keeper_array[1]; |
35 | }; | 36 | }; |
36 | 37 | ||
37 | static 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/ |
39 | static constexpr UniqueKey NIL_SENTINEL{ 0x7EAAFA003A1D11A1ull, "linda.null" }; | 39 | static constexpr UniqueKey NIL_SENTINEL{ 0x7EAAFA003A1D11A1ull, "linda.null" }; |
40 | 40 | ||
41 | void init_keepers(Universe* U, lua_State* L); | 41 | void init_keepers(Universe* U, lua_State* L); |
42 | void close_keepers(Universe* U); | 42 | void 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_); | ||
46 | void keeper_release(Keeper* K_); | ||
47 | void keeper_toggle_nil_sentinels(lua_State* L, int val_i_, LookupMode const mode_); | 44 | void 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 | ||
50 | using keeper_api_t = lua_CFunction; | 47 | using 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 |
46 | static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull, "linda.batched" }; | 45 | static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull, "linda.batched" }; |
47 | 46 | ||
47 | // ################################################################################################# | ||
48 | |||
48 | class LindaFactory : public DeepFactory | 49 | class LindaFactory : public DeepFactory |
49 | { | 50 | { |
50 | private: | 51 | private: |
@@ -58,117 +59,84 @@ class LindaFactory : public DeepFactory | |||
58 | static LindaFactory g_LindaFactory; | 59 | static 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 | */ | 67 | static constexpr uintptr_t KEEPER_MAGIC_SHIFT{ 3 }; |
66 | class Linda : public DeepPrelude // Deep userdata MUST start with this header | 68 | |
69 | Linda::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() | 79 | Linda::~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_) | 90 | void 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 | 115 | char 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 | ||
164 | template <bool OPT> | 132 | template <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 | */ |
877 | LUAG_FUNC(linda_towatch) | 845 | LUAG_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 | |||
12 | struct Keeper; | ||
13 | |||
14 | // ################################################################################################# | ||
15 | |||
16 | using LindaGroup = Unique<int>; | ||
17 | |||
18 | class 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 | }; | ||