# Trouble with mkfifo and forks

**URL:** <https://www.ruby-forum.com/t/trouble-with-mkfifo-and-forks/194542>\
**Category:** Ruby\
**Created:** [September 6, 2010, 10:10pm UTC](https://www.ruby-forum.com/t/trouble-with-mkfifo-and-forks/194542 "2010-09-06T22:10:55Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![joshs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e5b9ba/32.png) [@joshs](https://www.ruby-forum.com/u/joshs)\
**Post date:** [September 6, 2010, 10:10pm UTC](https://www.ruby-forum.com/t/trouble-with-mkfifo-and-forks/194542/1 "2010-09-06T22:10:55Z")

</div>

# In a nutshell:

1. Create a pipe
2. Fork twice
3. Each for sends 10 strings over the pipe
4. See what comes out the other side

So, why isn’t what I send coming out?

class WaitListTest \< Test::Unit::TestCase  
NUMBER\_OF\_ORDERS = 2  
PIPE = “test/wl\_pipe”

def setup  
`rm #{PIPE}`  
`mkfifo #{PIPE}`  
end

def test\_registering\_last\_race  
term\_count = 0  
responses = []  
pids = []

```
NUMBER_OF_ORDERS.times do |i|
  pids << Process.fork do
    sleep 1 + (0.1 * i)
    10.times do |a|
      File.open("#{RAILS_ROOT}/#{PIPE}", "w+") do |pipe|
        sleep 0.1
        pipe.puts "Fork #{i} - #{a}"
      end
    end
    File.open("#{RAILS_ROOT}/#{PIPE}", "a") do |pipe|
      sleep 0.1
      pipe.puts "DONE"
    end
  end
end

while term_count < NUMBER_OF_ORDERS
  File.open("#{RAILS_ROOT}/#{PIPE}", "r+") do |pipe|
    val = pipe.gets
    if val.match(/DONE/)
      term_count += 1
    else
      responses << val
    end
  end
end
puts responses.sort
puts responses.count

```

end  
end

# output

Started  
Fork 0 - 0  
Fork 0 - 1  
Fork 0 - 2  
Fork 0 - 3  
Fork 0 - 4  
Fork 0 - 5  
Fork 0 - 7  
Fork 0 - 9  
Fork 1 - 0  
Fork 1 - 1  
Fork 1 - 2  
Fork 1 - 3  
Fork 1 - 4  
Fork 1 - 5  
Fork 1 - 7  
Fork 1 - 8  
Fork 1 - 9  
17

Why isn’t this 20?

---

<div class="post-metadata">

**Author:** ![joshs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e5b9ba/32.png) [@joshs](https://www.ruby-forum.com/u/joshs)\
**Post date:** [September 7, 2010, 12:01am UTC](https://www.ruby-forum.com/t/trouble-with-mkfifo-and-forks/194542/2 "2010-09-07T00:01:34Z")

</div>

On 09/06/2010 01:10 PM, Josh wrote:

> # In a nutshell:
> 
> 1. Create a pipe
> 2. Fork twice
> 3. Each for sends 10 strings over the pipe
> 4. See what comes out the other side

I’m probably naive about something, but fifos almost never work right  
for what I want to do, and I usually end up using (unix domain) sockets  
(which also makes it easier to move to tcp sockets if you need to  
distribute across hosts).

Anyway, in your code, it seems that the repeated closing of the _read_  
end of the pipe causes some data to be lost. If you can modify your read  
loop to keep using the same IO object, it seems to work (see below).  
Also, I found I needed to watch for #gets returning nil (though that  
doesn’t happen reliably).

Perhaps someone who knows more about pipes will “pipe up” on this  
thread.

class WaitListTest  
NUMBER\_OF\_ORDERS = 2  
PIPE = File.expand\_path("~/tmp/wl\_pipe")

def setup  
`rm -f #{PIPE}`  
`mkfifo #{PIPE}`  
end

def test\_registering\_last\_race  
term\_count = 0  
responses = []  
pids = []

```
 NUMBER_OF_ORDERS.times do |i|
   pids << Process.fork do
     sleep 0.1 + (0.1 * i)
     10.times do |a|
       File.open(PIPE, "w") do |pipe|
         sleep 0.1
         pipe.puts "Fork #{i} - #{a}"
       end
     end
     File.open(PIPE, "w") do |pipe|
       sleep 0.1
       pipe.puts "DONE"
     end
   end
 end

 pipe = File.open(PIPE, "r")
 while term_count < NUMBER_OF_ORDERS

```

# File.open(PIPE, “r”) do |pipe|

```
     val = pipe.gets
     case val
     when /DONE/
       term_count += 1
     when nil
       puts "pipe closed" # need this too
     else
       responses << val
     end

```

# end

```
 end

 pids.each do |pid|
   Process.waitpid(pid)
 end

 puts responses.sort
 puts responses.count

```

end  
end

wlt = WaitListTest.new  
wlt.setup  
wlt.test\_registering\_last\_race

---

<div class="post-metadata">

**Author:** ![joshs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e5b9ba/32.png) [@joshs](https://www.ruby-forum.com/u/joshs)\
**Post date:** [September 7, 2010, 10:40am UTC](https://www.ruby-forum.com/t/trouble-with-mkfifo-and-forks/194542/3 "2010-09-07T10:40:19Z")

</div>

In the receiver, you are constantly opening the pipe for read and then  
closing it. I expect that if someone writes to the pipe while there’s no  
reader, the data is lost.

The program works for me if I swap the open and while around:

```
File.open("#{RAILS_ROOT}/#{PIPE}", "r") do |pipe|
  while term_count < NUMBER_OF_ORDERS
    val = pipe.gets
    if val.match(/DONE/)
      .. etc

```

But it is much better form to create an anonymous pipe in the parent  
(rd, wr = IO.pipe) then fork. Close the rd end in the child and the wr  
end in the parent.

Use Socket.pair if you want a bidirectional channel.
