mirror of
https://github.com/nestriness/nestri.git
synced 2026-11-03 22:22:39 +02:00
fix(nescope): end nescope when the hub stays gone after its socket closes
The input source now removes itself on EOF or reset and raises a flag; the main loop exits 2 s later unless a signal already ended it, so a hub crash mid-run still ends the compositor (and the box via CompositorDied) instead of holding a GPU with no input. Also corrects the comment that promised a reconnect that does not exist.
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
use std::io::{self, Read};
|
||||
use std::os::unix::io::{AsFd, BorrowedFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use calloop::{EventSource, Interest, Mode, Poll, PostAction, Readiness, Token, TokenFactory};
|
||||
|
||||
pub struct InputIpcSource {
|
||||
stream: UnixStream,
|
||||
buf: Vec<u8>,
|
||||
/// Set once the hub has closed its end, for the main loop to act on.
|
||||
closed: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl InputIpcSource {
|
||||
@@ -16,9 +20,17 @@ impl InputIpcSource {
|
||||
Ok(Self {
|
||||
stream,
|
||||
buf: Vec::new(),
|
||||
closed: Arc::new(AtomicBool::new(false)),
|
||||
})
|
||||
}
|
||||
|
||||
/// True once the hub has closed the socket. The main loop gives that a
|
||||
/// grace period and then ends nescope: a compositor with no input holds
|
||||
/// the GPU for nothing.
|
||||
pub fn closed_flag(&self) -> Arc<AtomicBool> {
|
||||
self.closed.clone()
|
||||
}
|
||||
|
||||
pub fn try_clone(&self) -> io::Result<UnixStream> {
|
||||
self.stream.try_clone()
|
||||
}
|
||||
@@ -34,6 +46,7 @@ impl InputIpcSource {
|
||||
Ok(n) => self.buf.extend_from_slice(&tmp[..n]),
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => return Ok(false),
|
||||
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||
Err(e) if e.kind() == io::ErrorKind::ConnectionReset => return Ok(true),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
@@ -81,6 +94,7 @@ impl EventSource for InputIpcSource {
|
||||
}
|
||||
|
||||
if closed {
|
||||
self.closed.store(true, Ordering::Relaxed);
|
||||
tracing::warn!("input IPC socket closed by the hub; no more input from it");
|
||||
return Ok(PostAction::Remove);
|
||||
}
|
||||
@@ -112,6 +126,7 @@ mod tests {
|
||||
InputIpcSource {
|
||||
stream: a,
|
||||
buf: Vec::new(),
|
||||
closed: Arc::new(AtomicBool::new(false)),
|
||||
},
|
||||
b,
|
||||
)
|
||||
|
||||
@@ -389,11 +389,15 @@ fn main() {
|
||||
|
||||
// ── Input IPC source ─────────────────────────────────────────────────
|
||||
// Connect to the neshub input socket and feed events into the
|
||||
// compositor seat. Reconnection is handled in the idle callback.
|
||||
// compositor seat. There is no reconnection: when the hub closes the socket
|
||||
// the source removes itself, and the loop below ends nescope after a short
|
||||
// grace (a signal that arrives first ends it sooner).
|
||||
let ipc_path = args.input_ipc.clone();
|
||||
let mut hub_closed_flag: Option<std::sync::Arc<std::sync::atomic::AtomicBool>> = None;
|
||||
match input_ipc::InputIpcSource::connect(&ipc_path) {
|
||||
Ok(source) => {
|
||||
tracing::info!("Connected to input IPC socket: {ipc_path}");
|
||||
hub_closed_flag = Some(source.closed_flag());
|
||||
match source.try_clone() {
|
||||
Ok(write_stream) => {
|
||||
state.ipc_write = Some(write_stream);
|
||||
@@ -476,6 +480,7 @@ fn main() {
|
||||
// never arrives there is nothing to wait for and no game to run.
|
||||
let startup = std::time::Instant::now();
|
||||
let mut xwayland_timed_out = false;
|
||||
let mut hub_closed_at: Option<std::time::Instant> = None;
|
||||
|
||||
event_loop
|
||||
.run(Some(Duration::from_secs(1)), &mut data, move |data| {
|
||||
@@ -578,6 +583,24 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Hub gone ──────────────────────────────────────────────
|
||||
// neshub closing the input socket mid-run leaves a compositor
|
||||
// nobody can drive. The grace lets a shutdown that is already
|
||||
// under way (SIGTERM stops the loop itself) finish its own way;
|
||||
// otherwise nescope ends, and nesinit ends the box on seeing it.
|
||||
if hub_closed_flag
|
||||
.as_ref()
|
||||
.is_some_and(|f| f.load(std::sync::atomic::Ordering::Relaxed))
|
||||
{
|
||||
let since = hub_closed_at.get_or_insert_with(std::time::Instant::now);
|
||||
if since.elapsed() > Duration::from_secs(2) {
|
||||
tracing::error!("input hub gone for 2 s with no shutdown — exiting");
|
||||
kill_all_children();
|
||||
data.loop_signal.stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Poll primary process ──────────────────────────────────
|
||||
// The launcher (e.g. Steam's shell wrapper) may exit quickly
|
||||
// while the real game client stays alive as a reparented
|
||||
|
||||
Reference in New Issue
Block a user