Skip to content

Commit

Permalink
3.1.2 Set the PTY fd to be non-blocking
Browse files Browse the repository at this point in the history
We have gone back and forth a couple of times, but we _do_ need to set
this FD to be non-blocking. Otherwise node will fully consume one I/O
thread for each PTY, and node only has four of those.

This change makes the controller fd (the one the parent process reads
from / writes to) as non-blocking for node.
  • Loading branch information
lhchavez committed Jul 15, 2024
1 parent 6acb8f2 commit 73b4b04
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 5 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@replit/ruspty",
"version": "3.1.1",
"version": "3.1.2",
"main": "dist/wrapper.js",
"types": "dist/wrapper.d.ts",
"author": "Szymon Kaliski <[email protected]>",
Expand Down
32 changes: 30 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFun
use napi::Status::GenericFailure;
use napi::{self, Env};
use nix::errno::Errno;
use nix::fcntl::{fcntl, FcntlArg, FdFlag};
use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag};
use nix::poll::{poll, PollFd, PollFlags, PollTimeout};
use nix::pty::{openpty, Winsize};
use nix::sys::termios::{self, SetArg};
Expand Down Expand Up @@ -117,12 +117,14 @@ impl Pty {
}

// open pty pair, and set close-on-exec to avoid unwanted copies of the FDs from finding their
// way into subprocesses.
// way into subprocesses. Also set the nonblocking flag to avoid Node from consuming a full I/O
// thread for this.
let pty_res = openpty(&window_size, None).map_err(cast_to_napi_error)?;
let controller_fd = pty_res.master;
let user_fd = pty_res.slave;
set_close_on_exec(controller_fd.as_raw_fd(), true)?;
set_close_on_exec(user_fd.as_raw_fd(), true)?;
set_nonblocking(controller_fd.as_raw_fd())?;

// duplicate pty user_fd to be the child's stdin, stdout, and stderr
cmd.stdin(Stdio::from(user_fd.try_clone()?));
Expand Down Expand Up @@ -362,3 +364,29 @@ fn get_close_on_exec(fd: i32) -> Result<bool, napi::Error> {
)),
}
}

/// Set the file descriptor to be non-blocking.
#[allow(dead_code)]
fn set_nonblocking(fd: i32) -> Result<(), napi::Error> {
let old_flags = match fcntl(fd, FcntlArg::F_GETFL) {
Ok(flags) => OFlag::from_bits_truncate(flags),
Err(err) => {
return Err(napi::Error::new(
GenericFailure,
format!("fcntl F_GETFL: {}", err),
));
}
};

let mut new_flags = old_flags;
new_flags.set(OFlag::O_NONBLOCK, true);
if old_flags != new_flags {
if let Err(err) = fcntl(fd, FcntlArg::F_SETFL(new_flags)) {
return Err(napi::Error::new(
GenericFailure,
format!("fcntl F_SETFL: {}", err),
));
}
}
Ok(())
}

0 comments on commit 73b4b04

Please sign in to comment.