Skip to content

Add note about stdin, pipe, and inherit #62

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

Merged
merged 3 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 16 additions & 6 deletions src/Node/ChildProcess/Aff.purs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import Data.Maybe (fromJust)
import Data.Posix (Pid)
import Effect.Aff (Aff, effectCanceler, makeAff)
import Effect.Ref as Ref
import Node.ChildProcess (ChildProcess, pid)
import Node.ChildProcess as CP
import Node.ChildProcess.Types (UnsafeChildProcess)
import Node.UnsafeChildProcess.Safe as CPSafe
import Node.ChildProcess (ChildProcess, toUnsafeChildProcess)
import Node.Errors.SystemError (SystemError)
import Node.EventEmitter (once)
import Partial.Unsafe (unsafePartial)
Expand All @@ -19,21 +20,30 @@ import Partial.Unsafe (unsafePartial)
-- | and the `pid` of the process can be obtained.
-- | If an `error` event fires, child process was not started successfully.
waitSpawned :: ChildProcess -> Aff (Either SystemError Pid)
waitSpawned cp = parOneOf [ pidOnSpawn, errored ]
waitSpawned = toUnsafeChildProcess >>> waitSpawned'

-- | Same as `waitSpawned` but works on `UnsafeChildProcess`
-- |
-- | Blocks until either a `spawn` or `error` event is fired.
-- | If a `spawn` event fired, child process was successfully started
-- | and the `pid` of the process can be obtained.
-- | If an `error` event fires, child process was not started successfully.
waitSpawned' :: UnsafeChildProcess -> Aff (Either SystemError Pid)
waitSpawned' cp = parOneOf [ pidOnSpawn, errored ]
where
pidOnSpawn = makeAff \done -> do
ref <- Ref.new mempty
removeListener <- cp # once CP.spawnH do
removeListener <- cp # once CPSafe.spawnH do
join $ Ref.read ref
pid' <- pid cp
pid' <- CPSafe.pid cp
done $ Right $ Right $ unsafePartial $ fromJust pid'
Ref.write removeListener ref
pure $ effectCanceler do
removeListener

errored = makeAff \done -> do
ref <- Ref.new mempty
removeListener <- cp # once CP.errorH \sysErr -> do
removeListener <- cp # once CPSafe.errorH \sysErr -> do
join $ Ref.read ref
done $ Right $ Left sysErr
Ref.write removeListener ref
Expand Down
20 changes: 20 additions & 0 deletions src/Node/ChildProcess/Types.purs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ foreign import data Handle :: Type
-- | See https://nodejs.org/docs/latest-v18.x/api/child_process.html#optionsstdio
foreign import data StdIO :: Type

-- | When used for X, then Y will exist on the child process where X and Y are
-- | - `stdio[0]` - `stdin`
-- | - `stdio[1]` - `stdout`
-- | - `stdio[2]` - `stderr`
-- |
-- | Note: when used with `stdin`, piping the parent stdin to this stream
-- | will not cause the child process to terminate when that parent stdin stream
-- | ends via `Ctrl+D` user input. Rather, the child process will hang
-- | until the parent process calls `Stream.end` on the child process'
-- | `stdin` stream. Since it's impossible to know when the user
-- | inputs `Ctrl+D`, `inherit` should be used instead.
pipe :: StdIO
pipe = unsafeCoerce "pipe"

Expand All @@ -56,6 +67,15 @@ overlapped = unsafeCoerce "overlapped"
ipc :: StdIO
ipc = unsafeCoerce "ipc"

-- | Uses the parent's corresponding stream.
-- |
-- | Note: this value must be used for `stdin` if one
-- | wants to pipe the parent's `stdin` into the child process' `stdin`
-- | AND cause the child process to terminate when the user closes
-- | the parent's `stdin` via `Ctrl+D`. Using `pipe` instead
-- | will cause the child process to hang, even when `Ctrl+D` is pressed,
-- | until the parent process calls `Stream.end`, which cannot be reliably
-- | called the moment AFTER `Ctrl+D` is pressed.
inherit :: StdIO
inherit = unsafeCoerce "inherit"

Expand Down