Introduction: This note considers only the issue of multiple inputs in Structured Message Passing. Other aspects are discussed in other essays.
Imagine a software component that has two inputs. Component A has one input named x and another input named y.
Some other component feeds data into x, and yet another component feeds data into input y. For this discussion, we’ll imagine that port r feeds port x and that port s feeds port y.
For this discussion, we don’t need to know if ports r and s are output ports of other child components or input ports of the container component.
Component A has a single input queue. The queue works in a first-in-first-out - FIFO - manner.
Let’s say that r fires a message into x, and then, s fires a message into y. The messages don’t wake up component A - only the Dispatcher gets to decide when A gets to run. The messages get queued up in order of arrival - FIFO - on A’s input queue. So, in this example, A’s input queue contains two messages, a message from r, followed by a message from s.
When the Dispatcher finally wakes A up, A gets fired with only the first message. A is woken up and given a message tagged with pin-name x. A must handle that message to completion and then go back to idle - go back to sleep to be re-awoken by the Dispatcher at some later time.
The dispatcher pops the first message (x) off of A’s input queue, leaving only message y on the input queue.
Later, the Dispatcher sees that A still has a non-empty input queue - A still has a message from s on input pin y pending. The Dispatcher wakes A up and tells it to handle message y.
The dispatcher pops the second message (y) off of A’s input queue, leaving no more messages on the input queue.
A is idle. The Dispatcher doesn’t need to wake A up again until A receives another message.
Relationship to Recursion
The FIFO-behaviour described above is fundamentally different from the behaviour of recursion.
Recursion uses a LIFO - last-in-first-out - strategy, where messages jump to the front of the line.
The behaviour described above puts new messages at the end of the queue, instead of at the front of the queue. Messages get processed in the order of arrival.
This behaviour preserves relative timing information. If message x arrives before message y, the order of message arrival is preserved in the FIFO queue.
CPUs with callstacks and function-based programming languages, encourage only the LIFO strategy, thus making FIFO behaviour and asynchrony more difficult to consider. LIFO behaviour can only be treated as a 2nd-class principle when using a FIFO-based notation. Most function-based programming languages use LIFO notation and behaviour.
Asynchronous Operation
Function-based, synchronous thinking cannot - by definition - deal with true asynchronous operation.
FIFO queues and dispatchers give us a way to de-synchronize parts of a system. This allows us to express true asynchrony in a way that is different from using function-based notation.
See Also
References: https://guitarvydas.github.io/2024/01/06/References.html
Blog: https://guitarvydas.github.io/
Videos: https://www.youtube.com/@programmingsimplicity2980
Discord: https://discord.gg/Jjx62ypR
Leanpub: https://leanpub.com/u/paul-tarvydas
Gumroad:
https://tarvydas.gumroad.com
Twitter: @paul_tarvydas