Non-blocking communication between Ruby processes

El Jueves, 7 de Enero de 2010, Iñaki Baz C.
escribió:> >

These queues are completely atomic at the message level and descriptors
can be safely shared between processes/threads. SysV message queues
weren’t thread-safe, but POSIX ones are.

Great!

I’ve already tested it :slight_smile:

I’ve also realized that in case two processes perform “receive” for the
same
mq then the messages received are distributed at 50% (one message for
each
receiver). :slight_smile:

El Jueves, 7 de Enero de 2010, Eric W.
escribió:> > pipe but a FIFO file). The only it occurs when it’s full (because no

reader is getting the data) is that the writer #flush operation gets
blocked. I’ve found no way to determine how “full” is a FIFO file.

FIFO are pipes, they just have a name on the filesystem.

In any case, use IO#write_nonblock. Any writes you do will raise
Errno::EAGAIN if your FIFO/pipe is full.

See the pipe(7) manpage on a Linux machine, it provides a great overview
of pipe semantics for blocking/non-blocking operations.

Thanks, I’ll do.

El Jueves, 7 de Enero de 2010, Robert K.
escribió:

I’d personally prefer to use the DRb approach because then you can
actually send typed messages, i.e. whatever information you need. Also,
it was fun to play around with those small test programs. :wink: And you
can have the reader run on any machine in the network.

Hi Robert, I’d like to thank you the help you gave me in this and other
threads. Finally I’ve decided to use posix message queue [*] under Ruby.

The reason is that it allows safely multiple processes or threads using
the
same mqueue to write message (atomic strings) and also having multiple
processes reading from the same mqueue which means load-balancing out of
the
box :slight_smile:

The queue size is configurable and the writer/reader can write/read in
the
mqueue in a blocking or non blocking way.

Also, mqueues allow setting a priority to the messages so those messages
with
higher priority are fetched first when reading the mqueue.

Posix message queues are just 20-40% slower than pipes in my benchmarks
(but
pipes are no multiprocess/thread safe).

I would like to share a working example:

---- posix_mq_reader.rb ------------------------------
require “posix_mq”

Parameters:

- queue name (must start by “/”)

- flags:

- IO::RDONLY => Just to read from the queue

- IO::CREAT => Create if it doesn’t exist

MQ = POSIX_MQ.new “/my_mq”, IO::RDONLY | IO::CREAT

loop do

Blocking waiting:

msg = MQ.receive.first # It returns an array [message, priority]
puts “messsage received: #{msg}”
end

---- posix_mq_writer.rb ------------------------------
require “posix_mq”

Open with these options:

- IO::WRONLY => Just to write into the queue.

- IO::CREAT => Create if it doesn’t exist.

- IO::NONBLOCK => Don’t block when writting (instead raise

Errno::EAGAIN)
MQ = POSIX_MQ.new(“/my_mq”, IO::WRONLY | IO::CREAT | IO::NONBLOCK)

def send(msg)
begin
MQ << msg
rescue Errno::EAGAIN
puts “Errno::EAGAIN received, the queue is full!”
end
end

Now the reader and writer can be open multiple times sharing the same
mqueue
:slight_smile:

I also tested your suggested solution with DRb with is really nice, but
I
don’t need all the features DRb provides (I just need to pass a simple
string
to other process(es) from multiple workers).

Again thanks a lot to all the people who contributed in this thread,
I’ve
learnt a lot.

Best regards.

[*] posix_mq - POSIX message queues for Ruby

On Jan 9, 2010, at 12:54 PM, Iñaki Baz C. wrote:

Posix message queues are just 20-40% slower than pipes in my benchmarks (but
pipes are no multiprocess/thread safe).

I believe pipes can be used concurrently if reads and writes are less
than
or equal to PIPE_BUF bytes. Is the size limitation the problem you were
hinting at or something else?

Gary W.

2010/1/9 Iñaki Baz C. [email protected]:

El Jueves, 7 de Enero de 2010, Robert K. escribió:

I’d personally prefer to use the DRb approach because then you can
actually send typed messages, i.e. whatever information you need. Also,
it was fun to play around with those small test programs. :wink: And you
can have the reader run on any machine in the network.

Hi Robert, I’d like to thank you the help you gave me in this and other
threads. Finally I’ve decided to use posix message queue [*] under Ruby.

You’re welcome!

Posix message queues are just 20-40% slower than pipes in my benchmarks (but
pipes are no multiprocess/thread safe).

That sounds good! I have never worked with POSIX MQ so I definitively
learned something new as well.

I would like to share a working example:

Thank you for the summary and the code! That way other readers will
benefit as well.

I also tested your suggested solution with DRb with is really nice, but I
don’t need all the features DRb provides (I just need to pass a simple string
to other process(es) from multiple workers).

Well, you don’t have to use them. :slight_smile: But POSIX MQ looks equally simple
to use.

Kind regards

robert

El Domingo, 10 de Enero de 2010, Gary W.
escribió:> On Jan 9, 2010, at 12:54 PM, Iñaki Baz C. wrote:

Posix message queues are just 20-40% slower than pipes in my benchmarks
(but pipes are no multiprocess/thread safe).

I believe pipes can be used concurrently if reads and writes are less than
or equal to PIPE_BUF bytes. Is the size limitation the problem you were
hinting at or something else?

Yes, pipes allow atomic operation but just in case the message is less
than 4
or 8 bytes, no more. In C it can be useful if you pass a pointer via
pipe.

I tested it (in ruby) by sending strings of 10 bytes size from various
processes to a shared pipe. The reader gets the strings mixed :slight_smile:
However this doesn’t occur with posix message queues.

Regards.

On Jan 10, 2010, at 11:17 AM, Iñaki Baz C. wrote:

I tested it (in ruby) by sending strings of 10 bytes size from various
processes to a shared pipe. The reader gets the strings mixed :slight_smile:
However this doesn’t occur with posix message queues.

I just tried this on Mac OS X using threads and using forks to write to
a shared pipe. Each write was 256 bytes done using syswrite. All the
reads on the other end were: sysread(256). I tried using forked writers
and simple Ruby threads. Each writer wrote 10000 messages and I didn’t
get any mixed up data in any of my tests. I also tried using plain
old write (vs. syswrite) and still didn’t get any mixed up data.

The behavior you described just doesn’t match my experience or
understanding of pipes.

Gary W.

On Jan 10, 2010, at 8:00 PM, Iñaki Baz C. wrote:

I got the mixed strings using a name pipe (created with “mkfifo” command) and
shared by two different Ruby programs (a writer and a reader).

A named pipe and a pipe should behave the same, the only difference
being
that one has a name in the filesystem.

El Lunes, 11 de Enero de 2010, Gary W.
escribió:> old write (vs. syswrite) and still didn’t get any mixed up data.

The behavior you described just doesn’t match my experience or
understanding of pipes.

I got the mixed strings using a name pipe (created with “mkfifo”
command) and
shared by two different Ruby programs (a writer and a reader).

However I don’t know if I’ve modified something since that test but the
fact
is that I don’t get the mixed strings…

El Lunes, 11 de Enero de 2010, Gary W.
escribió:> On Jan 10, 2010, at 8:00 PM, Iñaki Baz C. wrote:

I got the mixed strings using a name pipe (created with “mkfifo” command)
and shared by two different Ruby programs (a writer and a reader).

A named pipe and a pipe should behave the same, the only difference being
that one has a name in the filesystem.

Yes, I know, I just told it because I tested with named piped.
However it’s true that I don’t get the issue occuring when writting in a
named
pipe from different processes…