while (1) {
unless (-p $FIFO) {
unlink $FIFO;
system('mknod', $FIFO, 'p')
&& die "can't mknod $FIFO: $!";
}
# next line blocks until there's a reader
open (FIFO, "> $FIFO") || die "can't write $FIFO: $!";
print FIFO "John Smith (smith\@host.org)\n", `fortune -s`;
close FIFO;
sleep 2; # to avoid dup signals
}
Eh??? What kind of a signal is a "dup signal", and why it may appear,
and why one would want to avoid it?
Thanks,
Ilya
P.S. I see that head1 "Named pipes" break inside the section on
signals...
P.P.S. Googling on
dup/signals fifo OR mkfifo -perlipc
finds only one obscure ref on sched.c, which I can't fully decipher...
> # next line blocks until there's a reader
> open (FIFO, "> $FIFO") || die "can't write $FIFO: $!";
> print FIFO "John Smith (smith\@host.org)\n", `fortune -s`;
> close FIFO;
> sleep 2; # to avoid dup signals
> }
> Eh??? What kind of a signal is a "dup signal", and why it may appear,
> and why one would want to avoid it?
I suppose the purpose of the sleep is simply to give the reading
process time to leave. Many programs have been known, e.g., to
lazily consume two end-of-file checks to end their input loop.
This would not hurt much on normal files but could trigger
another round of our writing loop here, if the reading process
is still busy on the other end when our process reopens the
named pipe. It is somewhat improbable since our stat system
call also needs to squeeze in between consecutive reads on the
other end, but not quite impossible.
I agree that the comment is not very intuitive. "To avoid
duplicate signatures" (not signals) would certainly be more
appropriate, since this example describes a signature generating
service.
-Martin
<mbe-usen...@cozap.com>], who wrote in article <481d...@news.uni-ulm.de>:
> Ilya Zakharevich <nospam...@ilyaz.org> wrote:
> > open (FIFO, "> $FIFO") || die "can't write $FIFO: $!";
> > print FIFO "John Smith (smith\@host.org)\n", `fortune -s`;
> > close FIFO;
> > sleep 2; # to avoid dup signals
> > Eh??? What kind of a signal is a "dup signal", and why it may appear,
> > and why one would want to avoid it?
> I suppose the purpose of the sleep is simply to give the reading
> process time to leave. Many programs have been known, e.g., to
> lazily consume two end-of-file checks to end their input loop.
> This would not hurt much on normal files but could trigger
> another round of our writing loop here, if the reading process
> is still busy on the other end when our process reopens the
> named pipe.
You mean that read() on a close()d file could re-open() it? Is the
semantic of *nix pipes really so broken? [So that one reader process
can read from many writers without reopen()?]
Moreover, (e.g., perl's) eof() just reads 1 char. Would not this just
block for these 2sec, thus beating the purpose of this sleep
> It is somewhat improbable since our stat system
> call also needs to squeeze in between consecutive reads on the
> other end, but not quite impossible.
There is (was ;-) no stat() call there; neither explicitly, nor in the
underlying implementation (one inside perl executable). Could you
please comment on this more?
> I agree that the comment is not very intuitive. "To avoid
> duplicate signatures" (not signals) would certainly be more
> appropriate, since this example describes a signature generating
> service.
I suspected that this is just an indication of yet another fishy bit
of *nix architecture of IPC...
(Since the system was not designed for multiprocessing, one is
forced to use some kind of "voodoo programming" [e.g., why 2sec,
and not 200sec?!] which increases the probability that the program
succeeds - without any guarantie of success.)
It is just not clear which particular piece of fishyness is fighted
with above, and I would like to know...
Thanks a lot,
Ilya
> <mbe-usen...@cozap.com>], who wrote in article
> <481d...@news.uni-ulm.de>:
> > Ilya Zakharevich <nospam...@ilyaz.org> wrote:
> > > open (FIFO, "> $FIFO") || die "can't write $FIFO: $!";
> > > print FIFO "John Smith (smith\@host.org)\n", `fortune -s`;
> > > close FIFO;
> > > sleep 2; # to avoid dup signals
> > > Eh??? What kind of a signal is a "dup signal", and why it may appear,
> > > and why one would want to avoid it?
> > I suppose the purpose of the sleep is simply to give the reading
> > process time to leave. Many programs have been known, e.g., to
> > lazily consume two end-of-file checks to end their input loop.
> > This would not hurt much on normal files but could trigger
> > another round of our writing loop here, if the reading process
> > is still busy on the other end when our process reopens the
> > named pipe.
> You mean that read() on a close()d file could re-open() it? Is the
> semantic of *nix pipes really so broken? [So that one reader process
> can read from many writers without reopen()?]
Certainly not. In our scenario the reader would not have
closed the file, so there is no re-opening magic involved.
I do have to correct my statement, however, in that sleeping
will not cater for additional reading attempts after eof,
but rather help that eof is noticed at all. I'll elaborate
presently.
> Moreover, (e.g., perl's) eof() just reads 1 char. Would not this just
> block for these 2sec, thus beating the purpose of this sleep
On system call level, end of file is notified through a read
operation returning a byte count of zero. Subsequent reads
may or may not get the same result, especially if the file is
actually a tty or a fifo with no physical end.
Closing the (last remaining) writing connection is a way of
telling "there is nothing more to read right now", which allows
an ongoing read operation to finish, even when no bytes have been
read so far, which is precisely what is needed to detect eof.
If, however, another writing connection is established and
firing away before the reading process gets a time slice from the
scheduler, the "nothing to read" condition will pass unnoticed.
The pipe will not be empty and disconnected anymore by the time
it is finally looked at.
A sleep, even for a short time, changes the process status to
"not ready" and thus greatly improves the chance of "ready"
processes, like the one with the pending read operation, to
be resumed first. Of course that is not guaranteed to work,
but seems good enough in practice.
> > It is somewhat improbable since our stat system
> > call also needs to squeeze in between consecutive reads on the
> > other end, but not quite impossible.
> There is (was ;-) no stat() call there; neither explicitly, nor in the
> underlying implementation (one inside perl executable). Could you
> please comment on this more?
Yes there was a stat call in the code part you quoted in
your original posting, in the guise of a "-p" file test:
Ilya> unless (-p $FIFO) {
Ilya> [...]
Ilya> }
> > I agree that the comment is not very intuitive. "To avoid
> > duplicate signatures" (not signals) would certainly be more
> > appropriate, since this example describes a signature generating
> > service.
> I suspected that this is just an indication of yet another fishy bit
> of *nix architecture of IPC...
Fishy indeed, as in "one man's shiny, slim and streamlined creature,
roaming the oceans with swift and elegant motions, is another man's
smelly thing the cat found in a dustbin".
> (Since the system was not designed for multiprocessing, one is
> forced to use some kind of "voodoo programming" [e.g., why 2sec,
> and not 200sec?!] which increases the probability that the program
> succeeds - without any guarantie of success.)
The amount of two seconds might have been chosen because with
an accuracy of one second the actual sleeping time for sleep(1)
could be next to nothing in the "worst" case.
> It is just not clear which particular piece of fishyness is fighted
> with above, and I would like to know...
To sum it up, our short delay is supposed to keep the fifo long
enough in a "nothing to read" state for the current reader to
notice.
Admittedly, this is not a particularly nice example. As one
of the communicating parties is not supposed to be aware of
an active communication going on at all, there are not much
options for the protocol to deal with all sorts of special cases.
For a moment, I toyed with the idea of unlinking the fifo and
creating a new one each time through the loop, to deal with the
possibility of one reader getting more than one signature.
However, that would put the reader in danger of getting
blocked indefinitely (reading from a fifo no longer accessible
to anyone else), which would be far worse than the occasional
matter-of-seconds delay.
-Martin
> On system call level, end of file is notified through a read
> operation returning a byte count of zero. Subsequent reads
> may or may not get the same result, especially if the file is
> actually a tty or a fifo with no physical end.
>
> Closing the (last remaining) writing connection is a way of
> telling "there is nothing more to read right now", which allows
> an ongoing read operation to finish, even when no bytes have been
> read so far, which is precisely what is needed to detect eof.
> If, however, another writing connection is established and
> firing away before the reading process gets a time slice from the
> scheduler, the "nothing to read" condition will pass unnoticed.
> The pipe will not be empty and disconnected anymore by the time
> it is finally looked at.
I see now what is EXACTLY the design problem with UNIX named pipes:
there is no way to create a "persistent EOF" condition, as it is
possible to do with TTYs and sockets.
To create EOF condition on a named pipe, the writer MUST close the
pipe, and wait until the reader reads from a closed pipe. However,
there no API to wait until this moment.
Contrast this with a TTY, where
perl -wle "sleep 10; 1 while <>"
then press
^D abc
^D creates a "persistent EOF" on the stream, and it does not matter
that "abc" is typed *before* "the other end of the TTY" makes a read.
(This works on DOSISH systems too [system is passing an actual ^Z
character, which is handled by the actual CRTL code].)
With sockets, AFAIK, it is slightly different: one creates a "session"
relationship between a writer and a reader (via bind()/accept()), then
one can create EOF by closing the last writer *in this session*; it is
- obviously - persistent, since there is no chance to write anything
after this ;-).
(BTW, this is how named pipes are implemented on DOSISH systems:
they also support a possibility of sessions.)
Thanks,
Ilya