|Iced Quinn 6565fe9988||6 months ago|
|docs||11 months ago|
|README.md||6 months ago|
|build.ninja||6 months ago|
|icedpostbox.nim||11 months ago|
|mkfile||11 months ago|
|weak.nim||1 year ago|
icedpostbox automatically generates the plumbing for a traditional
message pump system.
Postboxes have a few parts:
An object that will be later stored in to a mailbox.
A variant object which holds different kinds of letters.
ref object; it holds a list of unprocessed envelopes and a dead-man
A handle which is a unique pointer to protect the pontoon.
An object who wraps letters in envelopes and leave them in postboxes.
Postboxes are strictly single owner. They cannot be copied, only moved.
Also when the postbox is destroyed the pontoons are marked as
so no more messages can be processed. This creates the standard "weak
reference" pattern. Event senders are expected to lazily remove
deliverers with dead pontoons as they attempt to send events.
icedpostboxis available under Mozilla Public License, version 2 (MPL-2.)
Define your events.
type MicrowaveSetting* = object heat*: int MicrowaveBeep* = object
List the events a postbox will handle.
make_postbox(Donk): MicrowaveBeep MicrowaveSetting
var x = Donk()
Postboxes can be moved but not copied.
Create posters who send single types of letters.
var beep_source = Poster[MicrowaveBeep]() var setting_source = Poster[MicrowaveSetting]()
Connect posters to postboxes.
connect(x, beep_source) connect(x, setting_source)
beep_source.post y setting_source.post z
Using the case dispatcher.
x.case_dispatch_all_unread(e): of MicrowaveSetting: echo "temperature is now ", e.heat of MicrowaveBeep: echo "beeeep" else: discard
Manually dispatching on event kind.
for event in x: case event.kind: of PBDonkKindEmpty: discard of PBDonkKindMicrowaveSetting: echo "microwave changed to ", event.sealedMicrowaveSetting.heat of PBDonkKindMicrowaveBeep: echo "beeep"
Manual dispatch is not recommended since it relies on knowing how
make_postbox mangles names.
Mailboxes are not presently thread-safe.
Laziest option: lock on sequence access. Can code this in a couple of minutes but has the greatest amount of lock contention.
Fanciest option: a lock-free stack. Dispatch from one end and append to another.
Double-ref: objects contain
ref objects called tombstones. The
tombstone is a
ptr back to the original object. Upon
you set the back pointer in the tombstone to nil. Then strong
references become copies of the
ref to the base object and weaks
ref to the tombstone.
Pontoons: weak references are simply
ref to the pontoon (which can
be handed out, it "floats") which contains a flag to indicate it has
been disposed . A buoy object then acts as the unique owner
of the pontoon. The buoy’s
=destroy does a partial
deinitialization of the pontoon and sets the disposed flag.
I originally used double-ref which is clean and straightforward
ref Tombstone is weak,
ref Frobnicator is strong.) It also adds
another heap allocation to track the tombstone.
Switched to pontoons because the buoy lives on the stack (or otherwise inline to the object owning it.) So there is one ref for the postbox itself.
In Postbox’es case access to the postbox itself is largely restricted. The buoy belongs to whoever is going to pick up and process messages from the postbox. Weak references belong to the deliverer objects you request form a postbox. No code other than the automatically generated boilerplate touches the internals of the postbox system.
 This is similar to how
IDisposable works in C#.