Elixir/Ports and external process wiring: Difference between revisions

Adamw (talk | contribs)
Adamw (talk | contribs)
Line 94: Line 94:


== Bad assumption: pipe-like processes ==
== Bad assumption: pipe-like processes ==
A straightforward use case for external processes would be to run a standard transformation such as compression or decompression.  A program like <code>gzip</code> or <code>cat</code> will stop once it detects that its input has ended, because the main loop usually makes a C system call to <code>read</code> like this:<syntaxhighlight lang="c">
A program like <code>gzip</code> or <code>cat</code> will stop once it detects that its input has ended because the main loop usually makes a C system call to <code>read</code> like this:<syntaxhighlight lang="c">
ssize_t n_read = read (input_desc, buf, bufsize);
ssize_t n_read = read (input_desc, buf, bufsize);
if (n_read < 0) { error... }
if (n_read < 0) { error... }
Line 100: Line 100:
</syntaxhighlight>The manual for read<ref>https://man.archlinux.org/man/read.2</ref> explains that reading 0 bytes indicates the end of file, and a negative number indicates an error such as the input file descriptor already being closed.  If you think this sounds weird, I would agree: how do we tell the difference between a stream which is stalled and one which has ended?  Does the calling process yield control until input arrives?  How do we know if more than bufsize bytes are available?  If that word salad excites you, read more about <code>O_NONBLOCK</code><ref>https://man.archlinux.org/man/open.2.en#O_NONBLOCK</ref> and unix pipes<ref>https://man.archlinux.org/man/pipe.7.en</ref>.
</syntaxhighlight>The manual for read<ref>https://man.archlinux.org/man/read.2</ref> explains that reading 0 bytes indicates the end of file, and a negative number indicates an error such as the input file descriptor already being closed.  If you think this sounds weird, I would agree: how do we tell the difference between a stream which is stalled and one which has ended?  Does the calling process yield control until input arrives?  How do we know if more than bufsize bytes are available?  If that word salad excites you, read more about <code>O_NONBLOCK</code><ref>https://man.archlinux.org/man/open.2.en#O_NONBLOCK</ref> and unix pipes<ref>https://man.archlinux.org/man/pipe.7.en</ref>.


But here we'll focus on how processes affect each other through pipes.  Surprising answer: they don't affect very much!  Try opening a "cat" in the terminal and then type <control>-d to "send" an end-of-file.  Oh no, you killed it!  You didn't actually send anything, though—the <control>-d is interpreted by bash and it responds by closing its pipe connected to "[[w:Standard streams|standard input]]" of the child process.  This is similar to how <control>-c is not sending a character but is interpreted by the terminal, trapped by the shell and forwarded as an interrupt signal to the child process, completely independently of the data pipe.  My entry point to learning more is this stty webzine<ref>https://wizardzines.com/comics/stty/</ref> by Julia Evans.  Go ahead and try this command, what could go wrong: <code>stty -a</code>
But here we'll focus on how processes affect each other through pipes.  Surprising answer: it doesn't affect very much!  Try opening a "cat" in the terminal and then type <control>-d to "send" an end-of-file.  Oh no, you killed it!  You didn't actually send anything, though—the <control>-d is interpreted by bash and it responds by closing its pipe connected to "[[w:Standard streams|standard input]]" of the child process.  This is similar to how <control>-c is not sending a character but is interpreted by the terminal, trapped by the shell and forwarded as an interrupt signal to the child process, completely independently of the data pipe.  My entry point to learning more is this stty webzine<ref>https://wizardzines.com/comics/stty/</ref> by Julia Evans.  Go ahead and try this command, what could go wrong: <code>stty -a</code>


Any special behavior at the other end of a pipe is the result of intentional programming decisions and "end of file" (EOF) is more a convention than a hard reality.  You could even reopen stdin from the application, to the great surprise of your friends and neighbors.  For example, try opening "watch ls" or "sleep 60" and try <control>-d all you want—no effect.  You did close its stdin but nobody cared, it wasn't listening to you anyway.
Any special behavior at the other end of a pipe is the result of intentional programming decisions and "end of file" (EOF) is more a convention than a hard reality.  You could even reopen stdin from the application, to the great surprise of your friends and neighbors.  For example, try opening "watch ls" or "sleep 60" and try <control>-d all you want—no effect.  You did close its stdin but nobody cared, it wasn't listening to you anyway.