Administrator Pattern
The administrator is a server task that does no real work — its job is to manage: accept client calls, delegate to worker tasks, collect results, push completed work on. Workers execute the actual computation so the admin stays free to accept the next call.
Why split server into admin + workers?
Because a monolithic server serializes everything — while it’s processing a request, no new client can drop off work. Adding a buffer helps only if consumption rate matches production; otherwise the buffer is always full or empty. The real fix is another thread: the admin never blocks (just delegates), workers do the blocking work, and concurrency scales with worker count.
Shape
clients → admin → { timer, notifier, simple-worker, complex-worker, courier }
↑
(admin only accepts calls; never makes blocking calls outward)
Worker types
| Worker | Role |
|---|---|
| timer | Prompts admin at fixed intervals (heartbeat, timeout). |
| notifier | Performs a blocking wait on an external event (keypress, fd ready); calls admin when it fires. |
| simple worker | Admin hands it work; worker returns result to admin. |
| complex worker | Admin hands it work; worker interacts directly with the client of that work. |
| courier | Performs a blocking outward call on behalf of admin (so admin itself doesn’t block). |
Rules
- Admin never makes a blocking call out — else it can’t accept new client calls. Use a courier for any outward call that might block.
- Admin uses
signalBlockwhen delegating so the worker runs immediately and admin is free to serve the next request. - Worker/client count balance — too few workers ⇒ clients queue; too many ⇒ workers starve (bounded-buffer problem).
Skeleton
Admin owns a work-list, accepts client submissions, and delegates one item per iteration to a free worker. Workers are simple tasks that loop: request work, compute, return result.
_Task Admin {
Queue<Work *> todo, done;
Queue<Worker *> freeWorkers;
public:
void submit( Work * w ) { todo.push( w ); } // client drops off
Work * complete() { return done.pop(); } // worker returns result
Worker * getWork( Work *& w ) { w = todo.pop(); return uThisTask(); }
private:
void main() {
for ( ;; ) {
_Accept( ~Admin ) { break; }
or _Accept( submit ); // new client work
or _When( ! todo.empty() && ! freeWorkers.empty() )
_Accept( getWork ); // hand work to worker
or _Accept( complete ); // collect finished work
}
}
};
_Task Worker {
Admin & admin;
void main() {
for ( ;; ) {
Work * w;
admin.getWork( w ); // blocks until work
process( w );
admin.complete(); // return result
}
}
};The admin’s main is a pure accept-loop with _When guards, it never calls out. Each worker’s outward blocking call (getWork) does not block the admin, only the worker.
Relationship
customer (client) ↔ manager (admin) ↔ employee (worker), same division-of-labor metaphor that shows up in enterprise apps.