Copyright | (c) Phil Hargett 2014 |
---|---|
License | MIT (see LICENSE file) |
Maintainer | phil@haphazardhouse.net |
Stability | experimental |
Portability | non-portable (uses STM) |
Safe Haskell | Safe |
Language | Haskell98 |
A Mailbox
is a drop-in replacement for TQueue
in Control.Concurrent.STM, except that
it also supports selective out of order message reception: that is, it allows the caller
to dequeue the first message among the messages available in the queue that matches
a supplied test function, or block if no such match is possible with the messages currently
in the queue.
As Mailbox
implements the same basic read write peek
group of functions as a TQueue
,
it offers a superset of TQueue
functionality by extending it with the find select handle
groups of functions. Thus, applications can safely use Mailbox
es in place of TQueue
s,
but choose when to take the slight extra overhead of Mailbox
functionality.
Because message selection in worst case requires fully traversing all messages in the queue,
application designers are encouraged to understand this aspect when choosing to use Mailbox
es
in their designs, or when using the additional features of Mailbox
es beyond that of TQueue
s.
Dispatching messages with a Mailbox
is analogous to using a case
expression (O(n)) to dispatch
messages to a handler function, except that new cases can be added or removed at any time. In essence,
one can regard Mailbox
es as a useful means of creating an extensible message dispatch function.
If, however, if O(1) message dispatching time is necessary or desired, (using hashmaps, for example)
then Mailbox
es are not the correct choice.
Despite this extra cost, Mailbox
es offer advantages to designers:
- Implementation of Erlang-style message reception: as messages can be received out of order, a mailbox is analogous to a process input queue in Erlang.
- Better composability: if applications must only dequeue messages in the order in which they are queued (which is sufficient for many applications), then the main message pump requires modification each time a new class of message must be handled. With selective message reception, multiple concurrent message pumps are possible (with a small performance impact), each processing the messages they expect and with no need to be aware of other message pumps performing their own work on the same mailbox.
- Mixing synchronous and asynchronous programming styles: if restricted to in order message
delivery, an application must carefully construct all logic to avoid blocking its central message
loop. By supporting out of message delivery and multiple selective recipients, it becomes possible
to combine synchronous and asynchronous programming styles using the same
Mailbox
.
Basic framework for Mailbox
brazenly copied from Control.Concurrent.STM.TQueue.
- data Mailbox m
- newMailbox :: STM (Mailbox m)
- newMailboxIO :: IO (Mailbox m)
- writeMailbox :: Mailbox m -> m -> STM ()
- readMailbox :: Mailbox m -> STM m
- tryReadMailbox :: Mailbox m -> STM (Maybe m)
- peekMailbox :: Mailbox m -> STM m
- tryPeekMailbox :: Mailbox m -> STM (Maybe m)
- selectMailbox :: Mailbox m -> (m -> Maybe v) -> STM v
- trySelectMailbox :: Mailbox m -> (m -> Maybe v) -> STM (Maybe v)
- handleMailbox :: Mailbox m -> (m -> Maybe v) -> (v -> IO r) -> IO r
- findMailbox :: Mailbox m -> (m -> Maybe v) -> STM v
- tryFindMailbox :: Mailbox m -> (m -> Maybe v) -> STM (Maybe v)
- unGetMailbox :: Mailbox m -> m -> STM ()
- isEmptyMailbox :: Mailbox m -> STM Bool
Mailbox
newMailboxIO :: IO (Mailbox m) Source #
IO
version of newMailbox
. This is useful for creating top-level
Mailbox
s using unsafePerformIO
, because using
atomically
inside unsafePerformIO
isn't
possible.
tryReadMailbox :: Mailbox m -> STM (Maybe m) Source #
A version of readMailbox
which does not retry. Instead it
returns Nothing
if no value is available.
peekMailbox :: Mailbox m -> STM m Source #
Get the next value from the Mailbox
without removing it,
retrying if the channel is empty.
tryPeekMailbox :: Mailbox m -> STM (Maybe m) Source #
A version of peekMailbox
which does not retry. Instead it
returns Nothing
if no value is available.
selectMailbox :: Mailbox m -> (m -> Maybe v) -> STM v Source #
Find the next message in the mailbox that matches the supplied test
function or block until there is a message that does. When a message
matches (e.g., test functions returns Just v
), return it.
trySelectMailbox :: Mailbox m -> (m -> Maybe v) -> STM (Maybe v) Source #
A version of selectMailbox
which does not retry. Instead it
returns Nothing
if no value is available.
handleMailbox :: Mailbox m -> (m -> Maybe v) -> (v -> IO r) -> IO r Source #
Wait until there is a message in the mailbox matching the supplied test
function (using selectMailbox
), then when a message is found, handle
it in the IO
monad with the supplied function.
findMailbox :: Mailbox m -> (m -> Maybe v) -> STM v Source #
Find the next value from the Mailbox
matching testFn
without removing it,
retrying if the channel is empty.
tryFindMailbox :: Mailbox m -> (m -> Maybe v) -> STM (Maybe v) Source #
A version of findMailbox
which does not retry. Instead it
returns Nothing
if no value is available.
unGetMailbox :: Mailbox m -> m -> STM () Source #
Put a data item back onto a mailbox, where it will be the next item read.