From ae582acdb1bfb3fb4171682b884545d174e95aa9 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Mon, 10 Jun 2024 16:49:58 +0200 Subject: linda:send() returns nil, in case of error --- docs/index.html | 41 ++++++++++++++++++++++++----------------- src/lanes.cpp | 4 ++-- src/lanes.lua | 3 ++- src/linda.cpp | 16 ++++++++++++---- tests/basic.lua | 14 +++++++------- 5 files changed, 47 insertions(+), 31 deletions(-) diff --git a/docs/index.html b/docs/index.html index 24c7c52..5675f65 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1221,7 +1221,7 @@

-	true|lanes.cancel_error = h:limit(key, n_uint)
+	true|(nil,[lanes.cancel_error|"timeout"]) = h:limit(key, n_uint)
 

@@ -1238,42 +1238,49 @@ Timeouts are given in seconds (>= 0, millisecond accuracy) or nil. Timeout can be omitted only if the first key is not a number (then it is equivalent to an infinite duration).
Each key acts as a FIFO queue. There is no limit to the number of keys a Linda may contain. Different Lindas can have identical keys, which are totally unrelated.

+ +

+ Multiple values can be sent to a given key at once, atomically (the send will fail unless all the values fit within the queue limit). This can be useful for multiple producer scenarios, if the protocols used are giving data in streams of multiple units. + Atomicity avoids the producers from garbling each others messages, which could happen if the units were sent individually. +

+

If no data is provided after the key, send() raises an error.
Also, if linda.null or lanes.null is sent as data in a Linda, it will be read as a nil.
- send() returns true if the sending succeeded, and false if the queue limit was met, and the queue did not empty enough during the given timeout.
- send() returns lanes.cancel_error if interrupted by a soft cancel request.
+ send() return values can be: +

 	key, val = h:receive([timeout_secs,] key [, key...])
 
-	key, val [, val...] = h:receive(timeout, h.batched, key, n_uint_min[, n_uint_max])
+	key, val [, val...] = h:receive([timeout,] h.batched, key, n_uint_min[, n_uint_max])
 
- -

- The send() and receive() methods use Linda keys as FIFO stacks (first in, first out).
- In batched mode, linda:receive() will raise an error if min_count < 1 or max_count < min_count. -

-

- Note that any number of lanes can be reading or writing a Linda. There can be many producers, and many consumers. It is up to you. + Unbatched receive() return values can be: +

- Hard cancellation will cause pending Linda operations to abort execution of the lane through a cancellation error. This means that you have to install a finalizer in your lane if you want to run some code in that situation. + In batched mode, linda:receive() will raise an error if min_count < 1 or max_count < min_count.

- Equally, receive() returns a key and the value extracted from it. Note that nils can be sent and received; the key value will tell it apart from a timeout.
- receive() returns nil, lanes.cancel_error if interrupted by a hard cancel request.
- receive() returns nil, "timeout" if nothing was available. + Note that any number of lanes can be reading or writing a Linda. There can be many producers, and many consumers. It is up to you.

- Multiple values can be sent to a given key at once, atomically (the send will fail unless all the values fit within the queue limit). This can be useful for multiple producer scenarios, if the protocols used are giving data in streams of multiple units. - Atomicity avoids the producers from garbling each others messages, which could happen if the units were sent individually. + Remember that Hard cancellation will cause pending Linda operations to abort execution of the lane through a cancellation error. This means that you have to install a finalizer in your lane if you want to run some code in that situation.

