# The gate that waits for readers **🔵 Expert · Lesson 35** THE ADMISSION GATE, and why it is not a mutex. Expert 16 rewired a running world and 17 staged a swap on a live object. Neither said what makes those safe. This does. MELDER HAS THREE GATES AND THEY EXIST FOR ONE REASON: RiftGate admission to a Rift's guarded paths (here) CreationGate the conduit's meld path LoadGate crystallizer loads "All three exist because some operations must wait for READERS rather than for other writers." Read that twice. A mutex protects writers from each other. These make a writer wait until the last READER has left - which is the only way to swap something out from under live callers without tearing. TICKETS ARE WHAT MAKE DRAINING TRUTHFUL. A caller registers a ticket entering guarded work and unregisters on exit, and drain waits for the count to hit zero. A boolean "busy" flag would be a guess; "a meld holds its ticket across the whole executor, so ticket-zero genuinely means no reader is inside". TWO CONTROL MODES, AND CONFLATING THEM IS THE BUG. BLOCKING is REVERSIBLE. Disable parks new entrants, open() releases them. This is what an ACL-driven projection refresh needs: block entrants, drain, refresh once, reopen. TERMINAL CLOSE is ONE-WAY. It exists for shutdown, "where reopening would be wrong". One is a door you hold shut; the other is a door you brick up. TWO ENTRY MODES, AND THIS ONE IS A POLICY CHOICE ABOUT YOUR AGENTS. While a gate is disabled, `admit()` either WAITS or RAISES: "wait" the caller blocks until someone reopens "raise" the caller is told no, immediately An agent that blocks is patient; an agent that raises can go do something else. Neither is right by default, which is why it is a setting rather than a behaviour. AND NOW THE HONEST PART, WHICH IS THE REASON THIS LESSON EXISTS. NO SINGLE VERB PROVES A RIFT IS EMPTY, and melder says so in its own docstrings rather than letting you assume otherwise: disable_rift_gate stops NEW entry. It "does NOT wait for threads already inside". close_and_wait_rift drains - but the timeout BOUNDS the wait, so "a return does not by itself prove the rift is empty". count_active_rift_threads tells you the truth, and is "a DIAGNOSTIC, not a synchronization primitive - do not spin on it". So quiescence is: close-and-wait (bounded), THEN check the count. Most systems ship a `drain()` that implies a guarantee it cannot make. This one hands you three verbs and tells you what each one does not cover. ONE TRAP, STATED PLAINLY: disable/enable SILENTLY NO-OP for an unknown rift id. A typo does not raise - it succeeds and changes nothing. ## Before you run Use the [Expert guide](../../expert/index.md) for prerequisite concepts. Run from a checkout with Melder installed and Python 3.14 free-threading selected. The collection download includes the level's local helper modules. ## Run the saved script ```bash python UX_and_AIX_experiences/04_expert/35_the_gate_that_waits_for_readers.py ``` ```powershell py -3.14t UX_and_AIX_experiences/04_expert/35_the_gate_that_waits_for_readers.py ``` {download}`Download this collection <../../downloads/expert-examples.zip>` · [Source on GitHub](https://github.com/Synaptic724/melder/blob/90d52e0a12cb0743d62568d5a19ce954a54443bc/UX_and_AIX_experiences/04_expert/35_the_gate_that_waits_for_readers.py) ## Public surface Nexus.get_rift_gate / enable_rift_gate / disable_rift_gate / set_rift_gate_entry_mode / count_active_rift_threads / count_active_rift_threads_total / close_and_wait_rift, Rift.id, and a command refused at the closed gate ## Code ```{literalinclude} ../../downloads/04_expert/35_the_gate_that_waits_for_readers.py :language: python :linenos: ``` ## Check the outcome The script contains its own assertions or demonstrated refusal paths. Run it to evaluate those checks against your installed version. The code above is taken directly from the saved file; no run output is invented here. [More expert examples](index.md) · [Level guide](../../expert/index.md) ## Related guides - [Structural transactions and admission](../../expert/transactions.md) - [Several rooms and shared worlds](../../expert/concurrency.md) ## API contracts - [Nexus](../../reference/api/inspection/nexus.md) - [Rift](../../reference/api/inspection/rift.md)