## both directions bound to one descriptor, then one of them dropped ## ## Binding an input and an output semaphore to the same handle gives the VM a ## tuple with two directions. Unbinding one of them leaves the other in place, ## so delete_sem_from_sem_io_tuple() modifies the registration rather than ## removing it - vm_muxmod() instead of vm_muxdel(). Nothing else in the suite ## takes that path, so the per-direction event purge behind it is otherwise ## never executed. ## ## A pipe's write end is used because it is always writable, so the output ## semaphore has something to report; the input side is registered but never ## fires, which is fine - what matters is that both directions are registered. fun chk(ok msg) { if ok { printf "OK: %s\n" msg } \ else { printf "ERROR: %s\n" msg } } sg := (core.semgr-new) isem := (core.sem-new) osem := (core.sem-new) tmo := (core.sem-new) core.semgr-add sg isem core.semgr-add sg osem core.semgr-add sg tmo p := (sys.pipe) r := (core.basicAt p 0) w := (core.basicAt p 1) ## one descriptor, two directions - the tuple now carries both core.sem-signal-on-input isem w core.sem-signal-on-output osem w ## drop only the input direction. the output registration must survive, and so ## must any event already reported for it core.sem-unsignal isem core.sem-signal tmo 5 0 s := (core.semgr-wait sg) chk (eqv? s osem) "the surviving direction still reports after a partial unbind" core.sem-unsignal tmo ## put the input direction back while the output one is still registered. this ## is the add branch of the modify path - the mirror of the drop above - and it ## has to leave the output registration alone. core.sem-signal-on-input isem w ## now drop the output direction instead, keeping input. a write end is never ## readable, so with input alone nothing can report and the timer must win. if ## the modify path had left the output registration behind, its writability ## would be reported here and the timer would lose. core.sem-unsignal osem core.sem-signal tmo 0 300000000 s := (core.semgr-wait sg) chk (eqv? s tmo) "the dropped direction stops reporting after a partial unbind" core.sem-unsignal tmo ## and the full unbind from that state really removes the registration core.sem-unsignal isem core.sem-signal tmo 0 300000000 s := (core.semgr-wait sg) chk (eqv? s tmo) "nothing reports once both directions are unbound" core.sem-unsignal tmo sys.close r sys.close w