On this page

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 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๏ƒ

python UX_and_AIX_experiences/04_expert/35_the_gate_that_waits_for_readers.py
py -3.14t UX_and_AIX_experiences/04_expert/35_the_gate_that_waits_for_readers.py

Download this collection ยท Source on GitHub

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๏ƒ

  1"""
  2TIER: expert (35)
  3GOAL: THE ADMISSION GATE, and why it is not a mutex. Expert 16 rewired a
  4      running world and 17 staged a swap on a live object. Neither said
  5      what makes those safe. This does.
  6
  7      MELDER HAS THREE GATES AND THEY EXIST FOR ONE REASON:
  8        RiftGate       admission to a Rift's guarded paths (here)
  9        CreationGate   the conduit's meld path
 10        LoadGate       crystallizer loads
 11      "All three exist because some operations must wait for READERS
 12      rather than for other writers." Read that twice. A mutex protects
 13      writers from each other. These make a writer wait until the last
 14      READER has left - which is the only way to swap something out from
 15      under live callers without tearing.
 16
 17      TICKETS ARE WHAT MAKE DRAINING TRUTHFUL. A caller registers a ticket
 18      entering guarded work and unregisters on exit, and drain waits for
 19      the count to hit zero. A boolean "busy" flag would be a guess; "a
 20      meld holds its ticket across the whole executor, so ticket-zero
 21      genuinely means no reader is inside".
 22
 23      TWO CONTROL MODES, AND CONFLATING THEM IS THE BUG.
 24        BLOCKING is REVERSIBLE. Disable parks new entrants, open() releases
 25          them. This is what an ACL-driven projection refresh needs: block
 26          entrants, drain, refresh once, reopen.
 27        TERMINAL CLOSE is ONE-WAY. It exists for shutdown, "where
 28          reopening would be wrong".
 29      One is a door you hold shut; the other is a door you brick up.
 30
 31      TWO ENTRY MODES, AND THIS ONE IS A POLICY CHOICE ABOUT YOUR AGENTS.
 32      While a gate is disabled, `admit()` either WAITS or RAISES:
 33        "wait"   the caller blocks until someone reopens
 34        "raise"  the caller is told no, immediately
 35      An agent that blocks is patient; an agent that raises can go do
 36      something else. Neither is right by default, which is why it is a
 37      setting rather than a behaviour.
 38
 39      AND NOW THE HONEST PART, WHICH IS THE REASON THIS LESSON EXISTS.
 40      NO SINGLE VERB PROVES A RIFT IS EMPTY, and melder says so in its own
 41      docstrings rather than letting you assume otherwise:
 42        disable_rift_gate       stops NEW entry. It "does NOT wait for
 43                                threads already inside".
 44        close_and_wait_rift     drains - but the timeout BOUNDS the wait,
 45                                so "a return does not by itself prove the
 46                                rift is empty".
 47        count_active_rift_threads
 48                                tells you the truth, and is "a DIAGNOSTIC,
 49                                not a synchronization primitive - do not
 50                                spin on it".
 51      So quiescence is: close-and-wait (bounded), THEN check the count.
 52      Most systems ship a `drain()` that implies a guarantee it cannot
 53      make. This one hands you three verbs and tells you what each one
 54      does not cover.
 55
 56      ONE TRAP, STATED PLAINLY: disable/enable SILENTLY NO-OP for an
 57      unknown rift id. A typo does not raise - it succeeds and changes
 58      nothing.
 59SURFACE EXERCISED: Nexus.get_rift_gate / enable_rift_gate /
 60                   disable_rift_gate / set_rift_gate_entry_mode /
 61                   count_active_rift_threads /
 62                   count_active_rift_threads_total / close_and_wait_rift,
 63                   Rift.id, and a command refused at the closed gate
 64VERIFY: authored 2026-08-05; not yet run.
 65"""
 66import melder as md
 67
 68
 69FRAME = "gatekeep-world"
 70
 71SAFE = "result = 2 + 2\n"
 72
 73
 74class Reader:
 75    def __init__(self) -> None:
 76        self.name = "reader"
 77
 78
 79def main() -> None:
 80    spellbook_configuration = (
 81        md.SpellbookConfiguration(FRAME).with_defaults().finalize()
 82    )
 83    book = md.Spellbook(aetheric_frame=FRAME,
 84                        configuration=spellbook_configuration)
 85    book.configure_aether_frame(
 86        system_state="dynamic",
 87        disposal=None,
 88        disposal_method_names=None,
 89        rift_enabled=True,
 90        ai_native=True,
 91    )
 92    book.bind(spell=Reader, existence="unique", permissions="create",
 93              binding_name="gatekeep-reader")
 94    book.conjure(name="gatekeep-root")
 95
 96    nexus = md.Nexus()
 97    system_configuration = nexus.create_configuration()
 98    system_configuration.with_rift_creation_enabled(True)
 99    system_configuration.with_allowed_target_frame_names([FRAME])
