mirror of
https://github.com/tmux/tmux.git
synced 2026-07-11 03:49:31 +00:00
Add a regression test for output queued to exiting control clients.
Checks that once a control client is marked for exit the server stops queueing notifications for it (PR 5357). The client's output is drained only slowly so a backlog holds the exit handshake open; the client is then detached and the window renamed, and the %window-renamed notifications must not appear in the client's output stream.
This commit is contained in:
111
regress/control-client-exit.sh
Normal file
111
regress/control-client-exit.sh
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Once a control client is marked for exit (by detach-client here, or by the
|
||||
# "too far behind" eviction in control_check_age), the server must stop
|
||||
# queueing new output and notifications for it. The exit handshake only waits
|
||||
# for already-queued blocks to drain (control_all_done), so anything queued
|
||||
# after that point is written to a client that will never read it, and can hold
|
||||
# the handshake - and the pane buffer it pins - open. This checks that a
|
||||
# notification generated while the client is exiting never reaches its output.
|
||||
#
|
||||
# To see this in seconds rather than waiting out CONTROL_MAXIMUM_AGE (300s) the
|
||||
# control client's output goes down a fifo that a helper drains only slowly.
|
||||
# The slow drain keeps a backlog stuck in the server so the exit handshake
|
||||
# cannot complete; while it is stuck we detach the client (which sets the exit
|
||||
# flag) and rename the window to a marker. Draining the fifo must then not turn
|
||||
# up the marker as a %window-renamed line.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
DIR=$(mktemp -d)
|
||||
FIFO=$DIR/fifo
|
||||
OUT=$DIR/out
|
||||
MARKER=exitmarker
|
||||
READER=
|
||||
|
||||
mkfifo "$FIFO" || exit 1
|
||||
: >"$OUT"
|
||||
|
||||
cleanup() {
|
||||
[ -n "$READER" ] && kill "$READER" 2>/dev/null
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exec 8<&- 2>/dev/null
|
||||
rm -rf "$DIR"
|
||||
}
|
||||
trap cleanup 0 1 15
|
||||
|
||||
# A detached session whose pane floods printable output forever.
|
||||
$TMUX -f/dev/null new -d -x 80 -y 24 -s rt 'cat /dev/zero | tr "\000" x' || exit 1
|
||||
$TMUX setw -g automatic-rename off || exit 1
|
||||
|
||||
# Attach a control client: output to the fifo, stdin held open by sleep so it
|
||||
# stays attached while we drive the session from outside. Open the read end
|
||||
# after the client has opened the write end.
|
||||
( sleep 30 ) | $TMUX -f/dev/null -C attach -t rt >"$FIFO" 2>&1 &
|
||||
exec 8<"$FIFO"
|
||||
|
||||
n=0
|
||||
while [ $n -lt 50 ]; do
|
||||
$TMUX lsc -F '#{client_name}' 2>/dev/null | grep -q . && break
|
||||
sleep 0.1
|
||||
n=$((n + 1))
|
||||
done
|
||||
$TMUX lsc -F '#{client_name}' 2>/dev/null | grep -q . ||
|
||||
{ echo "control client did not attach"; exit 1; }
|
||||
|
||||
# Drain the control stream slowly (~2.5KB/s). The pane floods far faster, so a
|
||||
# backlog stays stuck in the server and the fifo stays full, which is what
|
||||
# keeps the exit handshake from completing below.
|
||||
( while :; do
|
||||
dd bs=256 count=1 <&8 >>"$OUT" 2>/dev/null || exit 0
|
||||
sleep 0.1
|
||||
done ) &
|
||||
READER=$!
|
||||
|
||||
# Wait until the client is demonstrably backed up: the output only keeps
|
||||
# growing at the slow-drain rate while the flood is outrunning the reader.
|
||||
n=0
|
||||
while [ $n -lt 100 ]; do
|
||||
[ "$(wc -c <"$OUT")" -ge 8000 ] && break
|
||||
sleep 0.1
|
||||
n=$((n + 1))
|
||||
done
|
||||
[ "$(wc -c <"$OUT")" -ge 8000 ] || { echo "client output did not back up"; exit 1; }
|
||||
|
||||
# Detach the control client: this sets CLIENT_EXIT immediately. The stuck
|
||||
# backlog holds the window between CLIENT_EXIT and the exit handshake open.
|
||||
$TMUX detach-client -s rt
|
||||
|
||||
# While the client is exiting, rename the window to the marker. Vanilla queues
|
||||
# these %window-renamed notifications for the exiting client; the fix drops them.
|
||||
i=0
|
||||
while [ $i -lt 5 ]; do
|
||||
$TMUX rename-window -t rt:0 "$MARKER$i"
|
||||
i=$((i + 1))
|
||||
done
|
||||
sleep 0.5
|
||||
|
||||
# Drain fast (bounded) so the backlog, and any notification queued behind it,
|
||||
# reaches OUT. A watchdog keeps this from blocking.
|
||||
kill "$READER" 2>/dev/null
|
||||
READER=
|
||||
( dd bs=4096 count=128 <&8 >>"$OUT" 2>/dev/null ) &
|
||||
DRAIN=$!
|
||||
( sleep 3; kill "$DRAIN" 2>/dev/null ) &
|
||||
WATCHDOG=$!
|
||||
wait "$DRAIN" 2>/dev/null
|
||||
kill "$WATCHDOG" 2>/dev/null
|
||||
|
||||
# The marker must never appear as a notification to the exiting client.
|
||||
if grep -q "%window-renamed .*$MARKER" "$OUT"; then
|
||||
echo "notification queued to exiting control client:"
|
||||
grep "%window-renamed .*$MARKER" "$OUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user