aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-client
diff options
context:
space:
mode:
authorhar7an <99636919+har7an@users.noreply.github.com>2026-02-19 11:59:14 +0000
committerGitHub <noreply@github.com>2026-02-19 12:59:14 +0100
commit3a9cf0a6d6b98dee0246e6fa5a539e1f6d546084 (patch)
tree5021050e1d0984de3ca27295bd6ed6b83058d9c1 /zellij-client
parentfix: tab-bar wrapping for extremely small widths (#4728) (diff)
downloadsl0ppij-3a9cf0a6d6b98dee0246e6fa5a539e1f6d546084.tar.gz
sl0ppij-3a9cf0a6d6b98dee0246e6fa5a539e1f6d546084.tar.xz
sl0ppij-3a9cf0a6d6b98dee0246e6fa5a539e1f6d546084.zip
Purge `async_std` from the codebase (#4672)
* build(utils): Replace `async_std` with `tokio` removing an entire async runtime from the dependency tree. `tokio` has previously been required by `wasmtime` in any case, and the version of async_std we used was ancient (1.3) and relied on unstable features. * build(server): Remove `async_std` from the codebase and replace it with `tokio` instead, which is already used by `zellij-utils` and `wasmtime`. * build(server): Remove dead dependency. * fix: Don't keep an async runtime in the `zellij-utils` subcrate but prefer to reuse an existing runtime from a caller instead. If no such runtime exists, create an ephemeral runtime on the current thread to run async tasks instead. Ideally, the functions needing the async task would be async themselves, but these are called by the `Layout` code based on a `ClientMsg`, and the code handling this currently lives in `zellij-utils` and is sync itself. So, for the time being, there is no way around this runtime requirement without a huge refactoring. * refactor(server): Unclutter async tasks in a few cases where either blocking code was called in an (unnecessary) async task or an entire thread was spawned to block on the result of an async task. * docs(CHANGELOG): Mention PR 4672 * fix(server): Don't terminate when killing a panes children fails but log the error and continue instead. * feat: Add config option for number of async tasks to spawn so users on machines with few resources can configure a task limit without requiring recompilation.
Diffstat (limited to 'zellij-client')
-rw-r--r--zellij-client/Cargo.toml1
-rw-r--r--zellij-client/src/lib.rs48
-rw-r--r--zellij-client/src/remote_attach/mod.rs16
-rw-r--r--zellij-client/src/remote_attach/unit/remote_attach_tests.rs4
-rw-r--r--zellij-client/src/web_client/mod.rs2
5 files changed, 57 insertions, 14 deletions
diff --git a/zellij-client/Cargo.toml b/zellij-client/Cargo.toml
index 655f0f04..2e1a07fc 100644
--- a/zellij-client/Cargo.toml
+++ b/zellij-client/Cargo.toml
@@ -31,6 +31,7 @@ log = { workspace = true }
mio = { version = "0.8.11", default-features = false, features = ['os-ext'] }
nix = { workspace = true }
notify = { workspace = true }
+num_cpus = { workspace = true }
regex = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs
index e7b6bc97..8f0ed253 100644
--- a/zellij-client/src/lib.rs
+++ b/zellij-client/src/lib.rs
@@ -26,8 +26,6 @@ use zellij_utils::shared::web_server_base_url;
#[cfg(feature = "web_server_capability")]
use futures_util::{SinkExt, StreamExt};
#[cfg(feature = "web_server_capability")]
-use tokio::runtime::Runtime;
-#[cfg(feature = "web_server_capability")]
use tokio_tungstenite::tungstenite::Message;
#[cfg(feature = "web_server_capability")]
@@ -36,6 +34,47 @@ use crate::web_client::control_message::{
WebServerToWebClientControlMessage,
};
+static ASYNC_RUNTIME: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
+use std::sync::OnceLock;
+
+/// Spawn an async runtime for this client instance.
+///
+/// The number of workers can be configured to any nonzero value. Passing zero or `None` will spawn
+/// one worker per physical CPU on the current machine.
+pub(crate) fn async_runtime(maybe_number_of_workers: Option<usize>) -> tokio::runtime::Handle {
+ match tokio::runtime::Handle::try_current() {
+ Ok(handle) => handle.clone(),
+ _ => {
+ let number_of_workers = match maybe_number_of_workers {
+ Some(value) if value > 0 => {
+ log::debug!(
+ "Creating client async runtime with {} tasks based on user request",
+ value
+ );
+ value
+ },
+ _ => {
+ let cpus = num_cpus::get_physical();
+ log::debug!(
+ "Creating client async runtime with {} tasks based on CPU count",
+ cpus
+ );
+ cpus
+ },
+ };
+ let runtime = ASYNC_RUNTIME.get_or_init(|| {
+ tokio::runtime::Builder::new_multi_thread()
+ .worker_threads(number_of_workers)
+ .thread_name("zellij client async-runtime")
+ .enable_all()
+ .build()
+ .expect("Failed to create tokio runtime")
+ });
+ runtime.handle().clone()
+ },
+ }
+}
+
#[derive(Debug)]
pub enum RemoteClientError {
InvalidAuthToken,
@@ -458,13 +497,14 @@ pub fn start_remote_client(
token: Option<String>,
remember: bool,
forget: bool,
+ async_worker_tasks: Option<usize>,
) -> Result<Option<ConnectToSession>, RemoteClientError> {
info!("Starting Zellij client!");
- let runtime = Runtime::new().map_err(|e| RemoteClientError::IoError(e))?;
+ let runtime = crate::async_runtime(async_worker_tasks);
let connections = remote_attach::attach_to_remote_session(
- &runtime,
+ runtime.clone(),
os_input.clone(),
remote_session_url,
token,
diff --git a/zellij-client/src/remote_attach/mod.rs b/zellij-client/src/remote_attach/mod.rs
index 02664e82..d6d7cf32 100644
--- a/zellij-client/src/remote_attach/mod.rs
+++ b/zellij-client/src/remote_attach/mod.rs
@@ -10,7 +10,7 @@ pub use websockets::WebSocketConnections;
use crate::os_input_output::ClientOsApi;
use crate::RemoteClientError;
-use tokio::runtime::Runtime;
+use tokio::runtime::Handle;
use zellij_utils::remote_session_tokens;
// In tests, only attempt once (no retries) to avoid interactive prompts
@@ -32,7 +32,7 @@ const MAX_AUTH_ATTEMPTS: u32 = 3;
///
/// Returns WebSocketConnections on success
pub fn attach_to_remote_session(
- runtime: &Runtime,
+ runtime: Handle,
_os_input: Box<dyn ClientOsApi>,
remote_session_url: &str,
token: Option<String>,
@@ -53,9 +53,11 @@ pub fn attach_to_remote_session(
}
if token.is_none() {
- if let Some(connections) =
- try_to_connect_with_saved_session_token(runtime, remote_session_url, &server_url)?
- {
+ if let Some(connections) = try_to_connect_with_saved_session_token(
+ runtime.clone(),
+ remote_session_url,
+ &server_url,
+ )? {
return Ok(connections);
}
}
@@ -67,7 +69,7 @@ pub fn attach_to_remote_session(
/// Try to connect using a saved session token
/// Returns Ok(Some(connections)) on success, Ok(None) if should retry with auth
fn try_to_connect_with_saved_session_token(
- runtime: &Runtime,
+ runtime: Handle,
remote_session_url: &str,
server_url: &str,
) -> Result<Option<WebSocketConnections>, RemoteClientError> {
@@ -94,7 +96,7 @@ fn try_to_connect_with_saved_session_token(
}
fn authenticate_with_retry(
- runtime: &Runtime,
+ runtime: Handle,
remote_session_url: &str,
initial_token: Option<String>,
remember: bool,
diff --git a/zellij-client/src/remote_attach/unit/remote_attach_tests.rs b/zellij-client/src/remote_attach/unit/remote_attach_tests.rs
index 4673abb4..076e9ced 100644
--- a/zellij-client/src/remote_attach/unit/remote_attach_tests.rs
+++ b/zellij-client/src/remote_attach/unit/remote_attach_tests.rs
@@ -299,10 +299,10 @@ mod tests {
forget: bool,
) -> Result<WebSocketConnections, RemoteClientError> {
tokio::task::spawn_blocking(move || {
- let runtime = tokio::runtime::Runtime::new().unwrap();
+ let runtime = crate::async_runtime(None);
let os_input: Box<dyn crate::os_input_output::ClientOsApi> = Box::new(MockClientOsApi);
attach_to_remote_session(
- &runtime,
+ runtime,
os_input,
&remote_session_url,
token,
diff --git a/zellij-client/src/web_client/mod.rs b/zellij-client/src/web_client/mod.rs
index 02c9399f..0ef47302 100644
--- a/zellij-client/src/web_client/mod.rs
+++ b/zellij-client/src/web_client/mod.rs
@@ -23,6 +23,7 @@ use axum::{
routing::{any, get, post},
Router,
};
+use tokio::runtime::Runtime;
use axum_server::tls_rustls::RustlsConfig;
use axum_server::Handle;
@@ -32,7 +33,6 @@ use nix::sys::stat::{umask, Mode};
use interprocess::unnamed_pipe::pipe;
use std::io::{prelude::*, BufRead, BufReader};
-use tokio::runtime::Runtime;
use zellij_utils::input::{config::Config, options::Options};
use authentication::auth_middleware;