100    nexus.activate(system_configuration)
101    rift_configuration = nexus.create_rift_configuration()
102    rift_configuration.with_space_type("codegen")
103    rift = nexus.create_rift(configuration=rift_configuration,
104                             rift_name="gatekeeper")
105    rift.mark_active()
106    rift.create_frame_link(FRAME)
107    commands = rift.space.command_system
108
109    rift_id = rift.id
110
111    # `get_rift_gate` is used here the way `enable_rift_gate`'s own
112    # contract prescribes - as an EXISTENCE CHECK, because the enable and
113    # disable verbs silently no-op on an unknown id: "a typo'd id looks
114    # like success. Confirm with get_rift_gate(...) when the id is not
115    # known-good." The gate object itself is kernel machinery and this
116    # lesson never drives it; every state change below goes through Nexus.
117    assert nexus.get_rift_gate(rift_id) is not None, "this rift has a gate"
118    assert nexus.get_rift_gate("no-such-rift-id") is None, (
119        "an unknown id must answer None rather than raising"
120    )
121    print("rift:", rift_id[:14], "... gate registered: True")
122    print("  (None for an unknown id is an ANSWER, not an error channel)")
123
124    # NOBODY IS INSIDE. The count is a diagnostic, and right now it is a
125    # true one because this thread is not in a guarded call.
126    assert nexus.count_active_rift_threads(rift_id) == 0
127    assert nexus.count_active_rift_threads_total() == 0
128    print("active tickets:", nexus.count_active_rift_threads(rift_id),
129          "| across all rifts:", nexus.count_active_rift_threads_total())
130
131    # THE ENTRY MODE IS A POLICY CHOICE. Set it to `raise` BEFORE closing
132    # the gate - on a single thread, `wait` mode plus a closed gate is a
133    # deadlock, and that is not a melder bug, it is what "wait" means.
134    nexus.set_rift_gate_entry_mode(rift_id, "raise")
135    print()
136    print("entry mode set to 'raise' - a disabled gate will refuse rather")
137    print("than park the caller. On one thread, 'wait' here would hang")
138    print("forever, and correctly so.")
139
140    # CLOSE THE DOOR. New entry stops immediately.
141    nexus.disable_rift_gate(rift_id)
142    try:
143        commands.validate_codegen(SAFE, frame_name=FRAME)
144        raise AssertionError("expected the gate to refuse admission")
145    except RuntimeError as refused:
146        print()
147        print("validate_codegen at a disabled gate ->", str(refused)[:70])
148        print("  the command never ran. Admission is checked BEFORE the")
149        print("  work, not inside it")
150
151    # AND THE SAME COMMAND WORKS THE MOMENT IT REOPENS. Reversible.
152    nexus.enable_rift_gate(rift_id)
153    verdict = commands.validate_codegen(SAFE, frame_name=FRAME)
154    assert verdict["accepted"] is True
155    print()
156    print("enable_rift_gate -> the same call now answers:",
157          verdict["accepted"])
158    print("  blocking mode is REVERSIBLE. That is the whole point: block")
159    print("  entrants, drain, refresh the projection once, reopen.")
160
161    # THE SILENT NO-OP. A typo'd rift id does not raise.
162    nexus.disable_rift_gate("no-such-rift-id")
163    still_open = commands.validate_codegen(SAFE, frame_name=FRAME)
164    assert still_open["accepted"] is True, (
165        "disabling an unknown rift must not affect a real one"
166    )
167    print()
168    print("disable_rift_gate('no-such-rift-id') -> silently did nothing,")
169    print("  and our real gate is still open. A typo here does not raise;")
170    print("  it succeeds and changes nothing, which is why the contract")
171    print("  tells you to confirm the id with get_rift_gate(...) first -")
172    print("  the check at the top of this lesson is that idiom, not a")
173    print("  reach into the gate object")
174
175    # QUIESCENCE IS TWO STEPS, NOT ONE.
176    nexus.close_and_wait_rift(rift_id, timeout=5.0, interval=0.05)
177    remaining = nexus.count_active_rift_threads(rift_id)
178    assert remaining == 0, remaining
179    print()
180    print("close_and_wait_rift returned, and THEN we checked:", remaining,
181          "tickets")
182    print("  the return alone does not prove empty - the timeout bounds")
183    print("  the wait. Melder documents that rather than implying a")
184    print("  guarantee it cannot make, which is why quiescence is")
185    print("  close-and-wait FOLLOWED BY a count check")
186
187    # TERMINAL CLOSE IS ONE-WAY. The gate does not reopen for new work.
188    try:
189        commands.validate_codegen(SAFE, frame_name=FRAME)
190        raise AssertionError("expected the terminally closed gate to refuse")
191    except RuntimeError as closed:
192        print()
193        print("after close_and_wait ->", str(closed)[:70])
194        print("  terminal close is ONE-WAY. Blocking mode is a door you")
195        print("  hold shut; this is a door you brick up, and it exists for")
196        print("  shutdown where reopening would be wrong")
197
198    print()
199    print("a mutex protects writers from each other")
200    print("a gate makes a writer wait for the last READER to leave")
201    print("that is what lets melder swap a live object without tearing")
202
203
204if __name__ == "__main__":
205    main()

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 ยท Level guide

API contracts๏ƒ