From 2f27c25461578bb323a87501c0c4ccb84f1473e9 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Sun, 20 Nov 2016 19:10:24 -0500 Subject: [PATCH] Plumbing: modularize fifo, improve tq --- fifo/fifo-diag.lua | 5 +++ fifo/fifo.lua | 25 ++++++++++++++ host/pushcommon.sh | 6 ++-- host/pushinit.sh | 10 +++--- net/fifosock.lua | 26 ++++++++++++++ telnetd/telnetd.lua | 4 +-- test/fifotest.lua | 84 +++++++++++++++++++++++++++++++++++++++++++++ test/tqstress.lua | 28 +++++++++++++++ tq/tq.lua | 35 ++++++++++++++----- util/fifosock.lua | 22 ------------ 10 files changed, 206 insertions(+), 39 deletions(-) create mode 100644 fifo/fifo-diag.lua create mode 100644 fifo/fifo.lua create mode 100644 net/fifosock.lua create mode 100644 test/fifotest.lua create mode 100644 test/tqstress.lua delete mode 100644 util/fifosock.lua diff --git a/fifo/fifo-diag.lua b/fifo/fifo-diag.lua new file mode 100644 index 0000000..5a19d42 --- /dev/null +++ b/fifo/fifo-diag.lua @@ -0,0 +1,5 @@ +return function(q) + local k,v + print("FIFOQ") + for k,v in ipairs(q) do print("",k,v) end +end diff --git a/fifo/fifo.lua b/fifo/fifo.lua new file mode 100644 index 0000000..794ad08 --- /dev/null +++ b/fifo/fifo.lua @@ -0,0 +1,25 @@ +-- Remove an element and pass it to k; if that returns a function, leave that +-- pending at the top of the fifo. Thus, we can get events that do multiple +-- things. If the queue is empty, do not invoke k but flag it to enable +-- immediate execution at the next call to queue. +-- +-- Returns 'true' if the queue was not empty, 'false' otherwise. +local function dequeue(q,k) + if #q > 0 + then local new = k(q[1]) ; if new == nil then table.remove(q,1) else q[1] = new end ; return true + else q._go = true ; return false + end +end +-- Queue a on queue q. +-- +-- If k is provided and the queue has previously drained, dequeue immediately +-- as if k had passed to dequeue. This is useful when k will arrange for +-- subsequent dequeues. +local function queue(q,a,k) + table.insert(q,a) + if k ~= nil and q._go then q._go = false; q:dequeue(k) end +end +-- return a FIFO constructor +return function() + return { ['_go'] = true ; ['queue'] = queue ; ['dequeue'] = dequeue } +end diff --git a/host/pushcommon.sh b/host/pushcommon.sh index 730f70f..bc08bad 100644 --- a/host/pushcommon.sh +++ b/host/pushcommon.sh @@ -1,11 +1,11 @@ -if [ -z ${HOST:-} ]; then +if [ -z ${MCUHOST:-} ]; then # Uses LUATOOL to push init and dependencies to the device; bootstrap! PUSHCMD="${LUATOOL} --delay 0.1 -p ${MCUPORT} -b ${MCUBAUD}" dopush() { ${PUSHCMD} -f $1 -t ${2:-`basename $1`}; } dopushcompile() { ${PUSHCMD} -f $1 -t ${2:-`basename $1`} -c; } else - # Uses host/pushvia to push everything if HOST is set - PUSHCMD="./host/pushvia.expect ${HOST} ${PORT:-23}" + # Uses host/pushvia to push everything if MCUHOST is set + PUSHCMD="./host/pushvia.expect ${MCUHOST} ${PORT:-23}" dopush() { ${PUSHCMD} ${2:-`basename $1`} $1; } dopushcompile() { ${PUSHCMD} ${2:-`basename $1`} $1 compile; } fi diff --git a/host/pushinit.sh b/host/pushinit.sh index 504d587..708069a 100755 --- a/host/pushinit.sh +++ b/host/pushinit.sh @@ -4,16 +4,18 @@ set -e -u . ./host/pushcommon.sh dopushcompile util/diag.lua +dopushcompile fifo/fifo.lua +dopushcompile fifo/fifo-diag.lua dopushcompile tq/tq.lua dopushcompile tq/tq-diag.lua dopushcompile net/nwfnet.lua dopushcompile net/nwfnet-sntp.lua dopushcompile net/nwfnet-go.lua dopushcompile net/nwfnet-diag.lua -dopush net/conf/nwfnet.conf -dopush net/conf/nwfnet.cert -dopush net/conf/nwfnet.conf2 -dopushcompile util/fifosock.lua +#dopush net/conf/nwfnet.conf +#dopush net/conf/nwfnet.cert +#dopush net/conf/nwfnet.conf2 +dopushcompile net/fifosock.lua dopushcompile telnetd/telnetd.lua dopushcompile telnetd/telnetd-file.lua dopushcompile telnetd/telnetd-diag.lua diff --git a/net/fifosock.lua b/net/fifosock.lua new file mode 100644 index 0000000..d125894 --- /dev/null +++ b/net/fifosock.lua @@ -0,0 +1,26 @@ +-- STATELESS +-- +-- Wrap a socket object so that it queues sends. Must call :fini after +-- done to avoid a memory leak (that I don't understand in full) +-- +-- Ideally, import this once, wrap all the sockets you need, and then forget +-- it. If one needs new sockets periodically, it is unclear to me whether +-- it's better to load this once and hold it in RAM or to load it every +-- time. +return function(fifo,sock) + local function dosend(s) sock:send(s) end + sock:on("sent", function() fifo:dequeue(dosend) end) + + local nsock = {} + function nsock.send(_,s) + if s == nil or s == "" then return end + fifo:queue(s,dosend) + end + function nsock.fini(_) sock = nil end + local sockit = getmetatable(sock)["__index"] + setmetatable(nsock,{ __index = function(_,k) + local fn = sockit[k] + return function(a,...) if a == nsock then fn(sock,...) else fn(a,...) end end + end }) + return nsock +end diff --git a/telnetd/telnetd.lua b/telnetd/telnetd.lua index ef6c5ff..504367d 100644 --- a/telnetd/telnetd.lua +++ b/telnetd/telnetd.lua @@ -24,11 +24,11 @@ function self.rx(tx,input,k) k(true) end function self.server(sock_) - local sock = (dofile("fifosock.lc")).wrap(sock_) + local sock = (dofile("fifosock.lc"))((require "fifo")(), sock_) local dosend = function(...) sock:send(...) end local k = function(c) if c then sock:send("\n$ ") else sock:close() end end sock:on("receive",function(s_,input) self.rx(dosend,input,k) end) - sock:on("disconnection",function(s_) tryon("disconn",sock); sock.fini(); sock=nil end) + sock:on("disconnection",function(s_) tryon("disconn",sock) ; sock:fini() end) tryon("conn",sock) sock:send("\n$ ") end diff --git a/test/fifotest.lua b/test/fifotest.lua new file mode 100644 index 0000000..01fd784 --- /dev/null +++ b/test/fifotest.lua @@ -0,0 +1,84 @@ +fmk = dofile("util/fifo.lua") +tmk = dofile("tq/tq.lua") +tdiag = loadfile("tq/tq-diag.lua")() + +f = fmk() +t = tmk() + +ttime = 0 +tfirearm = nil +t.arm = function(_,fire,when) tfirearm = fire; print("t arm: ", when) end +t.now = function(_) return ttime end +function tfire(step) + ttime = ttime + step*1000 + if tfirearm + then print("t fire"); tfa = tfirearm; tfirearm = nil; tfa() + else print("t fizzle") + end +end + +function tqp(w,s) t:queue(w,print,s) end +function td() + tdiag(t, + function(...) print("td set",...) end, + function(...) print("td elem",...) end) +end + +function deq(n) + if f:dequeue(function(e) print("deq", e) ; return n end) + then print("deq enqueue"); t:queue(1000,deq,nil) + end +end + +f:queue(1) +f:queue(2) +f:queue(3) +f:queue(4) + +print("SETUP") +t:queue(1000,deq,5) +t:queue(1500,deq,6) +t:queue(2000,deq,7) +td() +print() + +print("PREMATURE FIRE") +tfire(50) +td() +print() + +print("POSTMATURE FIRE") +tfire(1000) +td() +print() + +print("EXACT FIRE 1") +tfire(450) +td() +print() + +print("EXACT FIRE 2") +tfire(500) +td() +print() + +tfire(50) +print() + +tfire(450) +print() + +tfire(500) +print() + +tfire(50) +print() + +tfire(450) +print() + +tfire(600) +print() + +td() +print() diff --git a/test/tqstress.lua b/test/tqstress.lua new file mode 100644 index 0000000..4e55a38 --- /dev/null +++ b/test/tqstress.lua @@ -0,0 +1,28 @@ +t = dofile("tq/tq.lua")() + +tdiag = dofile("tq/tq-diag.lua") +function td() + tdiag(t, + function(...) print("td set",...) end, + function(...) print("td elem",...) end) +end + +ttime = 0 +tfirearm = nil +tfirewhen = nil +t.arm = function(_,fire,when) tfirearm = fire ; tfirewhen = when end +t.now = function(_) return ttime end +function tfire(step) + ttime = ttime + step*1000 + if tfirearm then tfa = tfirearm; tfirearm = nil; tfa() end +end + +ctr = 0 +function rec() + ctr = ctr + 1; t:queue(math.random(5),rec) +end +rec() + +for i = 1, 100000 do tfire(tfirewhen or 1) end +print(ctr, ttime) +td() diff --git a/tq/tq.lua b/tq/tq.lua index 9fda3d8..c9ef8ba 100644 --- a/tq/tq.lua +++ b/tq/tq.lua @@ -1,21 +1,40 @@ -- DEPENDS: tmr [only by default] local fire, doarm function fire(self) - local cbs = table.remove(self._q,1) - if #self._q > 0 then doarm(self, self._q[1]["t"]) end - local v - for _,v in ipairs(cbs) do v() end + local cbt = {} + local entryt = self:now() + local lapsed = (entryt - self._tst)/1000 + if #self._q > 0 and lapsed < self._q[1].t then + -- premature fire? adjust and rearm + self._q[1].t = self._q[1].t - lapsed + doarm(self, self._q[1].t) + return + end + while #self._q > 0 and self._q[1].t <= lapsed do + -- collect events in the past into cbt + local cbs = table.remove(self._q,1) + lapsed = lapsed - cbs.t + table.insert(cbt,cbs) + end + if #self._q > 0 then + -- leftover events: credit excess lapsed time and rearm + self._q[1].t = self._q[1].t - lapsed + doarm(self, self._q[1].t) + end + -- run all collected callbacks, having adjusted queue + local k, cbs, v + for k,cbs in ipairs(cbt) do for k,v in ipairs(cbs) do v() end end end function doarm(self,when) self:arm(function() fire(self) end, when); self._tst = self:now() end local function queue(self,when,what,...) if #self._q > 0 then local lapsed = (self:now() - self._tst)/1000 - self._q[1]["t"] = self._q[1]["t"] - lapsed + self._q[1].t = self._q[1].t - lapsed end local ix = 0; local tleft = when while (ix < #self._q) do - if (tleft < self._q[ix+1]["t"]) then break end - ix = ix + 1; tleft = tleft - self._q[ix]["t"] + if (tleft < self._q[ix+1].t) then break end + ix = ix + 1; tleft = tleft - self._q[ix].t end local warg = {...}; local nwarg = select('#',...) local wfn = function () return what(unpack(warg,1,nwarg)) end @@ -24,7 +43,7 @@ local function queue(self,when,what,...) end table.insert(self._q,ix+1,{["t"] = tleft, [1] = wfn}) if ix+1 < #self._q then - self._q[ix+2]["t"] = self._q[ix+2]["t"] - tleft + self._q[ix+2].t = self._q[ix+2].t - tleft end return wfn end diff --git a/util/fifosock.lua b/util/fifosock.lua deleted file mode 100644 index 0c0f882..0000000 --- a/util/fifosock.lua +++ /dev/null @@ -1,22 +0,0 @@ --- STATELESS -local fs = {} -function fs.wrap(sock) - local sf = {} - local sfe = true - local function sfd() if #sf > 0 then sock:send(table.remove(sf,1)) else sfe = true end end - sock:on("sent", sfd) - local nsock = {} - nsock.send = function(k,s) - if s == nil or s == "" then return end - table.insert(sf,s) - if sfe then sfe = false; sfd() end - end - nsock.fini = function() sf=nil; sock=nil end - local sockit = getmetatable(sock)["__index"] - setmetatable(nsock,{ __index = function(_,k) - local fn = sockit[k] - return function(a,...) if a == nsock then fn(sock,...) else fn(a,...) end end - end }) - return nsock -end -return fs -- 2.50.1