|Origin / Contributor
This module provides flexible, generic FIFOs built around Lua tables and callback functions. It is specifically engineered to work well with the NodeMCU event-based and memory-constrained environment.
fifo = (require "fifo").new()
Fetch an element from the fifo and pass it to the function
k, together with a
boolean indicating whether this is the last element in the fifo. If the fifo
k will not be called and the fifo will enter "immediate dequeue"
mode (see below).
k is called, ordinarily,
k will return
nil, which will cause the
element given to
k to be removed from the fifo and the queue to advance. If,
k returns a non-
nil value, that value will replace the element at
the head of the fifo. This may be useful for generators, for example, which
stand in for several elements.
nil, it may also return a boolean as its second result. If
false, processing ends and
fifo:dequeue returns. If that is
true, the fifo will be advanced again (i.e.
fifo:dequeue(k) will be tail
called). Elements for which
nil, true are called "phantom", as
they cause the fifo to act as though they were not there. Phantom elements are
useful for callback-like behavior as the fifo advances: when
k sees a phantom
element, it knows that all prior entries in the fifo have been seen, but the
phantom does not necessarily know how to generate the next element of the fifo.
true if the queue contained at least one non-phantom entry,
Enqueue the element
a onto the fifo. If
k is not
nil and the fifo is in
"immediate dequeue" mode (whence it starts), immediately pass the first element
of the fifo (usually, but not necessarily,
k, as if
had been called, and exit "immediate dequeue" mode.
The elements stored in the FIFO are simply the integer indices of the fifo
table itself, with
1 being the head of the fifo. The depth of the queue for
fifo is just its table size, i.e.
#fifo. Direct access to the
elements is strongly discouraged. The number of elements in the fifo is also
unlikely to be of interest; especially, decisions about the fifo's emptiness
should instead be rewritten to use the existing interface, if possible, or may
peek a bit at the immediate dequeueing state (see below). See the discussion
of corking, below, too.
The "immediate dequeue" behavior may seem counterintuitive, but it is very
useful for the case that
k arranges for subsequent
fifo:dequeue, say by scheduling the next invocation of a timer
or by sending on a socket with an
on("sent") callback wired to
Because the fifo enters "immediate dequeue" mode only when
dequeue has been
called and the fifo was empty at the time of the call, rather than when the
fifo becomes empty,
fifo:queue will sometimes not invoke its
k even if
the queued element
a ends up at the front of the fifo. This, too, is quite
useful: it ensures that
k will not be called in contexts where it would
overlap any ongoing processing of the most-recently dequeued, fifo-emptying
The immediate deququeing status of the fifo is visible as the
which may be read (even if said reads are politely discouraged, but on occasion
it is handy to know) but should never be written.
The fifo has no special support for corking (that is, queueing several elements
which are guaranteed to not be dequeued until some later point, called
"uncorking"). As one often wants to cork only when the fifo is transitioning
out of immediate deququeing mode, the existing machinery is generally good
enough to provide an easy emulation thereof. While it is typical to pass the
k to both
:dequeue, there is nothing necessitating this
convention. And so one may, as in the
fifosock module, use the
k to record the transition out of immediate dequeueing mode for later,
when one wishes to uncork:
local corked = false
fifo:queue(e1, function(e) corked = true ; return e end)
-- e1 is now in the fifo, and corked is true if the fifo has exited
-- immediate dequeue mode. e1 will be returned back to the fifo and
-- so will not be deququed by the function argument.
-- We can now queue more elements to the fifo. These will certainly
-- queue behind e1.
-- If we should have initiated draining the fifo above, we can do so now,
-- instead, having built up a backlog as desired.
if corked then fifo:dequeue(k) end