Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

poll for TIOCINQ and TIOCOUTQ instead of POLLIN, bump repeats #51

Merged
merged 60 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
b14bc3b
bump repeats, add debug
jackyzha0 Oct 24, 2024
92edf92
no manual eio
jackyzha0 Oct 24, 2024
4cce184
more rearranging
jackyzha0 Oct 24, 2024
be250b3
fix the concurrent shells test
jackyzha0 Oct 24, 2024
6068305
log close path
jackyzha0 Oct 24, 2024
9407e1c
use async pattern for the tests
jackyzha0 Oct 24, 2024
036ecc0
kill proc
jackyzha0 Oct 24, 2024
dd6356e
fix exit code
jackyzha0 Oct 24, 2024
f482437
small test changes
jackyzha0 Oct 24, 2024
1f65122
1000 repeats
jackyzha0 Oct 24, 2024
5863313
no console
jackyzha0 Oct 24, 2024
76daf08
100 repeats
jackyzha0 Oct 24, 2024
326a9da
readd log
jackyzha0 Oct 24, 2024
51e3072
modify canon settings
jackyzha0 Oct 24, 2024
115cd70
re-add repeats
jackyzha0 Oct 24, 2024
e26aa2d
undo lib changes, format after build
jackyzha0 Oct 24, 2024
28d0f2a
different ordering test
jackyzha0 Oct 25, 2024
2c725bf
allow only
jackyzha0 Oct 25, 2024
91925ba
better log
jackyzha0 Oct 25, 2024
c67fe75
line no check
jackyzha0 Oct 25, 2024
d5cfe4c
more logging changes
jackyzha0 Oct 25, 2024
16421d7
enable both
jackyzha0 Oct 25, 2024
67e0611
remove some linux specific stuff
jackyzha0 Oct 25, 2024
4c07825
try with timeout?
jackyzha0 Oct 25, 2024
48850d2
re-add require
jackyzha0 Oct 25, 2024
28dc611
try sleep?
jackyzha0 Oct 25, 2024
2a8829b
check readable side
jackyzha0 Oct 25, 2024
a370cd0
try suggestion
jackyzha0 Oct 25, 2024
5587772
oops forgor import
jackyzha0 Oct 25, 2024
8630ac2
test
jackyzha0 Oct 25, 2024
a9ba65c
try without eio
jackyzha0 Oct 25, 2024
79c5d98
only print out bytelength
jackyzha0 Oct 25, 2024
def3aef
more logging
jackyzha0 Oct 25, 2024
d46ce6b
even more logs
jackyzha0 Oct 25, 2024
8af59d0
zam
jackyzha0 Oct 25, 2024
5381170
strace analysis
jackyzha0 Oct 25, 2024
63a8b27
oops
jackyzha0 Oct 25, 2024
ed54c56
more tests
jackyzha0 Oct 25, 2024
7c32410
try with destroy soon
jackyzha0 Oct 25, 2024
2ef14a5
try with emit end
jackyzha0 Oct 25, 2024
e908971
run all the tests now
jackyzha0 Oct 25, 2024
084818d
put back only fuck
jackyzha0 Oct 25, 2024
fdbc2fb
wtf
jackyzha0 Oct 25, 2024
f5cfe8b
print some more expectations
jackyzha0 Oct 25, 2024
79567f6
even more print debug
jackyzha0 Oct 25, 2024
8e5b434
goddamn
jackyzha0 Oct 25, 2024
a915772
flush
jackyzha0 Oct 25, 2024
58f0329
i am confusion
jackyzha0 Oct 25, 2024
d1aa648
more
jackyzha0 Oct 25, 2024
a1a9998
another check
jackyzha0 Oct 26, 2024
1365d3a
also FIONREAD
jackyzha0 Oct 26, 2024
557045e
test poll for TIOCING and TIOCOUTQ
jackyzha0 Oct 28, 2024
c9768cc
cleanup some more
jackyzha0 Oct 28, 2024
7366a5a
use -1 instead
jackyzha0 Oct 28, 2024
a194294
run all, remove debug
jackyzha0 Oct 28, 2024
a0b5604
cleanup some more imports
jackyzha0 Oct 28, 2024
7810ed9
re-add write trait
jackyzha0 Oct 28, 2024
9c853e9
3.4.0
jackyzha0 Oct 28, 2024
b6c7de2
try with nix::sys:ioctl
jackyzha0 Oct 28, 2024
9f5a4e9
back to raw ioctl we go
jackyzha0 Oct 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion npm/darwin-arm64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@replit/ruspty-darwin-arm64",
"version": "3.3.0",
"version": "3.4.0",
"os": [
"darwin"
],
Expand Down
2 changes: 1 addition & 1 deletion npm/darwin-x64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@replit/ruspty-darwin-x64",
"version": "3.3.0",
"version": "3.4.0",
"os": [
"darwin"
],
Expand Down
2 changes: 1 addition & 1 deletion npm/linux-x64-gnu/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@replit/ruspty-linux-x64-gnu",
"version": "3.3.0",
"version": "3.4.0",
"os": [
"linux"
],
Expand Down
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.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@replit/ruspty",
"version": "3.3.0",
"version": "3.4.0",
"main": "dist/wrapper.js",
"types": "dist/wrapper.d.ts",
"author": "Szymon Kaliski <[email protected]>",
Expand Down Expand Up @@ -36,7 +36,7 @@
},
"scripts": {
"artifacts": "napi artifacts",
"build": "napi build --platform --release && npm run build:wrapper",
"build": "napi build --platform --release && npm run build:wrapper && npm run format",
"build:wrapper": "tsup",
"prepublishOnly": "napi prepublish -t npm",
"test": "vitest run",
Expand Down
68 changes: 36 additions & 32 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::Error;
use std::io::ErrorKind;
use std::io::Write;
use std::io::{Error, Write};
use std::os::fd::{AsRawFd, OwnedFd};
use std::os::fd::{BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use std::os::fd::{FromRawFd, IntoRawFd, RawFd};
use std::os::unix::process::CommandExt;
use std::process::{Command, Stdio};
use std::thread;
use std::time::Duration;

use backoff::backoff::Backoff;
use backoff::ExponentialBackoffBuilder;
use libc::{self, c_int};
use napi::bindgen_prelude::JsFunction;
use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode};
use napi::Status::GenericFailure;
use napi::{self, Env};
use nix::errno::Errno;
use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag};
use nix::poll::{poll, PollFd, PollFlags, PollTimeout};
use nix::libc::{self, c_int, ioctl, FIONREAD, TIOCOUTQ, TIOCSCTTY, TIOCSWINSZ};
use nix::pty::{openpty, Winsize};
use nix::sys::termios::{self, SetArg};

