The dRuby Book

8.1 Computing in Parallel with rinda_eval

In the previous chapter, I mentioned that I didn’t implement eval because I couldn’t come up with a good way to implement it. Actually, it’s possible to replicate similar functionality to eval for Portable Operating System Interface for Unix (POSIX)--compliant operating systems.[12] I didn’t include this as part of the standard library, because this doesn’t run on Windows machines and is also very difficult to unit test. Having said that, you should be able to use rinda_eval in environments such as Linux or OS X. We’ll first review what eval in Linda is like and then move on to the usage of rinda_eval and its internals.

By the way, don’t be fooled by the word eval. It doesn’t use Ruby’s eval.

Using eval with Linda

In Linda, there are two operations to generate a tuple: “out” and “eval.” The “out” operation is equivalent to Rinda’s Rinda::TupleSpace#write.

Here’s an example of generating a tuple that contains the result of a square root from 0 to 9 using the “out” operation:

  for (i = 0; i < 10; i++)​
  ​ out("sqrt", i, sqrt(i));​

The “eval” operation looks exactly the same as the “out” operation, but there is a big difference. The “eval” operation first generates processes while evaluating arguments and then puts the result into tuples. Here’s an example of generating ten processes and returning the result in each tuple. It’s important to understand that these newly generated processes evaluate the sqrt function, not the calling process.

  for (i = 0; i < 10; i++)​
  ​ eval("sqrt", i, sqrt(i));​

If you think in a normal way, this looks very strange—it should generate processes after each argument is evaluated. Apparently you can do this with C-Linda because it is some sort of preprocessor or language extension, rather than a library.

Rinda::rinda_eval

If you can use a fork system call in your OS, then you can generate a child process that inherits the entire environment of the Ruby runtime and then continue the process. Rinda::rinda_eval is implemented using this fork system call, so it won’t work if your OS doesn’t have fork. That’s why Rinda::rinda_eval is targeted for POSIX-compliant systems. This module method creates a new process and then executes the block. You can also pass a reference to TupleSpace to the arguments so that the returning Array is added into the tuplespace.

The API isn’t exactly the same as that of C-Linda, but nonetheless this is a practical API. As an example of a practical application, the next example generates worker processes and then stores the results of the calculation into tuples. It consists of two loops. The first loop creates ten worker processes that calculate Math.sqrt inside a rinda_eval block, and then the second loop receives the result sets.

  ​10.times do |n|​
  ​ Rinda::rinda_eval($ts) do |ts|​
  ​ [:sqrt, n, Math.sqrt(n)]​
  end
  end
  ​10.times do |n|​
  ​ p $ts.read([:sqrt, n, nil])​
  end

The block passed into rinda_eval gets executed inside the child processes generated by fork. The return value of the block will be written to TupleSpace.