diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-12-13 17:22:17 +0100 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-12-13 17:22:17 +0100 |
commit | dc7a2bc3a9c8316e17902493b832ca117805d29f (patch) | |
tree | 3c1a03edf586869119ef0de03631f6f603650720 /unit_tests/scripts/lane | |
parent | 7500a80fc06c5311c46df8f1761f25ae67277fc4 (diff) | |
download | lanes-dc7a2bc3a9c8316e17902493b832ca117805d29f.tar.gz lanes-dc7a2bc3a9c8316e17902493b832ca117805d29f.tar.bz2 lanes-dc7a2bc3a9c8316e17902493b832ca117805d29f.zip |
Append all unit tests to depot
Diffstat (limited to 'unit_tests/scripts/lane')
-rw-r--r-- | unit_tests/scripts/lane/cooperative_shutdown.lua | 45 | ||||
-rw-r--r-- | unit_tests/scripts/lane/stdlib_naming.lua | 87 | ||||
-rw-r--r-- | unit_tests/scripts/lane/tasking_basic.lua | 74 | ||||
-rw-r--r-- | unit_tests/scripts/lane/tasking_cancelling.lua | 122 | ||||
-rw-r--r-- | unit_tests/scripts/lane/tasking_comms_criss_cross.lua | 53 | ||||
-rw-r--r-- | unit_tests/scripts/lane/tasking_communications.lua | 149 | ||||
-rw-r--r-- | unit_tests/scripts/lane/tasking_error.lua | 49 | ||||
-rw-r--r-- | unit_tests/scripts/lane/tasking_join_test.lua | 65 | ||||
-rw-r--r-- | unit_tests/scripts/lane/tasking_send_receive_code.lua | 91 | ||||
-rw-r--r-- | unit_tests/scripts/lane/uncooperative_shutdown.lua | 24 |
10 files changed, 759 insertions, 0 deletions
diff --git a/unit_tests/scripts/lane/cooperative_shutdown.lua b/unit_tests/scripts/lane/cooperative_shutdown.lua new file mode 100644 index 0000000..2649079 --- /dev/null +++ b/unit_tests/scripts/lane/cooperative_shutdown.lua | |||
@@ -0,0 +1,45 @@ | |||
1 | local lanes = require "lanes" | ||
2 | |||
3 | -- launch lanes that cooperate properly with cancellation request | ||
4 | |||
5 | local lane1 = function() | ||
6 | -- loop breaks on cancellation request | ||
7 | repeat | ||
8 | lanes.sleep(0) | ||
9 | until cancel_test() | ||
10 | print "lane1 cancelled" | ||
11 | end | ||
12 | |||
13 | local lane2 = function(linda_) | ||
14 | -- loop breaks on lane/linda cancellation | ||
15 | repeat | ||
16 | local k, v = linda_:receive('k') | ||
17 | until v == lanes.cancel_error | ||
18 | print "lane2 cancelled" | ||
19 | end | ||
20 | |||
21 | local lane3 = function() | ||
22 | -- this one cooperates too, because of the hook cancellation modes that Lanes will be using | ||
23 | -- but not with LuaJIT, because the function is compiled, and we don't call anyone, so no hook triggers | ||
24 | repeat until false | ||
25 | end | ||
26 | |||
27 | |||
28 | |||
29 | -- the generators | ||
30 | local g1 = lanes.gen("*", lane1) | ||
31 | local g2 = lanes.gen("*", lane2) | ||
32 | local g3 = lanes.gen("*", lane3) | ||
33 | |||
34 | -- launch lanes | ||
35 | local h1 = g1() | ||
36 | |||
37 | local linda = lanes.linda() | ||
38 | local h2 = g2(linda) | ||
39 | |||
40 | local h3 = g3() | ||
41 | |||
42 | -- wait until they are both started | ||
43 | repeat until h1.status == "running" and h2.status == "waiting" and h3.status == "running" | ||
44 | |||
45 | -- let the script terminate, Lanes should not crash at shutdown | ||
diff --git a/unit_tests/scripts/lane/stdlib_naming.lua b/unit_tests/scripts/lane/stdlib_naming.lua new file mode 100644 index 0000000..2e045c3 --- /dev/null +++ b/unit_tests/scripts/lane/stdlib_naming.lua | |||
@@ -0,0 +1,87 @@ | |||
1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
3 | local lanes = require_lanes_result_1 | ||
4 | |||
5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
7 | |||
8 | local utils = lanes.require "_utils" | ||
9 | local PRINT = utils.MAKE_PRINT() | ||
10 | |||
11 | local lanes_gen = assert(lanes.gen) | ||
12 | local lanes_linda = assert(lanes.linda) | ||
13 | |||
14 | -- ################################################################################################## | ||
15 | -- ################################################################################################## | ||
16 | -- ################################################################################################## | ||
17 | |||
18 | local function task(a, b, c) | ||
19 | lane_threadname("task("..a..","..b..","..c..")") | ||
20 | --error "111" -- testing error messages | ||
21 | assert(hey) | ||
22 | local v=0 | ||
23 | for i=a,b,c do | ||
24 | v= v+i | ||
25 | end | ||
26 | return v, hey | ||
27 | end | ||
28 | |||
29 | local gc_cb = function(name_, status_) | ||
30 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
31 | end | ||
32 | |||
33 | -- ################################################################################################## | ||
34 | -- ################################################################################################## | ||
35 | -- ################################################################################################## | ||
36 | |||
37 | PRINT("\n\n", "---=== Stdlib naming ===---", "\n\n") | ||
38 | |||
39 | local function dump_g(_x) | ||
40 | lane_threadname "dump_g" | ||
41 | assert(print) | ||
42 | print("### dumping _G for '" .. _x .. "'") | ||
43 | for k, v in pairs(_G) do | ||
44 | print("\t" .. k .. ": " .. type(v)) | ||
45 | end | ||
46 | return true | ||
47 | end | ||
48 | |||
49 | local function io_os_f(_x) | ||
50 | lane_threadname "io_os_f" | ||
51 | assert(print) | ||
52 | print("### checking io and os libs existence for '" .. _x .. "'") | ||
53 | assert(io) | ||
54 | assert(os) | ||
55 | return true | ||
56 | end | ||
57 | |||
58 | local function coro_f(_x) | ||
59 | lane_threadname "coro_f" | ||
60 | assert(print) | ||
61 | print("### checking coroutine lib existence for '" .. _x .. "'") | ||
62 | assert(coroutine) | ||
63 | return true | ||
64 | end | ||
65 | |||
66 | assert.fails(function() lanes_gen("xxx", {gc_cb = gc_cb}, io_os_f) end) | ||
67 | |||
68 | local stdlib_naming_tests = | ||
69 | { | ||
70 | -- { "", dump_g}, | ||
71 | -- { "coroutine", dump_g}, | ||
72 | -- { "io", dump_g}, | ||
73 | -- { "bit32", dump_g}, | ||
74 | { "coroutine?", coro_f}, -- in Lua 5.1, the coroutine base library doesn't exist (coroutine table is created when 'base' is opened) | ||
75 | { "*", io_os_f}, | ||
76 | { "io,os", io_os_f}, | ||
77 | { "io+os", io_os_f}, | ||
78 | { "/io;os[base{", io_os_f}, -- use unconventional name separators to check that everything works fine | ||
79 | } | ||
80 | |||
81 | for _, t in ipairs(stdlib_naming_tests) do | ||
82 | local f= lanes_gen(t[1], {gc_cb = gc_cb}, t[2]) -- any delimiter will do | ||
83 | assert(f(t[1])[1]) | ||
84 | end | ||
85 | |||
86 | PRINT("collectgarbage") | ||
87 | collectgarbage() | ||
diff --git a/unit_tests/scripts/lane/tasking_basic.lua b/unit_tests/scripts/lane/tasking_basic.lua new file mode 100644 index 0000000..99be321 --- /dev/null +++ b/unit_tests/scripts/lane/tasking_basic.lua | |||
@@ -0,0 +1,74 @@ | |||
1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
3 | local lanes = require_lanes_result_1 | ||
4 | |||
5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
7 | |||
8 | local utils = lanes.require "_utils" | ||
9 | local PRINT = utils.MAKE_PRINT() | ||
10 | |||
11 | local lanes_gen = assert(lanes.gen) | ||
12 | local lanes_linda = assert(lanes.linda) | ||
13 | |||
14 | -- ################################################################################################## | ||
15 | -- ################################################################################################## | ||
16 | -- ################################################################################################## | ||
17 | |||
18 | local function task(a, b, c) | ||
19 | local new_name = "task("..a..","..b..","..c..")" | ||
20 | -- test lane naming change | ||
21 | lane_threadname(new_name) | ||
22 | assert(lane_threadname() == new_name) | ||
23 | --error "111" -- testing error messages | ||
24 | assert(hey) | ||
25 | local v=0 | ||
26 | for i=a,b,c do | ||
27 | v= v+i | ||
28 | end | ||
29 | return v, hey | ||
30 | end | ||
31 | |||
32 | local gc_cb = function(name_, status_) | ||
33 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
34 | end | ||
35 | |||
36 | -- ################################################################################################## | ||
37 | -- ################################################################################################## | ||
38 | -- ################################################################################################## | ||
39 | |||
40 | PRINT("\n\n", "---=== Tasking (basic) ===---", "\n\n") | ||
41 | |||
42 | local task_launch = lanes_gen("", { globals={hey=true}, gc_cb = gc_cb}, task) | ||
43 | -- base stdlibs, normal priority | ||
44 | |||
45 | -- 'task_launch' is a factory of multithreaded tasks, we can launch several: | ||
46 | |||
47 | local lane1 = task_launch(100,200,3) | ||
48 | assert.fails(function() print(lane1[lane1]) end) -- indexing the lane with anything other than a string or a number should fail | ||
49 | lanes.sleep(0.1) -- give some time so that the lane can set its name | ||
50 | assert(lane1:get_threadname() == "task(100,200,3)", "Lane name is " .. lane1:get_threadname()) | ||
51 | |||
52 | local lane2= task_launch(200,300,4) | ||
53 | |||
54 | -- At this stage, states may be "pending", "running" or "done" | ||
55 | |||
56 | local st1,st2= lane1.status, lane2.status | ||
57 | PRINT(st1,st2) | ||
58 | assert(st1=="pending" or st1=="running" or st1=="done") | ||
59 | assert(st2=="pending" or st2=="running" or st2=="done") | ||
60 | |||
61 | -- Accessing results ([1..N]) pends until they are available | ||
62 | -- | ||
63 | PRINT("waiting...") | ||
64 | local v1, v1_hey= lane1[1], lane1[2] | ||
65 | local v2, v2_hey= lane2[1], lane2[2] | ||
66 | |||
67 | PRINT(v1, v1_hey) | ||
68 | assert(v1_hey == true) | ||
69 | |||
70 | PRINT(v2, v2_hey) | ||
71 | assert(v2_hey == true) | ||
72 | |||
73 | assert(lane1.status == "done") | ||
74 | assert(lane1.status == "done") | ||
diff --git a/unit_tests/scripts/lane/tasking_cancelling.lua b/unit_tests/scripts/lane/tasking_cancelling.lua new file mode 100644 index 0000000..a4e0fde --- /dev/null +++ b/unit_tests/scripts/lane/tasking_cancelling.lua | |||
@@ -0,0 +1,122 @@ | |||
1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
3 | local lanes = require_lanes_result_1 | ||
4 | |||
5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
7 | |||
8 | local utils = lanes.require "_utils" | ||
9 | local PRINT = utils.MAKE_PRINT() | ||
10 | |||
11 | local lanes_gen = assert(lanes.gen) | ||
12 | local lanes_linda = assert(lanes.linda) | ||
13 | |||
14 | -- ################################################################################################## | ||
15 | -- ################################################################################################## | ||
16 | -- ################################################################################################## | ||
17 | |||
18 | local function task(a, b, c) | ||
19 | lane_threadname("task("..a..","..b..","..c..")") | ||
20 | --error "111" -- testing error messages | ||
21 | assert(hey) | ||
22 | local v=0 | ||
23 | for i=a,b,c do | ||
24 | v= v+i | ||
25 | end | ||
26 | return v, hey | ||
27 | end | ||
28 | |||
29 | local gc_cb = function(name_, status_) | ||
30 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
31 | end | ||
32 | |||
33 | -- ################################################################################################## | ||
34 | -- ################################################################################################## | ||
35 | -- ################################################################################################## | ||
36 | |||
37 | PRINT("\n\n", "---=== Tasking (cancelling) ===---", "\n\n") | ||
38 | |||
39 | local task_launch2 = lanes_gen("", { globals={hey=true}, gc_cb = gc_cb}, task) | ||
40 | |||
41 | local N=999999999 | ||
42 | local lane9= task_launch2(1,N,1) -- huuuuuuge... | ||
43 | |||
44 | -- Wait until state changes "pending"->"running" | ||
45 | -- | ||
46 | local st | ||
47 | local t0= os.time() | ||
48 | while os.time()-t0 < 5 do | ||
49 | st= lane9.status | ||
50 | io.stderr:write((i==1) and st.." " or '.') | ||
51 | if st~="pending" then break end | ||
52 | end | ||
53 | PRINT(" "..st) | ||
54 | |||
55 | if st=="error" then | ||
56 | local _= lane9[0] -- propagate the error here | ||
57 | end | ||
58 | if st=="done" then | ||
59 | error("Looping to "..N.." was not long enough (cannot test cancellation)") | ||
60 | end | ||
61 | assert(st=="running", "st == " .. st) | ||
62 | |||
63 | -- when running under luajit, the function is JIT-ed, and the instruction count isn't hit, so we need a different hook | ||
64 | lane9:cancel(jit and "line" or "count", 100) -- 0 timeout, hook triggers cancelslation when reaching the specified count | ||
65 | |||
66 | local t0= os.time() | ||
67 | while os.time()-t0 < 5 do | ||
68 | st= lane9.status | ||
69 | io.stderr:write((i==1) and st.." " or '.') | ||
70 | if st~="running" then break end | ||
71 | end | ||
72 | PRINT(" "..st) | ||
73 | assert(st == "cancelled", "st is '" .. st .. "' instead of 'cancelled'") | ||
74 | |||
75 | -- cancellation of lanes waiting on a linda | ||
76 | local limited = lanes_linda("limited") | ||
77 | assert.fails(function() limited:limit("key", -1) end) | ||
78 | assert.failsnot(function() limited:limit("key", 1) end) | ||
79 | -- [[################################################ | ||
80 | limited:send("key", "hello") -- saturate linda | ||
81 | for k, v in pairs(limited:dump()) do | ||
82 | PRINT("limited[" .. tostring(k) .. "] = " .. tostring(v)) | ||
83 | end | ||
84 | local wait_send = function() | ||
85 | local a,b | ||
86 | set_finalizer(function() print("wait_send", a, b) end) | ||
87 | a,b = limited:send("key", "bybye") -- infinite timeout, returns only when lane is cancelled | ||
88 | end | ||
89 | |||
90 | local wait_send_lane = lanes.gen("*", wait_send)() | ||
91 | repeat until wait_send_lane.status == "waiting" | ||
92 | print "wait_send_lane is waiting" | ||
93 | wait_send_lane:cancel() -- hard cancel, 0 timeout | ||
94 | repeat until wait_send_lane.status == "cancelled" | ||
95 | print "wait_send_lane is cancelled" | ||
96 | --################################################]] | ||
97 | local wait_receive = function() | ||
98 | local k, v | ||
99 | set_finalizer(function() print("wait_receive", k, v) end) | ||
100 | k, v = limited:receive("dummy") -- infinite timeout, returns only when lane is cancelled | ||
101 | end | ||
102 | |||
103 | local wait_receive_lane = lanes.gen("*", wait_receive)() | ||
104 | repeat until wait_receive_lane.status == "waiting" | ||
105 | print "wait_receive_lane is waiting" | ||
106 | wait_receive_lane:cancel() -- hard cancel, 0 timeout | ||
107 | repeat until wait_receive_lane.status == "cancelled" | ||
108 | print "wait_receive_lane is cancelled" | ||
109 | --################################################]] | ||
110 | local wait_receive_batched = function() | ||
111 | local k, v1, v2 | ||
112 | set_finalizer(function() print("wait_receive_batched", k, v1, v2) end) | ||
113 | k, v1, v2 = limited:receive(limited.batched, "dummy", 2) -- infinite timeout, returns only when lane is cancelled | ||
114 | end | ||
115 | |||
116 | local wait_receive_batched_lane = lanes.gen("*", wait_receive_batched)() | ||
117 | repeat until wait_receive_batched_lane.status == "waiting" | ||
118 | print "wait_receive_batched_lane is waiting" | ||
119 | wait_receive_batched_lane:cancel() -- hard cancel, 0 timeout | ||
120 | repeat until wait_receive_batched_lane.status == "cancelled" | ||
121 | print "wait_receive_batched_lane is cancelled" | ||
122 | --################################################]] | ||
diff --git a/unit_tests/scripts/lane/tasking_comms_criss_cross.lua b/unit_tests/scripts/lane/tasking_comms_criss_cross.lua new file mode 100644 index 0000000..db63b8e --- /dev/null +++ b/unit_tests/scripts/lane/tasking_comms_criss_cross.lua | |||
@@ -0,0 +1,53 @@ | |||
1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
3 | local lanes = require_lanes_result_1 | ||
4 | |||
5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
7 | |||
8 | local utils = lanes.require "_utils" | ||
9 | local PRINT = utils.MAKE_PRINT() | ||
10 | |||
11 | local lanes_gen = assert(lanes.gen) | ||
12 | local lanes_linda = assert(lanes.linda) | ||
13 | |||
14 | -- ################################################################################################## | ||
15 | -- ################################################################################################## | ||
16 | -- ################################################################################################## | ||
17 | |||
18 | local gc_cb = function(name_, status_) | ||
19 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
20 | end | ||
21 | |||
22 | -- ################################################################################################## | ||
23 | -- ################################################################################################## | ||
24 | -- ################################################################################################## | ||
25 | |||
26 | PRINT("\n\n", "---=== Comms criss cross ===---", "\n\n") | ||
27 | |||
28 | -- We make two identical lanes, which are using the same Linda channel. | ||
29 | -- | ||
30 | local tc = lanes_gen("io", {gc_cb = gc_cb}, | ||
31 | function(linda, ch_in, ch_out) | ||
32 | lane_threadname("criss cross " .. ch_in .. " -> " .. ch_out) | ||
33 | local function STAGE(str) | ||
34 | io.stderr:write(ch_in..": "..str.."\n") | ||
35 | linda:send(nil, ch_out, str) | ||
36 | local k,v= linda:receive(nil, ch_in) | ||
37 | assert(v==str) | ||
38 | end | ||
39 | STAGE("Hello") | ||
40 | STAGE("I was here first!") | ||
41 | STAGE("So what?") | ||
42 | end | ||
43 | ) | ||
44 | |||
45 | local linda= lanes_linda("criss cross") | ||
46 | |||
47 | local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms | ||
48 | |||
49 | local _= a[0],b[0] -- waits until they are both ready | ||
50 | |||
51 | PRINT("collectgarbage") | ||
52 | a, b = nil | ||
53 | collectgarbage() | ||
diff --git a/unit_tests/scripts/lane/tasking_communications.lua b/unit_tests/scripts/lane/tasking_communications.lua new file mode 100644 index 0000000..b922973 --- /dev/null +++ b/unit_tests/scripts/lane/tasking_communications.lua | |||
@@ -0,0 +1,149 @@ | |||
1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
3 | local lanes = require_lanes_result_1 | ||
4 | |||
5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
7 | |||
8 | local utils = lanes.require "_utils" | ||
9 | local PRINT = utils.MAKE_PRINT() | ||
10 | |||
11 | local lanes_gen = assert(lanes.gen) | ||
12 | local lanes_linda = assert(lanes.linda) | ||
13 | |||
14 | -- ################################################################################################## | ||
15 | -- ################################################################################################## | ||
16 | -- ################################################################################################## | ||
17 | |||
18 | local function task(a, b, c) | ||
19 | lane_threadname("task("..a..","..b..","..c..")") | ||
20 | --error "111" -- testing error messages | ||
21 | assert(hey) | ||
22 | local v=0 | ||
23 | for i=a,b,c do | ||
24 | v= v+i | ||
25 | end | ||
26 | return v, hey | ||
27 | end | ||
28 | |||
29 | local gc_cb = function(name_, status_) | ||
30 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
31 | end | ||
32 | |||
33 | -- ################################################################################################## | ||
34 | -- ################################################################################################## | ||
35 | -- ################################################################################################## | ||
36 | |||
37 | local tables_match = utils.tables_match | ||
38 | |||
39 | local SLEEP = function(...) | ||
40 | local k, v = lanes.sleep(...) | ||
41 | assert(k == nil and v == "timeout") | ||
42 | end | ||
43 | |||
44 | PRINT("\n\n", "---=== Communications ===---", "\n\n") | ||
45 | |||
46 | local function WR(...) io.stderr:write(...) end | ||
47 | |||
48 | local chunk= function(linda) | ||
49 | local function receive() return linda:receive("->") end | ||
50 | local function send(...) local _res, _err = linda:send("<-", ...) assert(_res == true and _err == nil) end | ||
51 | |||
52 | WR("chunk ", "Lane starts!\n") | ||
53 | |||
54 | local k,v | ||
55 | k,v=receive(); WR("chunk ", v.." received (expecting 1)\n"); assert(k and v==1) | ||
56 | k,v=receive(); WR("chunk ", v.." received (expecting 2)\n"); assert(k and v==2) | ||
57 | k,v=receive(); WR("chunk ", v.." received (expecting 3)\n"); assert(k and v==3) | ||
58 | 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 | ||
59 | k,v=receive(); WR("chunk ", tostring(v).." received (expecting nil)\n"); assert(k and v==nil) | ||
60 | |||
61 | send(4,5,6); WR("chunk ", "4,5,6 sent\n") | ||
62 | send 'aaa'; WR("chunk ", "'aaa' sent\n") | ||
63 | send(nil); WR("chunk ", "nil sent\n") | ||
64 | send { 'a', 'b', 'c', d=10 }; WR("chunk ","{'a','b','c',d=10} sent\n") | ||
65 | |||
66 | k,v=receive(); WR("chunk ", v.." received\n"); assert(v==4) | ||
67 | |||
68 | local subT1 = { "subT1"} | ||
69 | local subT2 = { "subT2"} | ||
70 | send { subT1, subT2, subT1, subT2}; WR("chunk ", "{ subT1, subT2, subT1, subT2} sent\n") | ||
71 | |||
72 | WR("chunk ", "Lane ends!\n") | ||
73 | end | ||
74 | |||
75 | local linda = lanes_linda("communications") | ||
76 | assert(type(linda) == "userdata" and tostring(linda) == "Linda: communications") | ||
77 | -- | ||
78 | -- ["->"] master -> slave | ||
79 | -- ["<-"] slave <- master | ||
80 | |||
81 | WR "test linda:get/set..." | ||
82 | linda:set("<->", "x", "y", "z") | ||
83 | local b,x,y,z = linda:get("<->", 1) | ||
84 | assert(b == 1 and x == "x" and y == nil and z == nil) | ||
85 | local b,x,y,z = linda:get("<->", 2) | ||
86 | assert(b == 2 and x == "x" and y == "y" and z == nil) | ||
87 | local b,x,y,z = linda:get("<->", 3) | ||
88 | assert(b == 3 and x == "x" and y == "y" and z == "z") | ||
89 | local b,x,y,z,w = linda:get("<->", 4) | ||
90 | assert(b == 3 and x == "x" and y == "y" and z == "z" and w == nil) | ||
91 | local k, x = linda:receive("<->") | ||
92 | assert(k == "<->" and x == "x") | ||
93 | local k,y,z = linda:receive(linda.batched, "<->", 2) | ||
94 | assert(k == "<->" and y == "y" and z == "z") | ||
95 | linda:set("<->") | ||
96 | local b,x,y,z,w = linda:get("<->", 4) | ||
97 | assert(b == 0 and x == nil and y == nil and z == nil and w == nil) | ||
98 | WR "ok\n" | ||
99 | |||
100 | local function PEEK(...) return linda:get("<-", ...) end | ||
101 | local function SEND(...) local _res, _err = linda:send("->", ...) assert(_res == true and _err == nil) end | ||
102 | local function RECEIVE() local k,v = linda:receive(1, "<-") return v end | ||
103 | |||
104 | local comms_lane = lanes_gen("io", {gc_cb = gc_cb, name = "auto"}, chunk)(linda) -- prepare & launch | ||
105 | |||
106 | SEND(1); WR("main ", "1 sent\n") | ||
107 | SEND(2); WR("main ", "2 sent\n") | ||
108 | SEND(3); WR("main ", "3 sent\n") | ||
109 | SEND(setmetatable({"should be ignored"},{__lanesconvert=lanes.null})); WR("main ", "__lanesconvert table sent\n") | ||
110 | for i=1,40 do | ||
111 | WR "." | ||
112 | SLEEP(0.0001) | ||
113 | assert(PEEK() == 0) -- nothing coming in, yet | ||
114 | end | ||
115 | SEND(nil); WR("\nmain ", "nil sent\n") | ||
116 | |||
117 | local a,b,c = RECEIVE(), RECEIVE(), RECEIVE() | ||
118 | |||
119 | print("lane status: " .. comms_lane.status) | ||
120 | if comms_lane.status == "error" then | ||
121 | print(comms_lane:join()) | ||
122 | else | ||
123 | WR("main ", tostring(a)..", "..tostring(b)..", "..tostring(c).." received\n") | ||
124 | end | ||
125 | |||
126 | assert(a==4 and b==5 and c==6) | ||
127 | |||
128 | local aaa = RECEIVE(); WR("main ", aaa.." received\n") | ||
129 | assert(aaa=='aaa') | ||
130 | |||
131 | local null = RECEIVE(); WR(tostring(null).." received\n") | ||
132 | assert(null==nil) | ||
133 | |||
134 | local out_t = RECEIVE(); WR(type(out_t).." received\n") | ||
135 | assert(tables_match(out_t, {'a','b','c',d=10})) | ||
136 | |||
137 | assert(PEEK() == 0) | ||
138 | SEND(4) | ||
139 | |||
140 | local complex_table = RECEIVE(); WR(type(complex_table).." received\n") | ||
141 | assert(complex_table[1] == complex_table[3] and complex_table[2] == complex_table[4]) | ||
142 | WR(table.concat({complex_table[1][1],complex_table[2][1],complex_table[3][1],complex_table[4][1]},", ")) | ||
143 | |||
144 | WR("collectgarbage") | ||
145 | comms_lane = nil | ||
146 | collectgarbage() | ||
147 | -- wait | ||
148 | WR("waiting 1s") | ||
149 | SLEEP(1) | ||
diff --git a/unit_tests/scripts/lane/tasking_error.lua b/unit_tests/scripts/lane/tasking_error.lua new file mode 100644 index 0000000..1e2347f --- /dev/null +++ b/unit_tests/scripts/lane/tasking_error.lua | |||
@@ -0,0 +1,49 @@ | |||
1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
3 | local lanes = require_lanes_result_1 | ||
4 | |||
5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
7 | |||
8 | local utils = lanes.require "_utils" | ||
9 | local PRINT = utils.MAKE_PRINT() | ||
10 | |||
11 | local lanes_gen = assert(lanes.gen) | ||
12 | local lanes_linda = assert(lanes.linda) | ||
13 | |||
14 | -- ################################################################################################## | ||
15 | -- ################################################################################################## | ||
16 | -- ################################################################################################## | ||
17 | |||
18 | local gc_cb = function(name_, status_) | ||
19 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
20 | end | ||
21 | |||
22 | PRINT("---=== Tasking (error) ===---", "\n\n") | ||
23 | |||
24 | -- a lane that throws immediately the error value it received | ||
25 | local g = lanes_gen("", {gc_cb = gc_cb}, error) | ||
26 | local errmsg = "ERROR!" | ||
27 | |||
28 | if true then | ||
29 | -- if you index an errored lane, it should throw the error again | ||
30 | local lane = g(errmsg) | ||
31 | assert.fails(function() return lane[1] end) | ||
32 | assert(lane.status == "error") | ||
33 | -- even after indexing, joining a lane in error should give nil,<error> | ||
34 | local a,b = lane:join() | ||
35 | assert(a == nil and string.find(b, errmsg)) | ||
36 | end | ||
37 | |||
38 | if true then | ||
39 | local lane = g(errmsg) | ||
40 | -- after indexing, joining a lane in error should give nil,<error> | ||
41 | local a, b = lane:join() | ||
42 | assert(lane.status == "error") | ||
43 | assert(a == nil and string.find(b, errmsg)) | ||
44 | -- even after joining, indexing should raise an error | ||
45 | assert.fails(function() return lane[1] end) | ||
46 | -- unless we index with a negative value to get the error message | ||
47 | local c = lane[-1] | ||
48 | assert(c == b) | ||
49 | end | ||
diff --git a/unit_tests/scripts/lane/tasking_join_test.lua b/unit_tests/scripts/lane/tasking_join_test.lua new file mode 100644 index 0000000..8f2d4db --- /dev/null +++ b/unit_tests/scripts/lane/tasking_join_test.lua | |||
@@ -0,0 +1,65 @@ | |||
1 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
2 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
3 | local lanes = require_lanes_result_1 | ||
4 | |||
5 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
6 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
7 | |||
8 | local utils = lanes.require "_utils" | ||
9 | local PRINT = utils.MAKE_PRINT() | ||
10 | |||
11 | local lanes_gen = assert(lanes.gen) | ||
12 | local lanes_linda = assert(lanes.linda) | ||
13 | |||
14 | -- ################################################################################################## | ||
15 | -- ################################################################################################## | ||
16 | -- ################################################################################################## | ||
17 | |||
18 | local gc_cb = function(name_, status_) | ||
19 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
20 | end | ||
21 | |||
22 | -- ################################################################################################## | ||
23 | -- ################################################################################################## | ||
24 | -- ################################################################################################## | ||
25 | |||
26 | local SLEEP = function(...) | ||
27 | local k, v = lanes.sleep(...) | ||
28 | assert(k == nil and v == "timeout") | ||
29 | end | ||
30 | |||
31 | PRINT("---=== :join test ===---", "\n\n") | ||
32 | |||
33 | -- NOTE: 'unpack()' cannot be used on the lane handle; it will always return nil | ||
34 | -- (unless [1..n] has been read earlier, in which case it would seemingly | ||
35 | -- work). | ||
36 | |||
37 | local S= lanes_gen("table", {gc_cb = gc_cb}, | ||
38 | function(arg) | ||
39 | lane_threadname "join test lane" | ||
40 | set_finalizer(function() end) | ||
41 | local aux= {} | ||
42 | for i, v in ipairs(arg) do | ||
43 | table.insert(aux, 1, v) | ||
44 | end | ||
45 | -- unpack was renamed table.unpack in Lua 5.2: cater for both! | ||
46 | return (unpack or table.unpack)(aux) | ||
47 | end) | ||
48 | |||
49 | h= S { 12, 13, 14 } -- execution starts, h[1..3] will get the return values | ||
50 | -- wait a bit so that the lane has a chance to set its debug name | ||
51 | SLEEP(0.5) | ||
52 | print("joining with '" .. h:get_threadname() .. "'") | ||
53 | local a,b,c,d= h:join() | ||
54 | if h.status == "error" then | ||
55 | print(h:get_threadname(), "error: " , a, b, c, d) | ||
56 | else | ||
57 | print(h:get_threadname(), a,b,c,d) | ||
58 | assert(a==14, "a == " .. tostring(a)) | ||
59 | assert(b==13, "b == " .. tostring(b)) | ||
60 | assert(c==12, "c == " .. tostring(c)) | ||
61 | assert(d==nil, "d == " .. tostring(d)) | ||
62 | end | ||
63 | |||
64 | local nameof_type, nameof_name = lanes.nameof(print) | ||
65 | PRINT("name of " .. nameof_type .. " print = '" .. nameof_name .. "'") | ||
diff --git a/unit_tests/scripts/lane/tasking_send_receive_code.lua b/unit_tests/scripts/lane/tasking_send_receive_code.lua new file mode 100644 index 0000000..77a4b12 --- /dev/null +++ b/unit_tests/scripts/lane/tasking_send_receive_code.lua | |||
@@ -0,0 +1,91 @@ | |||
1 | local config = { with_timers = false, strip_functions = false, internal_allocator = "libc"} | ||
2 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() | ||
3 | print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) | ||
4 | local lanes = require_lanes_result_1 | ||
5 | |||
6 | local require_assert_result_1, require_assert_result_2 = require "_assert" | ||
7 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
8 | |||
9 | local utils = lanes.require "_utils" | ||
10 | local PRINT = utils.MAKE_PRINT() | ||
11 | |||
12 | local lanes_gen = assert(lanes.gen) | ||
13 | local lanes_linda = assert(lanes.linda) | ||
14 | |||
15 | -- ################################################################################################## | ||
16 | -- ################################################################################################## | ||
17 | -- ################################################################################################## | ||
18 | |||
19 | local gc_cb = function(name_, status_) | ||
20 | PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") | ||
21 | end | ||
22 | |||
23 | -- ################################################################################################## | ||
24 | -- ################################################################################################## | ||
25 | -- ################################################################################################## | ||
26 | |||
27 | PRINT("---=== Receive & send of code ===---", "\n") | ||
28 | |||
29 | local upvalue = "123" | ||
30 | local tostring = tostring | ||
31 | |||
32 | local function chunk2(linda) | ||
33 | local utils = require "_utils" | ||
34 | local PRINT = utils.MAKE_PRINT() | ||
35 | PRINT("here") | ||
36 | assert(upvalue == "123") -- even when running as separate thread | ||
37 | -- function name & line number should be there even as separate thread | ||
38 | -- | ||
39 | local info= debug.getinfo(1) -- 1 = us | ||
40 | -- | ||
41 | PRINT("linda named-> '" ..tostring(linda).."'") | ||
42 | PRINT "debug.getinfo->" | ||
43 | for k,v in pairs(info) do PRINT(k,v) end | ||
44 | |||
45 | -- some assertions are adjusted depending on config.strip_functions, because it changes what we get out of debug.getinfo | ||
46 | assert(info.nups == (_VERSION == "Lua 5.1" and 3 or 4), "bad nups " .. info.nups) -- upvalue + config + tostring + _ENV (Lua > 5.2 only) | ||
47 | assert(info.what == "Lua", "bad what") | ||
48 | --assert(info.name == "chunk2") -- name does not seem to come through | ||
49 | assert(config.strip_functions and info.source=="=?" or string.match(info.source, "^@.*tasking_send_receive_code.lua$"), "bad info.source " .. info.source) | ||
50 | assert(config.strip_functions and info.short_src=="?" or string.match(info.short_src, "^.*tasking_send_receive_code.lua$"), "bad info.short_src " .. info.short_src) | ||
51 | -- These vary so let's not be picky (they're there..) | ||
52 | -- | ||
53 | assert(info.linedefined == 32, "bad linedefined") -- start of 'chunk2' | ||
54 | assert(config.strip_functions and info.currentline==-1 or info.currentline > info.linedefined, "bad currentline") -- line of 'debug.getinfo' | ||
55 | assert(info.lastlinedefined > info.currentline, "bad lastlinedefined") -- end of 'chunk2' | ||
56 | local k,func= linda:receive("down") | ||
57 | assert(type(func)=="function", "not a function") | ||
58 | assert(k=="down") | ||
59 | |||
60 | func(linda) | ||
61 | |||
62 | local k,str= linda:receive("down") | ||
63 | assert(str=="ok", "bad receive result") | ||
64 | |||
65 | linda:send("up", function() return ":)" end, "ok2") | ||
66 | end | ||
67 | |||
68 | local linda = lanes_linda("auto") | ||
69 | local t2= lanes_gen("debug,package,string,io", {gc_cb = gc_cb}, chunk2)(linda) -- prepare & launch | ||
70 | linda:send("down", function(linda) linda:send("up", "ready!") end, | ||
71 | "ok") | ||
72 | -- wait to see if the tiny function gets executed | ||
73 | -- | ||
74 | local k,s= linda:receive(1, "up") | ||
75 | if t2.status == "error" then | ||
76 | PRINT("t2 error: " , t2:join()) | ||
77 | assert(false) | ||
78 | end | ||
79 | PRINT(s) | ||
80 | assert(s=="ready!", s .. " is not 'ready!'") | ||
81 | |||
82 | -- returns of the 'chunk2' itself | ||
83 | -- | ||
84 | local k,f= linda:receive("up") | ||
85 | assert(type(f)=="function") | ||
86 | |||
87 | local s2= f() | ||
88 | assert(s2==":)") | ||
89 | |||
90 | local k,ok2= linda:receive("up") | ||
91 | assert(ok2 == "ok2") | ||
diff --git a/unit_tests/scripts/lane/uncooperative_shutdown.lua b/unit_tests/scripts/lane/uncooperative_shutdown.lua new file mode 100644 index 0000000..ce7df57 --- /dev/null +++ b/unit_tests/scripts/lane/uncooperative_shutdown.lua | |||
@@ -0,0 +1,24 @@ | |||
1 | if not package.preload.fixture then | ||
2 | error "can't be run outside of UnitTest framework" | ||
3 | end | ||
4 | local fixture = require "fixture" | ||
5 | |||
6 | local lanes = require "lanes".configure{shutdown_timeout = 0.001, on_state_create = fixture.on_state_create} | ||
7 | |||
8 | -- launch lanes that blocks forever | ||
9 | local lane = function() | ||
10 | local fixture = require "fixture" | ||
11 | fixture.forever() | ||
12 | end | ||
13 | |||
14 | -- the generator | ||
15 | local g1 = lanes.gen("*", lane) | ||
16 | |||
17 | -- launch lane | ||
18 | local h1 = g1() | ||
19 | |||
20 | -- wait until the lane is running | ||
21 | repeat until h1.status == "running" | ||
22 | |||
23 | -- let the script end, Lanes should throw std::logic_error because the lane did not gracefully terminate | ||
24 | lanes.finally(fixture.throwing_finalizer) | ||