aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/keeper.cpp410
-rw-r--r--src/linda.cpp26
-rw-r--r--src/linda.h1
-rw-r--r--tests/keeper.lua6
4 files changed, 231 insertions, 212 deletions
diff --git a/src/keeper.cpp b/src/keeper.cpp
index ea924e8..26e5723 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -49,10 +49,13 @@
49#include <ranges> 49#include <ranges>
50 50
51// ################################################################################################# 51// #################################################################################################
52// Keeper implementation 52// #################################################################################################
53// ############################################ KeyUD ##############################################
54// #################################################################################################
53// ################################################################################################# 55// #################################################################################################
54 56
55class keeper_fifo 57// the full userdata associated to a given Linda key to store its contents
58class KeyUD
56{ 59{
57 public: 60 public:
58 int first{ 1 }; 61 int first{ 1 };
@@ -60,65 +63,59 @@ class keeper_fifo
60 int limit{ -1 }; 63 int limit{ -1 };
61 64
62 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents 65 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents
63 [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, KeeperState L_) noexcept { return lua_newuserdatauv<keeper_fifo>(L_, 1); } 66 [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, KeeperState L_) noexcept { return lua_newuserdatauv<KeyUD>(L_, 1); }
64 // always embedded somewhere else or "in-place constructed" as a full userdata 67 // always embedded somewhere else or "in-place constructed" as a full userdata
65 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception 68 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
66 static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] KeeperState L_) { LUA_ASSERT(L_, !"should never be called"); } 69 static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] KeeperState L_) { LUA_ASSERT(L_, !"should never be called"); }
67 70
68 [[nodiscard]] static keeper_fifo* getPtr(lua_State* L_, int idx_) 71 [[nodiscard]] static KeyUD* GetPtr(lua_State* L_, int idx_);
69 { 72 [[nodiscard]] static KeyUD* Create(KeeperState L_);
70 return lua_tofulluserdata<keeper_fifo>(L_, idx_); 73 [[nodiscard]] static KeyUD* PrepareAccess(lua_State* L_, int idx_);
71 } 74 void peek(lua_State* L_, int count_);
75 void pop(lua_State* L_, int count_);
76 void push(lua_State* L_, int count_);
72}; 77};
73 78
74static constexpr int kContentsTableIndex{ 1 }; 79static constexpr int kContentsTableIndex{ 1 };
75 80
76// ################################################################################################# 81// #################################################################################################
77 82
78// replaces the fifo ud by its uservalue on the stack
79[[nodiscard]] static keeper_fifo* prepare_fifo_access(lua_State* L_, int idx_)
80{
81 keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, idx_) };
82 if (_fifo != nullptr) {
83 idx_ = lua_absindex(L_, idx_);
84 STACK_GROW(L_, 1);
85 // we can replace the fifo userdata in the stack without fear of it being GCed, there are other references around
86 lua_getiuservalue(L_, idx_, kContentsTableIndex);
87 lua_replace(L_, idx_);
88 }
89 return _fifo;
90}
91
92// #################################################################################################
93
94// in: nothing 83// in: nothing
95// out: { first = 1, count = 0, limit = -1} 84// out: { first = 1, count = 0, limit = -1}
96[[nodiscard]] static keeper_fifo* fifo_new(KeeperState L_) 85KeyUD* KeyUD::Create(KeeperState const L_)
97{ 86{
98 STACK_GROW(L_, 2); 87 STACK_GROW(L_, 2);
99 STACK_CHECK_START_REL(L_, 0); 88 STACK_CHECK_START_REL(L_, 0);
100 keeper_fifo* const _fifo{ new (L_) keeper_fifo{} }; 89 KeyUD* const _key{ new (L_) KeyUD{} };
101 STACK_CHECK(L_, 1); 90 STACK_CHECK(L_, 1);
102 lua_newtable(L_); 91 lua_newtable(L_);
103 lua_setiuservalue(L_, -2, kContentsTableIndex); 92 lua_setiuservalue(L_, -2, kContentsTableIndex);
104 STACK_CHECK(L_, 1); 93 STACK_CHECK(L_, 1);
105 return _fifo; 94 return _key;
106} 95}
107 96
108// ################################################################################################# 97// #################################################################################################
109 98
110// in: expect fifo ... on top of the stack 99KeyUD* KeyUD::GetPtr(lua_State* L_, int idx_)
111// out: nothing, removes all pushed values from the stack
112static void fifo_push(lua_State* L_, keeper_fifo* fifo_, int count_)
113{ 100{
114 int const _idx{ lua_gettop(L_) - count_ }; 101 return lua_tofulluserdata<KeyUD>(L_, idx_);
115 int const _start{ fifo_->first + fifo_->count - 1 }; 102}
116 // pop all additional arguments, storing them in the fifo 103
117 for (int const _i : std::ranges::reverse_view{ std::ranges::iota_view{ 1, count_ + 1 } }) { 104
118 // store in the fifo the value at the top of the stack at the specified index, popping it from the stack 105// #################################################################################################
119 lua_rawseti(L_, _idx, _start + _i); 106
107// replaces the fifo ud by its uservalue on the stack
108KeyUD* KeyUD::PrepareAccess(lua_State* const L_, int const idx_)
109{
110 KeyUD* const _key{ KeyUD::GetPtr(L_, idx_) };
111 if (_key) {
112 int const _idx{ lua_absindex(L_, idx_) };
113 STACK_GROW(L_, 1);
114 // we can replace the key userdata in the stack without fear of it being GCed, there are other references around
115 lua_getiuservalue(L_, _idx, kContentsTableIndex);
116 lua_replace(L_, _idx);
120 } 117 }
121 fifo_->count += count_; 118 return _key;
122} 119}
123 120
124// ################################################################################################# 121// #################################################################################################
@@ -128,11 +125,11 @@ static void fifo_push(lua_State* L_, keeper_fifo* fifo_, int count_)
128// expects exactly 1 value on the stack! 125// expects exactly 1 value on the stack!
129// currently only called with a count of 1, but this may change in the future 126// currently only called with a count of 1, but this may change in the future
130// function assumes that there is enough data in the fifo to satisfy the request 127// function assumes that there is enough data in the fifo to satisfy the request
131static void fifo_peek(lua_State* const L_, keeper_fifo const* const fifo_, int const count_) 128void KeyUD::peek(lua_State* const L_, int const count_)
132{ 129{
133 STACK_GROW(L_, count_); 130 STACK_GROW(L_, count_);
134 for (int const _i : std::ranges::iota_view{ 0, count_ }) { 131 for (int const _i : std::ranges::iota_view{ 0, count_ }) {
135 lua_rawgeti(L_, 1, (fifo_->first + _i)); 132 lua_rawgeti(L_, 1, first + _i);
136 } 133 }
137} 134}
138 135
@@ -140,7 +137,7 @@ static void fifo_peek(lua_State* const L_, keeper_fifo const* const fifo_, int c
140 137
141// in: fifo 138// in: fifo
142// out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number) 139// out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number)
143static void fifo_pop(lua_State* L_, keeper_fifo* fifo_, int count_) 140void KeyUD::pop(lua_State* const L_, int const count_)
144{ 141{
145 LUA_ASSERT(L_, lua_istable(L_, -1)); 142 LUA_ASSERT(L_, lua_istable(L_, -1));
146 int const _fifo_idx{ lua_gettop(L_) }; // L_: ... fifotbl 143 int const _fifo_idx{ lua_gettop(L_) }; // L_: ... fifotbl
@@ -148,7 +145,7 @@ static void fifo_pop(lua_State* L_, keeper_fifo* fifo_, int count_)
148 STACK_GROW(L_, count_ + 2); 145 STACK_GROW(L_, count_ + 2);
149 // skip first item, we will push it last 146 // skip first item, we will push it last
150 for (int const _i : std::ranges::iota_view{ 1, count_ }) { 147 for (int const _i : std::ranges::iota_view{ 1, count_ }) {
151 int const _at{ fifo_->first + _i }; 148 int const _at{ first + _i };
152 // push item on the stack 149 // push item on the stack
153 lua_rawgeti(L_, _fifo_idx, _at); // L_: ... fifotbl val 150 lua_rawgeti(L_, _fifo_idx, _at); // L_: ... fifotbl val
154 // remove item from the fifo 151 // remove item from the fifo
@@ -157,7 +154,7 @@ static void fifo_pop(lua_State* L_, keeper_fifo* fifo_, int count_)
157 } 154 }
158 // now process first item 155 // now process first item
159 { 156 {
160 int const _at{ fifo_->first }; 157 int const _at{ first };
161 lua_rawgeti(L_, _fifo_idx, _at); // L_: ... fifotbl vals val 158 lua_rawgeti(L_, _fifo_idx, _at); // L_: ... fifotbl vals val
162 lua_pushnil(L_); // L_: ... fifotbl vals val nil 159 lua_pushnil(L_); // L_: ... fifotbl vals val nil
163 lua_rawseti(L_, _fifo_idx, _at); // L_: ... fifotbl vals val 160 lua_rawseti(L_, _fifo_idx, _at); // L_: ... fifotbl vals val
@@ -166,14 +163,31 @@ static void fifo_pop(lua_State* L_, keeper_fifo* fifo_, int count_)
166 163
167 // avoid ever-growing indexes by resetting each time we detect the fifo is empty 164 // avoid ever-growing indexes by resetting each time we detect the fifo is empty
168 { 165 {
169 int const _new_count{ fifo_->count - count_ }; 166 int const _new_count{ count - count_ };
170 fifo_->first = (_new_count == 0) ? 1 : (fifo_->first + count_); 167 first = (_new_count == 0) ? 1 : (first + count_);
171 fifo_->count = _new_count; 168 count = _new_count;
172 } 169 }
173} 170}
174 171
175// ################################################################################################# 172// #################################################################################################
176 173
174// in: expect fifo ... on top of the stack
175// out: nothing, removes all pushed values from the stack
176void KeyUD::push(lua_State* const L_, int const count_)
177{
178 int const _idx{ lua_gettop(L_) - count_ };
179 int const _start{ first + count - 1 };
180 // pop all additional arguments, storing them in the fifo
181 for (int const _i : std::ranges::reverse_view{ std::ranges::iota_view{ 1, count_ + 1 } }) {
182 // store in the fifo the value at the top of the stack at the specified index, popping it from the stack
183 lua_rawseti(L_, _idx, _start + _i);
184 }
185 count += count_;
186}
187
188// #################################################################################################
189// #################################################################################################
190
177// in: linda_ud expected at stack slot idx 191// in: linda_ud expected at stack slot idx
178// out: fifos[ud] 192// out: fifos[ud]
179// xxh64 of string "kFifosRegKey" generated at https://www.pelock.com/products/hash-calculator 193// xxh64 of string "kFifosRegKey" generated at https://www.pelock.com/products/hash-calculator
@@ -226,19 +240,19 @@ int keeper_push_linda_storage(Linda& linda_, DestState L_)
226 InterCopyContext _c{ linda_.U, L_, _KL, {}, {}, {}, LookupMode::FromKeeper, {} }; 240 InterCopyContext _c{ linda_.U, L_, _KL, {}, {}, {}, LookupMode::FromKeeper, {} };
227 lua_pushnil(_KL); // KL: storage nil L_: out 241 lua_pushnil(_KL); // KL: storage nil L_: out
228 while (lua_next(_KL, -2)) { // KL: storage key fifo L_: out 242 while (lua_next(_KL, -2)) { // KL: storage key fifo L_: out
229 keeper_fifo* fifo = prepare_fifo_access(_KL, -1); // KL: storage key fifotbl L_: out 243 KeyUD* const _keyUD{ KeyUD::PrepareAccess(_KL, -1) }; // KL: storage key fifotbl L_: out
230 lua_pushvalue(_KL, -2); // KL: storage key fifotbl key L_: out 244 lua_pushvalue(_KL, -2); // KL: storage key fifotbl key L_: out
231 std::ignore = _c.inter_move(1); // KL: storage key fifotbl L_: out key 245 std::ignore = _c.inter_move(1); // KL: storage key fifotbl L_: out key
232 STACK_CHECK(L_, 2); 246 STACK_CHECK(L_, 2);
233 lua_newtable(L_); // KL: storage key L_: out key keyout 247 lua_newtable(L_); // KL: storage key L_: out key keyout
234 std::ignore = _c.inter_move(1); // KL: storage key L_: out key keyout fifotbl 248 std::ignore = _c.inter_move(1); // KL: storage key L_: out key keyout fifotbl
235 lua_pushinteger(L_, fifo->first); // KL: storage key L_: out key keyout fifotbl first 249 lua_pushinteger(L_, _keyUD->first); // KL: storage key L_: out key keyout fifotbl first
236 STACK_CHECK(L_, 5); 250 STACK_CHECK(L_, 5);
237 lua_setfield(L_, -3, "first"); // KL: storage key L_: out key keyout fifotbl 251 lua_setfield(L_, -3, "first"); // KL: storage key L_: out key keyout fifotbl
238 lua_pushinteger(L_, fifo->count); // KL: storage key L_: out key keyout fifobtl count 252 lua_pushinteger(L_, _keyUD->count); // KL: storage key L_: out key keyout fifobtl count
239 STACK_CHECK(L_, 5); 253 STACK_CHECK(L_, 5);
240 lua_setfield(L_, -3, "count"); // KL: storage key L_: out key keyout fifotbl 254 lua_setfield(L_, -3, "count"); // KL: storage key L_: out key keyout fifotbl
241 lua_pushinteger(L_, fifo->limit); // KL: storage key L_: out key keyout fifotbl limit 255 lua_pushinteger(L_, _keyUD->limit); // KL: storage key L_: out key keyout fifotbl limit
242 STACK_CHECK(L_, 5); 256 STACK_CHECK(L_, 5);
243 lua_setfield(L_, -3, "limit"); // KL: storage key L_: out key keyout fifotbl 257 lua_setfield(L_, -3, "limit"); // KL: storage key L_: out key keyout fifotbl
244 lua_setfield(L_, -2, "fifo"); // KL: storage key L_: out key keyout 258 lua_setfield(L_, -2, "fifo"); // KL: storage key L_: out key keyout
@@ -252,9 +266,13 @@ int keeper_push_linda_storage(Linda& linda_, DestState L_)
252} 266}
253 267
254// ################################################################################################# 268// #################################################################################################
269// #################################################################################################
270// ######################################## keepercall_XXX #########################################
271// #################################################################################################
272// #################################################################################################
255 273
256// in: linda_ud 274// in: linda_ud
257int keepercall_clear(lua_State* L_) 275int keepercall_clear(lua_State* const L_)
258{ 276{
259 STACK_GROW(L_, 3); 277 STACK_GROW(L_, 3);
260 STACK_CHECK_START_REL(L_, 0); 278 STACK_CHECK_START_REL(L_, 0);
@@ -271,7 +289,7 @@ int keepercall_clear(lua_State* L_)
271 289
272// in: linda_ud, key, ... 290// in: linda_ud, key, ...
273// out: true|false 291// out: true|false
274int keepercall_send(lua_State* L_) 292int keepercall_send(lua_State* const L_)
275{ 293{
276 int const _n{ lua_gettop(L_) - 2 }; 294 int const _n{ lua_gettop(L_) - 2 };
277 push_table(L_, 1); // L_: ud key ... fifos 295 push_table(L_, 1); // L_: ud key ... fifos
@@ -280,20 +298,20 @@ int keepercall_send(lua_State* L_)
280 lua_rawget(L_, -2); // L_: ud key ... fifos fifo 298 lua_rawget(L_, -2); // L_: ud key ... fifos fifo
281 if (lua_isnil(L_, -1)) { 299 if (lua_isnil(L_, -1)) {
282 lua_pop(L_, 1); // L_: ud key ... fifos 300 lua_pop(L_, 1); // L_: ud key ... fifos
283 std::ignore = fifo_new(KeeperState{ L_ }); // L_: ud key ... fifos fifo 301 std::ignore = KeyUD::Create(KeeperState{ L_ }); // L_: ud key ... fifos fifo
284 lua_pushvalue(L_, 2); // L_: ud key ... fifos fifo key 302 lua_pushvalue(L_, 2); // L_: ud key ... fifos fifo key
285 lua_pushvalue(L_, -2); // L_: ud key ... fifos fifo key fifo 303 lua_pushvalue(L_, -2); // L_: ud key ... fifos fifo key fifo
286 lua_rawset(L_, -4); // L_: ud key ... fifos fifo 304 lua_rawset(L_, -4); // L_: ud key ... fifos fifo
287 } 305 }
288 lua_remove(L_, -2); // L_: ud key ... fifo 306 lua_remove(L_, -2); // L_: ud key ... fifo
289 keeper_fifo* _fifo{ keeper_fifo::getPtr(L_, -1) }; 307 KeyUD* _key{ KeyUD::GetPtr(L_, -1) };
290 if (_fifo->limit >= 0 && _fifo->count + _n > _fifo->limit) { 308 if (_key->limit >= 0 && _key->count + _n > _key->limit) {
291 lua_settop(L_, 0); // L_: 309 lua_settop(L_, 0); // L_:
292 lua_pushboolean(L_, 0); // L_:false 310 lua_pushboolean(L_, 0); // L_:false
293 } else { 311 } else {
294 _fifo = prepare_fifo_access(L_, -1); // L_: ud fifotbl 312 _key = KeyUD::PrepareAccess(L_, -1); // L_: ud fifotbl
295 lua_replace(L_, 2); // L_: ud fifotbl ... 313 lua_replace(L_, 2); // L_: ud fifotbl ...
296 fifo_push(L_, _fifo, _n); // L_: ud fifotbl 314 _key->push(L_, _n); // L_: ud fifotbl
297 lua_settop(L_, 0); // L_: 315 lua_settop(L_, 0); // L_:
298 lua_pushboolean(L_, 1); // L_: true 316 lua_pushboolean(L_, 1); // L_: true
299 } 317 }
@@ -304,17 +322,18 @@ int keepercall_send(lua_State* L_)
304 322
305// in: linda_ud, key [, key]? 323// in: linda_ud, key [, key]?
306// out: (key, val) or nothing 324// out: (key, val) or nothing
307int keepercall_receive(lua_State* L_) 325int keepercall_receive(lua_State* const L_)
308{ 326{
309 int const _top{ lua_gettop(L_) }; 327 int const _top{ lua_gettop(L_) };
310 push_table(L_, 1); // L_: ud keys fifos 328 push_table(L_, 1); // L_: ud keys fifos
311 lua_replace(L_, 1); // L_: fifos keys 329 lua_replace(L_, 1); // L_: fifos keys
312 for (int _i = 2; _i <= _top; ++_i) { 330
331 for (int const _i : std::ranges::iota_view{ 2, _top + 1 }) {
313 lua_pushvalue(L_, _i); // L_: fifos keys key[i] 332 lua_pushvalue(L_, _i); // L_: fifos keys key[i]
314 lua_rawget(L_, 1); // L_: fifos keys fifo 333 lua_rawget(L_, 1); // L_: fifos keys fifo
315 keeper_fifo* const _fifo{ prepare_fifo_access(L_, -1) }; // L_: fifos keys fifotbl 334 KeyUD* const _key{ KeyUD::PrepareAccess(L_, -1) }; // L_: fifos keys fifotbl
316 if (_fifo != nullptr && _fifo->count > 0) { 335 if (_key != nullptr && _key->count > 0) {
317 fifo_pop(L_, _fifo, 1); // L_: fifos keys val 336 _key->pop(L_, 1); // L_: fifos keys val
318 if (!lua_isnil(L_, -1)) { 337 if (!lua_isnil(L_, -1)) {
319 lua_replace(L_, 1); // L_: val keys 338 lua_replace(L_, 1); // L_: val keys
320 lua_settop(L_, _i); // L_: val keys key[i] 339 lua_settop(L_, _i); // L_: val keys key[i]
@@ -335,7 +354,7 @@ int keepercall_receive(lua_State* L_)
335// ################################################################################################# 354// #################################################################################################
336 355
337// in: linda_ud key mincount [maxcount] 356// in: linda_ud key mincount [maxcount]
338int keepercall_receive_batched(lua_State* L_) 357int keepercall_receive_batched(lua_State* const L_)
339{ 358{
340 int const _min_count{ static_cast<int>(lua_tointeger(L_, 3)) }; 359 int const _min_count{ static_cast<int>(lua_tointeger(L_, 3)) };
341 if (_min_count > 0) { 360 if (_min_count > 0) {
@@ -347,9 +366,9 @@ int keepercall_receive_batched(lua_State* L_)
347 lua_pushvalue(L_, 1); // L_: key fifos key 366 lua_pushvalue(L_, 1); // L_: key fifos key
348 lua_rawget(L_, 2); // L_: key fifos fifo 367 lua_rawget(L_, 2); // L_: key fifos fifo
349 lua_remove(L_, 2); // L_: key fifo 368 lua_remove(L_, 2); // L_: key fifo
350 keeper_fifo* const _fifo{ prepare_fifo_access(L_, 2) }; // L_: key fifotbl 369 KeyUD* const _key{ KeyUD::PrepareAccess(L_, 2) }; // L_: key fifotbl
351 if (_fifo != nullptr && _fifo->count >= _min_count) { 370 if (_key != nullptr && _key->count >= _min_count) {
352 fifo_pop(L_, _fifo, std::min(_max_count, _fifo->count)); // L_: key ... 371 _key->pop(L_, std::min(_max_count, _key->count)); // L_: key ...
353 } else { 372 } else {
354 lua_settop(L_, 0); // L_: 373 lua_settop(L_, 0); // L_:
355 } 374 }
@@ -363,7 +382,7 @@ int keepercall_receive_batched(lua_State* L_)
363 382
364// in: linda_ud key [n|nil] 383// in: linda_ud key [n|nil]
365// out: true or nil 384// out: true or nil
366int keepercall_limit(lua_State* L_) 385int keepercall_limit(lua_State* const L_)
367{ 386{
368 int const _limit{ static_cast<int>(luaL_optinteger(L_, 3, -1)) }; // -1 if we read nil because the argument is absent 387 int const _limit{ static_cast<int>(luaL_optinteger(L_, 3, -1)) }; // -1 if we read nil because the argument is absent
369 push_table(L_, 1); // L_: ud key n? fifos 388 push_table(L_, 1); // L_: ud key n? fifos
@@ -371,10 +390,10 @@ int keepercall_limit(lua_State* L_)
371 lua_settop(L_, 2); // L_: fifos key 390 lua_settop(L_, 2); // L_: fifos key
372 lua_pushvalue(L_, -1); // L_: fifos key key 391 lua_pushvalue(L_, -1); // L_: fifos key key
373 lua_rawget(L_, -3); // L_: fifos key fifo|nil 392 lua_rawget(L_, -3); // L_: fifos key fifo|nil
374 keeper_fifo* _fifo{ keeper_fifo::getPtr(L_, -1) }; 393 KeyUD* _key{ KeyUD::GetPtr(L_, -1) };
375 if (_fifo == nullptr) { // L_: fifos key nil 394 if (_key == nullptr) { // L_: fifos key nil
376 lua_pop(L_, 1); // L_: fifos key 395 lua_pop(L_, 1); // L_: fifos key
377 _fifo = fifo_new(KeeperState{ L_ }); // L_: fifos key fifo 396 _key = KeyUD::Create(KeeperState{ L_ }); // L_: fifos key fifo
378 lua_rawset(L_, -3); // L_: fifos 397 lua_rawset(L_, -3); // L_: fifos
379 } 398 }
380 // remove any clutter on the stack 399 // remove any clutter on the stack
@@ -382,13 +401,13 @@ int keepercall_limit(lua_State* L_)
382 // return true if we decide that blocked threads waiting to write on that key should be awakened 401 // return true if we decide that blocked threads waiting to write on that key should be awakened
383 // this is the case if we detect the key was full but it is no longer the case 402 // this is the case if we detect the key was full but it is no longer the case
384 if ( 403 if (
385 ((_fifo->limit >= 0) && (_fifo->count >= _fifo->limit)) // the key was full if limited and count exceeded the previous limit 404 ((_key->limit >= 0) && (_key->count >= _key->limit)) // the key was full if limited and count exceeded the previous limit
386 && ((_limit < 0) || (_fifo->count < _limit)) // the key is not full if unlimited or count is lower than the new limit 405 && ((_limit < 0) || (_key->count < _limit)) // the key is not full if unlimited or count is lower than the new limit
387 ) { 406 ) {
388 lua_pushboolean(L_, 1); // L_: true 407 lua_pushboolean(L_, 1); // L_: true
389 } 408 }
390 // set the new limit 409 // set the new limit
391 _fifo->limit = _limit; 410 _key->limit = _limit;
392 // return 0 or 1 value 411 // return 0 or 1 value
393 return lua_gettop(L_); 412 return lua_gettop(L_);
394} 413}
@@ -397,7 +416,7 @@ int keepercall_limit(lua_State* L_)
397 416
398// in: linda_ud key [[val] ...] 417// in: linda_ud key [[val] ...]
399// out: true if the linda was full but it's no longer the case, else nothing 418// out: true if the linda was full but it's no longer the case, else nothing
400int keepercall_set(lua_State* L_) 419int keepercall_set(lua_State* const L_)
401{ 420{
402 bool _should_wake_writers{ false }; 421 bool _should_wake_writers{ false };
403 STACK_GROW(L_, 6); 422 STACK_GROW(L_, 6);
@@ -411,48 +430,48 @@ int keepercall_set(lua_State* L_)
411 lua_pushvalue(L_, -1); // L_: fifos key key 430 lua_pushvalue(L_, -1); // L_: fifos key key
412 lua_rawget(L_, 1); // L_: fifos key fifo|nil 431 lua_rawget(L_, 1); // L_: fifos key fifo|nil
413 // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! 432 // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged!
414 keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, -1) }; 433 KeyUD* const _key{ KeyUD::GetPtr(L_, -1) };
415 if (_fifo != nullptr) { // might be nullptr if we set a nonexistent key to nil // L_: fifos key fifo 434 if (_key != nullptr) { // might be nullptr if we set a nonexistent key to nil // L_: fifos key fifo
416 if (_fifo->limit < 0) { // fifo limit value is the default (unlimited): we can totally remove it 435 if (_key->limit < 0) { // fifo limit value is the default (unlimited): we can totally remove it
417 lua_pop(L_, 1); // L_: fifos key 436 lua_pop(L_, 1); // L_: fifos key
418 lua_pushnil(L_); // L_: fifos key nil 437 lua_pushnil(L_); // L_: fifos key nil
419 lua_rawset(L_, -3); // L_: fifos 438 lua_rawset(L_, -3); // L_: fifos
420 } else { 439 } else {
421 // we create room if the fifo was full but it is no longer the case 440 // we create room if the fifo was full but it is no longer the case
422 _should_wake_writers = (_fifo->limit > 0) && (_fifo->count >= _fifo->limit); 441 _should_wake_writers = (_key->limit > 0) && (_key->count >= _key->limit);
423 lua_remove(L_, -2); // L_: fifos fifo 442 lua_remove(L_, -2); // L_: fifos fifo
424 lua_newtable(L_); // L_: fifos fifo {} 443 lua_newtable(L_); // L_: fifos fifo {}
425 lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos fifo 444 lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos fifo
426 _fifo->first = 1; 445 _key->first = 1;
427 _fifo->count = 0; 446 _key->count = 0;
428 } 447 }
429 } 448 }
430 } else { // set/replace contents stored at the specified key? 449 } else { // set/replace contents stored at the specified key?
431 int const _count{ lua_gettop(L_) - 2 }; // number of items we want to store 450 int const _count{ lua_gettop(L_) - 2 }; // number of items we want to store
432 lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] key 451 lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] key
433 lua_rawget(L_, 1); // L_: fifos key [val [, ...]] fifo|nil 452 lua_rawget(L_, 1); // L_: fifos key [val [, ...]] fifo|nil
434 keeper_fifo* _fifo{ keeper_fifo::getPtr(L_, -1) }; 453 KeyUD* _key{ KeyUD::GetPtr(L_, -1) };
435 if (_fifo == nullptr) { // can be nullptr if we store a value at a new key // fifos key [val [, ...]] nil 454 if (_key == nullptr) { // can be nullptr if we store a value at a new key // fifos key [val [, ...]] nil
436 // no need to wake writers in that case, because a writer can't wait on an inexistent key 455 // no need to wake writers in that case, because a writer can't wait on an inexistent key
437 lua_pop(L_, 1); // L_: fifos key [val [, ...]] 456 lua_pop(L_, 1); // L_: fifos key [val [, ...]]
438 std::ignore = fifo_new(KeeperState{ L_ }); // L_: fifos key [val [, ...]] fifo 457 std::ignore = KeyUD::Create(KeeperState{ L_ }); // L_: fifos key [val [, ...]] fifo
439 lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] fifo key 458 lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] fifo key
440 lua_pushvalue(L_, -2); // L_: fifos key [val [, ...]] fifo key fifo 459 lua_pushvalue(L_, -2); // L_: fifos key [val [, ...]] fifo key fifo
441 lua_rawset(L_, 1); // L_: fifos key [val [, ...]] fifo 460 lua_rawset(L_, 1); // L_: fifos key [val [, ...]] fifo
442 } else { // L_: fifos key [val [, ...]] fifo 461 } else { // L_: fifos key [val [, ...]] fifo
443 // the fifo exists, we just want to update its contents 462 // the fifo exists, we just want to update its contents
444 // we create room if the fifo was full but it is no longer the case 463 // we create room if the fifo was full but it is no longer the case
445 _should_wake_writers = (_fifo->limit > 0) && (_fifo->count >= _fifo->limit) && (_count < _fifo->limit); 464 _should_wake_writers = (_key->limit > 0) && (_key->count >= _key->limit) && (_count < _key->limit);
446 // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! 465 // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged!
447 lua_newtable(L_); // L_: fifos key [val [, ...]] fifo {} 466 lua_newtable(L_); // L_: fifos key [val [, ...]] fifo {}
448 lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos key [val [, ...]] fifo 467 lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos key [val [, ...]] fifo
449 _fifo->first = 1; 468 _key->first = 1;
450 _fifo->count = 0; 469 _key->count = 0;
451 } 470 }
452 _fifo = prepare_fifo_access(L_, -1); // L_: fifos key [val [, ...]] fifotbl 471 _key = KeyUD::PrepareAccess(L_, -1); // L_: fifos key [val [, ...]] fifotbl
453 // move the fifo below the values we want to store 472 // move the fifo below the values we want to store
454 lua_insert(L_, 3); // L_: fifos key fifotbl [val [, ...]] 473 lua_insert(L_, 3); // L_: fifos key fifotbl [val [, ...]]
455 fifo_push(L_, _fifo, _count); // L_: fifos key fifotbl 474 _key->push(L_, _count); // L_: fifos key fifotbl
456 } 475 }
457 return _should_wake_writers ? (lua_pushboolean(L_, 1), 1) : 0; 476 return _should_wake_writers ? (lua_pushboolean(L_, 1), 1) : 0;
458} 477}
@@ -461,7 +480,7 @@ int keepercall_set(lua_State* L_)
461 480
462// in: linda_ud key [count] 481// in: linda_ud key [count]
463// out: at most <count> values 482// out: at most <count> values
464int keepercall_get(lua_State* L_) 483int keepercall_get(lua_State* const L_)
465{ 484{
466 int _count{ 1 }; 485 int _count{ 1 };
467 if (lua_gettop(L_) == 3) { // L_: ud key count 486 if (lua_gettop(L_) == 3) { // L_: ud key count
@@ -471,12 +490,12 @@ int keepercall_get(lua_State* L_)
471 push_table(L_, 1); // L_: ud key fifos 490 push_table(L_, 1); // L_: ud key fifos
472 lua_replace(L_, 1); // L_: fifos key 491 lua_replace(L_, 1); // L_: fifos key
473 lua_rawget(L_, 1); // L_: fifos fifo 492 lua_rawget(L_, 1); // L_: fifos fifo
474 keeper_fifo* const _fifo{ prepare_fifo_access(L_, -1) }; // L_: fifos fifotbl 493 KeyUD* const _key{ KeyUD::PrepareAccess(L_, -1) }; // L_: fifos fifotbl
475 if (_fifo != nullptr && _fifo->count > 0) { 494 if (_key != nullptr && _key->count > 0) {
476 lua_remove(L_, 1); // L_: fifotbl 495 lua_remove(L_, 1); // L_: fifotbl
477 _count = std::min(_count, _fifo->count); 496 _count = std::min(_count, _key->count);
478 // read <count> value off the fifo 497 // read <count> value off the fifo
479 fifo_peek(L_, _fifo, _count); // L_: fifotbl ... 498 _key->peek(L_, _count); // L_: fifotbl ...
480 return _count; 499 return _count;
481 } 500 }
482 // no fifo was ever registered for this key, or it is empty 501 // no fifo was ever registered for this key, or it is empty
@@ -486,7 +505,7 @@ int keepercall_get(lua_State* L_)
486// ################################################################################################# 505// #################################################################################################
487 506
488// in: linda_ud [, key [, ...]] 507// in: linda_ud [, key [, ...]]
489int keepercall_count(lua_State* L_) 508int keepercall_count(lua_State* const L_)
490{ 509{
491 push_table(L_, 1); // L_: ud keys fifos 510 push_table(L_, 1); // L_: ud keys fifos
492 switch (lua_gettop(L_)) { 511 switch (lua_gettop(L_)) {
@@ -496,10 +515,10 @@ int keepercall_count(lua_State* L_)
496 lua_replace(L_, 1); // L_: out fifos 515 lua_replace(L_, 1); // L_: out fifos
497 lua_pushnil(L_); // L_: out fifos nil 516 lua_pushnil(L_); // L_: out fifos nil
498 while (lua_next(L_, 2)) { // L_: out fifos key fifo 517 while (lua_next(L_, 2)) { // L_: out fifos key fifo
499 keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, -1) }; 518 KeyUD* const _key{ KeyUD::GetPtr(L_, -1) };
500 lua_pop(L_, 1); // L_: out fifos key 519 lua_pop(L_, 1); // L_: out fifos key
501 lua_pushvalue(L_, -1); // L_: out fifos key key 520 lua_pushvalue(L_, -1); // L_: out fifos key key
502 lua_pushinteger(L_, _fifo->count); // L_: out fifos key key count 521 lua_pushinteger(L_, _key->count); // L_: out fifos key key count
503 lua_rawset(L_, -5); // L_: out fifos key 522 lua_rawset(L_, -5); // L_: out fifos key
504 } 523 }
505 lua_pop(L_, 1); // L_: out 524 lua_pop(L_, 1); // L_: out
@@ -512,8 +531,8 @@ int keepercall_count(lua_State* L_)
512 if (lua_isnil(L_, -1)) { // L_: the key is unknown // L_: fifos nil 531 if (lua_isnil(L_, -1)) { // L_: the key is unknown // L_: fifos nil
513 lua_remove(L_, -2); // L_: nil 532 lua_remove(L_, -2); // L_: nil
514 } else { // the key is known // L_: fifos fifo 533 } else { // the key is known // L_: fifos fifo
515 keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, -1) }; 534 KeyUD* const _key{ KeyUD::GetPtr(L_, -1) };
516 lua_pushinteger(L_, _fifo->count); // L_: fifos fifo count 535 lua_pushinteger(L_, _key->count); // L_: fifos fifo count
517 lua_replace(L_, -3); // L_: count fifo 536 lua_replace(L_, -3); // L_: count fifo
518 lua_pop(L_, 1); // L_: count 537 lua_pop(L_, 1); // L_: count
519 } 538 }
@@ -528,10 +547,10 @@ int keepercall_count(lua_State* L_)
528 while (lua_gettop(L_) > 2) { 547 while (lua_gettop(L_) > 2) {
529 lua_pushvalue(L_, -1); // L_: out fifos keys... key 548 lua_pushvalue(L_, -1); // L_: out fifos keys... key
530 lua_rawget(L_, 2); // L_: out fifos keys... fifo|nil 549 lua_rawget(L_, 2); // L_: out fifos keys... fifo|nil
531 keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, -1) }; 550 KeyUD* const _key{ KeyUD::GetPtr(L_, -1) };
532 lua_pop(L_, 1); // L_: out fifos keys... 551 lua_pop(L_, 1); // L_: out fifos keys...
533 if (_fifo != nullptr) { // L_: the key is known 552 if (_key != nullptr) { // L_: the key is known
534 lua_pushinteger(L_, _fifo->count); // L_: out fifos keys... count 553 lua_pushinteger(L_, _key->count); // L_: out fifos keys... count
535 lua_rawset(L_, 1); // L_: out fifos keys... 554 lua_rawset(L_, 1); // L_: out fifos keys...
536 } else { // the key is unknown 555 } else { // the key is unknown
537 lua_pop(L_, 1); // L_: out fifos keys... 556 lua_pop(L_, 1); // L_: out fifos keys...
@@ -544,30 +563,6 @@ int keepercall_count(lua_State* L_)
544} 563}
545 564
546// ################################################################################################# 565// #################################################################################################
547// Keeper API, accessed from linda methods
548// #################################################################################################
549
550Keeper* Linda::acquireKeeper() const
551{
552 // can be nullptr if this happens during main state shutdown (lanes is being GC'ed -> no keepers)
553 Keeper* const _K{ whichKeeper() };
554 if (_K) {
555 _K->mutex.lock();
556 }
557 return _K;
558}
559
560// #################################################################################################
561
562void Linda::releaseKeeper(Keeper* const K_) const
563{
564 if (K_) { // can be nullptr if we tried to acquire during shutdown
565 assert(K_ == whichKeeper());
566 K_->mutex.unlock();
567 }
568}
569
570// #################################################################################################
571 566
572/* 567/*
573 * Call a function ('func_name') in the keeper state, and pass on the returned 568 * Call a function ('func_name') in the keeper state, and pass on the returned
@@ -632,7 +627,7 @@ KeeperCallResult keeper_call(KeeperState K_, keeper_api_t func_, lua_State* L_,
632 627
633// ################################################################################################# 628// #################################################################################################
634// ################################################################################################# 629// #################################################################################################
635// Keeper 630// ########################################## Keeper ###############################################
636// ################################################################################################# 631// #################################################################################################
637// ################################################################################################# 632// #################################################################################################
638 633
@@ -652,7 +647,7 @@ void Keeper::operator delete[](void* p_, Universe* U_)
652 647
653// ################################################################################################# 648// #################################################################################################
654// ################################################################################################# 649// #################################################################################################
655// Keepers 650// ########################################## Keepers ##############################################
656// ################################################################################################# 651// #################################################################################################
657// ################################################################################################# 652// #################################################################################################
658 653
@@ -666,6 +661,86 @@ void Keepers::DeleteKV::operator()(Keeper* k_) const
666} 661}
667 662
668// ################################################################################################# 663// #################################################################################################
664
665void Keepers::close()
666{
667 if (isClosing.test_and_set(std::memory_order_release)) {
668 assert(false); // should never close more than once in practice
669 return;
670 }
671
672 if (std::holds_alternative<std::monostate>(keeper_array)) {
673 return;
674 }
675
676 auto _closeOneKeeper = [](Keeper& keeper_) {
677 lua_State* const _K{ std::exchange(keeper_.L, KeeperState{ nullptr }) };
678 if (_K) {
679 lua_close(_K);
680 }
681 return _K ? true : false;
682 };
683
684 if (std::holds_alternative<Keeper>(keeper_array)) {
685 _closeOneKeeper(std::get<Keeper>(keeper_array));
686 } else {
687 KV& _kv = std::get<KV>(keeper_array);
688
689 // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it
690 // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists
691 // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success
692 // which is early-outed with a keepers->nbKeepers null-check
693 size_t const _nbKeepers{ std::exchange(_kv.nbKeepers, 0) };
694 for (size_t const _i : std::ranges::iota_view{ size_t{ 0 }, _nbKeepers }) {
695 if (!_closeOneKeeper(_kv.keepers[_i])) {
696 // detected partial init: destroy only the mutexes that got initialized properly
697 break;
698 }
699 }
700 }
701
702 keeper_array.emplace<std::monostate>();
703}
704
705// #################################################################################################
706
707[[nodiscard]] Keeper* Keepers::getKeeper(int idx_)
708{
709 if (isClosing.test(std::memory_order_acquire)) {
710 return nullptr;
711 }
712
713 if (std::holds_alternative<std::monostate>(keeper_array)) {
714 return nullptr;
715 }
716
717 if (std::holds_alternative<Keeper>(keeper_array)) {
718 return &std::get<Keeper>(keeper_array);
719 }
720
721 return &std::get<KV>(keeper_array).keepers.get()[idx_];
722}
723
724// #################################################################################################
725
726[[nodiscard]] int Keepers::getNbKeepers() const
727{
728 if (isClosing.test(std::memory_order_acquire)) {
729 return 0;
730 }
731
732 if (std::holds_alternative<std::monostate>(keeper_array)) {
733 return 0;
734 }
735
736 if (std::holds_alternative<Keeper>(keeper_array)) {
737 return 1;
738 }
739
740 return static_cast<int>(std::get<KV>(keeper_array).nbKeepers);
741}
742
743// #################################################################################################
669/* 744/*
670 * Initialize keeper states 745 * Initialize keeper states
671 * 746 *
@@ -762,84 +837,3 @@ void Keepers::initialize(Universe& U_, lua_State* L_, int const nbKeepers_, int
762 } 837 }
763 } 838 }
764} 839}
765
766// #################################################################################################
767
768void Keepers::close()
769{
770 if (isClosing.test_and_set(std::memory_order_release)) {
771 assert(false); // should never close more than once in practice
772 return;
773 }
774
775 if (std::holds_alternative<std::monostate>(keeper_array)) {
776 return;
777 }
778
779 auto _closeOneKeeper = [](Keeper& keeper_)
780 {
781 lua_State* const _K{ std::exchange(keeper_.L, KeeperState{ nullptr }) };
782 if (_K) {
783 lua_close(_K);
784 }
785 return _K ? true : false;
786 };
787
788 if (std::holds_alternative<Keeper>(keeper_array)) {
789 _closeOneKeeper(std::get<Keeper>(keeper_array));
790 } else {
791 KV& _kv = std::get<KV>(keeper_array);
792
793 // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it
794 // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists
795 // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success
796 // which is early-outed with a keepers->nbKeepers null-check
797 size_t const _nbKeepers{ std::exchange(_kv.nbKeepers, 0) };
798 for (size_t const _i : std::ranges::iota_view{ size_t{ 0 }, _nbKeepers }) {
799 if (!_closeOneKeeper(_kv.keepers[_i])) {
800 // detected partial init: destroy only the mutexes that got initialized properly
801 break;
802 }
803 }
804 }
805
806 keeper_array.emplace<std::monostate>();
807}
808
809// #################################################################################################
810
811[[nodiscard]] Keeper* Keepers::getKeeper(int idx_)
812{
813 if (isClosing.test(std::memory_order_acquire)) {
814 return nullptr;
815 }
816
817 if (std::holds_alternative<std::monostate>(keeper_array)) {
818 return nullptr;
819 }
820
821 if (std::holds_alternative<Keeper>(keeper_array)) {
822 return &std::get<Keeper>(keeper_array);
823 }
824
825 return &std::get<KV>(keeper_array).keepers.get()[idx_];
826}
827
828// #################################################################################################
829
830[[nodiscard]] int Keepers::getNbKeepers() const
831{
832 if (isClosing.test(std::memory_order_acquire)) {
833 return 0;
834 }
835
836 if (std::holds_alternative<std::monostate>(keeper_array)) {
837 return 0;
838 }
839
840 if (std::holds_alternative<Keeper>(keeper_array)) {
841 return 1;
842 }
843
844 return static_cast<int>(std::get<KV>(keeper_array).nbKeepers);
845}
diff --git a/src/linda.cpp b/src/linda.cpp
index f8d6cdc..4ab89ab 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -106,8 +106,10 @@ template <bool OPT>
106} 106}
107 107
108// ################################################################################################# 108// #################################################################################################
109// #################################################################################################
109// #################################### Linda implementation ####################################### 110// #################################### Linda implementation #######################################
110// ################################################################################################# 111// #################################################################################################
112// #################################################################################################
111 113
112Linda::Linda(Universe* U_, LindaGroup group_, std::string_view const& name_) 114Linda::Linda(Universe* U_, LindaGroup group_, std::string_view const& name_)
113: DeepPrelude{ LindaFactory::Instance } 115: DeepPrelude{ LindaFactory::Instance }
@@ -126,6 +128,18 @@ Linda::~Linda()
126 128
127// ################################################################################################# 129// #################################################################################################
128 130
131Keeper* Linda::acquireKeeper() const
132{
133 // can be nullptr if this happens during main state shutdown (lanes is being GC'ed -> no keepers)
134 Keeper* const _K{ whichKeeper() };
135 if (_K) {
136 _K->mutex.lock();
137 }
138 return _K;
139}
140
141// #################################################################################################
142
129void Linda::freeAllocatedName() 143void Linda::freeAllocatedName()
130{ 144{
131 if (std::holds_alternative<std::string_view>(nameVariant)) { 145 if (std::holds_alternative<std::string_view>(nameVariant)) {
@@ -186,6 +200,16 @@ int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_)
186 200
187// ################################################################################################# 201// #################################################################################################
188 202
203void Linda::releaseKeeper(Keeper* const K_) const
204{
205 if (K_) { // can be nullptr if we tried to acquire during shutdown
206 assert(K_ == whichKeeper());
207 K_->mutex.unlock();
208 }
209}
210
211// #################################################################################################
212
189void Linda::setName(std::string_view const& name_) 213void Linda::setName(std::string_view const& name_)
190{ 214{
191 // keep default 215 // keep default
@@ -210,8 +234,10 @@ void Linda::setName(std::string_view const& name_)
210} 234}
211 235
212// ################################################################################################# 236// #################################################################################################
237// #################################################################################################
213// ########################################## Lua API ############################################## 238// ########################################## Lua API ##############################################
214// ################################################################################################# 239// #################################################################################################
240// #################################################################################################
215 241
216/* 242/*
217 * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") 243 * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none")
diff --git a/src/linda.h b/src/linda.h
index 5753258..8db380a 100644
--- a/src/linda.h
+++ b/src/linda.h
@@ -2,7 +2,6 @@
2 2
3#include "cancel.h" 3#include "cancel.h"
4#include "deep.h" 4#include "deep.h"
5#include "keeper.h"
6#include "universe.h" 5#include "universe.h"
7 6
8#include <array> 7#include <array>
diff --git a/tests/keeper.lua b/tests/keeper.lua
index 2d432f4..0220eba 100644
--- a/tests/keeper.lua
+++ b/tests/keeper.lua
@@ -31,7 +31,7 @@ end
31-- ################################################################################################# 31-- #################################################################################################
32-- ################################################################################################# 32-- #################################################################################################
33 33
34if false then 34if true then
35 PRINT "=========================================================================================" 35 PRINT "========================================================================================="
36 PRINT "Linda groups test:" 36 PRINT "Linda groups test:"
37 37
@@ -55,7 +55,7 @@ DONE()
55 55
56-- ################################################################################################# 56-- #################################################################################################
57 57
58if false then 58if true then
59 PRINT "=========================================================================================" 59 PRINT "========================================================================================="
60 PRINT "Linda names test:" 60 PRINT "Linda names test:"
61 local unnamedLinda1 = lanes.linda(1) 61 local unnamedLinda1 = lanes.linda(1)
@@ -102,7 +102,7 @@ DONE()
102 102
103-- ################################################################################################# 103-- #################################################################################################
104 104
105if false then 105if true then
106 PRINT "=========================================================================================" 106 PRINT "========================================================================================="
107 PRINT "General test:" 107 PRINT "General test:"
108 108