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

Ensure that waitForProcess is never called more than once (fixes #69) #70

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

sol
Copy link
Contributor

@sol sol commented May 11, 2023

This fixes the issue described in #69:

ghci> import Control.Exception
ghci> import System.Process.Typed
ghci> let config = setStdout createPipe (proc "echo" ["foo"])
ghci> withProcessWait config $ \ p -> Data.ByteString.hGetContents (getStdout p) >> throwIO DivideByZero
*** Exception: waitForProcess: does not exist (No child processes)

This is what I think is happening:

  1. We cancel the waitingThread here:
    cancel waitingThread
  2. The external echo process terminates normally and the waitingThread receives AsyncCancelled right after waitForProcess returns:
    then P.waitForProcess pHandle
    Note that at this point we lost the ExitCode: waitForProcess has concluded, but pExitCode is still empty.
  3. eec <- waitCatch waitingThread
    Now waitCatch returns a Left-value here and we erroneously assume that waitForProcess did not conclude yet.
  4. We call waitForProcess a second time which results in an IOException of type NoSuchThing:
    Right () -> P.waitForProcess pHandle

This PR always relies on the waitingThread to call waitForProcess and by extension structures the code in a away so that waitForProcess can only ever be called once.

@sol sol marked this pull request as ready for review May 11, 2023 10:54
@sol
Copy link
Contributor Author

sol commented May 11, 2023

@snoyberg this is ready to go. I edited the PR description to include the exact execution sequence that I think led to the issue.

@sol sol force-pushed the dev branch 2 times, most recently from 46ddc7b to 218be46 Compare May 11, 2023 11:05
@sol sol changed the title Ensure that waitForProcess is never called multiple times (fixes #69) Ensure that waitForProcess is never called more than once (fixes #69) May 11, 2023
@sol
Copy link
Contributor Author

sol commented May 11, 2023

Also note that this will allow us to further simplify the code if we choose to, to make sure that we can't possibly forget to put the ExitCode into the TMVar (sol@3e37a48).

@snoyberg
Copy link
Member

Overall this looks OK to me, but to be honest this has been a very sensitive part of the code and I don't really remember all the different edge cases. @nh2 would you mind giving this a look over as well?

@sol
Copy link
Contributor Author

sol commented May 16, 2023

Thanks @snoyberg for taking a look.

I don't want to rush this in, but I have a (private) PR pending, that is blocked by this bug fix. So, naturally, I care about getting this merged.

Is there anything I can do to help move this forward? Would it help if I add more verbose comments on how I think best to review this?

@snoyberg
Copy link
Member

I've pinged some people for review, but I'm not comfortable moving ahead on my own review alone, I know this code has gone through lots of revisions with lots of different bugs coming from each iteration.

@sol
Copy link
Contributor Author

sol commented May 23, 2023

Thanks @snoyberg. Can we get #71 merged first? I would then rebase this PR. This will give us a much smaller diff, which I hope will significantly aid in reviewing it.

Note that #71 does not change any behavior.

@snoyberg
Copy link
Member

I see that @tomjaguarpaw already commented on that PR, so I'd defer to him. Personally, I find the change in code harder to parse than the original version.

-- Process already exited, nothing to do
Right _ec -> return ()
Just r -> either throwIO return r
Copy link
Contributor Author

@sol sol May 23, 2023

Choose a reason for hiding this comment

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

Most of the time this will be a Right-value, but with delegate_ctlc this can be UserInterrupt, which we then re-throw here (see #73).

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.

2 participants