Mittwoch, 26. August 2009

Nested Synchronize Blocks

In my new simulation project I have different threads accessing objects. The simulation server calculates periodically their states and the tournament server, handling all agents, is accessing them over distributed Ruby. Therefore I need to synchronize the sim objects. My first attempt looked something like this:
class SyncObject

  def initialize
    @semaphore = Mutex.new
  end

  def synchronize &block
    @semaphore.synchronize &block
  end

end
Before changing something in the sim objects, I synchronize them :
  object.synchronize do
    # change the state of the object
  end
But as things were growing, I suddently got some deadlocks. After some research I found out, that a thread was invoking synchronize on the same object multiple times. The semaphore object doesn't care which thread is locking it. As long it's locked it will block all threads until the block is finished. Even if it's the same thread, that locked the semaphore just before.
But I needed a different behaviour, I want to synchronize the objects from different threads. As far as I remember Java's synchronize behaves the same way.
Using rspec we could specify this behaviour with 2 tests:
it "should not block the same thread when synchronizing" do
    object = SyncObject.new
    object.synchronize do
      object.synchronize do
        object.synchronize do
          true.should be_true
        end
      end
    end
  end
it "should block two different threads when synchronizing" do
    object = SyncObject.new
    wait_for_me = true
    object.synchronize do
      Thread.fork do
        object.synchronize do
          wait_for_me = false
        end
      end
      sleep 1
      wait_for_me.should be_true
    end
    wait_for_me.should be_false
  end
The first test's pretty straight forward, we want to allow nested synchronize blocks for the same thread.
For the second test we need two different threads. The first thread locks the object and lunches a second thread. The first one returns immediately after fork, while the second one is launched in the meantime. So while the first is waiting for a second, the second thread is trying to achieve the lock and is blocked until the first one releases it on line 12. Only now the second thread gets access to the object and can set wait_for_me to false.
Of course both tests fail (or get some deadlocks) with the implemention above. The second version of synchronize looks now like this:
class SyncObject

  def initialize
    @semaphore = Mutex.new
  end

  def synchronize
    unless @current_thread == Thread.current
      @semaphore.synchronize do
        @current_thread = Thread.current
        yield
        @current_thread = nil
      end
    else
      yield
    end
  end

end
The first time synchronize is accessed, @current_thread is set to the current running thread. The semaphore is locked and the blocked yield. If in the meantime a different thread invokes synchronize, it will be blocked until the first one finishes the block. Else if the same thread access the method in the meantime, it will not try to lock the semaphore again, but can yield its new block immediately.

Donnerstag, 13. August 2009

Migrating to Cucumber

Today I migrated my simulation project to Cucumber. Until now I used rspec's plain text user stories.
The first part, migrating the rails application, was easy thanks to the good documentation in the wiki. The second part, the simulation server, was surprisingly easy as well. All I had to do was creating the same directory structure and following the same steps to move the files from stories to features as for the rails app. Then I removed all rails related lines from env.rb and added my init file, that loads all the classes for the simulation server.
require File.expand_path(File.dirname(__FILE__)+'/../../init')

# Comment out the next line if you don't want Cucumber Unicode support
require 'cucumber/formatter/unicode'
To run the stories I need to execute cucumber features/ in the corresponding direcotries. Running the rake features doesn't work for me as env.rb is not loaded.

Mittwoch, 5. August 2009

Closures in Distributed Ruby

In this article I want to show how closures work with distributed ruby and what you have to do to make them work.
Here's a very simple server to start:
require 'drb'
 
class SimpleServer

 def initialize
   DRb.start_service('druby://localhost:9000', self)
 end
 
 def server_time
   Time.now
 end

end

SimpleServer.new
DRb.thread.join
In the constructor we start the drb service and register the server to localhost on port 9000. Then we have a very simple method, which just returns the time on the server, that we now want to call from our client. The last line prevents the programm to exit and keeps the server thread waiting for incoming requests.
require 'drb'

class SimpleClient 

 def initialize
   @remote = DRbObject.new(nil, 'druby://localhost:9000')
 end

 def time_on_server
   puts "Time on server #{@remote.server_time}"
 end

end

client = SimpleClient.new
client.time_on_server
In the client's constructor we get the remote object of the previous registered server. After that we can call the "time_on_server" method to print it on the client side.
Now let's add a method that uses a block parameter.
in the server:
  def time_on_client
    puts "Time on client #{yield}"
  end
and in the client:
  def client_time
    @remote.time_on_client { Time.now }
  end
Calling now "client_time" from the client, will raise a DRb::DRbServerNotFound error. The client fails to provide a drb proxy object for the server, which would need it to call the block on the client again. Blocks are bind to the environment, where they have been defined, in our case on the client.
All we have to do to make it work is starting also a drb service on the client side.
  def initialize
    DRb.start_service
    @remote = DRbObject.new(nil, 'druby://localhost:9000')
  end
So in Ruby we often have symetric roles, both sides need to act as client and server.