From 89d9c98af1ac352ba4d49d660e61b0853d6e1a86 Mon Sep 17 00:00:00 2001 From: Peter Drahoš Date: Fri, 1 Oct 2010 03:22:32 +0200 Subject: Import to git --- tests/argtable.lua | 38 ++++++ tests/assert.lua | 318 ++++++++++++++++++++++++++++++++++++++++++++ tests/atomic.lua | 18 +++ tests/basic.lua | 331 ++++++++++++++++++++++++++++++++++++++++++++++ tests/cyclic.lua | 64 +++++++++ tests/ehynes.lua | 52 ++++++++ tests/error.lua | 47 +++++++ tests/fibonacci.lua | 75 +++++++++++ tests/fifo.lua | 43 ++++++ tests/finalizer.lua | 81 ++++++++++++ tests/hangtest.lua | 26 ++++ tests/irayo_closure.lua | 35 +++++ tests/irayo_recursive.lua | 18 +++ tests/keeper.lua | 47 +++++++ tests/launchtest.lua | 78 +++++++++++ tests/objects.lua | 76 +++++++++++ tests/perftest.lua | 184 ++++++++++++++++++++++++++ tests/recursive.lua | 21 +++ tests/require.lua | 30 +++++ tests/timer.lua | 93 +++++++++++++ 20 files changed, 1675 insertions(+) create mode 100644 tests/argtable.lua create mode 100644 tests/assert.lua create mode 100644 tests/atomic.lua create mode 100644 tests/basic.lua create mode 100644 tests/cyclic.lua create mode 100644 tests/ehynes.lua create mode 100644 tests/error.lua create mode 100644 tests/fibonacci.lua create mode 100644 tests/fifo.lua create mode 100644 tests/finalizer.lua create mode 100644 tests/hangtest.lua create mode 100644 tests/irayo_closure.lua create mode 100644 tests/irayo_recursive.lua create mode 100644 tests/keeper.lua create mode 100644 tests/launchtest.lua create mode 100644 tests/objects.lua create mode 100644 tests/perftest.lua create mode 100644 tests/recursive.lua create mode 100644 tests/require.lua create mode 100644 tests/timer.lua (limited to 'tests') diff --git a/tests/argtable.lua b/tests/argtable.lua new file mode 100644 index 0000000..5ed5d4e --- /dev/null +++ b/tests/argtable.lua @@ -0,0 +1,38 @@ +-- +-- ARGTABLE.LUA Copyright (c) 2007, Asko Kauppi +-- +-- Command line parameter parsing +-- +-- NOTE: Wouldn't hurt having such a service built-in to Lua...? :P +-- + +local m= {} + +-- tbl= argtable(...) +-- +-- Returns a table with 1..N indices being 'value' parameters, and any +-- "-flag[=xxx]" or "--flag[=xxx]" parameters set to { flag=xxx/true }. +-- +-- In other words, makes handling command line parameters simple. :) +-- +-- 15 --> { 15 } +-- -20 --> { -20 } +-- -a --> { ['a']=true } +-- --some=15 --> { ['some']=15 } +-- --more=big --> { ['more']='big' } +-- +function m.argtable(...) + local ret= {} + for i=1,select('#',...) do + local v= select(i,...) + local flag,val= string.match( v, "^%-+([^=]+)%=?(.*)" ) + if flag and not tonumber(v) then + ret[flag]= (val=="") and true or tonumber(val) or val + else + table.insert( ret, v ) -- 1..N + end + end + return ret +end + +return m diff --git a/tests/assert.lua b/tests/assert.lua new file mode 100644 index 0000000..85febfb --- /dev/null +++ b/tests/assert.lua @@ -0,0 +1,318 @@ +-- +-- ASSERT.LUA Copyright (c) 2006-07, +-- +-- Converting the Lua 'assert' function into a namespace table (without +-- breaking compatibility with the basic 'assert()' calling). +-- +-- This module allows shorthand use s.a. 'assert.table()' for asserting +-- variable types, and is also being used by Lua-super constraints system +-- for testing function parameter & return types. +-- +-- All in all, a worthy module and could be part of Lua future versions. +-- +-- Note: the 'assert' table is available for your own assertions, too. Just add +-- more functions s.a. 'assert.myobj()' to check for custom invariants. +-- They will then be available for the constraints check, too. +-- +-- Author: +-- +--[[ +/****************************************************************************** +* Lua 5.1.1 support and extension functions (assert.lua) +* +* Copyright (C) 2006-07, Asko Kauppi. +* +* NOTE: This license concerns only the particular source file; not necessarily +* the project with which it has been delivered (the project may have a more +* restrictive license, s.a. [L]GPL). +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ +]]-- + +local m= { _info= { MODULE= "Assert.* functions for constraints, and unit testing", + AUTHOR= "akauppi@gmail.com", + VERSION= 20070603, -- last change (yyyymmdd) + LICENSE= "MIT/X11" } } + +-- Global changes: +-- 'assert' redefined, in a backwards compatible way +-- +-- Module functions: +-- none + +assert( type(assert) == "function" ) -- system assert function + +----- +-- Integer range: INT_MIN..INT_MAX +-- +local function try_maxint( n ) + return (n > n-1) and n-1 -- false when outside the integer range +end + +local INT_MAX= + try_maxint( 2^64 ) or + try_maxint( 2^32 ) or + try_maxint( 2^24 ) or -- float (24-bit mantissa) + assert( false ) + +local INT_MIN= -(INT_MAX+1) + + +---=== assert.*() ===--- + +local at_msg= "type assertion error" -- TBD: better messages, about that exact situation +local av_msg= "value assertion error" + +-- void= _assert( val [, msg_str [, lev_uint]] ) +-- +local function _assert( cond, msg, lev ) + -- original 'assert' provides no level override, so we use 'error'. + -- + if not cond then + error( msg or "assertion failed!", (lev or 1)+1 ) + end +end + +-- Note: following code uses the _new_ 'assert()' by purpose, since it provides +-- a level override (original doesn't) +-- +local function assert_v( v0 ) + return function(v,msg) + _assert( v == v0, msg or av_msg, 2 ) + return v + end +end +local function assert_t( str ) + return function(v,msg) + _assert( type(v) == str, msg or at_msg, 2 ) + return v + end +end +local function assert_t2( str ) + return function(v,subtype,msg) + local t,st= type(v) + _assert( t==str and ((not subtype) or (st==subtype)), + msg or at_msg, 2 ) + return v + end +end + +assert= + { + __call= function(_,v,msg) -- plain 'assert()' (compatibility) + if v then return v end + _assert( v, msg, 2 ) + end, + + -- Hopefully, Lua will allow use of 'nil', 'function' and other reserved words as table + -- shortcuts in the future (5.1.1 does not). + -- + ["nil"]= assert_v( nil ), + boolean= assert_t "boolean", + table= assert_t2 "table", + ["function"]= assert_t "function", + userdata= assert_t2 "userdata", + + string= function( v, msg ) + local s= tostring(v) + _assert( s, msg or at_msg, 2 ) + return v + end, + + char= function( v, msg ) + -- 'char' is _not_ doing int->string conversion + _assert( type(v)=="string" and v:len()==1, msg or at_msg, 2 ) + return v + end, + + number= function( v, msg ) + _assert( tonumber(v), msg or at_msg, 2 ) + return v + end, + + int= function( v, msg ) + local n= tonumber(v) + _assert( n and (n >= INT_MIN) and (n <= INT_MAX) and math.floor(n) == n, + msg or at_msg, 2 ) + return v + end, + + uint= function( v, msg ) + local n= tonumber(v) + -- unsigned integer upper range is the same as integers' (there's no + -- real unsigned within the Lua) + _assert( n and (n >= 0) and (n <= INT_MAX) and math.floor(n) == n, + msg or at_msg, 2 ) + return v + end, + + ['true']= assert_v( true ), + ['false']= assert_v( false ), + + string_or_table= function( v, msg ) + assert( tostring(v) or type(v)=="table", msg or at_msg, 2 ) + return v + end, + + number_or_string= function( v, msg ) + assert( tonumber(v) or type(v)=="table", msg or at_msg, 2 ) + return v + end, + + any= function( v, msg ) + assert( v ~= nil, msg or av_msg, 2 ) + return v + end, + + -- Range assertion, with extra parameters per instance + -- + -- Note: values may be of _any_ type that can do >=, <= comparisons. + -- + range= function( lo, hi ) + _assert( lo and hi and lo <= hi, "Bad limits", 2 ) + -- make sure the limits make sense (just once) + + return function(v,msg,lev) + if ( (lo and vhi) ) then + msg= msg or "not in range: ("..(lo or "")..","..(hi or "")..")" + _assert( false, msg, 2 ) + end + return v + end + end, + + -- Table contents assertion + -- - no unknown (non-numeric) keys are allowed + -- - numeric keys are ignored + -- + -- Constraints patch should point to this, when using the ":{ ... }" constraint. + -- + ["{}"]= function( tbl ) + + -- check all keys in 't' (including numeric, if any) that they do exist, + -- and carry the right type + -- + local function subf1(v,t,msg,lev) + _assert(lev) + for k,f in pairs(t) do + -- 'f' is an assert function, or subtable + local ft= type(f) + if ft=="function" then + f( v[k], msg, lev+1 ) + elseif ft=="table" then + _assert( type(v[k])=="table", msg or "no subtable "..tostring(k), lev+1 ) + subf1( v[k], f, msg, lev+1 ) + else + error( "Bad constraints table for '"..tostring(k).."'! (not a function)", lev+1 ) + end + end + end + + -- check there are no other (non-numeric) keys in 'v' + local function subf2(v,t,msg,lev) + _assert(lev) + for k,vv in pairs(v) do + if type(k)=="number" then + -- skip them + elseif not t[k] then + _assert( false, msg or "extra field: '"..tostring(k).."'", lev+1 ) + elseif type(vv)=="table" then + subf2( vv, t[k], msg, lev+1 ) + end + end + end + + _assert( type(tbl)=="table", "Wrong parameter to assert['{}']" ) + + return function( v, msg, lev ) + lev= (lev or 1)+1 + _assert( type(v)=="table" ,msg, lev ) + subf1( v, tbl, msg, lev ) + subf2( v, tbl, msg, lev ) + return v + end + end, + + -- ... +} +setmetatable( assert, assert ) + +assert.void= assert["nil"] + + +----- +-- void= assert.fails( function [,err_msg_str] ) +-- +-- Special assert function, to make sure the call within it fails, and gives a +-- specific error message (to be used in unit testing). +-- +function assert.fails( func_block, err_msg ) + -- + local st,err= pcall( func_block ) + if st then + _assert( false, "Block expected to fail, but didn't.", 2 ) + elseif err_msg and err ~= err_msg then + _assert( false, "Failed with wrong error message: \n".. + "'"..err.."'\nexpected: '"..err_msg.."'", 2 ) + end +end + + +----- +-- void= assert.failsnot( function [,err_msg_str] ) +-- +-- Similar to 'assert.fails' but expects the code to survive. +-- +function assert.failsnot( func_block, err_msg ) + -- + local st,err= pcall( func_block ) + if not st then + _assert( false, "Block expected NOT to fail, but did.".. + (err and "\n\tError: '"..err.."'" or ""), 2 ) + end +end + + +----- +-- void= assert.nilerr( function [,err_msg_str] ) +-- +-- Expects the function to return with 'nil,err' failure code, with +-- optionally error string matching. Similar to --> 'assert.fails()' +-- +function assert.nilerr( func_block, err_msg ) + -- + local v,err= func_block() + _assert( v==nil, "Expected to return nil, but didn't: "..tostring(v), 2 ) + if err_msg and err ~= err_msg then + _assert( false, "Failed with wrong error message: \n".. + "'"..err.."'\nexpected: '"..err_msg.."'", 2 ) + end +end + + +-- Sanity check +-- +assert( true ) +assert.fails( function() assert( false ) end ) +assert.fails( function() assert( nil ) end ) + + +return m -- just info diff --git a/tests/atomic.lua b/tests/atomic.lua new file mode 100644 index 0000000..a027453 --- /dev/null +++ b/tests/atomic.lua @@ -0,0 +1,18 @@ +-- +-- ATOMIC.LUA +-- +-- Test program for Lua Lanes +-- + +require "lanes" + +local linda= lanes.linda() +local key= "$" + +local f= lanes.genatomic( linda, key, 5 ) + +local v +v= f(); print(v); assert(v==6) +v= f(-0.5); print(v); assert(v==5.5) + +v= f(-10); print(v); assert(v==-4.5) diff --git a/tests/basic.lua b/tests/basic.lua new file mode 100644 index 0000000..ee31ed1 --- /dev/null +++ b/tests/basic.lua @@ -0,0 +1,331 @@ +-- +-- BASIC.LUA Copyright (c) 2007-08, Asko Kauppi +-- +-- 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" diff --git a/tests/cyclic.lua b/tests/cyclic.lua new file mode 100644 index 0000000..06452bd --- /dev/null +++ b/tests/cyclic.lua @@ -0,0 +1,64 @@ +-- +-- CYCLIC.LUA +-- +-- Test program for Lua Lanes +-- + +require "lanes" + +local table_concat= assert(table.concat) + +local function WR(str,...) + for i=1,select('#',...) do + str= str.."\t"..tostring( select(i,...) ) + end + io.stderr:write( str..'\n' ) +end + +local function same(k,l) + return k==l and "same" or ("NOT SAME: "..k.." "..l) +end + +local a= {} +local b= {a} +a[1]= b + +-- Getting the tables as upvalues should still have the <-> linkage +-- +local function lane1() + WR( "Via upvalue: ", same(a,b[1]), same(a[1],b) ) + assert( a[1]==b ) + assert( b[1]==a ) +end +local L1= lanes.gen( "io", lane1 )() + -- ...running + +-- Getting the tables as parameters should also keep the linkage +-- +local function lane2( aa, bb ) + WR( "Via parameters:", same(aa,bb[1]), same(aa[1],bb) ) + assert( aa[1]==bb ) + assert( bb[1]==aa ) +end +local L2= lanes.gen( "io", lane2 )( a, b ) + -- ...running + +-- Really unnecessary, but let's try a directly recursive table +-- +c= {} +c.a= c + +local function lane3( cc ) + WR( "Directly recursive: ", same(cc, cc.a) ) + assert( cc and cc.a==cc ) +end +local L3= lanes.gen("io", lane3)(c) + +-- Without a wait, exit from the main lane will close the process +-- +-- Waiting for multiple lanes at once could be done using a Linda +-- (but we're okay waiting them in order) +-- +L1:join() +L2:join() +L3:join() diff --git a/tests/ehynes.lua b/tests/ehynes.lua new file mode 100644 index 0000000..4cc370e --- /dev/null +++ b/tests/ehynes.lua @@ -0,0 +1,52 @@ +-- +-- Test from +-- +require 'lanes' + +local function PRINT_FMT( fmt, ... ) + io.stderr:write( string.format(fmt,...).."\n" ) +end + +-- a linda for sending messages +local linda = lanes.linda() + +-- a linda message receiver +local receiver_gen = lanes.gen( 'base', 'os', 'string', 'io', + function (message_name) + PRINT_FMT( 'receiver for message %s entered', message_name ) + local n = 1 + while linda:receive(message_name) do + PRINT_FMT( '%s %d receieved', message_name, n ) + n = n + 1 + end + end +) + +-- create a receiver +local receiver1 = receiver_gen('message') + +-- create a second receiver (a second receiver in the same linda +-- appears to be needed to trigger the delays) +-- +-- AKa 4-Aug-2008: No, with svn version it isn't. But it causes the 2nd +-- message to be hanging... +-- +local receiver2 = receiver_gen('another message') + +-- a function to pause and log the execution for debugging +local function logf(s, f, ...) + os.execute('sleep 1') + PRINT_FMT( "*** %s", s ) + f(...) +end + +-- first message sent is received right away +logf('first message sent', linda.send, linda, 'message', true) + +-- second message sent is not received immediatly +logf('second message sent', linda.send, linda, 'message', true) + +-- third message sent triggers receipt of both second and third messages +logf('third message sent', linda.send, linda, 'message', true) + +logf('all done', function() end) diff --git a/tests/error.lua b/tests/error.lua new file mode 100644 index 0000000..673bcb5 --- /dev/null +++ b/tests/error.lua @@ -0,0 +1,47 @@ +-- +-- Error reporting +-- +-- Note: this code is supposed to end in errors; not included in 'make test' +-- + +require "lanes" + +local function lane() + + local subf= function() -- this so that we can see the call stack + --error "aa" + error({}) + error(error) + end + local subf2= function() + subf() + end + subf2() +end + +local function cleanup(err) +end + +local lgen = lanes.gen("*", { --[[finalizer=cleanup]] }, lane) + +--- +io.stderr:write( "\n** Error catching **\n" ) +-- +local h= lgen() +local _,err,stack= h:join() -- wait for the lane (no automatic error propagation) + +if err then + assert( type(stack)=="table" ) + io.stderr:write( "Lane error: "..tostring(err).."\n" ) + + io.stderr:write( "\t", table.concat(stack,"\n\t"), "\n" ); +end + +--- +io.stderr:write( "\n** Error propagation **\n" ) +-- +local h2= lgen() +local _= h2[0] +assert(false) -- does NOT get here + +--never ends diff --git a/tests/fibonacci.lua b/tests/fibonacci.lua new file mode 100644 index 0000000..8867e14 --- /dev/null +++ b/tests/fibonacci.lua @@ -0,0 +1,75 @@ +-- +-- FIBONACCI.LUA Copyright (c) 2007-08, Asko Kauppi +-- +-- Parallel calculation of Fibonacci numbers +-- +-- A sample of task splitting like Intel TBB library does. +-- +-- References: +-- Intel Threading Building Blocks, 'test all' +-- +-- + +-- Need to say it's 'local' so it can be an upvalue +-- +local lanes= require "lanes" + +local function WR(str) + io.stderr:write( str.."\n" ) +end + +-- Threshold for serial calculation (optimal depends on multithreading fixed +-- cost and is system specific) +-- +local KNOWN= { [0]=0, 1,1,2,3,5,8,13,21,34,55,89,144 } + +-- +-- uint= fib( n_uint ) +-- +local function fib( n ) + -- + local sum + local floor= assert(math.floor) + + WR( "fib("..n..")" ) + + if n <= #KNOWN then + sum= KNOWN[n] + else + -- Splits into two; this task remains waiting for the results + -- + local gen_f= lanes.gen( "io,math,debug", fib ) + + local n1=floor(n/2) +1 + local n2=floor(n/2) -1 + n%2 + + WR( "splitting "..n.." -> "..n1.." "..n2 ) + + local a= gen_f( n1 ) + local b= gen_f( n2 ) + + -- children running... + + local a2= a[1]^2 + local b2= b[1]^2 + + sum = (n%2==1) and a2+b2 or a2-b2 + end + + io.stderr:write( "fib("..n..") = "..sum.."\n" ) + + return sum +end + +-- +-- Right answers from: +-- +local right= { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676220, 23416728348467684, 37889062373143900, 61305790721611580, 99194853094755490, 160500643816367070, 259695496911122560, 420196140727489660, 679891637638612200, 1100087778366101900, 1779979416004714000, 2880067194370816000, 4660046610375530000, 7540113804746346000, 12200160415121877000, 19740274219868226000, 31940434634990105000, 51680708854858334000, 83621143489848440000, 135301852344706780000, 218922995834555200000 +} +assert( #right==99 ) + +local N= 80 +local res= fib(N) +print( right[N] ) +assert( res==right[N] ) + diff --git a/tests/fifo.lua b/tests/fifo.lua new file mode 100644 index 0000000..898b04d --- /dev/null +++ b/tests/fifo.lua @@ -0,0 +1,43 @@ +-- +-- FIFO.LUA +-- +-- Sample program for Lua Lanes +-- + +require "lanes" + +local linda= lanes.linda() +local atomic_inc= lanes.genatomic( linda, "FIFO_n" ) + +assert( atomic_inc()==1 ) +assert( atomic_inc()==2 ) + +local function FIFO() + local my_channel= "FIFO"..atomic_inc() + + return { + -- Giving explicit 'nil' timeout allows numbers to be used as 'my_channel' + -- + send= function(...) linda:send( nil, my_channel, ... ) end, + receive= function(timeout) linda:receive( timeout, my_channel ) end + } +end + +local A= FIFO() +local B= FIFO() + +print "Sending to A.." +A:send( 1,2,3,4,5 ) + +print "Sending to B.." +B:send( 'a','b','c' ) + +print "Reading A.." +print( A:receive( 1.0 ) ) + +print "Reading B.." +print( B:receive( 2.0 ) ) + +-- Note: A and B can be passed between threads, or used as upvalues +-- by multiple threads (other parts will be copied but the 'linda' +-- handle is shared userdata and will thus point to the single place) diff --git a/tests/finalizer.lua b/tests/finalizer.lua new file mode 100644 index 0000000..c94b36d --- /dev/null +++ b/tests/finalizer.lua @@ -0,0 +1,81 @@ +-- +-- Test resource cleanup +-- +-- This feature was ... by discussion on the Lua list about exceptions. +-- The idea is to always run a certain block at exit, whether due to success +-- or error. Normally, 'pcall' would be used for such but as Lua already +-- does that, simply giving a 'cleanup=function' parameter is a logical +-- thing to do. -- AKa 22-Jan-2009 +-- + +require "lanes" + +local FN= "finalizer-test.tmp" + +local cleanup + +local which= os.time() % 2 -- 0/1 + +local function lane() + + set_finalizer(cleanup) + + local f,err= io.open(FN,"w") + if not f then + error( "Could not create "..FN..": "..err ) + end + + f:write( "Test file that should get removed." ) + + io.stderr:write( "File "..FN.." created\n" ) + + if which==0 then + error("aa") -- exception here; the value needs NOT be a string + end + + -- no exception +end + +-- +-- This is called at the end of the lane; whether succesful or not. +-- Gets the 'error()' parameter as parameter ('nil' if normal return). +-- +cleanup= function(err) + + -- An error in finalizer will override an error (or success) in the main + -- chunk. + -- + --error( "This is important!" ) + + if err then + io.stderr:write( "Cleanup after error: "..tostring(err).."\n" ) + else + io.stderr:write( "Cleanup after normal return\n" ) + end + + local _,err2= os.remove(FN) + assert(not err2) -- if this fails, it will be shown in the calling script + -- as an error from the lane itself + + io.stderr:write( "Removed file "..FN.."\n" ) +end + +local lgen = lanes.gen("*", lane) + +io.stderr:write "Launching the lane!\n" + +local h= lgen() + +local _,err,stack= h:join() -- wait for the lane (no automatic error propagation) +if err then + assert(stack) + io.stderr:write( "Lane error: "..tostring(err).."\n" ) + io.stderr:write( "\t", table.concat(stack,"\t\n"), "\n" ) +end + +local f= io.open(FN,"r") +if f then + error( "CLEANUP DID NOT WORK: "..FN.." still exists!" ) +end + +io.stderr:write "Finished!\n" diff --git a/tests/hangtest.lua b/tests/hangtest.lua new file mode 100644 index 0000000..d0bbea4 --- /dev/null +++ b/tests/hangtest.lua @@ -0,0 +1,26 @@ +-- +-- Test case for hang on [1]s and :join()s. +-- + +require "lanes" + +local function ret(b) + return b +end +local lgen = lanes.gen("*", {}, ret) + +for i=1,10000 do + local ln = lgen(i) + + print( "getting result for "..i ) + + -- Hangs here forever every few hundred runs or so, + -- can be illustrated by putting another print() statement + -- after, which will never be called. + -- + local result = ln[1]; + + assert (result == i); +end + +print "Finished!" diff --git a/tests/irayo_closure.lua b/tests/irayo_closure.lua new file mode 100644 index 0000000..faf08fd --- /dev/null +++ b/tests/irayo_closure.lua @@ -0,0 +1,35 @@ +-- +-- Bugs filed by irayo Jul-2008 +-- +--[[ +"Another issue I've noticed is trying to pass a table with a function +that uses closures in it as a global variable into a new lane. This +causes a segmentation fault and it appears to be related to the +luaG_inter_move function near line 835-836 or so in lanes.c, but I +haven't investigated further. +e.g. { globals = { data = 1, func = function() useclosurehere() end } }" +]] + +require "lanes" + +local function testrun() + assert( print ) + assert( data==1 ) + assert( type(func)=="function" ) + func() -- "using the closure" + return true +end + +-- Should also work without these lines, but currently doesn't (bug in Lanes, +-- a function thrown over isn't connected to receiving lane's globals) +-- +--local print=print +--local assert=assert + +local function useclosurehere() + assert( print ) + print "using the closure" +end + +local lane= lanes.gen( "", { globals = { data=1, func=useclosurehere } }, testrun )() +print(lane[1]) diff --git a/tests/irayo_recursive.lua b/tests/irayo_recursive.lua new file mode 100644 index 0000000..82e5a54 --- /dev/null +++ b/tests/irayo_recursive.lua @@ -0,0 +1,18 @@ +-- +-- Bugs filed by irayo Jul-2008 +-- +--[[ +This code showed lack of caching 'select', 'type' etc. in 'src/lanes.lua'. +]] +local function recurse() + print("level "..i); + if i > 10 then return "finished" end + + require "lanes" + + local lane = lanes.gen( "*", { globals = { ["i"]= i + 1 } }, recurse ) () + return lane[1] +end + +i = 0; +recurse() diff --git a/tests/keeper.lua b/tests/keeper.lua new file mode 100644 index 0000000..5c8c23a --- /dev/null +++ b/tests/keeper.lua @@ -0,0 +1,47 @@ +-- +-- KEEPER.LUA +-- +-- Test program for Lua Lanes +-- + +require "lanes" + +local function keeper(linda) + local mt= { + __index= function( _, key ) + return linda:get( key ) + end, + __newindex= function( _, key, val ) + linda:set( key, val ) + end + } + return setmetatable( {}, mt ) +end + +-- +local lindaA= lanes.linda() +local A= keeper( lindaA ) + +local lindaB= lanes.linda() +local B= keeper( lindaB ) + +A.some= 1 +print( A.some ) +assert( A.some==1 ) + +B.some= "hoo" +assert( B.some=="hoo" ) +assert( A.some==1 ) + +function lane() + local a= keeper(lindaA) + print( a.some ) + assert( a.some==1 ) + a.some= 2 +end + +local h= lanes.gen( "io", lane )() +h:join() + +print( A.some ) -- 2 +assert( A.some==2 ) diff --git a/tests/launchtest.lua b/tests/launchtest.lua new file mode 100644 index 0000000..5e3037f --- /dev/null +++ b/tests/launchtest.lua @@ -0,0 +1,78 @@ +-- +-- LAUNCHTEST.LUA Copyright (c) 2007-08, Asko Kauppi +-- +-- Tests launching speed of N threads +-- +-- Usage: +-- [time] lua -lstrict launchtest.lua [threads] [-libs[=io,os,math,...]] +-- +-- threads: number of threads to launch (like: 2000) :) +-- libs: combination of "os","io","math","package", ... +-- just "-libs" for all libraries +-- +-- Note: +-- One _can_ reach the system threading level, ie. doing 10000 on +-- PowerBook G4: +-- << +-- pthread_create( ref, &a, lane_main, data ) failed @ line 316: 35 +-- Command terminated abnormally. +-- << +-- +-- (Lua Lanes _can_ be made tolerable to such congestion cases. Just +-- currently, it is not. btw, 5000 seems to run okay - system limit +-- being 2040 simultaneous threads) +-- +-- To do: +-- - ... +-- + +local N= 1000 -- threads/loops to use +local M= 1000 -- sieves from 1..M +local LIBS= nil -- default: load no libraries + +local function HELP() + io.stderr:write( "Usage: lua launchtest.lua [threads] [-libs[=io,os,math,...]]\n" ) + exit(1) +end + +local m= require "argtable" +local argtable= assert(m.argtable) + +for k,v in pairs( argtable(...) ) do + if k==1 then N= tonumber(v) or HELP() + elseif k=="libs" then LIBS= (v==true) and "*" or v + else HELP() + end +end + +require "lanes" + +local g= lanes.gen( LIBS, function(i) + --io.stderr:write( i.."\t" ) + return i + end ) + +local t= {} + +for i=1,N do + t[i]= g(i) +end + +if false then + -- just finish here, without waiting for threads to finish + -- + local st= t[N].status + print(st) -- if that is "done", they flew already! :) +else + -- mark that all have been launched, now wait for them to return + -- + io.stderr:write( N.." lanes launched.\n" ) + + for i=1,N do + local rc= t[i]:join() + assert( rc==i ) + end + + io.stderr:write( N.." lanes finished.\n" ) +end + diff --git a/tests/objects.lua b/tests/objects.lua new file mode 100644 index 0000000..8f56a5f --- /dev/null +++ b/tests/objects.lua @@ -0,0 +1,76 @@ +-- +-- OBJECTS.LUA +-- +-- Tests that objects (metatables) can be passed between lanes. +-- + +require "lanes" + +local linda= lanes.linda() + +local start_lane= lanes.gen( "io", + function( obj1 ) + + assert( obj1.v ) + assert( obj1.print ) + + assert( obj1 ) + local mt1= getmetatable(obj1) + assert(mt1) + + local obj2= linda:receive("") + assert( obj2 ) + local mt2= getmetatable(obj2) + assert(mt2) + assert( mt1==mt2 ) + + local v= obj1:print() + assert( v=="aaa" ) + + v= obj2:print() + assert( v=="bbb" ) + + return true + end +) + + +local WR= function(str) + io.stderr:write( tostring(str).."\n") +end + + +-- Lanes identifies metatables and copies them only once per each lane. +-- +-- Having methods in the metatable will make passing objects lighter than +-- having the methods 'fixed' in the object tables themselves. +-- +local o_mt= { + __index= function( me, key ) + if key=="print" then + return function() WR(me.v); return me.v end + end + end +} + +local function obj_gen(v) + local o= { v=v } + setmetatable( o, o_mt ) + return o +end + +local a= obj_gen("aaa") +local b= obj_gen("bbb") + +assert( a and b ) + +local mt_a= getmetatable(a) +local mt_b= getmetatable(b) +assert( mt_a and mt_a==mt_b ) + +local h= start_lane(a) -- 1st object as parameter + +linda:send( "", b ) -- 2nd object via Linda + +assert( h[1]==true ) -- wait for return + diff --git a/tests/perftest.lua b/tests/perftest.lua new file mode 100644 index 0000000..8ce1b3c --- /dev/null +++ b/tests/perftest.lua @@ -0,0 +1,184 @@ +-- +-- PERFTEST.LUA Copyright (c) 2007-08, Asko Kauppi +-- +-- Performance comparison of multithreaded Lua (= how much ballast does using +-- Lua Lanes introduce) +-- +-- Usage: +-- [time] lua -lstrict perftest.lua [threads] [-plain|-single[=2..n]] [-time] [-prio[=-2..+2[,-2..+2]]] +-- +-- threads: number of threads to launch (loops in 'plain' mode) +-- -plain: runs in nonthreaded mode, to get a comparison baseline +-- -single: runs using just a single CPU core (or 'n' cores if given) +-- -prio: sets odd numbered threads to higher/lower priority +-- +-- History: +-- AKa 20-Jul-08: updated to Lanes 2008 +-- AK 14-Apr-07: works on Win32 +-- +-- To do: +-- (none?) +-- + +-- On MSYS, stderr is buffered. In this test it matters. +-- Seems, even with this MSYS wants to buffer linewise, needing '\n' +-- before actual output. +-- +local MSYS= os.getenv("OSTYPE")=="msys" + + +require "lanes" + +local m= require "argtable" +local argtable= assert( m.argtable ) + +local N= 1000 -- threads/loops to use +local M= 1000 -- sieves from 1..M +local PLAIN= false -- single threaded (true) or using Lanes (false) +local SINGLE= false -- cores to use (false / 1..n) +local TIME= false -- use Lua for the timing +local PRIO_ODD, PRIO_EVEN -- -3..+3 + +local function HELP() + io.stderr:write( "Usage: lua perftest.lua [threads]\n" ) +end + +-- nil -> +2 +-- odd_prio[,even_prio] +-- +local function prio_param(v) + if v==true then return 2,-2 end + + local a,b= string.match( v, "^([%+%-]?%d+)%,([%+%-]?%d+)$" ) + if a then + return tonumber(a), tonumber(b) + elseif tonumber(v) then + return tonumber(v) + else + error( "Bad priority: "..v ) + end +end + +for k,v in pairs( argtable(...) ) do + if k==1 then N= tonumber(v) or HELP() + elseif k=="plain" then PLAIN= true + elseif k=="single" then SINGLE= v -- true/number + elseif k=="time" then TIME= true + elseif k=="prio" then PRIO_ODD, PRIO_EVEN= prio_param(v) + else HELP() + end +end + +PRIO_ODD= PRIO_ODD or 0 +PRIO_EVEN= PRIO_EVEN or 0 + + +-- SAMPLE ADOPTED FROM Lua 5.1.1 test/sieve.lua -- + +-- the sieve of of Eratosthenes programmed with coroutines +-- typical usage: lua -e N=1000 sieve.lua | column + +-- AK: Wrapped within a surrounding function, so we can pass it to Lanes +-- Note that coroutines can perfectly fine be used within each Lane. :) +-- +-- AKa 20-Jul-2008: Now the wrapping to one function is no longer needed; +-- Lanes 2008 can take the used functions as upvalues. +-- +local function sieve_lane(N,id) + + if MSYS then + io.stderr:setvbuf "no" + end + + -- generate all the numbers from 2 to n + local function gen (n) + return coroutine.wrap(function () + for i=2,n do coroutine.yield(i) end + end) + end + + -- filter the numbers generated by `g', removing multiples of `p' + local function filter (p, g) + return coroutine.wrap(function () + while 1 do + local n = g() + if n == nil then return end + if math.mod(n, p) ~= 0 then coroutine.yield(n) end + end + end) + end + + local ret= {} -- returned values: { 2, 3, 5, 7, 11, ... } + N=N or 1000 -- from caller + local x = gen(N) -- generate primes up to N + while 1 do + local n = x() -- pick a number until done + if n == nil then break end + --print(n) -- must be a prime number + table.insert( ret, n ) + + x = filter(n, x) -- now remove its multiples + end + + io.stderr:write(id..(MSYS and "\n" or "\t")) -- mark we're ready + + return ret +end +-- ** END OF LANE ** -- + + +-- Keep preparation code outside of the performance test +-- +local f_even= lanes.gen( "base,coroutine,math,table,io", -- "*" = all + { priority= PRIO_EVEN }, sieve_lane ) + +local f_odd= lanes.gen( "base,coroutine,math,table,io", -- "*" = all + { priority= PRIO_ODD }, sieve_lane ) + +io.stderr:write( "*** Counting primes 1.."..M.." "..N.." times ***\n\n" ) + +local t0= TIME and os.time() + +if PLAIN then + io.stderr:write( "Plain (no multithreading):\n" ) + + for i=1,N do + local tmp= sieve_lane(M,i) + assert( type(tmp)=="table" and tmp[1]==2 and tmp[168]==997 ) + end +else + if SINGLE then + io.stderr:write( (tonumber(SINGLE) and SINGLE or 1) .. " core(s):\n" ) + lanes.single(SINGLE) -- limit to N cores (just OS X) + else + io.stderr:write( "Multi core:\n" ) + end + + if PRIO_ODD ~= PRIO_EVEN then + io.stderr:write( ( PRIO_ODD > PRIO_EVEN and "ODD" or "EVEN" ).. + " LANES should come first (odd:"..PRIO_ODD..", even:"..PRIO_EVEN..")\n\n" ) + else + io.stderr:write( "EVEN AND ODD lanes should be mingled (both: "..PRIO_ODD..")\n\n" ) + end + local t= {} + for i=1,N do + t[i]= ((i%2==0) and f_even or f_odd) (M,i) + end + + -- Make sure all lanes finished + -- + for i=1,N do + local tmp= t[i]:join() + assert( type(tmp)=="table" and tmp[1]==2 and tmp[168]==997 ) + end +end + +io.stderr:write "\n" + +if TIME then + local t= os.time() - t0 + io.stderr:write( "*** TIMING: "..t.." seconds ***\n" ) +end + +-- +-- end diff --git a/tests/recursive.lua b/tests/recursive.lua new file mode 100644 index 0000000..49c03d3 --- /dev/null +++ b/tests/recursive.lua @@ -0,0 +1,21 @@ +-- +-- RECURSIVE.LUA +-- +-- Test program for Lua Lanes +-- + +io.stderr:write( "depth:" ) +local function func( depth ) + io.stderr:write(" " .. depth) + if depth > 10 then + return "done!" + end + + require "lanes" + local lane= lanes.gen("*", func)( depth+1 ) + return lane[1] +end + +local v= func(0) +assert(v=="done!") +io.stderr:write("\n") diff --git a/tests/require.lua b/tests/require.lua new file mode 100644 index 0000000..2cfe780 --- /dev/null +++ b/tests/require.lua @@ -0,0 +1,30 @@ +-- +-- REQUIRE.LUA +-- +-- Test that 'require' works from sublanes +-- +require 'lanes' + +local function a_lane() + -- To require 'math' we still actually need to have it initialized for + -- the lane. + -- + require "math" + assert( math and math.sqrt ) + assert( math.sqrt(4)==2 ) + + assert( lanes==nil ) + require "lanes" + assert( lanes and lanes.gen ) + + local h= lanes.gen( function() return 42 end ) () + local v= h[1] + + return v==42 +end + +local gen= lanes.gen( "math,package,string,table", a_lane ) + +local h= gen() +local ret= h[1] +assert( ret==true ) diff --git a/tests/timer.lua b/tests/timer.lua new file mode 100644 index 0000000..e95f326 --- /dev/null +++ b/tests/timer.lua @@ -0,0 +1,93 @@ +-- +-- TIMER.LUA +-- +-- Sample program for Lua Lanes +-- + +-- On MSYS, stderr is buffered. In this test it matters. +io.stderr:setvbuf "no" + + +require "lanes" + +local linda= lanes.linda() + +local function PRINT(str) + io.stderr:write(str.."\n") +end + +local T1= "1s" -- these keys can be anything... +local T2= "5s" + +local step= {} + +lanes.timer( linda, T1, 1.0, 1.0 ) +step[T1]= 1.0 + +PRINT( "\n*** Timers every second (not synced to wall clock) ***\n" ) + +local v_first +local v_last= {} -- { [channel]= num } +local T2_first_round= true + +local caught= {} -- { [T1]= bool, [T2]= bool } + +while true do + io.stderr:write("waiting...\t") + local v,channel= linda:receive( 6.0, T1,T2 ) + assert( channel==T1 or channel==T2 ) + caught[channel]= true + + io.stderr:write( ((channel==T1) and "" or "\t\t").. string.format("%.3f",v),"\n" ) + assert( type(v)=="number" ) + + if v_last[channel] then + if channel==T2 and T2_first_round then + -- do not make measurements, first round is not 5secs due to wall clock adjustment + T2_first_round= false + else + assert( math.abs(v-v_last[channel]- step[channel]) < 0.02 ) + end + end + + if not v_first then + v_first= v + elseif v-v_first > 3.0 and (not step[T2]) then + PRINT( "\n*** Adding timers every 5 second (synced to wall clock) ***\n" ) + + -- The first event can be in the past (just cut seconds down to 5s) + -- + local date= os.date("*t") + date.sec = date.sec - date.sec%5 + + lanes.timer( linda, T2, date, 5.0 ) + step[T2]= 5.0 + + elseif v-v_first > 10 then -- exit condition + break + end + v_last[channel]= v +end + +-- Windows version had a bug where T2 timers were not coming through, at all. +-- AKa 24-Jan-2009 +-- +assert( caught[T1] ) +assert( caught[T2] ) + +PRINT( "\n*** Clearing timers ***\n" ) + +lanes.timer( linda, T1, 0 ) -- reset; no reoccuring ticks +lanes.timer( linda, T2, 0 ) + +linda:receive( 0, T1 ) -- clear out; there could be one tick left +linda:receive( 0, T2 ) + +assert( linda:get(T1) == nil ) +assert( linda:get(T2) == nil ) + +PRINT "...making sure no ticks are coming..." + +local v= linda:receive( 1.5, T1,T2 ) -- should not get any +assert(v==nil) + -- cgit v1.2.3-55-g6feb