diff --git a/src/lanes.cpp b/src/lanes.cpp index 9b3b28c..5d40ed4 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp @@ -867,10 +867,10 @@ LANES_API void luaopen_lanes_embedded(lua_State* L_, lua_CFunction _luaopen_lane { STACK_CHECK_START_REL(L_, 0); // pre-require lanes.core so that when lanes.lua calls require "lanes.core" it finds it is already loaded - luaL_requiref(L_, "lanes.core", luaopen_lanes_core, 0); // L_: ... lanes.core + luaL_requiref(L_, kLanesCoreLibName, luaopen_lanes_core, 0); // L_: ... lanes.core lua_pop(L_, 1); // L_: ... STACK_CHECK(L_, 0); // call user-provided function that runs the chunk "lanes.lua" from wherever they stored it - luaL_requiref(L_, "lanes", _luaopen_lanes ? _luaopen_lanes : default_luaopen_lanes, 0); // L_: ... lanes + luaL_requiref(L_, kLanesLibName, _luaopen_lanes ? _luaopen_lanes : default_luaopen_lanes, 0); // L_: ... lanes STACK_CHECK(L_, 1); } diff --git a/src/lanes.lua b/src/lanes.lua index 95c4eff..92e773a 100644 --- a/src/lanes.lua +++ b/src/lanes.lua @@ -728,7 +728,8 @@ local genatomic = function(linda_, key_, initial_val_) return function(diff_) -- 'nil' allows 'key_' to be numeric -- suspends until our 'true' is in - if linda_:send(nil, key_, true) == cancel_error then + local _res, _err = linda_:send(nil, key_, true) + if _err == cancel_error then return cancel_error end local val = linda_:get(key_) diff --git a/src/linda.cpp b/src/linda.cpp index 4dc2162..1bd5d16 100644 --- a/src/linda.cpp +++ b/src/linda.cpp @@ -709,17 +709,25 @@ LUAG_FUNC(linda_send) switch (_cancel) { case CancelRequest::Soft: - // if user wants to soft-cancel, the call returns lanes.cancel_error + // if user wants to soft-cancel, the call returns nil, kCancelError + lua_pushnil(L_); kCancelError.pushKey(L_); - return 1; + return 2; case CancelRequest::Hard: // raise an error interrupting execution only in case of hard cancel raise_cancel_error(L_); // raises an error and doesn't return default: - lua_pushboolean(L_, _ret); // true (success) or false (timeout) - return 1; + if (_ret) { + lua_pushboolean(L_, _ret); // true (success) + return 1; + } else { + // not enough room in the Linda slot to fulfill the request, return nil, "timeout" + lua_pushnil(L_); + std::ignore = luaG_pushstring(L_, "timeout"); + return 2; + } } }; return Linda::ProtectedCall(L_, _send); diff --git a/tests/basic.lua b/tests/basic.lua index 28f0334..cad3764 100644 --- a/tests/basic.lua +++ b/tests/basic.lua @@ -218,16 +218,16 @@ local function WR(...) io.stderr:write(...) end local chunk= function(linda) local function receive() return linda:receive("->") end - local function send(...) linda:send("<-", ...) end + local function send(...) local _res, _err = linda:send("<-", ...) assert(_res == true and _err == nil) end WR("chunk ", "Lane starts!\n") local k,v - k,v=receive(); WR("chunk ", v.." received (expecting 1)\n"); assert(v==1) - k,v=receive(); WR("chunk ", v.." received (expecting 2)\n"); assert(v==2) - k,v=receive(); WR("chunk ", v.." received (expecting 3)\n"); assert(v==3) - k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil from __lanesconvert)\n"); assert(v==nil, "table with __lanesconvert==lanes.null should be received as nil, got " .. tostring(v)) -- a table with __lanesconvert was sent - k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil)\n"); assert(v==nil) + k,v=receive(); WR("chunk ", v.." received (expecting 1)\n"); assert(k and v==1) + k,v=receive(); WR("chunk ", v.." received (expecting 2)\n"); assert(k and v==2) + k,v=receive(); WR("chunk ", v.." received (expecting 3)\n"); assert(k and v==3) + k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil from __lanesconvert)\n"); assert(k and v==nil, "table with __lanesconvert==lanes.null should be received as nil, got " .. tostring(v)) -- a table with __lanesconvert was sent + k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil)\n"); assert(k and v==nil) send(4,5,6); WR("chunk ", "4,5,6 sent\n") send 'aaa'; WR("chunk ", "'aaa' sent\n") @@ -269,7 +269,7 @@ assert(x == nil and y == nil and z == nil and w == nil) WR "ok\n" local function PEEK(...) return linda:get("<-", ...) end -local function SEND(...) linda:send("->", ...) end +local function SEND(...) local _res, _err = linda:send("->", ...) assert(_res == true and _err == nil) end local function RECEIVE() local k,v = linda:receive(1, "<-") return v end local comms_lane = lanes_gen("io", {gc_cb = gc_cb, name = "auto"}, chunk)(linda) -- prepare & launch -- cgit v1.2.3-55-g6feb