109 lines
4.9 KiB
Markdown
109 lines
4.9 KiB
Markdown
# FIFO Module
|
|
| Since | Origin / Contributor | Maintainer | Source |
|
|
| :----- | :-------------------- | :---------- | :------ |
|
|
| 2019-02-10 | [nwf](https://github.com/nwf) | [nwf](https://github.com/nwf) | [fifo.lua](../../lua_modules/fifo/fifo.lua) |
|
|
|
|
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.
|
|
|
|
## Constructor
|
|
```lua
|
|
fifo = (require "fifo").new()
|
|
```
|
|
|
|
## fifo.dequeue()
|
|
#### Syntax
|
|
`fifo:dequeue(k)`
|
|
|
|
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
|
|
is empty, `k` will not be called and the fifo will enter "immediate dequeue"
|
|
mode (see below).
|
|
|
|
Assuming `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,
|
|
however, `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.
|
|
|
|
When `k` returns `nil`, it may also return a boolean as its second result. If
|
|
that is `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 `k` returns `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.
|
|
|
|
#### Returns
|
|
`true` if the queue contained at least one non-phantom entry, `false` otherwise.
|
|
|
|
## fifo.queue()
|
|
#### Syntax
|
|
`fifo:queue(a,k)`
|
|
|
|
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, `a`) to `k`, as if `fifo:dequeue(k)`
|
|
had been called, and exit "immediate dequeue" mode.
|
|
|
|
## FIFO Elements
|
|
|
|
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
|
|
a given `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.
|
|
|
|
## Immediate Dequeueing
|
|
|
|
The "immediate dequeue" behavior may seem counterintuitive, but it is very
|
|
useful for the case that `fifo:dequeue`'s `k` arranges for subsequent
|
|
invocations of `fifo:dequeue`, say by scheduling the next invocation of a timer
|
|
or by sending on a socket with an `on("sent")` callback wired to
|
|
`fifo:dequeue`.
|
|
|
|
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
|
|
element.
|
|
|
|
The immediate deququeing status of the fifo is visible as the `_go` member,
|
|
which may be read (even if said reads are politely discouraged, but on occasion
|
|
it is handy to know) but should never be written.
|
|
|
|
## Corking
|
|
|
|
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
|
|
same `k` to both `:queue` and `:dequeue`, there is nothing necessitating this
|
|
convention. And so one may, as in the `fifosock` module, use the `:queue`
|
|
`k` to record the transition out of immediate dequeueing mode for later,
|
|
when one wishes to uncork:
|
|
|
|
```lua
|
|
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.
|
|
fifo:queue(e2)
|
|
|
|
-- 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
|
|
```
|