diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-04-25 13:36:47 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-04-25 13:36:47 +0200 |
| commit | 0b5108a8a0d9b7a4a63bd6aae0271b71a887beea (patch) | |
| tree | 0fdf9ee45d05c25935af1dcefb52673675f49eb1 /src | |
| parent | 45fab6f5b12f00631005b0086931a2f25385146e (diff) | |
| download | lanes-0b5108a8a0d9b7a4a63bd6aae0271b71a887beea.tar.gz lanes-0b5108a8a0d9b7a4a63bd6aae0271b71a887beea.tar.bz2 lanes-0b5108a8a0d9b7a4a63bd6aae0271b71a887beea.zip | |
C++ integration: cleanup in Linda/Keeper interaction
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 | }; | ||
