Concurrency in application code
Immutable after start-up, or guarded. There is no third option.
Route handlers run concurrently on different tasks. Module-level state must therefore be either immutable after start-up, or explicitly guarded.
Immutable after start-up
The simplest correct answer, and the one this wiki uses for every document it serves:
private const posts = [ /* ... */ ]; // read by every task, no synchronisationA const built during module initialisation is finished before main runs, and a value nobody writes needs no lock. Parsing content at start-up rather than per request is not only faster — it converts a shared mutable cache into a shared immutable one, which deletes the synchronisation question instead of answering it.
Guarded
When state must change, a sync variable is the idiomatic mutex, and blocking on one yields the task to the scheduler — exactly what blocking inside a foreign call would not do:
private var mutex: sync bool;
private var items: list(Task);
private proc lock() { mutex.writeEF(true); }
private proc unlock() { mutex.readFE(); }
proc snapshot(): list(Task) {
lock();
defer unlock();
return items; // a copy, so the lock is not held across serialisation
}Two details in that snippet earn their place:
defer unlock()releases on every path out, including a throw.- The snapshot is a copy. Serialising to JSON while holding the lock would hold it for as long as the response takes to build.
What not to do
Do not reach for a mutable module-level cache to memoise per-request work. Either it is safe to compute at start-up — in which case compute it there and make it const — or it changes per request, in which case it belongs in the handler's own scope or in ctx.setLocal.
Reading shared structures
A map indexed with [] can insert a default value for a missing key, which is a write. Test first, then read:
proc find(urlPath: string, ref result: Document): bool {
if !pathIndex.contains(urlPath) then return false;
result = store[pathIndex[urlPath]];
return true;
}That pattern keeps every lookup a pure read, which is what makes the whole registry safe to share across tasks without a lock.