diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-10 16:49:58 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-10 16:49:58 +0200 |
commit | ae582acdb1bfb3fb4171682b884545d174e95aa9 (patch) | |
tree | b0c9c0b14f6d57cd8ce1c14d4d23df1adb0bc539 | |
parent | 3f5c16116a3a7740ac4ac62b663661d772543c2e (diff) | |
download | lanes-ae582acdb1bfb3fb4171682b884545d174e95aa9.tar.gz lanes-ae582acdb1bfb3fb4171682b884545d174e95aa9.tar.bz2 lanes-ae582acdb1bfb3fb4171682b884545d174e95aa9.zip |
linda:send() returns nil,<something> in case of error
-rw-r--r-- | docs/index.html | 41 | ||||
-rw-r--r-- | src/lanes.cpp | 4 | ||||
-rw-r--r-- | src/lanes.lua | 3 | ||||
-rw-r--r-- | src/linda.cpp | 16 | ||||
-rw-r--r-- | 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 @@ | |||
1221 | </p> | 1221 | </p> |
1222 | 1222 | ||
1223 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1223 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1224 | true|lanes.cancel_error = h:limit(key, n_uint) | 1224 | true|(nil,[lanes.cancel_error|"timeout"]) = h:limit(key, n_uint) |
1225 | </pre></td></tr></table> | 1225 | </pre></td></tr></table> |
1226 | 1226 | ||
1227 | <p> | 1227 | <p> |
@@ -1238,42 +1238,49 @@ | |||
1238 | Timeouts are given in seconds (>= 0, millisecond accuracy) or <tt>nil</tt>. Timeout can be omitted only if the first key is not a number (then it is equivalent to an infinite duration).<br/> | 1238 | Timeouts are given in seconds (>= 0, millisecond accuracy) or <tt>nil</tt>. Timeout can be omitted only if the first key is not a number (then it is equivalent to an infinite duration).<br/> |
1239 | 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. | 1239 | 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. |
1240 | </p> | 1240 | </p> |
1241 | |||
1242 | <p> | ||
1243 | 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. | ||
1244 | Atomicity avoids the producers from garbling each others messages, which could happen if the units were sent individually. | ||
1245 | </p> | ||
1246 | |||
1241 | <p> | 1247 | <p> |
1242 | If no data is provided after the key, <tt>send()</tt> raises an error.<br/> | 1248 | If no data is provided after the key, <tt>send()</tt> raises an error.<br/> |
1243 | Also, if <tt>linda.null</tt> or <tt>lanes.null</tt> is sent as data in a Linda, it will be read as a <tt>nil</tt>.<br/> | 1249 | Also, if <tt>linda.null</tt> or <tt>lanes.null</tt> is sent as data in a Linda, it will be read as a <tt>nil</tt>.<br/> |
1244 | <tt>send()</tt> returns <tt>true</tt> if the sending succeeded, and <tt>false</tt> if the queue limit was met, and the queue did not empty enough during the given timeout.<br/> | 1250 | <tt>send()</tt> return values can be: |
1245 | <tt>send()</tt> returns <tt>lanes.cancel_error</tt> if interrupted by a soft cancel request.<br/> | 1251 | <ul> |
1252 | <li><tt>true</tt> on success.</li> | ||
1253 | <li><tt>nil, "timeout"</tt> if the queue limit was met, and the queue did not empty enough during the given duration.</li> | ||
1254 | <li><tt>nil, lanes.cancel_error</tt> if interrupted by a soft cancel request.</li> | ||
1255 | <li>Raises <tt>lanes.cancel_error</tt> if interrupted by a hard cancel request.</li> | ||
1256 | </ul> | ||
1246 | </p> | 1257 | </p> |
1247 | 1258 | ||
1248 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1259 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1249 | key, val = h:receive([timeout_secs,] key [, key...]) | 1260 | key, val = h:receive([timeout_secs,] key [, key...]) |
1250 | 1261 | ||
1251 | key, val [, val...] = h:receive(timeout, h.batched, key, n_uint_min[, n_uint_max]) | 1262 | key, val [, val...] = h:receive([timeout,] h.batched, key, n_uint_min[, n_uint_max]) |
1252 | </pre></td></tr></table> | 1263 | </pre></td></tr></table> |
1253 | 1264 | ||
1254 | |||
1255 | <p> | ||
1256 | The <tt>send()</tt> and <tt>receive()</tt> methods use Linda keys as FIFO stacks (first in, first out).<br/> | ||
1257 | In batched mode, <tt>linda:receive()</tt> will raise an error if <tt>min_count < 1</tt> or <tt>max_count < min_count</tt>. | ||
1258 | </p> | ||
1259 | |||
1260 | <p> | 1265 | <p> |
1261 | 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. | 1266 | Unbatched <tt>receive()</tt> return values can be: |
1267 | <ul> | ||
1268 | <li><tt>nil, lanes.cancel_error</tt> if interrupted by a hard cancel request.</li> | ||
1269 | <li><tt>nil, "timeout"</tt> if nothing was available.</li> | ||
1270 | <li>A key and the value extracted from it. Note that <tt>nil</tt> can be sent and received; the <tt>key</tt> value will tell it apart from a timeout.</li> | ||
1271 | </ul> | ||
1262 | </p> | 1272 | </p> |
1263 | 1273 | ||
1264 | <p> | 1274 | <p> |
1265 | <a href="#cancelling">Hard cancellation</a> will cause pending Linda operations to abort execution of the lane through a cancellation error. This means that you have to install a <a href="#finalizers">finalizer</a> in your lane if you want to run some code in that situation. | 1275 | In batched mode, <tt>linda:receive()</tt> will raise an error if <tt>min_count < 1</tt> or <tt>max_count < min_count</tt>. |
1266 | </p> | 1276 | </p> |
1267 | 1277 | ||
1268 | <p> | 1278 | <p> |
1269 | Equally, <tt>receive()</tt> returns a key and the value extracted from it. Note that <tt>nil</tt>s can be sent and received; the <tt>key</tt> value will tell it apart from a timeout.<br/> | 1279 | 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. |
1270 | <tt>receive()</tt> returns <tt>nil, lanes.cancel_error</tt> if interrupted by a hard cancel request.<br/> | ||
1271 | <tt>receive()</tt> returns <tt>nil, "timeout"</tt> if nothing was available. | ||
1272 | </p> | 1280 | </p> |
1273 | 1281 | ||
1274 | <p> | 1282 | <p> |
1275 | 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. | 1283 | Remember that <a href="#cancelling">Hard cancellation</a> will cause pending Linda operations to abort execution of the lane through a cancellation error. This means that you have to install a <a href="#finalizers">finalizer</a> in your lane if you want to run some code in that situation. |
1276 | Atomicity avoids the producers from garbling each others messages, which could happen if the units were sent individually. | ||
1277 | </p> | 1284 | </p> |
1278 | 1285 | ||
1279 | <p> | 1286 | <p> |
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 | |||
867 | { | 867 | { |
868 | STACK_CHECK_START_REL(L_, 0); | 868 | STACK_CHECK_START_REL(L_, 0); |
869 | // pre-require lanes.core so that when lanes.lua calls require "lanes.core" it finds it is already loaded | 869 | // pre-require lanes.core so that when lanes.lua calls require "lanes.core" it finds it is already loaded |
870 | luaL_requiref(L_, "lanes.core", luaopen_lanes_core, 0); // L_: ... lanes.core | 870 | luaL_requiref(L_, kLanesCoreLibName, luaopen_lanes_core, 0); // L_: ... lanes.core |
871 | lua_pop(L_, 1); // L_: ... | 871 | lua_pop(L_, 1); // L_: ... |
872 | STACK_CHECK(L_, 0); | 872 | STACK_CHECK(L_, 0); |
873 | // call user-provided function that runs the chunk "lanes.lua" from wherever they stored it | 873 | // call user-provided function that runs the chunk "lanes.lua" from wherever they stored it |
874 | luaL_requiref(L_, "lanes", _luaopen_lanes ? _luaopen_lanes : default_luaopen_lanes, 0); // L_: ... lanes | 874 | luaL_requiref(L_, kLanesLibName, _luaopen_lanes ? _luaopen_lanes : default_luaopen_lanes, 0); // L_: ... lanes |
875 | STACK_CHECK(L_, 1); | 875 | STACK_CHECK(L_, 1); |
876 | } | 876 | } |
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_) | |||
728 | return function(diff_) | 728 | return function(diff_) |
729 | -- 'nil' allows 'key_' to be numeric | 729 | -- 'nil' allows 'key_' to be numeric |
730 | -- suspends until our 'true' is in | 730 | -- suspends until our 'true' is in |
731 | if linda_:send(nil, key_, true) == cancel_error then | 731 | local _res, _err = linda_:send(nil, key_, true) |
732 | if _err == cancel_error then | ||
732 | return cancel_error | 733 | return cancel_error |
733 | end | 734 | end |
734 | local val = linda_:get(key_) | 735 | 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) | |||
709 | 709 | ||
710 | switch (_cancel) { | 710 | switch (_cancel) { |
711 | case CancelRequest::Soft: | 711 | case CancelRequest::Soft: |
712 | // if user wants to soft-cancel, the call returns lanes.cancel_error | 712 | // if user wants to soft-cancel, the call returns nil, kCancelError |
713 | lua_pushnil(L_); | ||
713 | kCancelError.pushKey(L_); | 714 | kCancelError.pushKey(L_); |
714 | return 1; | 715 | return 2; |
715 | 716 | ||
716 | case CancelRequest::Hard: | 717 | case CancelRequest::Hard: |
717 | // raise an error interrupting execution only in case of hard cancel | 718 | // raise an error interrupting execution only in case of hard cancel |
718 | raise_cancel_error(L_); // raises an error and doesn't return | 719 | raise_cancel_error(L_); // raises an error and doesn't return |
719 | 720 | ||
720 | default: | 721 | default: |
721 | lua_pushboolean(L_, _ret); // true (success) or false (timeout) | 722 | if (_ret) { |
722 | return 1; | 723 | lua_pushboolean(L_, _ret); // true (success) |
724 | return 1; | ||
725 | } else { | ||
726 | // not enough room in the Linda slot to fulfill the request, return nil, "timeout" | ||
727 | lua_pushnil(L_); | ||
728 | std::ignore = luaG_pushstring(L_, "timeout"); | ||
729 | return 2; | ||
730 | } | ||
723 | } | 731 | } |
724 | }; | 732 | }; |
725 | return Linda::ProtectedCall(L_, _send); | 733 | 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 | |||
218 | 218 | ||
219 | local chunk= function(linda) | 219 | local chunk= function(linda) |
220 | local function receive() return linda:receive("->") end | 220 | local function receive() return linda:receive("->") end |
221 | local function send(...) linda:send("<-", ...) end | 221 | local function send(...) local _res, _err = linda:send("<-", ...) assert(_res == true and _err == nil) end |
222 | 222 | ||
223 | WR("chunk ", "Lane starts!\n") | 223 | WR("chunk ", "Lane starts!\n") |
224 | 224 | ||
225 | local k,v | 225 | local k,v |
226 | k,v=receive(); WR("chunk ", v.." received (expecting 1)\n"); assert(v==1) | 226 | k,v=receive(); WR("chunk ", v.." received (expecting 1)\n"); assert(k and v==1) |
227 | k,v=receive(); WR("chunk ", v.." received (expecting 2)\n"); assert(v==2) | 227 | k,v=receive(); WR("chunk ", v.." received (expecting 2)\n"); assert(k and v==2) |
228 | k,v=receive(); WR("chunk ", v.." received (expecting 3)\n"); assert(v==3) | 228 | k,v=receive(); WR("chunk ", v.." received (expecting 3)\n"); assert(k and v==3) |
229 | 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 | 229 | 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 |
230 | k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil)\n"); assert(v==nil) | 230 | k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil)\n"); assert(k and v==nil) |
231 | 231 | ||
232 | send(4,5,6); WR("chunk ", "4,5,6 sent\n") | 232 | send(4,5,6); WR("chunk ", "4,5,6 sent\n") |
233 | send 'aaa'; WR("chunk ", "'aaa' sent\n") | 233 | send 'aaa'; WR("chunk ", "'aaa' sent\n") |
@@ -269,7 +269,7 @@ assert(x == nil and y == nil and z == nil and w == nil) | |||
269 | WR "ok\n" | 269 | WR "ok\n" |
270 | 270 | ||
271 | local function PEEK(...) return linda:get("<-", ...) end | 271 | local function PEEK(...) return linda:get("<-", ...) end |
272 | local function SEND(...) linda:send("->", ...) end | 272 | local function SEND(...) local _res, _err = linda:send("->", ...) assert(_res == true and _err == nil) end |
273 | local function RECEIVE() local k,v = linda:receive(1, "<-") return v end | 273 | local function RECEIVE() local k,v = linda:receive(1, "<-") return v end |
274 | 274 | ||
275 | local comms_lane = lanes_gen("io", {gc_cb = gc_cb, name = "auto"}, chunk)(linda) -- prepare & launch | 275 | local comms_lane = lanes_gen("io", {gc_cb = gc_cb, name = "auto"}, chunk)(linda) -- prepare & launch |