aboutsummaryrefslogtreecommitdiff
path: root/src/linda.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/linda.cpp')
-rw-r--r--src/linda.cpp190
1 files changed, 79 insertions, 111 deletions
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