Expand Down Expand Up @@ -59,45 +57,54 @@ fn cast_to_napi_error(err: Errno) -> napi::Error {
napi::Error::new(GenericFailure, err)
}

// if the child process exits before the controller fd is fully read, we might accidentally
// end in a case where onExit is called but js hasn't had the chance to fully read the controller fd
// if the child process exits before the controller fd is fully read or the user fd is fully
// flushed, we might accidentally end in a case where onExit is called but js hasn't had
// the chance to fully read the controller fd
// let's wait until the controller fd is fully read before we call onExit
fn poll_controller_fd_until_read(raw_fd: RawFd) {
// wait until fd is fully read (i.e. POLLIN no longer set)
let borrowed_fd = unsafe { BorrowedFd::borrow_raw(raw_fd) };
let poll_fd = PollFd::new(borrowed_fd, PollFlags::POLLIN);

fn poll_pty_fds_until_read(controller_fd: RawFd, user_fd: RawFd) {
let mut backoff = ExponentialBackoffBuilder::default()
.with_initial_interval(Duration::from_millis(1))
.with_max_interval(Duration::from_millis(100))
.with_max_elapsed_time(Some(Duration::from_secs(1)))
.build();

loop {
if let Err(err) = poll(&mut [poll_fd], PollTimeout::ZERO) {
if err == Errno::EINTR || err == Errno::EAGAIN {
// we were interrupted, so we should just try again
continue;
}
// check both input and output queues for both FDs
let mut controller_inq: i32 = 0;
let mut controller_outq: i32 = 0;
let mut user_inq: i32 = 0;
let mut user_outq: i32 = 0;

// we should almost never hit this, but if we do, we should just break out of the loop. this
// can happen if Node destroys the terminal before waiting for the child process to go away.
break;
}
// safe because we're passing valid file descriptors and properly sized integers
unsafe {
// check bytes waiting to be read (FIONREAD, equivalent to TIOCINQ on Linux)
if ioctl(controller_fd, FIONREAD, &mut controller_inq) == -1
|| ioctl(user_fd, FIONREAD, &mut user_inq) == -1
{
// break if we can't read
break;
}

// check if POLLIN is no longer set (i.e. there is no more data to read)
if let Some(flags) = poll_fd.revents() {
if !flags.contains(PollFlags::POLLIN) {
// check bytes waiting to be written (TIOCOUTQ)
if ioctl(controller_fd, TIOCOUTQ, &mut controller_outq) == -1
|| ioctl(user_fd, TIOCOUTQ, &mut user_outq) == -1
{
// break if we can't read
break;
}
}

// wait for a bit before trying again
// if all queues are empty, we're done
if controller_inq == 0 && controller_outq == 0 && user_inq == 0 && user_outq == 0 {
break;
}

// apply backoff strategy
if let Some(d) = backoff.next_backoff() {
thread::sleep(d);
continue;
} else {
// we have exhausted our attempts, its joever
// we have exhausted our attempts
break;
}
}
Expand Down Expand Up @@ -181,7 +188,7 @@ impl Pty {
}

// become the controlling tty for the program
let err = libc::ioctl(raw_user_fd, libc::TIOCSCTTY.into(), 0);
let err = libc::ioctl(raw_user_fd, TIOCSCTTY.into(), 0);
if err == -1 {
return Err(Error::new(ErrorKind::Other, "ioctl-TIOCSCTTY"));
}
Expand Down Expand Up @@ -244,10 +251,7 @@ impl Pty {
let wait_result = child.wait();

// try to wait for the controller fd to be fully read
poll_controller_fd_until_read(raw_controller_fd);

// we don't drop the controller fd immediately
// let pty.close() be responsible for closing it
poll_pty_fds_until_read(raw_controller_fd, raw_user_fd);
drop(user_fd);

match wait_result {
Expand Down Expand Up @@ -310,7 +314,7 @@ fn pty_resize(fd: i32, size: Size) -> Result<(), napi::Error> {
ws_ypixel: 0,
};

let res = unsafe { libc::ioctl(fd, libc::TIOCSWINSZ, &window_size as *const _) };
let res = unsafe { libc::ioctl(fd, TIOCSWINSZ, &window_size as *const _) };
if res == -1 {
return Err(napi::Error::new(
napi::Status::GenericFailure,
Expand Down
Loading