summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Vilain <sam@vilain.net>2009-07-31 15:32:14 +1200
committerDavid Mitchell <davem@iabyn.com>2009-08-10 09:17:20 +0100
commit87650ddec1f387a9085353bfab3c564f817e3320 (patch)
treebe2abb4d93e1b354131b40826dfa13feda382cc4
parent8813a02abcb79b7a2b69c4397f55d9783fb6ed55 (diff)
downloadperl-87650ddec1f387a9085353bfab3c564f817e3320.tar.gz
pod/perlipc.pod: add some hints on avoiding pipe deadlocks
Tracking down deadlocks when using pipes for IPC can be hard, so put even more notes about gotchas in this section of perlipc. (cherry picked from commit c40e8e9bf43b15cbc5725b65e3085fba60a67489)
-rw-r--r--pod/perlipc.pod86
1 files changed, 86 insertions, 0 deletions
diff --git a/pod/perlipc.pod b/pod/perlipc.pod
index 416ded5605..77f0b6e3a2 100644
--- a/pod/perlipc.pod
+++ b/pod/perlipc.pod
@@ -630,6 +630,68 @@ And here's a safe pipe open for writing:
# NOTREACHED
}
+It is very easy to dead-lock a process using this form of open(), or
+indeed any use of pipe() and multiple sub-processes. The above
+example is 'safe' because it is simple and calls exec(). See
+L</"Avoiding Pipe Deadlocks"> for general safety principles, but there
+are extra gotchas with Safe Pipe Opens.
+
+In particular, if you opened the pipe using C<open FH, "|-">, then you
+cannot simply use close() in the parent process to close an unwanted
+writer. Consider this code:
+
+ $pid = open WRITER, "|-";
+ defined $pid or die "fork failed; $!";
+ if ($pid) {
+ if (my $sub_pid = fork()) {
+ close WRITER;
+ # do something else...
+ }
+ else {
+ # write to WRITER...
+ exit;
+ }
+ }
+ else {
+ # do something with STDIN...
+ exit;
+ }
+
+In the above, the true parent does not want to write to the WRITER
+filehandle, so it closes it. However, because WRITER was opened using
+C<open FH, "|-">, it has a special behaviour: closing it will call
+waitpid() (see L<perlfunc/waitpid>), which waits for the sub-process
+to exit. If the child process ends up waiting for something happening
+in the section marked "do something else", then you have a deadlock.
+
+This can also be a problem with intermediate sub-processes in more
+complicated code, which will call waitpid() on all open filehandles
+during global destruction; in no predictable order.
+
+To solve this, you must manually use pipe(), fork(), and the form of
+open() which sets one file descriptor to another, as below:
+
+ pipe(READER, WRITER);
+ $pid = fork();
+ defined $pid or die "fork failed; $!";
+ if ($pid) {
+ close READER;
+ if (my $sub_pid = fork()) {
+ close WRITER;
+ }
+ else {
+ # write to WRITER...
+ exit;
+ }
+ # write to WRITER...
+ }
+ else {
+ open STDIN, "<&READER";
+ close WRITER;
+ # do something...
+ exit;
+ }
+
Since Perl 5.8.0, you can also use the list form of C<open> for pipes :
the syntax
@@ -645,6 +707,30 @@ correctly implemented on alien systems. Additionally, these are not true
multithreading. If you'd like to learn more about threading, see the
F<modules> file mentioned below in the SEE ALSO section.
+=head2 Avoiding Pipe Deadlocks
+
+In general, if you have more than one sub-process, you need to be very
+careful that any process which does not need the writer half of any
+pipe you create for inter-process communication does not have it open.
+
+The reason for this is that any child process which is reading from
+the pipe and expecting an EOF will never receive it, and therefore
+never exit. A single process closing a pipe is not enough to close it;
+the last process with the pipe open must close it for it to read EOF.
+
+There are some features built-in to unix to help prevent this most of
+the time. For instance, filehandles have a 'close on exec' flag (set
+I<en masse> with Perl using the C<$^F> L<perlvar>), so that any
+filehandles which you didn't explicitly route to the STDIN, STDOUT or
+STDERR of a child I<program> will automatically be closed for you.
+
+So, always explicitly and immediately call close() on the writable end
+of any pipe, unless that process is actually writing to it. If you
+don't explicitly call close() then be warned Perl will still close()
+all the filehandles during global destruction. As warned above, if
+those filehandles were opened with Safe Pipe Open, they will also call
+waitpid() and you might again deadlock.
+
=head2 Bidirectional Communication with Another Process
While this works reasonably well for unidirectional communication, what