diff options
-rw-r--r-- | src/cancel.h | 2 | ||||
-rw-r--r-- | src/keeper.cpp | 28 | ||||
-rw-r--r-- | src/keeper.h | 6 | ||||
-rw-r--r-- | src/linda.cpp | 147 | ||||
-rw-r--r-- | src/macros_and_utils.h | 44 | ||||
-rw-r--r-- | src/uniquekey.h | 5 | ||||
-rw-r--r-- | tests/errhangtest.lua | 74 |
7 files changed, 200 insertions, 106 deletions
diff --git a/src/cancel.h b/src/cancel.h index 060edb3..10a9804 100644 --- a/src/cancel.h +++ b/src/cancel.h | |||
@@ -47,7 +47,7 @@ enum class CancelOp | |||
47 | }; | 47 | }; |
48 | 48 | ||
49 | // crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/ | 49 | // crc64/we of string "CANCEL_ERROR" generated at http://www.nitrxgen.net/hashgen/ |
50 | static constexpr UniqueKey CANCEL_ERROR{ 0xe97d41626cc97577ull }; // 'raise_cancel_error' sentinel | 50 | static constexpr UniqueKey CANCEL_ERROR{ 0xe97d41626cc97577ull, "lanes.cancel_error" }; // 'raise_cancel_error' sentinel |
51 | 51 | ||
52 | [[nodiscard]] CancelOp which_cancel_op(char const* op_string_); | 52 | [[nodiscard]] CancelOp which_cancel_op(char const* op_string_); |
53 | [[nodiscard]] CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hook_count_, lua_Duration secs_, bool wake_lindas_); | 53 | [[nodiscard]] CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hook_count_, lua_Duration secs_, bool wake_lindas_); |
diff --git a/src/keeper.cpp b/src/keeper.cpp index f56c50c..61321e1 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp | |||
@@ -418,7 +418,7 @@ int keepercall_limit(lua_State* L) | |||
418 | // ################################################################################################## | 418 | // ################################################################################################## |
419 | 419 | ||
420 | // in: linda_ud key [[val] ...] | 420 | // in: linda_ud key [[val] ...] |
421 | //out: true or nil | 421 | //out: true if the linda was full but it's no longer the case, else nothing |
422 | int keepercall_set(lua_State* L) | 422 | int keepercall_set(lua_State* L) |
423 | { | 423 | { |
424 | bool should_wake_writers{ false }; | 424 | bool should_wake_writers{ false }; |
@@ -826,35 +826,35 @@ void keeper_toggle_nil_sentinels(lua_State* L, int val_i_, LookupMode const mode | |||
826 | * 'linda': deep Linda pointer (used only as a unique table key, first parameter) | 826 | * 'linda': deep Linda pointer (used only as a unique table key, first parameter) |
827 | * 'starting_index': first of the rest of parameters (none if 0) | 827 | * 'starting_index': first of the rest of parameters (none if 0) |
828 | * | 828 | * |
829 | * Returns: number of return values (pushed to 'L') or -1 in case of error | 829 | * Returns: number of return values (pushed to 'L'), unset in case of error |
830 | */ | 830 | */ |
831 | int keeper_call(Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, void* linda, int starting_index) | 831 | KeeperCallResult keeper_call(Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, void* linda, int starting_index) |
832 | { | 832 | { |
833 | KeeperCallResult result; | ||
833 | int const args{ starting_index ? (lua_gettop(L) - starting_index + 1) : 0 }; | 834 | int const args{ starting_index ? (lua_gettop(L) - starting_index + 1) : 0 }; |
834 | int const Ktos{ lua_gettop(K) }; | 835 | int const top_K{ lua_gettop(K) }; |
835 | int retvals = -1; | ||
836 | 836 | ||
837 | STACK_GROW(K, 2); | 837 | STACK_GROW(K, 2); |
838 | 838 | ||
839 | PUSH_KEEPER_FUNC(K, func_); | 839 | PUSH_KEEPER_FUNC(K, func_); // func_ |
840 | 840 | ||
841 | lua_pushlightuserdata(K, linda); | 841 | lua_pushlightuserdata(K, linda); // func_ linda |
842 | 842 | ||
843 | if ((args == 0) || luaG_inter_copy(U, Source{ L }, Dest{ K }, args, LookupMode::ToKeeper) == InterCopyResult::Success) // L->K | 843 | if ((args == 0) || luaG_inter_copy(U, Source{ L }, Dest{ K }, args, LookupMode::ToKeeper) == InterCopyResult::Success) // func_ linda args... |
844 | { | 844 | { |
845 | lua_call(K, 1 + args, LUA_MULTRET); | 845 | lua_call(K, 1 + args, LUA_MULTRET); // result... |
846 | retvals = lua_gettop(K) - Ktos; | 846 | int const retvals{ lua_gettop(K) - top_K }; |
847 | // note that this can raise a luaL_error while the keeper state (and its mutex) is acquired | 847 | // note that this can raise a luaL_error while the keeper state (and its mutex) is acquired |
848 | // this may interrupt a lane, causing the destruction of the underlying OS thread | 848 | // this may interrupt a lane, causing the destruction of the underlying OS thread |
849 | // after this, another lane making use of this keeper can get an error code from the mutex-locking function | 849 | // after this, another lane making use of this keeper can get an error code from the mutex-locking function |
850 | // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) | 850 | // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) |
851 | if ((retvals > 0) && luaG_inter_move(U, Source{ K }, Dest{ L }, retvals, LookupMode::FromKeeper) != InterCopyResult::Success) // K->L | 851 | if ((retvals == 0) || (luaG_inter_move(U, Source{ K }, Dest{ L }, retvals, LookupMode::FromKeeper) == InterCopyResult::Success)) // K->L |
852 | { | 852 | { |
853 | retvals = -1; | 853 | result.emplace(retvals); |
854 | } | 854 | } |
855 | } | 855 | } |
856 | // whatever happens, restore the stack to where it was at the origin | 856 | // whatever happens, restore the stack to where it was at the origin |
857 | lua_settop(K, Ktos); | 857 | lua_settop(K, top_K); |
858 | 858 | ||
859 | // don't do this for this particular function, as it is only called during Linda destruction, and we don't want to raise an error, ever | 859 | // don't do this for this particular function, as it is only called during Linda destruction, and we don't want to raise an error, ever |
860 | if (func_ != KEEPER_API(clear)) [[unlikely]] | 860 | if (func_ != KEEPER_API(clear)) [[unlikely]] |
@@ -880,5 +880,5 @@ int keeper_call(Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, voi | |||
880 | } | 880 | } |
881 | } | 881 | } |
882 | 882 | ||
883 | return retvals; | 883 | return result; |
884 | } | 884 | } |
diff --git a/src/keeper.h b/src/keeper.h index 627c7ea..7ec8b15 100644 --- a/src/keeper.h +++ b/src/keeper.h | |||
@@ -11,6 +11,7 @@ extern "C" { | |||
11 | #include "threading.h" | 11 | #include "threading.h" |
12 | #include "uniquekey.h" | 12 | #include "uniquekey.h" |
13 | 13 | ||
14 | #include <optional> | ||
14 | #include <mutex> | 15 | #include <mutex> |
15 | 16 | ||
16 | // forwards | 17 | // forwards |
@@ -33,7 +34,7 @@ struct Keepers | |||
33 | 34 | ||
34 | static constexpr uintptr_t KEEPER_MAGIC_SHIFT{ 3 }; | 35 | static constexpr uintptr_t KEEPER_MAGIC_SHIFT{ 3 }; |
35 | // crc64/we of string "NIL_SENTINEL" generated at http://www.nitrxgen.net/hashgen/ | 36 | // crc64/we of string "NIL_SENTINEL" generated at http://www.nitrxgen.net/hashgen/ |
36 | static constexpr UniqueKey NIL_SENTINEL{ 0x7eaafa003a1d11a1ull }; | 37 | static constexpr UniqueKey NIL_SENTINEL{ 0x7eaafa003a1d11a1ull, "internal nil sentinel" }; |
37 | 38 | ||
38 | void init_keepers(Universe* U, lua_State* L); | 39 | void init_keepers(Universe* U, lua_State* L); |
39 | void close_keepers(Universe* U); | 40 | void close_keepers(Universe* U); |
@@ -57,4 +58,5 @@ using keeper_api_t = lua_CFunction; | |||
57 | [[nodiscard]] int keepercall_set(lua_State* L); | 58 | [[nodiscard]] int keepercall_set(lua_State* L); |
58 | [[nodiscard]] int keepercall_count(lua_State* L); | 59 | [[nodiscard]] int keepercall_count(lua_State* L); |
59 | 60 | ||
60 | [[nodiscard]] int keeper_call(Universe* U, lua_State* K, keeper_api_t _func, lua_State* L, void* linda, int starting_index); | 61 | using KeeperCallResult = Unique<std::optional<int>>; |
62 | [[nodiscard]] KeeperCallResult keeper_call(Universe* U, lua_State* K, keeper_api_t _func, lua_State* L, void* linda, int starting_index); | ||
diff --git a/src/linda.cpp b/src/linda.cpp index e749f52..dbd6b21 100644 --- a/src/linda.cpp +++ b/src/linda.cpp | |||
@@ -39,8 +39,12 @@ THE SOFTWARE. | |||
39 | #include "universe.h" | 39 | #include "universe.h" |
40 | 40 | ||
41 | #include <array> | 41 | #include <array> |
42 | #include <functional> | ||
42 | #include <variant> | 43 | #include <variant> |
43 | 44 | ||
45 | // xxh64 of string "CANCEL_ERROR" generated at https://www.pelock.com/products/hash-calculator | ||
46 | static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull, "linda.batched" }; | ||
47 | |||
44 | /* | 48 | /* |
45 | * Actual data is kept within a keeper state, which is hashed by the 'Linda' | 49 | * Actual data is kept within a keeper state, which is hashed by the 'Linda' |
46 | * pointer (which is same to all userdatas pointing to it). | 50 | * pointer (which is same to all userdatas pointing to it). |
@@ -140,14 +144,14 @@ class Linda : public DeepPrelude // Deep userdata MUST start with this header | |||
140 | [[nodiscard]] static void* linda_id(lua_State*, DeepOp); | 144 | [[nodiscard]] static void* linda_id(lua_State*, DeepOp); |
141 | 145 | ||
142 | template<bool OPT> | 146 | template<bool OPT> |
143 | [[nodiscard]] static inline Linda* lua_toLinda(lua_State* L, int idx_) | 147 | [[nodiscard]] static inline Linda* ToLinda(lua_State* L, int idx_) |
144 | { | 148 | { |
145 | Linda* const linda{ static_cast<Linda*>(luaG_todeep(L, linda_id, idx_)) }; | 149 | Linda* const linda{ static_cast<Linda*>(luaG_todeep(L, linda_id, idx_)) }; |
146 | if (!OPT) | 150 | if constexpr (!OPT) |
147 | { | 151 | { |
148 | luaL_argcheck(L, linda != nullptr, idx_, "expecting a linda object"); | 152 | luaL_argcheck(L, linda != nullptr, idx_, "expecting a linda object"); |
153 | ASSERT_L(linda->U == universe_get(L)); | ||
149 | } | 154 | } |
150 | ASSERT_L(linda->U == universe_get(L)); | ||
151 | return linda; | 155 | return linda; |
152 | } | 156 | } |
153 | 157 | ||
@@ -157,10 +161,28 @@ static void check_key_types(lua_State* L, int start_, int end_) | |||
157 | { | 161 | { |
158 | for (int i{ start_ }; i <= end_; ++i) | 162 | for (int i{ start_ }; i <= end_; ++i) |
159 | { | 163 | { |
160 | int const t{ lua_type(L, i) }; | 164 | LuaType const t{ lua_type_as_enum(L, i) }; |
161 | if (t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) | 165 | switch (t) |
162 | { | 166 | { |
167 | case LuaType::BOOLEAN: | ||
168 | case LuaType::NUMBER: | ||
169 | case LuaType::STRING: | ||
163 | continue; | 170 | continue; |
171 | |||
172 | case LuaType::LIGHTUSERDATA: | ||
173 | { | ||
174 | // NIL_SENTINEL isn't publicly exposed, but it doesn't hurt to check | ||
175 | static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> to_check{ BATCH_SENTINEL, CANCEL_ERROR, NIL_SENTINEL }; | ||
176 | for (UniqueKey const& key : to_check) | ||
177 | { | ||
178 | if (key.equals(L, i)) | ||
179 | { | ||
180 | luaL_error(L, "argument #%d: can't use %s as a key", i, key.m_debugName); // doesn't return | ||
181 | break; | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | break; | ||
164 | } | 186 | } |
165 | luaL_error(L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i); // doesn't return | 187 | luaL_error(L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i); // doesn't return |
166 | } | 188 | } |
@@ -170,7 +192,7 @@ static void check_key_types(lua_State* L, int start_, int end_) | |||
170 | 192 | ||
171 | LUAG_FUNC(linda_protected_call) | 193 | LUAG_FUNC(linda_protected_call) |
172 | { | 194 | { |
173 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 195 | Linda* const linda{ ToLinda<false>(L, 1) }; |
174 | 196 | ||
175 | // acquire the keeper | 197 | // acquire the keeper |
176 | Keeper* const K{ keeper_acquire(linda->U->keepers, linda->hashSeed()) }; | 198 | Keeper* const K{ keeper_acquire(linda->U->keepers, linda->hashSeed()) }; |
@@ -209,7 +231,7 @@ LUAG_FUNC(linda_protected_call) | |||
209 | */ | 231 | */ |
210 | LUAG_FUNC(linda_send) | 232 | LUAG_FUNC(linda_send) |
211 | { | 233 | { |
212 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 234 | Linda* const linda{ ToLinda<false>(L, 1) }; |
213 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | 235 | std::chrono::time_point<std::chrono::steady_clock> until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; |
214 | int key_i{ 2 }; // index of first key, if timeout not there | 236 | int key_i{ 2 }; // index of first key, if timeout not there |
215 | 237 | ||
@@ -257,7 +279,7 @@ LUAG_FUNC(linda_send) | |||
257 | keeper_toggle_nil_sentinels(L, key_i + 1, LookupMode::ToKeeper); | 279 | keeper_toggle_nil_sentinels(L, key_i + 1, LookupMode::ToKeeper); |
258 | bool ret{ false }; | 280 | bool ret{ false }; |
259 | CancelRequest cancel{ CancelRequest::None }; | 281 | CancelRequest cancel{ CancelRequest::None }; |
260 | int pushed{ 0 }; | 282 | KeeperCallResult pushed; |
261 | { | 283 | { |
262 | Lane* const lane{ LANE_POINTER_REGKEY.readLightUserDataValue<Lane>(L) }; | 284 | Lane* const lane{ LANE_POINTER_REGKEY.readLightUserDataValue<Lane>(L) }; |
263 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | 285 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; |
@@ -276,17 +298,17 @@ LUAG_FUNC(linda_send) | |||
276 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | 298 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything |
277 | if (!try_again || cancel != CancelRequest::None) | 299 | if (!try_again || cancel != CancelRequest::None) |
278 | { | 300 | { |
279 | pushed = 0; | 301 | pushed.emplace(0); |
280 | break; | 302 | break; |
281 | } | 303 | } |
282 | 304 | ||
283 | STACK_CHECK(KL, 0); | 305 | STACK_CHECK(KL, 0); |
284 | pushed = keeper_call(linda->U, KL, KEEPER_API(send), L, linda, key_i); | 306 | pushed = keeper_call(linda->U, KL, KEEPER_API(send), L, linda, key_i); |
285 | if (pushed < 0) | 307 | if (!pushed.has_value()) |
286 | { | 308 | { |
287 | break; | 309 | break; |
288 | } | 310 | } |
289 | ASSERT_L(pushed == 1); | 311 | ASSERT_L(pushed.value() == 1); |
290 | 312 | ||
291 | ret = lua_toboolean(L, -1) ? true : false; | 313 | ret = lua_toboolean(L, -1) ? true : false; |
292 | lua_pop(L, 1); | 314 | lua_pop(L, 1); |
@@ -331,9 +353,9 @@ LUAG_FUNC(linda_send) | |||
331 | STACK_CHECK(KL, 0); | 353 | STACK_CHECK(KL, 0); |
332 | } | 354 | } |
333 | 355 | ||
334 | if (pushed < 0) | 356 | if (!pushed.has_value()) |
335 | { | 357 | { |
336 | return luaL_error(L, "tried to copy unsupported types"); | 358 | luaL_error(L, "tried to copy unsupported types"); // doesn't return |
337 | } | 359 | } |
338 | 360 | ||
339 | switch (cancel) | 361 | switch (cancel) |
@@ -366,11 +388,9 @@ LUAG_FUNC(linda_send) | |||
366 | * returns the actual consumed values, or nil if there weren't enough values to consume | 388 | * returns the actual consumed values, or nil if there weren't enough values to consume |
367 | * | 389 | * |
368 | */ | 390 | */ |
369 | // xxh64 of string "CANCEL_ERROR" generated at https://www.pelock.com/products/hash-calculator | ||
370 | static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull }; | ||
371 | LUAG_FUNC(linda_receive) | 391 | LUAG_FUNC(linda_receive) |
372 | { | 392 | { |
373 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 393 | 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() }; | 394 | 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 | 395 | int key_i{ 2 }; // index of first key, if timeout not there |
376 | 396 | ||
@@ -430,7 +450,7 @@ LUAG_FUNC(linda_receive) | |||
430 | return 0; | 450 | return 0; |
431 | 451 | ||
432 | CancelRequest cancel{ CancelRequest::None }; | 452 | CancelRequest cancel{ CancelRequest::None }; |
433 | int pushed{ 0 }; | 453 | KeeperCallResult pushed; |
434 | STACK_CHECK_START_REL(KL, 0); | 454 | STACK_CHECK_START_REL(KL, 0); |
435 | for (bool try_again{ true };;) | 455 | for (bool try_again{ true };;) |
436 | { | 456 | { |
@@ -442,21 +462,21 @@ LUAG_FUNC(linda_receive) | |||
442 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | 462 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything |
443 | if (!try_again || cancel != CancelRequest::None) | 463 | if (!try_again || cancel != CancelRequest::None) |
444 | { | 464 | { |
445 | pushed = 0; | 465 | pushed.emplace(0); |
446 | break; | 466 | break; |
447 | } | 467 | } |
448 | 468 | ||
449 | // all arguments of receive() but the first are passed to the keeper's receive function | 469 | // 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); | 470 | pushed = keeper_call(linda->U, KL, selected_keeper_receive, L, linda, key_i); |
451 | if (pushed < 0) | 471 | if (!pushed.has_value()) |
452 | { | 472 | { |
453 | break; | 473 | break; |
454 | } | 474 | } |
455 | if (pushed > 0) | 475 | if (pushed.value() > 0) |
456 | { | 476 | { |
457 | ASSERT_L(pushed >= expected_pushed_min && pushed <= expected_pushed_max); | 477 | ASSERT_L(pushed.value() >= expected_pushed_min && pushed.value() <= expected_pushed_max); |
458 | // replace sentinels with real nils | 478 | // replace sentinels with real nils |
459 | keeper_toggle_nil_sentinels(L, lua_gettop(L) - pushed, LookupMode::FromKeeper); | 479 | keeper_toggle_nil_sentinels(L, lua_gettop(L) - pushed.value(), LookupMode::FromKeeper); |
460 | // To be done from within the 'K' locking area | 480 | // To be done from within the 'K' locking area |
461 | // | 481 | // |
462 | linda->m_read_happened.notify_all(); | 482 | linda->m_read_happened.notify_all(); |
@@ -494,7 +514,7 @@ LUAG_FUNC(linda_receive) | |||
494 | } | 514 | } |
495 | STACK_CHECK(KL, 0); | 515 | STACK_CHECK(KL, 0); |
496 | 516 | ||
497 | if (pushed < 0) | 517 | if (!pushed.has_value()) |
498 | { | 518 | { |
499 | return luaL_error(L, "tried to copy unsupported types"); | 519 | return luaL_error(L, "tried to copy unsupported types"); |
500 | } | 520 | } |
@@ -511,7 +531,7 @@ LUAG_FUNC(linda_receive) | |||
511 | raise_cancel_error(L); // raises an error and doesn't return | 531 | raise_cancel_error(L); // raises an error and doesn't return |
512 | 532 | ||
513 | default: | 533 | default: |
514 | return pushed; | 534 | return pushed.value(); |
515 | } | 535 | } |
516 | } | 536 | } |
517 | 537 | ||
@@ -527,13 +547,13 @@ LUAG_FUNC(linda_receive) | |||
527 | */ | 547 | */ |
528 | LUAG_FUNC(linda_set) | 548 | LUAG_FUNC(linda_set) |
529 | { | 549 | { |
530 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 550 | Linda* const linda{ ToLinda<false>(L, 1) }; |
531 | bool const has_value{ lua_gettop(L) > 2 }; | 551 | bool const has_value{ lua_gettop(L) > 2 }; |
532 | // 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) |
533 | check_key_types(L, 2, 2); | 553 | check_key_types(L, 2, 2); |
534 | 554 | ||
535 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | 555 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; |
536 | int pushed{ 0 }; | 556 | KeeperCallResult pushed; |
537 | if (linda->simulate_cancel == CancelRequest::None) | 557 | if (linda->simulate_cancel == CancelRequest::None) |
538 | { | 558 | { |
539 | if (has_value) | 559 | if (has_value) |
@@ -542,16 +562,16 @@ LUAG_FUNC(linda_set) | |||
542 | keeper_toggle_nil_sentinels(L, 3, LookupMode::ToKeeper); | 562 | keeper_toggle_nil_sentinels(L, 3, LookupMode::ToKeeper); |
543 | } | 563 | } |
544 | pushed = keeper_call(linda->U, K->L, KEEPER_API(set), L, linda, 2); | 564 | pushed = keeper_call(linda->U, K->L, KEEPER_API(set), L, linda, 2); |
545 | if (pushed >= 0) // no error? | 565 | if (pushed.has_value()) // no error? |
546 | { | 566 | { |
547 | ASSERT_L(pushed == 0 || pushed == 1); | 567 | ASSERT_L(pushed.value() == 0 || pushed.value() == 1); |
548 | 568 | ||
549 | if (has_value) | 569 | if (has_value) |
550 | { | 570 | { |
551 | // we put some data in the slot, tell readers that they should wake | 571 | // we put some data in the slot, tell readers that they should wake |
552 | linda->m_write_happened.notify_all(); // To be done from within the 'K' locking area | 572 | linda->m_write_happened.notify_all(); // To be done from within the 'K' locking area |
553 | } | 573 | } |
554 | if (pushed == 1) | 574 | if (pushed.value() == 1) |
555 | { | 575 | { |
556 | // the key was full, but it is no longer the case, tell writers they should wake | 576 | // the key was full, but it is no longer the case, tell writers they should wake |
557 | ASSERT_L(lua_type(L, -1) == LUA_TBOOLEAN && lua_toboolean(L, -1) == 1); | 577 | ASSERT_L(lua_type(L, -1) == LUA_TBOOLEAN && lua_toboolean(L, -1) == 1); |
@@ -563,11 +583,11 @@ LUAG_FUNC(linda_set) | |||
563 | { | 583 | { |
564 | // do nothing and return lanes.cancel_error | 584 | // do nothing and return lanes.cancel_error |
565 | CANCEL_ERROR.pushKey(L); | 585 | CANCEL_ERROR.pushKey(L); |
566 | pushed = 1; | 586 | pushed.emplace(1); |
567 | } | 587 | } |
568 | 588 | ||
569 | // must trigger any error after keeper state has been released | 589 | // must trigger any error after keeper state has been released |
570 | return (pushed < 0) ? luaL_error(L, "tried to copy unsupported types") : pushed; | 590 | return OptionalValue(pushed, L, "tried to copy unsupported types"); |
571 | } | 591 | } |
572 | 592 | ||
573 | // ################################################################################################# | 593 | // ################################################################################################# |
@@ -579,17 +599,13 @@ LUAG_FUNC(linda_set) | |||
579 | */ | 599 | */ |
580 | LUAG_FUNC(linda_count) | 600 | LUAG_FUNC(linda_count) |
581 | { | 601 | { |
582 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 602 | Linda* const linda{ ToLinda<false>(L, 1) }; |
583 | // make sure the keys are of a valid type | 603 | // make sure the keys are of a valid type |
584 | check_key_types(L, 2, lua_gettop(L)); | 604 | check_key_types(L, 2, lua_gettop(L)); |
585 | 605 | ||
586 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | 606 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; |
587 | int const pushed{ keeper_call(linda->U, K->L, KEEPER_API(count), L, linda, 2) }; | 607 | KeeperCallResult const pushed{ keeper_call(linda->U, K->L, KEEPER_API(count), L, linda, 2) }; |
588 | if (pushed < 0) | 608 | return OptionalValue(pushed, L, "tried to count an invalid key"); |
589 | { | ||
590 | return luaL_error(L, "tried to count an invalid key"); | ||
591 | } | ||
592 | return pushed; | ||
593 | } | 609 | } |
594 | 610 | ||
595 | // ################################################################################################# | 611 | // ################################################################################################# |
@@ -601,36 +617,31 @@ LUAG_FUNC(linda_count) | |||
601 | */ | 617 | */ |
602 | LUAG_FUNC(linda_get) | 618 | LUAG_FUNC(linda_get) |
603 | { | 619 | { |
604 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 620 | Linda* const linda{ ToLinda<false>(L, 1) }; |
605 | lua_Integer const count{ luaL_optinteger(L, 3, 1) }; | 621 | lua_Integer const count{ luaL_optinteger(L, 3, 1) }; |
606 | luaL_argcheck(L, count >= 1, 3, "count should be >= 1"); | 622 | luaL_argcheck(L, count >= 1, 3, "count should be >= 1"); |
607 | luaL_argcheck(L, lua_gettop(L) <= 3, 4, "too many arguments"); | 623 | luaL_argcheck(L, lua_gettop(L) <= 3, 4, "too many arguments"); |
608 | // make sure the key is of a valid type (throws an error if not the case) | 624 | // make sure the key is of a valid type (throws an error if not the case) |
609 | check_key_types(L, 2, 2); | 625 | check_key_types(L, 2, 2); |
610 | 626 | ||
611 | int pushed{ 0 }; | 627 | KeeperCallResult pushed; |
612 | if (linda->simulate_cancel == CancelRequest::None) | 628 | if (linda->simulate_cancel == CancelRequest::None) |
613 | { | 629 | { |
614 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | 630 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; |
615 | pushed = keeper_call(linda->U, K->L, KEEPER_API(get), L, linda, 2); | 631 | pushed = keeper_call(linda->U, K->L, KEEPER_API(get), L, linda, 2); |
616 | if (pushed > 0) | 632 | if (pushed.value_or(0) > 0) |
617 | { | 633 | { |
618 | keeper_toggle_nil_sentinels(L, lua_gettop(L) - pushed, LookupMode::FromKeeper); | 634 | keeper_toggle_nil_sentinels(L, lua_gettop(L) - pushed.value(), LookupMode::FromKeeper); |
619 | } | 635 | } |
620 | } | 636 | } |
621 | else // linda is cancelled | 637 | else // linda is cancelled |
622 | { | 638 | { |
623 | // do nothing and return lanes.cancel_error | 639 | // do nothing and return lanes.cancel_error |
624 | CANCEL_ERROR.pushKey(L); | 640 | CANCEL_ERROR.pushKey(L); |
625 | pushed = 1; | 641 | pushed.emplace(1); |
626 | } | 642 | } |
627 | // an error can be raised if we attempt to read an unregistered function | 643 | // an error can be raised if we attempt to read an unregistered function |
628 | if (pushed < 0) | 644 | return OptionalValue(pushed, L, "tried to copy unsupported types"); |
629 | { | ||
630 | return luaL_error(L, "tried to copy unsupported types"); | ||
631 | } | ||
632 | |||
633 | return pushed; | ||
634 | } | 645 | } |
635 | 646 | ||
636 | // ################################################################################################# | 647 | // ################################################################################################# |
@@ -641,9 +652,9 @@ LUAG_FUNC(linda_get) | |||
641 | * Set limit to 1 Linda keys. | 652 | * Set limit to 1 Linda keys. |
642 | * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so | 653 | * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so |
643 | */ | 654 | */ |
644 | LUAG_FUNC( linda_limit) | 655 | LUAG_FUNC(linda_limit) |
645 | { | 656 | { |
646 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 657 | Linda* const linda{ ToLinda<false>(L, 1) }; |
647 | // make sure we got 3 arguments: the linda, a key and a limit | 658 | // make sure we got 3 arguments: the linda, a key and a limit |
648 | luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); | 659 | luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); |
649 | // make sure we got a numeric limit | 660 | // make sure we got a numeric limit |
@@ -651,13 +662,13 @@ LUAG_FUNC( linda_limit) | |||
651 | // make sure the key is of a valid type | 662 | // make sure the key is of a valid type |
652 | check_key_types( L, 2, 2); | 663 | check_key_types( L, 2, 2); |
653 | 664 | ||
654 | int pushed{ 0 }; | 665 | KeeperCallResult pushed; |
655 | if (linda->simulate_cancel == CancelRequest::None) | 666 | if (linda->simulate_cancel == CancelRequest::None) |
656 | { | 667 | { |
657 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | 668 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; |
658 | pushed = keeper_call(linda->U, K->L, KEEPER_API(limit), L, linda, 2); | 669 | pushed = keeper_call(linda->U, K->L, KEEPER_API(limit), L, linda, 2); |
659 | ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads | 670 | ASSERT_L( pushed.has_value() && (pushed.value() == 0 || pushed.value() == 1)); // no error, optional boolean value saying if we should wake blocked writer threads |
660 | if( pushed == 1) | 671 | if (pushed.value() == 1) |
661 | { | 672 | { |
662 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | 673 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); |
663 | linda->m_read_happened.notify_all(); // To be done from within the 'K' locking area | 674 | linda->m_read_happened.notify_all(); // To be done from within the 'K' locking area |
@@ -667,10 +678,10 @@ LUAG_FUNC( linda_limit) | |||
667 | { | 678 | { |
668 | // do nothing and return lanes.cancel_error | 679 | // do nothing and return lanes.cancel_error |
669 | CANCEL_ERROR.pushKey(L); | 680 | CANCEL_ERROR.pushKey(L); |
670 | pushed = 1; | 681 | pushed.emplace(1); |
671 | } | 682 | } |
672 | // propagate pushed boolean if any | 683 | // propagate pushed boolean if any |
673 | return pushed; | 684 | return pushed.value(); |
674 | } | 685 | } |
675 | 686 | ||
676 | // ################################################################################################# | 687 | // ################################################################################################# |
@@ -682,7 +693,7 @@ LUAG_FUNC( linda_limit) | |||
682 | */ | 693 | */ |
683 | LUAG_FUNC(linda_cancel) | 694 | LUAG_FUNC(linda_cancel) |
684 | { | 695 | { |
685 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 696 | Linda* const linda{ ToLinda<false>(L, 1) }; |
686 | char const* who = luaL_optstring(L, 2, "both"); | 697 | char const* who = luaL_optstring(L, 2, "both"); |
687 | // make sure we got 3 arguments: the linda, a key and a limit | 698 | // make sure we got 3 arguments: the linda, a key and a limit |
688 | luaL_argcheck(L, lua_gettop(L) <= 2, 2, "wrong number of arguments"); | 699 | luaL_argcheck(L, lua_gettop(L) <= 2, 2, "wrong number of arguments"); |
@@ -726,7 +737,7 @@ LUAG_FUNC(linda_cancel) | |||
726 | */ | 737 | */ |
727 | LUAG_FUNC(linda_deep) | 738 | LUAG_FUNC(linda_deep) |
728 | { | 739 | { |
729 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 740 | Linda* const linda{ ToLinda<false>(L, 1) }; |
730 | lua_pushlightuserdata(L, linda); // just the address | 741 | lua_pushlightuserdata(L, linda); // just the address |
731 | return 1; | 742 | return 1; |
732 | } | 743 | } |
@@ -742,9 +753,9 @@ LUAG_FUNC(linda_deep) | |||
742 | */ | 753 | */ |
743 | 754 | ||
744 | template <bool OPT> | 755 | template <bool OPT> |
745 | [[nodiscard]] static int linda_tostring(lua_State* L, int idx_) | 756 | [[nodiscard]] static int LindaToString(lua_State* L, int idx_) |
746 | { | 757 | { |
747 | Linda* const linda{ lua_toLinda<OPT>(L, idx_) }; | 758 | Linda* const linda{ ToLinda<OPT>(L, idx_) }; |
748 | if (linda != nullptr) | 759 | if (linda != nullptr) |
749 | { | 760 | { |
750 | char text[128]; | 761 | char text[128]; |
@@ -761,7 +772,7 @@ template <bool OPT> | |||
761 | 772 | ||
762 | LUAG_FUNC(linda_tostring) | 773 | LUAG_FUNC(linda_tostring) |
763 | { | 774 | { |
764 | return linda_tostring<false>(L, 1); | 775 | return LindaToString<false>(L, 1); |
765 | } | 776 | } |
766 | 777 | ||
767 | // ################################################################################################# | 778 | // ################################################################################################# |
@@ -777,12 +788,12 @@ LUAG_FUNC(linda_concat) | |||
777 | { // linda1? linda2? | 788 | { // linda1? linda2? |
778 | bool atLeastOneLinda{ false }; | 789 | bool atLeastOneLinda{ false }; |
779 | // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. | 790 | // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. |
780 | if (linda_tostring<true>(L, 1)) | 791 | if (LindaToString<true>(L, 1)) |
781 | { | 792 | { |
782 | atLeastOneLinda = true; | 793 | atLeastOneLinda = true; |
783 | lua_replace(L, 1); | 794 | lua_replace(L, 1); |
784 | } | 795 | } |
785 | if (linda_tostring<true>(L, 2)) | 796 | if (LindaToString<true>(L, 2)) |
786 | { | 797 | { |
787 | atLeastOneLinda = true; | 798 | atLeastOneLinda = true; |
788 | lua_replace(L, 2); | 799 | lua_replace(L, 2); |
@@ -803,7 +814,7 @@ LUAG_FUNC(linda_concat) | |||
803 | */ | 814 | */ |
804 | LUAG_FUNC(linda_dump) | 815 | LUAG_FUNC(linda_dump) |
805 | { | 816 | { |
806 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 817 | Linda* const linda{ ToLinda<false>(L, 1) }; |
807 | return keeper_push_linda_storage(linda->U, Dest{ L }, linda, linda->hashSeed()); | 818 | return keeper_push_linda_storage(linda->U, Dest{ L }, linda, linda->hashSeed()); |
808 | } | 819 | } |
809 | 820 | ||
@@ -815,12 +826,12 @@ LUAG_FUNC(linda_dump) | |||
815 | */ | 826 | */ |
816 | LUAG_FUNC(linda_towatch) | 827 | LUAG_FUNC(linda_towatch) |
817 | { | 828 | { |
818 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 829 | Linda* const linda{ ToLinda<false>(L, 1) }; |
819 | int pushed{ keeper_push_linda_storage(linda->U, Dest{ L }, linda, linda->hashSeed()) }; | 830 | int pushed{ keeper_push_linda_storage(linda->U, Dest{ L }, linda, linda->hashSeed()) }; |
820 | if (pushed == 0) | 831 | if (pushed == 0) |
821 | { | 832 | { |
822 | // if the linda is empty, don't return nil | 833 | // if the linda is empty, don't return nil |
823 | pushed = linda_tostring<false>(L, 1); | 834 | pushed = LindaToString<false>(L, 1); |
824 | } | 835 | } |
825 | return pushed; | 836 | return pushed; |
826 | } | 837 | } |
@@ -907,7 +918,8 @@ LUAG_FUNC(linda_towatch) | |||
907 | // Clean associated structures in the keeper state. | 918 | // Clean associated structures in the keeper state. |
908 | Keeper* const K{ need_acquire_release ? keeper_acquire(linda->U->keepers, linda->hashSeed()) : myK }; | 919 | Keeper* const K{ need_acquire_release ? keeper_acquire(linda->U->keepers, linda->hashSeed()) : myK }; |
909 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... | 920 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... |
910 | std::ignore = keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0); | 921 | [[maybe_unused]] KeeperCallResult const result{ keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0) }; |
922 | ASSERT_L(result.has_value() && result.value() == 0); | ||
911 | if (need_acquire_release) | 923 | if (need_acquire_release) |
912 | { | 924 | { |
913 | keeper_release(K); | 925 | keeper_release(K); |
@@ -943,6 +955,7 @@ LUAG_FUNC(linda_towatch) | |||
943 | // protected calls, to ensure associated keeper is always released even in case of error | 955 | // protected calls, to ensure associated keeper is always released even in case of error |
944 | // all function are the protected call wrapper, where the actual operation is provided as upvalue | 956 | // all function are the protected call wrapper, where the actual operation is provided as upvalue |
945 | // note that this kind of thing can break function lookup as we use the function pointer here and there | 957 | // note that this kind of thing can break function lookup as we use the function pointer here and there |
958 | // TODO: change that and use different functions! | ||
946 | 959 | ||
947 | lua_pushcfunction(L, LG_linda_send); | 960 | lua_pushcfunction(L, LG_linda_send); |
948 | lua_pushcclosure(L, LG_linda_protected_call, 1); | 961 | lua_pushcclosure(L, LG_linda_protected_call, 1); |
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h index e8d5ab5..77bcfe2 100644 --- a/src/macros_and_utils.h +++ b/src/macros_and_utils.h | |||
@@ -178,16 +178,48 @@ using lua_Duration = std::chrono::template duration<lua_Number>; | |||
178 | // ################################################################################################# | 178 | // ################################################################################################# |
179 | 179 | ||
180 | // A unique type generator | 180 | // A unique type generator |
181 | template <typename T, auto = []{}> | 181 | template <typename T, auto = [] {}, typename specialization = void> |
182 | struct Unique | 182 | class Unique |
183 | { | 183 | { |
184 | private: | ||
185 | |||
184 | T m_val; | 186 | T m_val; |
185 | constexpr Unique() = default; | 187 | |
186 | constexpr operator T() const { return m_val; } | 188 | public: |
187 | constexpr explicit Unique(T b_) : m_val{ b_ } {} | 189 | |
190 | Unique() = default; | ||
191 | operator T() const { return m_val; } | ||
192 | explicit Unique(T b_) : m_val{ b_ } {} | ||
193 | }; | ||
194 | |||
195 | template <typename T, auto lambda> | ||
196 | class Unique<T, lambda, std::enable_if_t<!std::is_scalar_v<T>>> : public T | ||
197 | { | ||
198 | public: | ||
199 | |||
200 | using T::T; | ||
201 | explicit Unique(T const& b_) : T{ b_ } {} | ||
188 | }; | 202 | }; |
189 | 203 | ||
190 | // ################################################################################################# | 204 | // ################################################################################################# |
191 | 205 | ||
192 | using Source = Unique<lua_State*>; | 206 | using Source = Unique<lua_State*>; |
193 | using Dest = Unique<lua_State*>; \ No newline at end of file | 207 | using Dest = Unique<lua_State*>; |
208 | |||
209 | // ################################################################################################# | ||
210 | |||
211 | // A helper to issue an error if the provided optional doesn't contain a value | ||
212 | // we can't use std::optional::value_or(luaL_error(...)), because the 'or' value is always evaluated | ||
213 | template <typename T> | ||
214 | concept IsOptional = requires(T x){ x.value_or(T{}); }; | ||
215 | |||
216 | template<typename T, typename ...Ts> | ||
217 | requires IsOptional<T> | ||
218 | typename T::value_type const& OptionalValue(T const& x_, Ts... args_) | ||
219 | { | ||
220 | if (!x_.has_value()) | ||
221 | { | ||
222 | luaL_error(std::forward<Ts>(args_)...); // doesn't return | ||
223 | } | ||
224 | return x_.value(); | ||
225 | } | ||
diff --git a/src/uniquekey.h b/src/uniquekey.h index a89ecd3..78c0765 100644 --- a/src/uniquekey.h +++ b/src/uniquekey.h | |||
@@ -13,12 +13,15 @@ class UniqueKey | |||
13 | 13 | ||
14 | public: | 14 | public: |
15 | 15 | ||
16 | constexpr explicit UniqueKey(uint64_t val_) | 16 | char const* m_debugName{ nullptr }; |
17 | |||
18 | constexpr explicit UniqueKey(uint64_t val_, char const* debugName_ = nullptr) | ||
17 | #if LUAJIT_FLAVOR() == 64 // building against LuaJIT headers for 64 bits, light userdata is restricted to 47 significant bits, because LuaJIT uses the other bits for internal optimizations | 19 | #if LUAJIT_FLAVOR() == 64 // building against LuaJIT headers for 64 bits, light userdata is restricted to 47 significant bits, because LuaJIT uses the other bits for internal optimizations |
18 | : m_storage{ static_cast<uintptr_t>(val_ & 0x7fffffffffffull) } | 20 | : m_storage{ static_cast<uintptr_t>(val_ & 0x7fffffffffffull) } |
19 | #else // LUAJIT_FLAVOR() | 21 | #else // LUAJIT_FLAVOR() |
20 | : m_storage{ static_cast<uintptr_t>(val_) } | 22 | : m_storage{ static_cast<uintptr_t>(val_) } |
21 | #endif // LUAJIT_FLAVOR() | 23 | #endif // LUAJIT_FLAVOR() |
24 | , m_debugName{ debugName_ } | ||
22 | { | 25 | { |
23 | } | 26 | } |
24 | constexpr UniqueKey(UniqueKey const& rhs_) = default; | 27 | constexpr UniqueKey(UniqueKey const& rhs_) = default; |
diff --git a/tests/errhangtest.lua b/tests/errhangtest.lua index d26dcef..811601e 100644 --- a/tests/errhangtest.lua +++ b/tests/errhangtest.lua | |||
@@ -1,22 +1,66 @@ | |||
1 | local lanes = require "lanes".configure{with_timers=false} | 1 | local lanes = require "lanes".configure{with_timers=false} |
2 | |||
3 | local linda = lanes.linda() | 2 | local linda = lanes.linda() |
4 | 3 | ||
5 | local coro = coroutine.create(function() end) | 4 | -- we are not allowed to send coroutines through a lane |
5 | -- however, this should raise an error, not hang the program... | ||
6 | if true then | ||
7 | print "#### coro set" | ||
8 | local coro = coroutine.create(function() end) | ||
9 | print(pcall(linda.set, linda, 'test', coro)) | ||
10 | assert(linda:get("test") == nil) | ||
11 | print "OK" | ||
12 | end | ||
13 | |||
14 | if true then | ||
15 | print "\n#### reserved sentinels" | ||
16 | print(pcall(linda.set, linda, lanes.cancel_error)) | ||
17 | print(pcall(linda.set, linda, linda.batched)) | ||
18 | assert(linda:get("test") == nil) | ||
19 | print "OK" | ||
20 | end | ||
6 | 21 | ||
7 | local fun = function() print "fun" end | 22 | -- get/set a few values |
8 | local t_in = { [fun] = fun, fun = fun } | 23 | if true then |
24 | print "\n#### set 3 -> receive batched" | ||
25 | local fun = function() print "function test ok" end | ||
26 | print(pcall(linda.set, linda, 'test', true, nil, fun)) | ||
27 | local k,b,n,f = linda:receive(linda.batched, 'test', 3) -- read back the contents | ||
28 | assert(linda:get("test") == nil) | ||
29 | print(k, b, n) | ||
30 | f() | ||
31 | print "OK" | ||
32 | end | ||
9 | 33 | ||
10 | -- send a string | ||
11 | print( pcall(linda.send,linda, 'test', "oh boy")) | ||
12 | -- send a table that contains a function | 34 | -- send a table that contains a function |
13 | print( pcall(linda.send,linda, 'test', t_in)) | 35 | if true then |
14 | -- we are not allowed to send coroutines through a lanes | 36 | print "\n#### send table with a function" |
37 | local fun = function() print "function test ok" end | ||
38 | local t_in = { [fun] = fun, fun = fun } | ||
39 | print(pcall(linda.send, linda, 'test', t_in)) | ||
40 | local k,t_out = linda:receive('test') -- read the contents successfully sent | ||
41 | t_out.fun() | ||
42 | -- TODO: t_out should contain a single entry, as [fun] = fun should have been discarded because functions are not acceptable keys | ||
43 | print "OK" | ||
44 | end | ||
45 | |||
46 | -- send a string | ||
47 | if true then | ||
48 | print "\n#### send string" | ||
49 | print(pcall(linda.send, linda, 'test', "string test ok")) | ||
50 | local k,str = linda:receive('test') -- read the contents successfully sent | ||
51 | print(str) | ||
52 | print "OK" | ||
53 | end | ||
54 | |||
55 | -- we are not allowed to send coroutines through a lane | ||
15 | -- however, this should raise an error, not hang the program... | 56 | -- however, this should raise an error, not hang the program... |
16 | print( pcall(linda.send,linda, 'test', coro)) | 57 | if true then |
17 | k,str = linda:receive('test') -- read the contents successfully sent | 58 | print "\n#### coro send" |
18 | print( str) -- "oh boy" | 59 | local coro = coroutine.create(function() end) |
19 | k,t_out = linda:receive('test') -- read the contents successfully sent | 60 | print(pcall(linda.send, linda, 'test', coro)) |
20 | t_out.fun() -- "fun" | 61 | assert(linda:get("test") == nil) |
21 | -- linda:send( 'test', coro) | 62 | print "OK" |
22 | print "SUCCESS" \ No newline at end of file | 63 | end |
64 | |||
65 | -- done | ||
66 | print "\nSUCCESS" \ No newline at end of file | ||