linuxsystem-calls

Non-blocking pipes in UNIX


Idea

This script doesn't work:

mkfifo pipe
echo Hello! > pipe
cat pipe # unreachable

...Because echo waits for someone to read from pipe before doing anything.

If there were space "inside" the pipe to store Hello!, then echo could exit and cat could read from the pipe. We could accomplish this if pipe were a normal file, but then there wouldn't be a way to limit its size.

Analogy

In Go, channels act like pipes. We can add a buffer which will store some number of values at a time:

...

func main() {
  // channel has a "buffer", which stores up to 1 message
  channel := make(chan *byte, 1)
  
  write("Hello!", channel)
  read(channel, fmt.Println)
}

Solution

  • I'm not entirely sure if this satisfies your criteria, but it seems to be an effective work-around:

    $ ( : < fifo & ); ( echo 'Hello!' > fifo & )
    $ cat fifo
    Hello! 
    

    The basic idea is to open a reader (which does not consume any data) so the the echo can write data into the pipe. (The parentheses are just to suppress the job control output and can be omitted.)

    It may be clearer to use:

    $ sleep 1 < fifo &  echo 'Hello!' > fifo 
    [1] 70954
    $ sed 1q fifo
    Hello!
    [1]+  Done                    sleep 1 < fifo
    $
    

    But there's a bit of a race condition. If the sleep terminates before echo runs (unlikely), then the echo will hang. It's safer to run echo in the background.