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

fix: work in progress macos support #8

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mightyiam
Copy link

Co-authored-by: alex [email protected]
Co-authored-by: echolinq [email protected]
Co-authored-by: warren2k [email protected]

@mightyiam
Copy link
Author

@doy thank you for this crate. We're trying to use it and we need macos support. So we've been trying to figure that out. I will comment on the individual changes. Could you please allow it to run?

Co-authored-by: alex <[email protected]>
Co-authored-by: echolinq <[email protected]>
Co-authored-by: warren2k <[email protected]>
Copy link
Author

@mightyiam mightyiam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -15,7 +15,6 @@ impl Pty {
/// unable to put it into non-blocking mode.
pub fn new() -> crate::Result<Self> {
let pty = crate::sys::Pty::open()?;
pty.set_nonblocking()?;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently, macos won't let ya do this prior to obtaining a pts.

Comment on lines -38 to +39
Ok(Pts(self.0.get_ref().pts()?))
let pts = Pts(self.0.get_ref().pts()?);
self.0.get_ref().set_nonblocking()?;
Ok(pts)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we do it here. And we're not certain whether this is idempotent enough to not be an issue with multiple calls.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this should be fine to call multiple times

Comment on lines +8 to 12
let pts = pty.pts().unwrap();
pty.resize(pty_process::Size::new(24, 80)).unwrap();
let mut child = pty_process::blocking::Command::new("cat")
.spawn(&pty.pts().unwrap())
.spawn(&pts)
.unwrap();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to setting nonblocking, other operations also seem to be disallowed prior to obtaining a pts.

Comment on lines -19 to +20
pty.write_all(&[4u8]).unwrap();
let status = child.wait().unwrap();
assert_eq!(status.code().unwrap(), 0);
child.kill().unwrap();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On macos, this ^D doesn't seem to kill the cat. 🤷 ??

If we care, we better figure out why. If we don't, we may as well allow the child to be dropped implicitly, I suppose.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd rather figure out what's going on here - ^D does work to terminate a cat process when run normally on macos, so this crate (or test) is doing something wrong here, and i don't want to just sidestep that.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this one is actually related to your change above - you're keeping the pts alive longer than before, which apparently prevents the wait from succeeding. if you instead do something like

    let mut child = {
        let pts = pty.pts().unwrap();
        pty.resize(pty_process::Size::new(24, 80)).unwrap();
        pty_process::blocking::Command::new("cat")
            .spawn(&pts)
            .unwrap()
    };

^D should work again.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm... perhaps. Assuming this is true, wouldn't a user be prone to making a similar mistake such as we made in this test?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When spawning a new process, the Pts is borrowed. Is this because it's meant for reuse? If not, can it instead by passed by value so that spawn() can drop it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pts is meant for reuse (probably in UNIX in general) as demonstrated in

#[test]
fn test_multiple() {
let pty = pty_process::blocking::Pty::new().unwrap();
let pts = pty.pts().unwrap();
pty.resize(pty_process::Size::new(24, 80)).unwrap();
let mut child = pty_process::blocking::Command::new("echo")
.arg("foo")
.spawn(&pts)
.unwrap();
let mut output = helpers::output(&pty);
assert_eq!(output.next().unwrap(), "foo\r\n");
let status = child.wait().unwrap();
assert_eq!(status.code().unwrap(), 0);
let mut child = pty_process::blocking::Command::new("echo")
.arg("bar")
.spawn(&pts)
.unwrap();
assert_eq!(output.next().unwrap(), "bar\r\n");
let status = child.wait().unwrap();
assert_eq!(status.code().unwrap(), 0);
}

So, alternatively, what does dropping the Pts do... or what does not dropping it do to prevent the cat child process from exiting on a signal?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that blocking::Pts doesn't have a Drop impl and neither does sys::Pts. It would be std::os::fd::OwnedFd that needs to be dropped in order for cat to terminate on a signal. On macos, at least.

Which kind of makes me think that it may be that cat is terminating and something else does not. But that's just a hunch. We'd have to take a closer look. We can start with the wait method.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This library's spawn method returns a plain std::process::Child. So it is std::process::Child::wait that is blocking forever even though a signal was sent to cat.

One option: cat is not receiving the signal.
Another option: cat is receiving the signal and terminating—what else would it do?—but for another reason the wait method keeps blocking. Something that has to do with that non-dropped Pts.

Comment on lines -71 to +69
child.kill().await.unwrap()
#[cfg(not(target_os = "macos"))]
child.kill().await.unwrap();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On macos this seems to hang. Why?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similarly, this isn't doing anything obviously wrong, so i'd like to understand what is broken here before just disabling it.

Comment on lines -43 to +42
pty_w.write_all(&[4u8]).await.unwrap();
let status = child.wait().await.unwrap();
assert_eq!(status.code().unwrap(), 0);
child.kill().await.unwrap()
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to test_cat_blocking.

@samuela
Copy link

samuela commented Nov 25, 2023

FWIW I tested this example resize usage on the changes from this PR, but found that it doesn't seem to make a difference. Not sure if you view that as in scope for this particular PR or not.

@mightyiam
Copy link
Author

@samuela I'm not sure what the restrictions on macos are. From what we've seen so far, resizing before creating a client is not tolerated. Which makes the abstraction provided by this crate less appealing on macos.

@samuela
Copy link

samuela commented Nov 25, 2023

Thanks @mightyiam! Only calling resize after spawning a child seems to fix the problem for me

@samuela
Copy link

samuela commented Nov 28, 2023

Ok I can now confirm that as long as I wait until after spawning a process, everything seems to be fine with this PR. Without this PR, it doesn't work regardless of order. So it's definitely a step forward IMHO.

@mightyiam
Copy link
Author

@samuela I'd love the crate author's feedback, as well, but haven't been able to get it yet.

@doy
Copy link
Owner

doy commented Dec 3, 2023

thanks for looking into this! these tests have been failing on macos for a while, and i don't have a mac so i'm glad you're looking into it.

@doy
Copy link
Owner

doy commented Dec 3, 2023

1895e6c this commit appears to get the test suite working on the macos github action workers - can you try this out and see if it works for your case?

@mightyiam
Copy link
Author

We just had a session where we made some experiments and learned a few things. We will move toward a PR tomorrow. Thank you for that wip commit. We will look at it tomorrow, as well.

@samuela
Copy link

samuela commented Dec 16, 2023

Hi @mightyiam , just curious if you created that PR? I'd be curious to try it!

@mightyiam
Copy link
Author

Next session scheduled 7 hours from now.

@samuela
Copy link

samuela commented Jan 24, 2024

Hi @doy @mightyiam friendly ping on this. I've been running on this PR for a few weeks now and it's worked great so far.

@mightyiam
Copy link
Author

@samuela we've extracted #9 as an isolated piece of valuable change that could be easily accepted. After which we intend to rebase and proceed in some reasonable way.

I also offered @doy my co-maintaining of this project.

@samuela samuela mentioned this pull request Mar 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants