summaryrefslogtreecommitdiff
path: root/9ns/test
diff options
context:
space:
mode:
Diffstat (limited to '9ns/test')
-rwxr-xr-x9ns/test/adv_bridge_interrupt.sh26
-rwxr-xr-x9ns/test/mntgen.sh49
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