332 lines
7.8 KiB
Lua
332 lines
7.8 KiB
Lua
--
|
|
-- BASIC.LUA Copyright (c) 2007-08, Asko Kauppi <akauppi@gmail.com>
|
|
--
|
|
-- Selftests for Lua Lanes
|
|
--
|
|
-- To do:
|
|
-- - ...
|
|
--
|
|
|
|
require "lanes"
|
|
require "assert" -- assert.fails()
|
|
|
|
local lanes_gen= assert( lanes.gen )
|
|
local lanes_linda= assert( lanes.linda )
|
|
|
|
local tostring= assert( tostring )
|
|
|
|
local function PRINT(...)
|
|
local str=""
|
|
for i=1,select('#',...) do
|
|
str= str..tostring(select(i,...)).."\t"
|
|
end
|
|
if io then
|
|
io.stderr:write(str.."\n")
|
|
end
|
|
end
|
|
|
|
|
|
---=== Local helpers ===---
|
|
|
|
local tables_match
|
|
|
|
-- true if 'a' is a subtable of 'b'
|
|
--
|
|
local function subtable( a, b )
|
|
--
|
|
assert( type(a)=="table" and type(b)=="table" )
|
|
|
|
for k,v in pairs(b) do
|
|
if type(v)~=type(a[k]) then
|
|
return false -- not subtable (different types, or missing key)
|
|
elseif type(v)=="table" then
|
|
if not tables_match(v,a[k]) then return false end
|
|
else
|
|
if a[k] ~= v then return false end
|
|
end
|
|
end
|
|
return true -- is a subtable
|
|
end
|
|
|
|
-- true when contents of 'a' and 'b' are identific
|
|
--
|
|
tables_match= function( a, b )
|
|
return subtable( a, b ) and subtable( b, a )
|
|
end
|
|
|
|
|
|
---=== Tasking (basic) ===---
|
|
|
|
local function task( a, b, c )
|
|
--error "111" -- testing error messages
|
|
assert(hey)
|
|
local v=0
|
|
for i=a,b,c do
|
|
v= v+i
|
|
end
|
|
return v, hey
|
|
end
|
|
|
|
local task_launch= lanes_gen( "", { globals={hey=true} }, task )
|
|
-- base stdlibs, normal priority
|
|
|
|
-- 'task_launch' is a factory of multithreaded tasks, we can launch several:
|
|
|
|
local lane1= task_launch( 100,200,3 )
|
|
local lane2= task_launch( 200,300,4 )
|
|
|
|
-- At this stage, states may be "pending", "running" or "done"
|
|
|
|
local st1,st2= lane1.status, lane2.status
|
|
PRINT(st1,st2)
|
|
assert( st1=="pending" or st1=="running" or st1=="done" )
|
|
assert( st2=="pending" or st2=="running" or st2=="done" )
|
|
|
|
-- Accessing results ([1..N]) pends until they are available
|
|
--
|
|
PRINT("waiting...")
|
|
local v1, v1_hey= lane1[1], lane1[2]
|
|
local v2, v2_hey= lane2[1], lane2[2]
|
|
|
|
PRINT( v1, v1_hey )
|
|
assert( v1_hey == true )
|
|
|
|
PRINT( v2, v2_hey )
|
|
assert( v2_hey == true )
|
|
|
|
assert( lane1.status == "done" )
|
|
assert( lane1.status == "done" )
|
|
|
|
|
|
---=== Tasking (cancelling) ===---
|
|
|
|
local task_launch2= lanes_gen( "", { cancelstep=100, globals={hey=true} }, task )
|
|
|
|
local N=999999999
|
|
local lane9= task_launch2(1,N,1) -- huuuuuuge...
|
|
|
|
-- Wait until state changes "pending"->"running"
|
|
--
|
|
local st
|
|
local t0= os.time()
|
|
while os.time()-t0 < 5 do
|
|
st= lane9.status
|
|
io.stderr:write( (i==1) and st.." " or '.' )
|
|
if st~="pending" then break end
|
|
end
|
|
PRINT(" "..st)
|
|
|
|
if st=="error" then
|
|
local _= lane9[0] -- propagate the error here
|
|
end
|
|
if st=="done" then
|
|
error( "Looping to "..N.." was not long enough (cannot test cancellation)" )
|
|
end
|
|
assert( st=="running" )
|
|
|
|
lane9:cancel()
|
|
|
|
local t0= os.time()
|
|
while os.time()-t0 < 5 do
|
|
st= lane9.status
|
|
io.stderr:write( (i==1) and st.." " or '.' )
|
|
if st~="running" then break end
|
|
end
|
|
PRINT(" "..st)
|
|
assert( st == "cancelled" )
|
|
|
|
|
|
---=== Communications ===---
|
|
|
|
local function WR(...) io.stderr:write(...) end
|
|
|
|
local chunk= function( linda )
|
|
|
|
local function receive() return linda:receive( "->" ) end
|
|
local function send(...) linda:send( "<-", ... ) end
|
|
|
|
WR( "Lane starts!\n" )
|
|
|
|
local v
|
|
v=receive(); WR( v.." received\n" ); assert( v==1 )
|
|
v=receive(); WR( v.." received\n" ); assert( v==2 )
|
|
v=receive(); WR( v.." received\n" ); assert( v==3 )
|
|
|
|
send( 1,2,3 ); WR( "1,2,3 sent\n" )
|
|
send 'a'; WR( "'a' sent\n" )
|
|
send { 'a', 'b', 'c', d=10 }; WR( "{'a','b','c',d=10} sent\n" )
|
|
|
|
v=receive(); WR( v.." received\n" ); assert( v==4 )
|
|
|
|
WR( "Lane ends!\n" )
|
|
end
|
|
|
|
local linda= lanes_linda()
|
|
assert( type(linda) == "userdata" )
|
|
--
|
|
-- ["->"] master -> slave
|
|
-- ["<-"] slave <- master
|
|
|
|
local function PEEK() return linda:get("<-") end
|
|
local function SEND(...) linda:send( "->", ... ) end
|
|
local function RECEIVE() return linda:receive( "<-" ) end
|
|
|
|
local t= lanes_gen("io",chunk)(linda) -- prepare & launch
|
|
|
|
SEND(1); WR( "1 sent\n" )
|
|
SEND(2); WR( "2 sent\n" )
|
|
for i=1,100 do
|
|
WR "."
|
|
assert( PEEK() == nil ) -- nothing coming in, yet
|
|
end
|
|
SEND(3); WR( "3 sent\n" )
|
|
|
|
local a,b,c= RECEIVE(), RECEIVE(), RECEIVE()
|
|
WR( a..", "..b..", "..c.." received\n" )
|
|
assert( a==1 and b==2 and c==3 )
|
|
|
|
local a= RECEIVE(); WR( a.." received\n" )
|
|
assert( a=='a' )
|
|
|
|
local a= RECEIVE(); WR( type(a).." received\n" )
|
|
assert( tables_match( a, {'a','b','c',d=10} ) )
|
|
|
|
assert( PEEK() == nil )
|
|
SEND(4)
|
|
|
|
|
|
---=== Stdlib naming ===---
|
|
|
|
local function io_os_f()
|
|
assert(io)
|
|
assert(os)
|
|
assert(print)
|
|
return true
|
|
end
|
|
|
|
local f1= lanes_gen( "io,os", io_os_f ) -- any delimiter will do
|
|
local f2= lanes_gen( "io+os", io_os_f )
|
|
local f3= lanes_gen( "io,os,base", io_os_f )
|
|
|
|
assert.fails( function() lanes_gen( "xxx", io_os_f ) end )
|
|
|
|
assert( f1()[1] )
|
|
assert( f2()[1] )
|
|
assert( f3()[1] )
|
|
|
|
|
|
---=== Comms criss cross ===---
|
|
|
|
-- We make two identical lanes, which are using the same Linda channel.
|
|
--
|
|
local tc= lanes_gen( "io",
|
|
function( linda, ch_in, ch_out )
|
|
|
|
local function STAGE(str)
|
|
io.stderr:write( ch_in..": "..str.."\n" )
|
|
linda:send( nil, ch_out, str )
|
|
local v= linda:receive( nil, ch_in )
|
|
assert(v==str)
|
|
end
|
|
STAGE("Hello")
|
|
STAGE("I was here first!")
|
|
STAGE("So waht?")
|
|
end
|
|
)
|
|
|
|
local linda= lanes_linda()
|
|
|
|
local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms
|
|
|
|
local _= a[1],b[1] -- waits until they are both ready
|
|
|
|
|
|
---=== Receive & send of code ===---
|
|
|
|
local upvalue="123"
|
|
|
|
local function chunk2( linda )
|
|
assert( upvalue=="123" ) -- even when running as separate thread
|
|
|
|
-- function name & line number should be there even as separate thread
|
|
--
|
|
local info= debug.getinfo(1) -- 1 = us
|
|
--
|
|
for k,v in pairs(info) do PRINT(k,v) end
|
|
|
|
assert( info.nups == 2 ) -- one upvalue + PRINT
|
|
assert( info.what == "Lua" )
|
|
|
|
--assert( info.name == "chunk2" ) -- name does not seem to come through
|
|
assert( string.match( info.source, "^@tests[/\\]basic.lua$" ) )
|
|
assert( string.match( info.short_src, "^tests[/\\]basic.lua$" ) )
|
|
|
|
-- These vary so let's not be picky (they're there..)
|
|
--
|
|
assert( info.linedefined > 200 ) -- start of 'chunk2'
|
|
assert( info.currentline > info.linedefined ) -- line of 'debug.getinfo'
|
|
assert( info.lastlinedefined > info.currentline ) -- end of 'chunk2'
|
|
|
|
local func,k= linda:receive( "down" )
|
|
assert( type(func)=="function" )
|
|
assert( k=="down" )
|
|
|
|
func(linda)
|
|
|
|
local str= linda:receive( "down" )
|
|
assert( str=="ok" )
|
|
|
|
linda:send( "up", function() return ":)" end, "ok2" )
|
|
end
|
|
|
|
local linda= lanes.linda()
|
|
|
|
local t2= lanes_gen( "debug,string", chunk2 )(linda) -- prepare & launch
|
|
|
|
linda:send( "down", function(linda) linda:send( "up", "ready!" ) end,
|
|
"ok" )
|
|
|
|
-- wait to see if the tiny function gets executed
|
|
--
|
|
local s= linda:receive( "up" )
|
|
PRINT(s)
|
|
assert( s=="ready!" )
|
|
|
|
-- returns of the 'chunk2' itself
|
|
--
|
|
local f= linda:receive( "up" )
|
|
assert( type(f)=="function" )
|
|
|
|
local s2= f()
|
|
assert( s2==":)" )
|
|
|
|
local ok2= linda:receive( "up" )
|
|
assert( ok2 == "ok2" )
|
|
|
|
|
|
---=== :join test ===---
|
|
|
|
-- NOTE: 'unpack()' cannot be used on the lane handle; it will always return nil
|
|
-- (unless [1..n] has been read earlier, in which case it would seemingly
|
|
-- work).
|
|
|
|
local S= lanes_gen( "table",
|
|
function(arg)
|
|
aux= {}
|
|
for i, v in ipairs(arg) do
|
|
table.insert (aux, 1, v)
|
|
end
|
|
return unpack(aux)
|
|
end )
|
|
|
|
h= S { 12, 13, 14 } -- execution starts, h[1..3] will get the return values
|
|
|
|
local a,b,c,d= h:join()
|
|
assert(a==14)
|
|
assert(b==13)
|
|
assert(c==12)
|
|
assert(d==nil)
|
|
|
|
--
|
|
io.stderr:write "Done! :)\n"
|