# Waiting and retrying

Source: https://libtmux.org/en/tmux/topics/waiting-and-retry/

> Polling a condition instead of guessing a sleep, and tmux's own wait-for signal channel as the alternative to polling.

After sending input or starting a process, wait for the state your next step
requires. [Pane
interaction](https://libtmux.org/en/tmux/topics/pane-interaction/#waiting-for-something-to-finish) covers
waiting for screen text. This page covers arbitrary conditions and tmux's named
`wait-for` signal channels.

## Polling a condition

Polling checks a condition repeatedly until it succeeds or a deadline expires.
Set a deadline and choose an interval that limits unnecessary tmux commands.

### Python

**Helper:** `libtmux.test.retry_until(fn, seconds=, interval=)`

**Where it lives:** [`src/libtmux/test/`](<https://github.com/tmux-python/libtmux/tree/9fdd083a8181a827889337b63cd4e6daea661a8c/src/libtmux/test>) in the main package; raises
[`WaitTimeout`](<https://libtmux.org/en/py/latest/reference/libtmux-exc-waittimeout/>).

### TypeScript

**Helper:** [`connectedServer.waitFor(matches, options)`](<https://libtmux.org/en/ts/latest/reference/types-connectedserver-waitfor/>)

**Where it lives:** The public control-connection API. Tests a predicate over
[`ServerSnapshot`](<https://libtmux.org/en/ts/latest/reference/types-serversnapshot/>); see [Control mode vs one-shot](https://libtmux.org/en/ts/latest/concepts/transports/).

### Go

**Helper:** [`tmuxtest.WaitFor(ctx, interval, condition)`](<https://libtmux.org/en/go/latest/reference/tmuxtest-waitfor/>)

**Where it lives:** [`tmuxtest`](<https://libtmux.org/en/go/latest/reference/#tmuxtest>), a separate test-support package from [`tmux`](<https://libtmux.org/en/go/latest/reference/#tmux>)

### Rust

**Helper:** [`libtmux::test::retry_until(within, condition)`](<https://libtmux.org/en/rs/latest/reference/test-retry_until/>)

**Where it lives:** [`crates/libtmux/src/test.rs`](<https://github.com/libtmux/libtmux-rs/blob/a6fc2a65674177b92b17fa380757155d2ba150fd/crates/libtmux/src/test.rs>), enabled with the `test-support` Cargo
feature.

### Java

**Helper:** Not listed.

**Where it lives:** not found in the shipped library; a package-private
`Await.until(...)` exists only inside the `integration-tests` module, which
downstream code cannot depend on

### C#

**Helper:** [`LibTmux.Testing.TmuxWait.UntilAsync(probe, timeout, interval)`](<https://libtmux.org/en/csharp/latest/reference/libtmux-tmuxwait-untilasync/>)

**Where it lives:** the separate [`LibTmux.Testing`](<https://libtmux.org/en/csharp/latest/reference/#LibTmux.Testing>) package

### C++

**Helper:** Not listed.

**Where it lives:** no generic condition-poll helper found in the public
library; a [`wait_until`](<https://libtmux.org/en/cxx/latest/reference/libtmux-commandoperation-wait_until/>) exists only in the private `testing` component, for
waiting on a spawned child process, not on tmux state

### Swift

**Helper:** Not listed.

**Where it lives:** a [`waitUntil`](<https://libtmux.org/en/swift/latest/reference/waituntil(within-_-)/>) helper exists only inside the test target's
own support code, not shipped

### Examples

[`waitFor`](<https://libtmux.org/en/ts/latest/reference/types-connectedserver-waitfor/>) subscribes before reading a server snapshot so it does not miss a
change between those steps.
[`tmuxtest.WaitFor`](<https://libtmux.org/en/go/latest/reference/tmuxtest-waitfor/>) probes immediately, then at the requested positive interval.
It returns a probe error or context error unchanged. Each probe must read fresh
state and honor its context. [`Session.Windows()`](<https://libtmux.org/en/go/latest/reference/tmux-session-windows/>) only reads a stored snapshot;
use [`Session.SearchWindows`](<https://libtmux.org/en/go/latest/reference/tmux-session-searchwindows/>) to query tmux on every probe.

```python
def is_window_up(pane, name):
    return any(w.window_name == name for w in pane.window.session.windows)

libtmux.test.retry_until(lambda: is_window_up(pane, "build"), seconds=5.0)
```

```typescript
await using live = await server.connect();
await live.waitFor((snapshot) => snapshot.windows.exists({ name: "build" }));
```

```go
waitCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
err := tmuxtest.WaitFor(waitCtx, 50*time.Millisecond, func(ctx context.Context) (bool, error) {
	windows, err := session.SearchWindows(ctx, nil)
	if err != nil {
		return false, err
	}
	for _, w := range windows {
		if name, ok := w.Name(); ok && name == "build" {
			return true, nil
		}
	}
	return false, nil
})
if err != nil {
    return fmt.Errorf("wait for build window: %w", err)
}
```

```rust
libtmux::test::retry_until(std::time::Duration::from_secs(5), async || {
    session.windows().await.map(|ws| ws.iter().any(|w| w.name() == "build")).unwrap_or(false)
})
.await?;
```

```csharp
await LibTmux.Testing.TmuxWait.UntilAsync(
    async ct => (await session.GetWindowsAsync(ct)).Any(w => w.Name == "build"),
    TimeSpan.FromSeconds(5),
    TimeSpan.FromMilliseconds(50));
```

Use a loop with a deadline and interval when a specific wait API does not fit.
[Pane interaction](https://libtmux.org/en/tmux/topics/pane-interaction/#waiting-for-something-to-finish)
covers output waits.

## tmux's own wait-for channel

Use `tmux wait-for -S <channel>` to signal and `tmux wait-for <channel>` to
block until signalled. This avoids repeated screen captures when the command can
announce its own completion:

### Python
Signal a channel with [`Server.wait_for`](<https://libtmux.org/en/py/latest/reference/libtmux-server-wait_for/>) and `set_flag=True`.
Call the same method without that flag to wait for the signal.

### TypeScript
The public API does not expose the channel handshake used internally
by the test server during startup.

### Go
Call [`Server.WaitFor`](<https://libtmux.org/en/go/latest/reference/tmux-server-waitfor/>) with a [`WaitForRequest`](<https://libtmux.org/en/go/latest/reference/tmux-waitforrequest/>). Set its mode to
[`WaitForModeSignal`](<https://libtmux.org/en/go/latest/reference/tmux-waitformodesignal/>) to signal the channel; the zero-value mode waits.

### Rust
[`Server.signal_channel`](<https://libtmux.org/en/rs/latest/reference/server-server-signal_channel/>) signals a channel.
[`Server.wait_for_channel`](<https://libtmux.org/en/rs/latest/reference/server-server-wait_for_channel/>) waits with a timeout and returns a [`ChannelWait`](<https://libtmux.org/en/rs/latest/reference/server-channelwait/>)
indicating whether it was signalled or timed out.

### Java
Obtain a channel through [`Server.channel`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-server-server-channel/>). Its [`signal()`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-channel-channel-signal/>) method
wakes a waiter; `await(timeout)` returns a [`WakeReason`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-wakereason-wakereason/>) so the caller can
distinguish a signal from a timeout.

### C#
[`Server.OpenWaitChannel`](<https://libtmux.org/en/csharp/latest/reference/libtmux-server-openwaitchannel/>) returns a [`TmuxWaitChannel`](<https://libtmux.org/en/csharp/latest/reference/libtmux-tmuxwaitchannel/>). Keep it in
an `await using` scope and call [`WaitAsync`](<https://libtmux.org/en/csharp/latest/reference/libtmux-tmuxwaitchannel-waitasync/>) with a budget. Select the
signal mode to signal the channel.

### C++
[`Server.signal`](<https://libtmux.org/en/cxx/latest/reference/libtmux-server-signal/>) signals a channel. [`Server.wait_for`](<https://libtmux.org/en/cxx/latest/reference/libtmux-server-wait_for/>) waits for the
channel with a timeout.

### Swift
[`Server.signal`](<https://libtmux.org/en/swift/latest/reference/server-signal(_-)/>) signals a channel, and [`Server.wait(for:)`](<https://libtmux.org/en/swift/latest/reference/server-wait(for-)/>) waits
for it. Both calls are async and throwing.

```python
server.new_session(session_name="work")
server.wait_for("built", set_flag=True)  # signal
server.wait_for("built")                 # block until signalled
```

```go
if err := server.WaitFor(ctx, tmux.WaitForRequest{
    Channel: "built", Mode: tmux.WaitForModeSignal,
}); err != nil {
    return err
}
if err := server.WaitFor(ctx, tmux.WaitForRequest{Channel: "built"}); err != nil {
    return err
}
```

```rust
server.signal_channel("built").await?;
let outcome = server.wait_for_channel("built", std::time::Duration::from_secs(5)).await?;
```

```java
Channel built = server.channel("built");
built.signal();
WakeReason reason = built.await(Duration.ofSeconds(5));
```

```csharp
await using TmuxWaitChannel channel = server.OpenWaitChannel("built");
bool signalled = await channel.WaitAsync(TimeSpan.FromSeconds(5));
```

```cpp
server.signal("built");
server.wait_for("built", std::chrono::seconds{5});
```

```swift
try await server.signal("built")
try await server.wait(for: "built")
```

tmux remembers a signal sent before a waiter starts. The next wait on that
channel returns immediately, so completion is not lost when the command finishes
first.

A raw `wait-for` client can exit zero when the server dies, as well as when the
channel is signalled. Verify server liveness when a lost server must be treated
as a failed task.

Check [`WakeReason`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-wakereason-wakereason/>) to distinguish server loss from a signal. [`drain()`](<https://libtmux.org/en/java/latest/reference/io-github-libtmux-channel-channel-drain/>) can
clear a remembered signal before reusing a channel.
[`wait(for:)`](<https://libtmux.org/en/swift/latest/reference/server-wait(for-)/>) checks the server PID before and after waiting to distinguish
server loss from a signal.

Use a channel name specific to the task. A remembered signal can otherwise
satisfy an unrelated later wait.

The [wait-for reference](https://libtmux.org/en/tmux/latest/manual/wait-for/) describes completion
signals and locks for each supported tmux version.

<details>
<summary>tmux manual and source</summary>

The tmux manual describes [completion channels](https://github.com/tmux/tmux/blob/94796f6b1182507efac8a272fc309a79e22e58a5/tmux.1#L8715).
The [channel implementation](https://github.com/tmux/tmux/blob/94796f6b1182507efac8a272fc309a79e22e58a5/cmd-wait-for.c#L388)
retains an early signal until a waiter consumes it. Use a fresh channel name
for each operation.

</details>
