01 · Message transfer
A mailbox transfers a value or handle; ownership remains a contract.
Mailbox put/get operations provide atomic communication and optional capacity. Correctness still depends on producer count, consumer count, termination, type safety, and whether sent objects are copied or shared.
Bounded channel
Producer → capacity → consumer
- →
- →
- →
- 01Producerput(txn)
Blocks when the bounded mailbox is full.
- 02Mailbox0…N entries
Atomic ordering and capacity, but no automatic end-of-stream.
- 03Consumerget(txn)
Blocks when empty and removes the oldest message.
- 04Shutdownexplicit token
A sentinel, count, or phase protocol terminates both sides.
Blocks when the bounded mailbox is full.
- new(0)
- Unbounded
- It removes backpressure and can hide runaway production.
- peek
- Non-consuming
- The item remains, so another consumer can change subsequent behavior.
- Class item
- Handle transfer
- Clone for independent ownership or freeze after send.
A mailbox is a synchronization and communication boundary. Capacity, end-of-stream behavior, and any separate ownership handoff belong in its public contract.
Observe
Count producers, consumers, maximum outstanding messages, blocking operations, termination tokens, and shared class handles.
Decide
Choose blocking put/get for backpressure, try_put/try_get for explicit polling, and peek only when consuming later is safe.
Failure
A final put can deadlock after consumers stop, and sending a class handle can create a race if the producer mutates the same object afterward.
Prove
Assert bounded occupancy, count sends and receives, run empty/full schedules, and poison or clone transferred objects according to ownership policy.
Blocking operations create a liveness graph
put waits for capacity, get waits for an item, and peek waits for an item without removing it. A system is live only when another active process can satisfy each wait. Count operations along every shutdown path, not just the steady-state loop.
typedef struct {
bit last;
int value;
} item_t;
mailbox #(item_t) channel = new(4);
task automatic produce();
for (int i = 0; i < 10; i++)
channel.put('{last: 0, value: i});
channel.put('{last: 1, value: 0});
endtask
task automatic consume();
item_t item;
forever begin
channel.get(item);
if (item.last) break;
check(item.value);
end
endtaskOperation counts expose deadlock
With a bounded mailbox, two gets do not guarantee that three later puts can complete. If the consumer stops after its second get, the final put may wait forever once capacity is exhausted. Build a balance table that includes the channel's initial and final occupancy.
| Method | Blocks? | Removes? | Use |
|---|---|---|---|
| put | If full | Adds | Backpressured send |
| try_put | No | Adds on success | Explicit retry or drop policy |
| get | If empty | Yes | Returns a value or handle; no automatic clone or ownership transfer |
| try_get | No | On success | Nonblocking receive |
| peek | If empty | No | Inspect next item |
| try_peek | No | No | Conditional inspection |
| num | No | No | Diagnostic snapshot, not atomic admission |
Explain it out loud
Interview reasoning checkpoints
A mailbox provides atomic put/get operations and blocking synchronization. A shared queue still needs locking, notification, capacity, and shutdown logic around every access.
- Identify the shared-state race around size and pop.
- Choose mailbox blocking or nonblocking operations deliberately.
- Define capacity and object ownership after transfer.
Good answers connect communication with synchronization, not only container syntax.
Assuming mailbox transfer deep-copies a class object.
Balance all puts and gets, include initial capacity and shutdown messages, and show that every blocking operation has a concurrently runnable process capable of satisfying it.
- Draw a wait-for graph for full and empty states.
- Check the terminal path as carefully as the steady state.
- Use watchdogs and occupancy assertions to expose violations.
The key concept is liveness across all schedules, not merely matching source-code counts.
Counting two gets and two puts while ignoring a final sentinel put.
