aboutsummaryrefslogtreecommitdiff
path: root/src/linda.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/linda.cpp')
-rw-r--r--src/linda.cpp389
1 files changed, 150 insertions, 239 deletions
diff --git a/src/linda.cpp b/src/linda.cpp
index f88158a..82f5f98 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -2,7 +2,7 @@
2 * LINDA.CPP Copyright (c) 2018-2024, Benoit Germain 2 * LINDA.CPP Copyright (c) 2018-2024, Benoit Germain
3 * 3 *
4 * Linda deep userdata. 4 * Linda deep userdata.
5*/ 5 */
6 6
7/* 7/*
8=============================================================================== 8===============================================================================
@@ -62,8 +62,7 @@ Linda::Linda(Universe* U_, LindaGroup group_, char const* name_, size_t len_)
62 62
63Linda::~Linda() 63Linda::~Linda()
64{ 64{
65 if (std::holds_alternative<AllocatedName>(m_name)) 65 if (std::holds_alternative<AllocatedName>(m_name)) {
66 {
67 AllocatedName& name = std::get<AllocatedName>(m_name); 66 AllocatedName& name = std::get<AllocatedName>(m_name);
68 U->internal_allocator.free(name.name, name.len); 67 U->internal_allocator.free(name.name, name.len);
69 } 68 }
@@ -74,19 +73,15 @@ Linda::~Linda()
74void Linda::setName(char const* name_, size_t len_) 73void Linda::setName(char const* name_, size_t len_)
75{ 74{
76 // keep default 75 // keep default
77 if (!name_ || len_ == 0) 76 if (!name_ || len_ == 0) {
78 {
79 return; 77 return;
80 } 78 }
81 ++len_; // don't forget terminating 0 79 ++len_; // don't forget terminating 0
82 if (len_ < kEmbeddedNameLength) 80 if (len_ < kEmbeddedNameLength) {
83 {
84 m_name.emplace<EmbeddedName>(); 81 m_name.emplace<EmbeddedName>();
85 char* const name{ std::get<EmbeddedName>(m_name).data() }; 82 char* const name{ std::get<EmbeddedName>(m_name).data() };
86 memcpy(name, name_, len_); 83 memcpy(name, name_, len_);
87 } 84 } else {
88 else
89 {
90 AllocatedName& name = std::get<AllocatedName>(m_name); 85 AllocatedName& name = std::get<AllocatedName>(m_name);
91 name.name = static_cast<char*>(U->internal_allocator.alloc(len_)); 86 name.name = static_cast<char*>(U->internal_allocator.alloc(len_));
92 name.len = len_; 87 name.len = len_;
@@ -98,13 +93,11 @@ void Linda::setName(char const* name_, size_t len_)
98 93
99char const* Linda::getName() const 94char const* Linda::getName() const
100{ 95{
101 if (std::holds_alternative<AllocatedName>(m_name)) 96 if (std::holds_alternative<AllocatedName>(m_name)) {
102 {
103 AllocatedName const& name = std::get<AllocatedName>(m_name); 97 AllocatedName const& name = std::get<AllocatedName>(m_name);
104 return name.name; 98 return name.name;
105 } 99 }
106 if (std::holds_alternative<EmbeddedName>(m_name)) 100 if (std::holds_alternative<EmbeddedName>(m_name)) {
107 {
108 char const* const name{ std::get<EmbeddedName>(m_name).data() }; 101 char const* const name{ std::get<EmbeddedName>(m_name).data() };
109 return name; 102 return name;
110 } 103 }
@@ -117,8 +110,7 @@ template <bool OPT>
117[[nodiscard]] static inline Linda* ToLinda(lua_State* L_, int idx_) 110[[nodiscard]] static inline Linda* ToLinda(lua_State* L_, int idx_)
118{ 111{
119 Linda* const linda{ static_cast<Linda*>(LindaFactory::Instance.toDeep(L_, idx_)) }; 112 Linda* const linda{ static_cast<Linda*>(LindaFactory::Instance.toDeep(L_, idx_)) };
120 if constexpr (!OPT) 113 if constexpr (!OPT) {
121 {
122 luaL_argcheck(L_, linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr 114 luaL_argcheck(L_, linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr
123 LUA_ASSERT(L_, linda->U == universe_get(L_)); 115 LUA_ASSERT(L_, linda->U == universe_get(L_));
124 } 116 }
@@ -129,26 +121,20 @@ template <bool OPT>
129 121
130static void check_key_types(lua_State* L_, int start_, int end_) 122static void check_key_types(lua_State* L_, int start_, int end_)
131{ 123{
132 for (int i{ start_ }; i <= end_; ++i) 124 for (int i{ start_ }; i <= end_; ++i) {
133 {
134 LuaType const t{ lua_type_as_enum(L_, i) }; 125 LuaType const t{ lua_type_as_enum(L_, i) };
135 switch (t) 126 switch (t) {
136 { 127 case LuaType::BOOLEAN:
137 case LuaType::BOOLEAN: 128 case LuaType::NUMBER:
138 case LuaType::NUMBER: 129 case LuaType::STRING:
139 case LuaType::STRING:
140 continue; 130 continue;
141 131
142 case LuaType::LIGHTUSERDATA: 132 case LuaType::LIGHTUSERDATA:
143 { 133 static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel };
144 static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel }; 134 for (UniqueKey const& key : kKeysToCheck) {
145 for (UniqueKey const& key : kKeysToCheck) 135 if (key.equals(L_, i)) {
146 { 136 raise_luaL_error(L_, "argument #%d: can't use %s as a key", i, key.m_debugName);
147 if (key.equals(L_, i)) 137 break;
148 {
149 raise_luaL_error(L_, "argument #%d: can't use %s as a key", i, key.m_debugName);
150 break;
151 }
152 } 138 }
153 } 139 }
154 break; 140 break;
@@ -184,8 +170,7 @@ int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_)
184 linda->releaseKeeper(K); 170 linda->releaseKeeper(K);
185 171
186 // if there was an error, forward it 172 // if there was an error, forward it
187 if (rc != LUA_OK) 173 if (rc != LUA_OK) {
188 {
189 raise_lua_error(L_); 174 raise_lua_error(L_);
190 } 175 }
191 // return whatever the actual operation provided 176 // return whatever the actual operation provided
@@ -195,39 +180,33 @@ int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_)
195// ################################################################################################# 180// #################################################################################################
196 181
197/* 182/*
198* bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... ) 183 * bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... )
199* 184 *
200* Send one or more values to a Linda. If there is a limit, all values must fit. 185 * Send one or more values to a Linda. If there is a limit, all values must fit.
201* 186 *
202* Returns: 'true' if the value was queued 187 * Returns: 'true' if the value was queued
203* 'false' for timeout (only happens when the queue size is limited) 188 * 'false' for timeout (only happens when the queue size is limited)
204* nil, kCancelError if cancelled 189 * nil, kCancelError if cancelled
205*/ 190 */
206LUAG_FUNC(linda_send) 191LUAG_FUNC(linda_send)
207{ 192{
208 auto send = [](lua_State* L_) 193 auto send = [](lua_State* L_) {
209 {
210 Linda* const linda{ ToLinda<false>(L_, 1) }; 194 Linda* const linda{ ToLinda<false>(L_, 1) };
211 std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; 195 std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
212 int key_i{ 2 }; // index of first key, if timeout not there 196 int key_i{ 2 }; // index of first key, if timeout not there
213 197
214 if (lua_type(L_, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion 198 if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion
215 {
216 lua_Duration const duration{ lua_tonumber(L_, 2) }; 199 lua_Duration const duration{ lua_tonumber(L_, 2) };
217 if (duration.count() >= 0.0) 200 if (duration.count() >= 0.0) {
218 {
219 until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); 201 until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration);
220 } 202 }
221 ++key_i; 203 ++key_i;
222 } 204 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key
223 else if (lua_isnil(L_, 2)) // alternate explicit "infinite timeout" by passing nil before the key
224 {
225 ++key_i; 205 ++key_i;
226 } 206 }
227 207
228 bool const as_nil_sentinel{ kNilSentinel.equals(L_, key_i) }; // if not nullptr, send() will silently send a single nil if nothing is provided 208 bool const as_nil_sentinel{ kNilSentinel.equals(L_, key_i) }; // if not nullptr, send() will silently send a single nil if nothing is provided
229 if (as_nil_sentinel) 209 if (as_nil_sentinel) {
230 {
231 // the real key to send data to is after the kNilSentinel marker 210 // the real key to send data to is after the kNilSentinel marker
232 ++key_i; 211 ++key_i;
233 } 212 }
@@ -238,15 +217,11 @@ LUAG_FUNC(linda_send)
238 STACK_GROW(L_, 1); 217 STACK_GROW(L_, 1);
239 218
240 // make sure there is something to send 219 // make sure there is something to send
241 if (lua_gettop(L_) == key_i) 220 if (lua_gettop(L_) == key_i) {
242 { 221 if (as_nil_sentinel) {
243 if (as_nil_sentinel)
244 {
245 // send a single nil if nothing is provided 222 // send a single nil if nothing is provided
246 kNilSentinel.pushKey(L_); 223 kNilSentinel.pushKey(L_);
247 } 224 } else {
248 else
249 {
250 raise_luaL_error(L_, "no data to send"); 225 raise_luaL_error(L_, "no data to send");
251 } 226 }
252 } 227 }
@@ -264,24 +239,20 @@ LUAG_FUNC(linda_send)
264 return 0; 239 return 0;
265 240
266 STACK_CHECK_START_REL(KL, 0); 241 STACK_CHECK_START_REL(KL, 0);
267 for (bool try_again{ true };;) 242 for (bool try_again{ true };;) {
268 { 243 if (lane != nullptr) {
269 if (lane != nullptr)
270 {
271 cancel = lane->cancel_request; 244 cancel = lane->cancel_request;
272 } 245 }
273 cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; 246 cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel;
274 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything 247 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
275 if (!try_again || cancel != CancelRequest::None) 248 if (!try_again || cancel != CancelRequest::None) {
276 {
277 pushed.emplace(0); 249 pushed.emplace(0);
278 break; 250 break;
279 } 251 }
280 252
281 STACK_CHECK(KL, 0); 253 STACK_CHECK(KL, 0);
282 pushed = keeper_call(linda->U, KL, KEEPER_API(send), L_, linda, key_i); 254 pushed = keeper_call(linda->U, KL, KEEPER_API(send), L_, linda, key_i);
283 if (!pushed.has_value()) 255 if (!pushed.has_value()) {
284 {
285 break; 256 break;
286 } 257 }
287 LUA_ASSERT(L_, pushed.value() == 1); 258 LUA_ASSERT(L_, pushed.value() == 1);
@@ -289,26 +260,23 @@ LUAG_FUNC(linda_send)
289 ret = lua_toboolean(L_, -1) ? true : false; 260 ret = lua_toboolean(L_, -1) ? true : false;
290 lua_pop(L_, 1); 261 lua_pop(L_, 1);
291 262
292 if (ret) 263 if (ret) {
293 {
294 // Wake up ALL waiting threads 264 // Wake up ALL waiting threads
295 linda->m_write_happened.notify_all(); 265 linda->m_write_happened.notify_all();
296 break; 266 break;
297 } 267 }
298 268
299 // instant timout to bypass the wait syscall 269 // instant timout to bypass the wait syscall
300 if (std::chrono::steady_clock::now() >= until) 270 if (std::chrono::steady_clock::now() >= until) {
301 {
302 break; /* no wait; instant timeout */ 271 break; /* no wait; instant timeout */
303 } 272 }
304 273
305 // storage limit hit, wait until timeout or signalled that we should try again 274 // storage limit hit, wait until timeout or signalled that we should try again
306 { 275 {
307 Lane::Status prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings 276 Lane::Status prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
308 if (lane != nullptr) 277 if (lane != nullptr) {
309 {
310 // change status of lane to "waiting" 278 // change status of lane to "waiting"
311 prev_status = lane->m_status; // Running, most likely 279 prev_status = lane->m_status; // Running, most likely
312 LUA_ASSERT(L_, prev_status == Lane::Running); // but check, just in case 280 LUA_ASSERT(L_, prev_status == Lane::Running); // but check, just in case
313 lane->m_status = Lane::Waiting; 281 lane->m_status = Lane::Waiting;
314 LUA_ASSERT(L_, lane->m_waiting_on == nullptr); 282 LUA_ASSERT(L_, lane->m_waiting_on == nullptr);
@@ -317,10 +285,9 @@ LUAG_FUNC(linda_send)
317 // could not send because no room: wait until some data was read before trying again, or until timeout is reached 285 // could not send because no room: wait until some data was read before trying again, or until timeout is reached
318 std::unique_lock<std::mutex> keeper_lock{ K->m_mutex, std::adopt_lock }; 286 std::unique_lock<std::mutex> keeper_lock{ K->m_mutex, std::adopt_lock };
319 std::cv_status const status{ linda->m_read_happened.wait_until(keeper_lock, until) }; 287 std::cv_status const status{ linda->m_read_happened.wait_until(keeper_lock, until) };
320 keeper_lock.release(); // we don't want to release the lock! 288 keeper_lock.release(); // we don't want to release the lock!
321 try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups 289 try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups
322 if (lane != nullptr) 290 if (lane != nullptr) {
323 {
324 lane->m_waiting_on = nullptr; 291 lane->m_waiting_on = nullptr;
325 lane->m_status = prev_status; 292 lane->m_status = prev_status;
326 } 293 }
@@ -329,23 +296,21 @@ LUAG_FUNC(linda_send)
329 STACK_CHECK(KL, 0); 296 STACK_CHECK(KL, 0);
330 } 297 }
331 298
332 if (!pushed.has_value()) 299 if (!pushed.has_value()) {
333 {
334 raise_luaL_error(L_, "tried to copy unsupported types"); 300 raise_luaL_error(L_, "tried to copy unsupported types");
335 } 301 }
336 302
337 switch (cancel) 303 switch (cancel) {
338 { 304 case CancelRequest::Soft:
339 case CancelRequest::Soft:
340 // if user wants to soft-cancel, the call returns lanes.cancel_error 305 // if user wants to soft-cancel, the call returns lanes.cancel_error
341 kCancelError.pushKey(L_); 306 kCancelError.pushKey(L_);
342 return 1; 307 return 1;
343 308
344 case CancelRequest::Hard: 309 case CancelRequest::Hard:
345 // raise an error interrupting execution only in case of hard cancel 310 // raise an error interrupting execution only in case of hard cancel
346 raise_cancel_error(L_); // raises an error and doesn't return 311 raise_cancel_error(L_); // raises an error and doesn't return
347 312
348 default: 313 default:
349 lua_pushboolean(L_, ret); // true (success) or false (timeout) 314 lua_pushboolean(L_, ret); // true (success) or false (timeout)
350 return 1; 315 return 1;
351 } 316 }
@@ -368,23 +333,18 @@ LUAG_FUNC(linda_send)
368 */ 333 */
369LUAG_FUNC(linda_receive) 334LUAG_FUNC(linda_receive)
370{ 335{
371 auto receive = [](lua_State* L_) 336 auto receive = [](lua_State* L_) {
372 {
373 Linda* const linda{ ToLinda<false>(L_, 1) }; 337 Linda* const linda{ ToLinda<false>(L_, 1) };
374 std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; 338 std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
375 int key_i{ 2 }; // index of first key, if timeout not there 339 int key_i{ 2 }; // index of first key, if timeout not there
376 340
377 if (lua_type(L_, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion 341 if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion
378 {
379 lua_Duration const duration{ lua_tonumber(L_, 2) }; 342 lua_Duration const duration{ lua_tonumber(L_, 2) };
380 if (duration.count() >= 0.0) 343 if (duration.count() >= 0.0) {
381 {
382 until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration); 344 until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(duration);
383 } 345 }
384 ++key_i; 346 ++key_i;
385 } 347 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key
386 else if (lua_isnil(L_, 2)) // alternate explicit "infinite timeout" by passing nil before the key
387 {
388 ++key_i; 348 ++key_i;
389 } 349 }
390 350
@@ -394,8 +354,7 @@ LUAG_FUNC(linda_receive)
394 kLindaBatched.pushKey(L_); 354 kLindaBatched.pushKey(L_);
395 int const is_batched{ lua501_equal(L_, key_i, -1) }; 355 int const is_batched{ lua501_equal(L_, key_i, -1) };
396 lua_pop(L_, 1); 356 lua_pop(L_, 1);
397 if (is_batched) 357 if (is_batched) {
398 {
399 // no need to pass linda.batched in the keeper state 358 // no need to pass linda.batched in the keeper state
400 ++key_i; 359 ++key_i;
401 // make sure the keys are of a valid type 360 // make sure the keys are of a valid type
@@ -408,13 +367,10 @@ LUAG_FUNC(linda_receive)
408 // don't forget to count the key in addition to the values 367 // don't forget to count the key in addition to the values
409 ++expected_pushed_min; 368 ++expected_pushed_min;
410 ++expected_pushed_max; 369 ++expected_pushed_max;
411 if (expected_pushed_min > expected_pushed_max) 370 if (expected_pushed_min > expected_pushed_max) {
412 {
413 raise_luaL_error(L_, "batched min/max error"); 371 raise_luaL_error(L_, "batched min/max error");
414 } 372 }
415 } 373 } else {
416 else
417 {
418 // make sure the keys are of a valid type 374 // make sure the keys are of a valid type
419 check_key_types(L_, key_i, lua_gettop(L_)); 375 check_key_types(L_, key_i, lua_gettop(L_));
420 // receive a single value, checking multiple slots 376 // receive a single value, checking multiple slots
@@ -432,28 +388,23 @@ LUAG_FUNC(linda_receive)
432 CancelRequest cancel{ CancelRequest::None }; 388 CancelRequest cancel{ CancelRequest::None };
433 KeeperCallResult pushed; 389 KeeperCallResult pushed;
434 STACK_CHECK_START_REL(KL, 0); 390 STACK_CHECK_START_REL(KL, 0);
435 for (bool try_again{ true };;) 391 for (bool try_again{ true };;) {
436 { 392 if (lane != nullptr) {
437 if (lane != nullptr)
438 {
439 cancel = lane->cancel_request; 393 cancel = lane->cancel_request;
440 } 394 }
441 cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; 395 cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel;
442 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything 396 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
443 if (!try_again || cancel != CancelRequest::None) 397 if (!try_again || cancel != CancelRequest::None) {
444 {
445 pushed.emplace(0); 398 pushed.emplace(0);
446 break; 399 break;
447 } 400 }
448 401
449 // all arguments of receive() but the first are passed to the keeper's receive function 402 // all arguments of receive() but the first are passed to the keeper's receive function
450 pushed = keeper_call(linda->U, KL, selected_keeper_receive, L_, linda, key_i); 403 pushed = keeper_call(linda->U, KL, selected_keeper_receive, L_, linda, key_i);
451 if (!pushed.has_value()) 404 if (!pushed.has_value()) {
452 {
453 break; 405 break;
454 } 406 }
455 if (pushed.value() > 0) 407 if (pushed.value() > 0) {
456 {
457 LUA_ASSERT(L_, pushed.value() >= expected_pushed_min && pushed.value() <= expected_pushed_max); 408 LUA_ASSERT(L_, pushed.value() >= expected_pushed_min && pushed.value() <= expected_pushed_max);
458 // replace sentinels with real nils 409 // replace sentinels with real nils
459 keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - pushed.value(), LookupMode::FromKeeper); 410 keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - pushed.value(), LookupMode::FromKeeper);
@@ -463,18 +414,16 @@ LUAG_FUNC(linda_receive)
463 break; 414 break;
464 } 415 }
465 416
466 if (std::chrono::steady_clock::now() >= until) 417 if (std::chrono::steady_clock::now() >= until) {
467 {
468 break; /* instant timeout */ 418 break; /* instant timeout */
469 } 419 }
470 420
471 // nothing received, wait until timeout or signalled that we should try again 421 // nothing received, wait until timeout or signalled that we should try again
472 { 422 {
473 Lane::Status prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings 423 Lane::Status prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings
474 if (lane != nullptr) 424 if (lane != nullptr) {
475 {
476 // change status of lane to "waiting" 425 // change status of lane to "waiting"
477 prev_status = lane->m_status; // Running, most likely 426 prev_status = lane->m_status; // Running, most likely
478 LUA_ASSERT(L_, prev_status == Lane::Running); // but check, just in case 427 LUA_ASSERT(L_, prev_status == Lane::Running); // but check, just in case
479 lane->m_status = Lane::Waiting; 428 lane->m_status = Lane::Waiting;
480 LUA_ASSERT(L_, lane->m_waiting_on == nullptr); 429 LUA_ASSERT(L_, lane->m_waiting_on == nullptr);
@@ -483,10 +432,9 @@ LUAG_FUNC(linda_receive)
483 // not enough data to read: wakeup when data was sent, or when timeout is reached 432 // not enough data to read: wakeup when data was sent, or when timeout is reached
484 std::unique_lock<std::mutex> keeper_lock{ K->m_mutex, std::adopt_lock }; 433 std::unique_lock<std::mutex> keeper_lock{ K->m_mutex, std::adopt_lock };
485 std::cv_status const status{ linda->m_write_happened.wait_until(keeper_lock, until) }; 434 std::cv_status const status{ linda->m_write_happened.wait_until(keeper_lock, until) };
486 keeper_lock.release(); // we don't want to release the lock! 435 keeper_lock.release(); // we don't want to release the lock!
487 try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups 436 try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups
488 if (lane != nullptr) 437 if (lane != nullptr) {
489 {
490 lane->m_waiting_on = nullptr; 438 lane->m_waiting_on = nullptr;
491 lane->m_status = prev_status; 439 lane->m_status = prev_status;
492 } 440 }
@@ -494,23 +442,21 @@ LUAG_FUNC(linda_receive)
494 } 442 }
495 STACK_CHECK(KL, 0); 443 STACK_CHECK(KL, 0);
496 444
497 if (!pushed.has_value()) 445 if (!pushed.has_value()) {
498 {
499 raise_luaL_error(L_, "tried to copy unsupported types"); 446 raise_luaL_error(L_, "tried to copy unsupported types");
500 } 447 }
501 448
502 switch (cancel) 449 switch (cancel) {
503 { 450 case CancelRequest::Soft:
504 case CancelRequest::Soft:
505 // if user wants to soft-cancel, the call returns kCancelError 451 // if user wants to soft-cancel, the call returns kCancelError
506 kCancelError.pushKey(L_); 452 kCancelError.pushKey(L_);
507 return 1; 453 return 1;
508 454
509 case CancelRequest::Hard: 455 case CancelRequest::Hard:
510 // raise an error interrupting execution only in case of hard cancel 456 // raise an error interrupting execution only in case of hard cancel
511 raise_cancel_error(L_); // raises an error and doesn't return 457 raise_cancel_error(L_); // raises an error and doesn't return
512 458
513 default: 459 default:
514 return pushed.value(); 460 return pushed.value();
515 } 461 }
516 }; 462 };
@@ -520,17 +466,16 @@ LUAG_FUNC(linda_receive)
520// ################################################################################################# 466// #################################################################################################
521 467
522/* 468/*
523* [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) 469 * [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]])
524* 470 *
525* Set one or more value to Linda. 471 * Set one or more value to Linda.
526* TODO: what do we do if we set to non-nil and limit is 0? 472 * TODO: what do we do if we set to non-nil and limit is 0?
527* 473 *
528* Existing slot value is replaced, and possible queued entries removed. 474 * Existing slot value is replaced, and possible queued entries removed.
529*/ 475 */
530LUAG_FUNC(linda_set) 476LUAG_FUNC(linda_set)
531{ 477{
532 auto set = [](lua_State* L_) 478 auto set = [](lua_State* L_) {
533 {
534 Linda* const linda{ ToLinda<false>(L_, 1) }; 479 Linda* const linda{ ToLinda<false>(L_, 1) };
535 bool const has_value{ lua_gettop(L_) > 2 }; 480 bool const has_value{ lua_gettop(L_) > 2 };
536 // make sure the key is of a valid type (throws an error if not the case) 481 // make sure the key is of a valid type (throws an error if not the case)
@@ -538,33 +483,26 @@ LUAG_FUNC(linda_set)
538 483
539 Keeper* const K{ linda->whichKeeper() }; 484 Keeper* const K{ linda->whichKeeper() };
540 KeeperCallResult pushed; 485 KeeperCallResult pushed;
541 if (linda->simulate_cancel == CancelRequest::None) 486 if (linda->simulate_cancel == CancelRequest::None) {
542 { 487 if (has_value) {
543 if (has_value)
544 {
545 // convert nils to some special non-nil sentinel in sent values 488 // convert nils to some special non-nil sentinel in sent values
546 keeper_toggle_nil_sentinels(L_, 3, LookupMode::ToKeeper); 489 keeper_toggle_nil_sentinels(L_, 3, LookupMode::ToKeeper);
547 } 490 }
548 pushed = keeper_call(linda->U, K->L, KEEPER_API(set), L_, linda, 2); 491 pushed = keeper_call(linda->U, K->L, KEEPER_API(set), L_, linda, 2);
549 if (pushed.has_value()) // no error? 492 if (pushed.has_value()) { // no error?
550 {
551 LUA_ASSERT(L_, pushed.value() == 0 || pushed.value() == 1); 493 LUA_ASSERT(L_, pushed.value() == 0 || pushed.value() == 1);
552 494
553 if (has_value) 495 if (has_value) {
554 {
555 // we put some data in the slot, tell readers that they should wake 496 // we put some data in the slot, tell readers that they should wake
556 linda->m_write_happened.notify_all(); // To be done from within the 'K' locking area 497 linda->m_write_happened.notify_all(); // To be done from within the 'K' locking area
557 } 498 }
558 if (pushed.value() == 1) 499 if (pushed.value() == 1) {
559 {
560 // the key was full, but it is no longer the case, tell writers they should wake 500 // the key was full, but it is no longer the case, tell writers they should wake
561 LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TBOOLEAN && lua_toboolean(L_, -1) == 1); 501 LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TBOOLEAN && lua_toboolean(L_, -1) == 1);
562 linda->m_read_happened.notify_all(); // To be done from within the 'K' locking area 502 linda->m_read_happened.notify_all(); // To be done from within the 'K' locking area
563 } 503 }
564 } 504 }
565 } 505 } else { // linda is cancelled
566 else // linda is cancelled
567 {
568 // do nothing and return lanes.cancel_error 506 // do nothing and return lanes.cancel_error
569 kCancelError.pushKey(L_); 507 kCancelError.pushKey(L_);
570 pushed.emplace(1); 508 pushed.emplace(1);
@@ -585,8 +523,7 @@ LUAG_FUNC(linda_set)
585 */ 523 */
586LUAG_FUNC(linda_count) 524LUAG_FUNC(linda_count)
587{ 525{
588 auto count = [](lua_State* L_) 526 auto count = [](lua_State* L_) {
589 {
590 Linda* const linda{ ToLinda<false>(L_, 1) }; 527 Linda* const linda{ ToLinda<false>(L_, 1) };
591 // make sure the keys are of a valid type 528 // make sure the keys are of a valid type
592 check_key_types(L_, 2, lua_gettop(L_)); 529 check_key_types(L_, 2, lua_gettop(L_));
@@ -601,14 +538,13 @@ LUAG_FUNC(linda_count)
601// ################################################################################################# 538// #################################################################################################
602 539
603/* 540/*
604* [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1]) 541 * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1])
605* 542 *
606* Get one or more values from Linda. 543 * Get one or more values from Linda.
607*/ 544 */
608LUAG_FUNC(linda_get) 545LUAG_FUNC(linda_get)
609{ 546{
610 auto get = [](lua_State* L_) 547 auto get = [](lua_State* L_) {
611 {
612 Linda* const linda{ ToLinda<false>(L_, 1) }; 548 Linda* const linda{ ToLinda<false>(L_, 1) };
613 lua_Integer const count{ luaL_optinteger(L_, 3, 1) }; 549 lua_Integer const count{ luaL_optinteger(L_, 3, 1) };
614 luaL_argcheck(L_, count >= 1, 3, "count should be >= 1"); 550 luaL_argcheck(L_, count >= 1, 3, "count should be >= 1");
@@ -617,17 +553,13 @@ LUAG_FUNC(linda_get)
617 check_key_types(L_, 2, 2); 553 check_key_types(L_, 2, 2);
618 554
619 KeeperCallResult pushed; 555 KeeperCallResult pushed;
620 if (linda->simulate_cancel == CancelRequest::None) 556 if (linda->simulate_cancel == CancelRequest::None) {
621 {
622 Keeper* const K{ linda->whichKeeper() }; 557 Keeper* const K{ linda->whichKeeper() };
623 pushed = keeper_call(linda->U, K->L, KEEPER_API(get), L_, linda, 2); 558 pushed = keeper_call(linda->U, K->L, KEEPER_API(get), L_, linda, 2);
624 if (pushed.value_or(0) > 0) 559 if (pushed.value_or(0) > 0) {
625 {
626 keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - pushed.value(), LookupMode::FromKeeper); 560 keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - pushed.value(), LookupMode::FromKeeper);
627 } 561 }
628 } 562 } else { // linda is cancelled
629 else // linda is cancelled
630 {
631 // do nothing and return lanes.cancel_error 563 // do nothing and return lanes.cancel_error
632 kCancelError.pushKey(L_); 564 kCancelError.pushKey(L_);
633 pushed.emplace(1); 565 pushed.emplace(1);
@@ -641,37 +573,32 @@ LUAG_FUNC(linda_get)
641// ################################################################################################# 573// #################################################################################################
642 574
643/* 575/*
644* [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int) 576 * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int)
645* 577 *
646* Set limit to 1 Linda keys. 578 * Set limit to 1 Linda keys.
647* Optionally wake threads waiting to write on the linda, in case the limit enables them to do so 579 * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so
648*/ 580 */
649LUAG_FUNC(linda_limit) 581LUAG_FUNC(linda_limit)
650{ 582{
651 auto limit = [](lua_State* L_) 583 auto limit = [](lua_State* L_) {
652 {
653 Linda* const linda{ ToLinda<false>(L_, 1) }; 584 Linda* const linda{ ToLinda<false>(L_, 1) };
654 // make sure we got 3 arguments: the linda, a key and a limit 585 // make sure we got 3 arguments: the linda, a key and a limit
655 luaL_argcheck( L_, lua_gettop( L_) == 3, 2, "wrong number of arguments"); 586 luaL_argcheck(L_, lua_gettop(L_) == 3, 2, "wrong number of arguments");
656 // make sure we got a numeric limit 587 // make sure we got a numeric limit
657 luaL_checknumber( L_, 3); 588 luaL_checknumber(L_, 3);
658 // make sure the key is of a valid type 589 // make sure the key is of a valid type
659 check_key_types( L_, 2, 2); 590 check_key_types(L_, 2, 2);
660 591
661 KeeperCallResult pushed; 592 KeeperCallResult pushed;
662 if (linda->simulate_cancel == CancelRequest::None) 593 if (linda->simulate_cancel == CancelRequest::None) {
663 {
664 Keeper* const K{ linda->whichKeeper() }; 594 Keeper* const K{ linda->whichKeeper() };
665 pushed = keeper_call(linda->U, K->L, KEEPER_API(limit), L_, linda, 2); 595 pushed = keeper_call(linda->U, K->L, KEEPER_API(limit), L_, linda, 2);
666 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 596 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
667 if (pushed.value() == 1) 597 if (pushed.value() == 1) {
668 { 598 LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TBOOLEAN && lua_toboolean(L_, -1) == 1);
669 LUA_ASSERT(L_, lua_type( L_, -1) == LUA_TBOOLEAN && lua_toboolean( L_, -1) == 1);
670 linda->m_read_happened.notify_all(); // To be done from within the 'K' locking area 599 linda->m_read_happened.notify_all(); // To be done from within the 'K' locking area
671 } 600 }
672 } 601 } else { // linda is cancelled
673 else // linda is cancelled
674 {
675 // do nothing and return lanes.cancel_error 602 // do nothing and return lanes.cancel_error
676 kCancelError.pushKey(L_); 603 kCancelError.pushKey(L_);
677 pushed.emplace(1); 604 pushed.emplace(1);
@@ -685,10 +612,10 @@ LUAG_FUNC(linda_limit)
685// ################################################################################################# 612// #################################################################################################
686 613
687/* 614/*
688* (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") 615 * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none")
689* 616 *
690* Signal linda so that waiting threads wake up as if their own lane was cancelled 617 * Signal linda so that waiting threads wake up as if their own lane was cancelled
691*/ 618 */
692LUAG_FUNC(linda_cancel) 619LUAG_FUNC(linda_cancel)
693{ 620{
694 Linda* const linda{ ToLinda<false>(L_, 1) }; 621 Linda* const linda{ ToLinda<false>(L_, 1) };
@@ -697,25 +624,16 @@ LUAG_FUNC(linda_cancel)
697 luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments"); 624 luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments");
698 625
699 linda->simulate_cancel = CancelRequest::Soft; 626 linda->simulate_cancel = CancelRequest::Soft;
700 if (strcmp(who, "both") == 0) // tell everyone writers to wake up 627 if (strcmp(who, "both") == 0) { // tell everyone writers to wake up
701 {
702 linda->m_write_happened.notify_all(); 628 linda->m_write_happened.notify_all();
703 linda->m_read_happened.notify_all(); 629 linda->m_read_happened.notify_all();
704 } 630 } else if (strcmp(who, "none") == 0) { // reset flag
705 else if (strcmp(who, "none") == 0) // reset flag
706 {
707 linda->simulate_cancel = CancelRequest::None; 631 linda->simulate_cancel = CancelRequest::None;
708 } 632 } else if (strcmp(who, "read") == 0) { // tell blocked readers to wake up
709 else if (strcmp(who, "read") == 0) // tell blocked readers to wake up
710 {
711 linda->m_write_happened.notify_all(); 633 linda->m_write_happened.notify_all();
712 } 634 } else if (strcmp(who, "write") == 0) { // tell blocked writers to wake up
713 else if (strcmp(who, "write") == 0) // tell blocked writers to wake up
714 {
715 linda->m_read_happened.notify_all(); 635 linda->m_read_happened.notify_all();
716 } 636 } else {
717 else
718 {
719 raise_luaL_error(L_, "unknown wake hint '%s'", who); 637 raise_luaL_error(L_, "unknown wake hint '%s'", who);
720 } 638 }
721 return 0; 639 return 0;
@@ -724,15 +642,15 @@ LUAG_FUNC(linda_cancel)
724// ################################################################################################# 642// #################################################################################################
725 643
726/* 644/*
727* lightuserdata= linda_deep( linda_ud ) 645 * lightuserdata= linda_deep( linda_ud )
728* 646 *
729* Return the 'deep' userdata pointer, identifying the Linda. 647 * Return the 'deep' userdata pointer, identifying the Linda.
730* 648 *
731* This is needed for using Lindas as key indices (timer system needs it); 649 * This is needed for using Lindas as key indices (timer system needs it);
732* separately created proxies of the same underlying deep object will have 650 * separately created proxies of the same underlying deep object will have
733* different userdata and won't be known to be essentially the same deep one 651 * different userdata and won't be known to be essentially the same deep one
734* without this. 652 * without this.
735*/ 653 */
736LUAG_FUNC(linda_deep) 654LUAG_FUNC(linda_deep)
737{ 655{
738 Linda* const linda{ ToLinda<false>(L_, 1) }; 656 Linda* const linda{ ToLinda<false>(L_, 1) };
@@ -743,19 +661,18 @@ LUAG_FUNC(linda_deep)
743// ################################################################################################# 661// #################################################################################################
744 662
745/* 663/*
746* string = linda:__tostring( linda_ud) 664 * string = linda:__tostring( linda_ud)
747* 665 *
748* Return the stringification of a linda 666 * Return the stringification of a linda
749* 667 *
750* Useful for concatenation or debugging purposes 668 * Useful for concatenation or debugging purposes
751*/ 669 */
752 670
753template <bool OPT> 671template <bool OPT>
754[[nodiscard]] static int LindaToString(lua_State* L_, int idx_) 672[[nodiscard]] static int LindaToString(lua_State* L_, int idx_)
755{ 673{
756 Linda* const linda{ ToLinda<OPT>(L_, idx_) }; 674 Linda* const linda{ ToLinda<OPT>(L_, idx_) };
757 if (linda != nullptr) 675 if (linda != nullptr) {
758 {
759 char text[128]; 676 char text[128];
760 int len; 677 int len;
761 if (linda->getName()) 678 if (linda->getName())
@@ -768,6 +685,8 @@ template <bool OPT>
768 return 0; 685 return 0;
769} 686}
770 687
688// #################################################################################################
689
771LUAG_FUNC(linda_tostring) 690LUAG_FUNC(linda_tostring)
772{ 691{
773 return LindaToString<false>(L_, 1); 692 return LindaToString<false>(L_, 1);
@@ -776,28 +695,25 @@ LUAG_FUNC(linda_tostring)
776// ################################################################################################# 695// #################################################################################################
777 696
778/* 697/*
779* string = linda:__concat( a, b) 698 * string = linda:__concat( a, b)
780* 699 *
781* Return the concatenation of a pair of items, one of them being a linda 700 * Return the concatenation of a pair of items, one of them being a linda
782* 701 *
783* Useful for concatenation or debugging purposes 702 * Useful for concatenation or debugging purposes
784*/ 703 */
785LUAG_FUNC(linda_concat) 704LUAG_FUNC(linda_concat)
786{ // linda1? linda2? 705{ // L_: linda1? linda2?
787 bool atLeastOneLinda{ false }; 706 bool atLeastOneLinda{ false };
788 // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. 707 // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both.
789 if (LindaToString<true>(L_, 1)) 708 if (LindaToString<true>(L_, 1)) {
790 {
791 atLeastOneLinda = true; 709 atLeastOneLinda = true;
792 lua_replace(L_, 1); 710 lua_replace(L_, 1);
793 } 711 }
794 if (LindaToString<true>(L_, 2)) 712 if (LindaToString<true>(L_, 2)) {
795 {
796 atLeastOneLinda = true; 713 atLeastOneLinda = true;
797 lua_replace(L_, 2); 714 lua_replace(L_, 2);
798 } 715 }
799 if (!atLeastOneLinda) // should not be possible 716 if (!atLeastOneLinda) { // should not be possible
800 {
801 raise_luaL_error(L_, "internal error: linda_concat called on non-Linda"); 717 raise_luaL_error(L_, "internal error: linda_concat called on non-Linda");
802 } 718 }
803 lua_concat(L_, 2); 719 lua_concat(L_, 2);
@@ -812,8 +728,7 @@ LUAG_FUNC(linda_concat)
812 */ 728 */
813LUAG_FUNC(linda_dump) 729LUAG_FUNC(linda_dump)
814{ 730{
815 auto dump = [](lua_State* L_) 731 auto dump = [](lua_State* L_) {
816 {
817 Linda* const linda{ ToLinda<false>(L_, 1) }; 732 Linda* const linda{ ToLinda<false>(L_, 1) };
818 return keeper_push_linda_storage(*linda, DestState{ L_ }); 733 return keeper_push_linda_storage(*linda, DestState{ L_ });
819 }; 734 };
@@ -830,8 +745,7 @@ LUAG_FUNC(linda_towatch)
830{ 745{
831 Linda* const linda{ ToLinda<false>(L_, 1) }; 746 Linda* const linda{ ToLinda<false>(L_, 1) };
832 int pushed{ keeper_push_linda_storage(*linda, DestState{ L_ }) }; 747 int pushed{ keeper_push_linda_storage(*linda, DestState{ L_ }) };
833 if (pushed == 0) 748 if (pushed == 0) {
834 {
835 // if the linda is empty, don't return nil 749 // if the linda is empty, don't return nil
836 pushed = LindaToString<false>(L_, 1); 750 pushed = LindaToString<false>(L_, 1);
837 } 751 }
@@ -872,13 +786,10 @@ LUAG_FUNC(linda)
872{ 786{
873 int const top{ lua_gettop(L_) }; 787 int const top{ lua_gettop(L_) };
874 luaL_argcheck(L_, top <= 2, top, "too many arguments"); 788 luaL_argcheck(L_, top <= 2, top, "too many arguments");
875 if (top == 1) 789 if (top == 1) {
876 {
877 LuaType const t{ lua_type_as_enum(L_, 1) }; 790 LuaType const t{ lua_type_as_enum(L_, 1) };
878 luaL_argcheck(L_, t == LuaType::STRING || t == LuaType::NUMBER, 1, "wrong parameter (should be a string or a number)"); 791 luaL_argcheck(L_, t == LuaType::STRING || t == LuaType::NUMBER, 1, "wrong parameter (should be a string or a number)");
879 } 792 } else if (top == 2) {
880 else if (top == 2)
881 {
882 luaL_checktype(L_, 1, LUA_TSTRING); 793 luaL_checktype(L_, 1, LUA_TSTRING);
883 luaL_checktype(L_, 2, LUA_TNUMBER); 794 luaL_checktype(L_, 2, LUA_TNUMBER);
884 } 795 }