diff options
Diffstat (limited to '9ns/test')
| -rwxr-xr-x | 9ns/test/adv_bridge_interrupt.sh | 26 | ||||
| -rwxr-xr-x | 9ns/test/mntgen.sh | 49 |
2 files changed, 52 insertions, 23 deletions
diff --git a/9ns/test/adv_bridge_interrupt.sh b/9ns/test/adv_bridge_interrupt.sh index e364143..ba76f78 100755 --- a/9ns/test/adv_bridge_interrupt.sh +++ b/9ns/test/adv_bridge_interrupt.sh @@ -118,18 +118,20 @@ expect_eq "never_flush/cache0: reader killed" "124" "$(field rc "$OUT")" fids_same "never_flush/cache0" bridge_fids_same "never_flush/cache0" -echo "# (b) a server that ignores Tflush too: the reader stays blocked, SIGTERM to 9ns still ends the session" -start_server never -timeout -s TERM 3 "$NS" --unix "$SOCK" --mount "$M" -- sh -c "timeout -s INT 1 cat $M/f; echo unreachable-rc=\$?" >"$TMP/never.out" 2>"$TMP/never.err" & -TPID=$! -sleep 4 -if kill -0 "$TPID" 2>/dev/null; then - fail "never: SIGTERM did not end 9ns while the reader was stuck"; kill -9 "$TPID" -else - pass "never: SIGTERM ends 9ns even though the server ignores the Tflush" -fi -wait "$TPID" 2>/dev/null -expect_eq "never: the reader never came back (server ignores Tflush)" "" "$(grep unreachable "$TMP/never.out")" +echo "# (b) a server that ignores Tflush too: the reader comes back after the flush grace, and the session is gone with it" +# `never` stops reading once the Tread hangs, so the Tflush is never even +# seen. The protocol says wait for the Rflush; a server that has not managed +# one in 3s (nine.Session.flush_grace_ms) is not going to, and the reader +# behind the interrupt is unkillable until we stop waiting. So the read fails +# EINTR after the grace, the session is wedged, and 9ns ends the mount: the +# next access answers ENOTCONN instead of parking another process forever. +run never "$(timed "timeout -s INT 1 cat $M/f"); cat $M/d/g 2>&1; echo after-rc=\$?" +no_crash "never/grace" +expect_eq "never/grace: reader killed by the signal (124)" "124" "$(field rc "$OUT")" +NEVER_MS=$(field ms "$OUT") +if [ "$NEVER_MS" -ge 3500 ] && [ "$NEVER_MS" -lt 9000 ]; then pass "never/grace: released after the 3s flush grace (${NEVER_MS}ms)"; else fail "never/grace: release time out of range" "ms=$NEVER_MS (expected 3500..9000)"; fi +expect_eq "never/grace: the mount is gone afterwards (not a hang)" "after-rc=1" "$(printf '%s\n' "$OUT" | grep '^after-rc=')" +expect_contains "never/grace: 9ns reports the closed session" "connection closed" "$STDERR" echo echo "passed=$PASSED failed=$FAILED" diff --git a/9ns/test/mntgen.sh b/9ns/test/mntgen.sh index aade76c..7dada53 100755 --- a/9ns/test/mntgen.sh +++ b/9ns/test/mntgen.sh @@ -254,14 +254,18 @@ echo "# --debug and --no-direct-io reach the mntgen dispatcher" DEBUG_ERR=$(timeout 60 "$NS" --mntgen --debug -- sh -c "ls $M/alpha >/dev/null" 2>&1 >/dev/null) expect_contains "--debug traces the dispatcher" "lookup 'alpha'" "$DEBUG_ERR" -echo "# a dial parked on a mute server unwedges when the program exits" -# A server that accepts the connection but never answers Tversion parks the -# dispatcher in the dial (pinned: no dial timeout, no concurrent dial). The -# dial must watch stop_fd through the whole handshake: when the program's -# main flow exits while a background walk is parked there, 9ns must follow it -# out instead of wedging forever (pre-fix it survived SIGTERM). +echo "# a mute server costs only the walks into its own name" +# A server that accepts the connection but never answers Tversion. The dial +# runs on that name's worker, inside the walk that asked, so: every other +# name (and the root) keeps answering; a signal releases the parked walker +# at once (the dial is abandoned, EINTR); a fresh walk parks again and is +# just as interruptible; and when the program exits with a walk still +# parked, 9ns follows it out. Background jobs of a non-interactive sh ignore +# SIGINT, so the parked walker is sent SIGTERM; the foreground `timeout -s +# INT` case covers Ctrl-C. Pre-fix the dial ran on the dispatcher thread and +# parked the whole mount, unkillably, until the program exited. if command -v python3 > /dev/null; then - python3 - "$REG/mute" <<'PYEOF' & + python3 - "$REG/mute" <<'MUTEEOF' & import socket, sys, os path = sys.argv[1] try: @@ -273,22 +277,45 @@ s.bind(path) s.listen(8) while True: conn, _ = s.accept() # accept, then never say a word -PYEOF +MUTEEOF MUTEPID=$! PIDS+=($MUTEPID) sleep 0.3 + MUTE_OUT=$(timeout 60 "$NS" --mntgen -- sh -c ' + stat '"$M"'/mute >/dev/null 2>&1 & W=$! + sleep 0.3 + echo "alpha=$(timeout 5 cat '"$M"'/alpha/build/zig_version)" + echo "root=$(timeout 5 ls '"$M"' | grep -c .)" + echo "readdir_alpha=$(timeout 5 ls '"$M"'/alpha | grep -c .)" + s=$(date +%s%N); kill -TERM $W; wait $W; echo "term_rc=$? term_ms=$(( ($(date +%s%N) - s) / 1000000 ))" + s=$(date +%s%N); timeout -s INT 2 stat '"$M"'/mute >/dev/null 2>&1; echo "int_rc=$? int_ms=$(( ($(date +%s%N) - s) / 1000000 ))" + echo "alpha_again=$(timeout 5 cat '"$M"'/alpha/build/zig_version)" + ' 2>"$TMP/mute.err") + expect_eq "another name is served while a walk into mute is parked" "alpha=$(zig version)" "$(printf '%s\n' "$MUTE_OUT" | grep '^alpha=')" + ROOT_N=$(printf '%s\n' "$MUTE_OUT" | sed -n 's/^root=//p') + [ -n "$ROOT_N" ] && [ "$ROOT_N" -ge 2 ] && pass "the root lists while a walk into mute is parked ($ROOT_N entries)" || fail "the root listing did not answer" "$MUTE_OUT" + READDIR_N=$(printf '%s\n' "$MUTE_OUT" | sed -n 's/^readdir_alpha=//p') + [ -n "$READDIR_N" ] && [ "$READDIR_N" -ge 1 ] && pass "a readdir on another name is served meanwhile" || fail "readdir on alpha did not answer" "$MUTE_OUT" + expect_eq "SIGTERM releases the parked walker (died of the signal)" "term_rc=143" "$(printf '%s\n' "$MUTE_OUT" | sed -n 's/^\(term_rc=[0-9]*\) .*/\1/p')" + TERM_MS=$(printf '%s\n' "$MUTE_OUT" | sed -n 's/.*term_ms=//p') + [ -n "$TERM_MS" ] && [ "$TERM_MS" -lt 1500 ] && pass "released promptly (${TERM_MS}ms)" || fail "release of the parked walker was slow or missing" "term_ms=$TERM_MS" + expect_eq "a fresh walk into mute is interruptible (Ctrl-C after 2s)" "int_rc=124" "$(printf '%s\n' "$MUTE_OUT" | sed -n 's/^\(int_rc=[0-9]*\) .*/\1/p')" + INT_MS=$(printf '%s\n' "$MUTE_OUT" | sed -n 's/.*int_ms=//p') + [ -n "$INT_MS" ] && [ "$INT_MS" -ge 1900 ] && [ "$INT_MS" -lt 4000 ] && pass "the interrupted walk came back on the signal (${INT_MS}ms)" || fail "interrupted walk timing off" "int_ms=$INT_MS" + expect_eq "alpha still served after all that" "alpha_again=$(zig version)" "$(printf '%s\n' "$MUTE_OUT" | grep '^alpha_again=')" + HANG_START=$(date +%s) timeout 20 "$NS" --mntgen -- bash -c "(stat $M/mute >/dev/null 2>&1) & sleep 1" >/dev/null 2>&1 HANG_RC=$? HANG_SECONDS=$(( $(date +%s) - HANG_START )) if [ "$HANG_RC" -eq 124 ] || [ "$HANG_SECONDS" -ge 15 ]; then - fail "9ns unwedges after the program exits a parked dial" "rc=$HANG_RC after ${HANG_SECONDS}s (wedged)" + fail "9ns exits with a walk still parked in a dial" "rc=$HANG_RC after ${HANG_SECONDS}s (wedged)" else - pass "9ns unwedges after the program exits a parked dial (rc=$HANG_RC after ${HANG_SECONDS}s)" + pass "9ns exits with a walk still parked in a dial (rc=$HANG_RC after ${HANG_SECONDS}s)" fi rm -f "$REG/mute" else - echo "SKIP: mute-server dial-hang check needs python3" + echo "SKIP: mute-server checks need python3" fi echo |
