Test concurrent Ruby code

Updated . Posted . Visible to the public.

To test concurrent code, you will need to run multiple threads. Unfortunately, when you use blocking system calls (e.g. locks on the database), Ruby 1.8 threads won't work because system calls will block the whole interpreter.

Luckily you can use processes instead. fork spins off a new process, IO.pipe sends messages between processes, Process.exit! kills the current process. You will need to take care of ActiveRecord database connections.

Here is a full-fledged example:

describe Lock, '.acquire' do

  before :each do
    @reader, @writer = IO.pipe
  end

  def fork_with_new_connection
    config = ActiveRecord::Base.remove_connection
    fork do
      begin
        ActiveRecord::Base.establish_connection(config)
        yield
      ensure
        ActiveRecord::Base.remove_connection
        Process.exit!
      end
    end
    ActiveRecord::Base.establish_connection(config)
  end

  it 'should synchronize processes on the same lock' do
    (1..20).each do |i|
      fork_with_new_connection do
        @reader.close
        ActiveRecord::Base.connection.reconnect!
        Lock.acquire('lock') do
          @writer.puts "Started: #{i}"
          sleep 0.01
          @writer.puts "Finished: #{i}"
        end
        @writer.close
      end
    end
    @writer.close

    # test whether we always get alternating "Started" / "Finished" lines
    lines = @reader.lines.to_a
    lines.should be_present # it is empty if the processes all crashed due to a typo or similar
    lines.each_slice(2) do |start, finish|
      start.should =~ /Started: (.*)/
      start_thread = $1
      finish.should =~ /Finished: (.*)/
      finish_thread = $1
      finish_thread.should == start_thread
    end

    @reader.close
  end
end

Note how Process.waitall waits for all child processes to finish.

IO.pipe caveat: Closing the pipe is important. In particular the reading process has to close the output pipe before it can begin to read.

Tobias Kraze
Last edit
Keywords
concurreny, threads, multithreaded, multi-threaded
License
Source code in this card is licensed under the MIT License.
Posted by Tobias Kraze to makandra dev (2010-08-25 13:39)