There are good reasons to use MonitorMixin instead of the plain old Mutex when writing multi-threaded code in Ruby. Namely to avoid deadlocks.

Consider this example.

# example.rb
require "thread"

class Example
  def initialize
    @mutex = Mutex.new
  end

  def work
    @mutex.synchronize do
      puts "sync in work"
      yield
    end
  end

  def start_work
    work do
      @mutex.synchronize do
        puts "sync in start_work block"
      end
    end
  end
end

Example.new.start_work

Af first glance you might not see it, but this will cause a deadlock because the block passed to work from start_work attempts to obtain a lock that it can never get. This is because work already has a lock in place.

Lets run it just to be sure.

$ ruby ./example.rb
sync in work
<internal:prelude>:8:in `lock': deadlock; recursive locking (ThreadError)
	from <internal:prelude>:8:in `synchronize'
	from ./example.rb:18:in `block in start_work'
	from ./example.rb:12:in `block in work'
	from <internal:prelude>:10:in `synchronize'
	from ./example.rb:10:in `work'
	from ./example.rb:17:in `start_work'
	from ./example.rb:25:in `<main>'

Sure enough, we have a deadlock on our hands. These types of bugs can be dificult to track down.

A solution for this problem is to use MonitorMixin because MonitorMixin is intelligent enough to know that a lock has already been obtained.

Lets refactor our example to use MonitorMixin.

# example.rb
require "monitor"

class Example
  include MonitorMixin

  def work
    synchronize do
      puts "sync in work"
      yield
    end
  end

  def start_work
    work do
      synchronize do
        puts "sync in start_work block"
      end
    end
  end
end

Example.new.start_work

Now lets run it.

$ ruby ./example.rb
sync in work
sync in start_work block

No deadlocks this time. The moral here is to use MonitorMixin for all but the simplest of use cases… and even then, you should consider using it.




comments powered by Disqus




comments powered by Disqus

Copyright © 2008-2013 Hopsoft