Version Française

Sp4ce.net rss

Global mutex in Ruby

When different processes require to run a critical section, some high level language provides global mutexes, but they don't exist in Ruby.

Global Mutex

In a multi thread environment, if you want to protect a critical section, you can use a semaphore. However in Ruby, this semaphore is only intantiated in your current process and cannot be shared among other ruby processes.

Imagine that you have a ruby code that is run by an external application. This code contains a critical section and you want to protect it.

chech.rb

puts "take lock"
# to be defined
puts "begin critical section"
sleep 2
puts "end critical section"
puts "release lock"

Then to simulate the external application calls, we have a small batch script loop.bat that calls the Ruby code four times.

loop.bat

for /L %%i in (0, 1, 3) do start ruby check.rb %%i

File.flock method

To solve this, the method File.flock allows you take an exclusive ressource on a file. So check.rb looks like:

chech.rb

require 'fileutils.rb'
require 'tmpdir'

File.open("#{Dir.tmpdir}/test.synchro", 'w') { |f|
    puts "take lock #{ARGV[0]}"
    f.flock(File::LOCK_EX)
    puts "sleep #{ARGV[0]}"
    sleep 2
    puts "end #{ARGV[0]}"
}

sleep 60

Then when you run loop.bat you have a global mutex, meaning that each process wait that every other process release the lock on the file test.synchro. The file is in the TEMP directory to be sure that you can have write access on this folder. It will be /temp on linux and c:\users\CURRENT_USER\app_data\temp on MS Windows. I didn’t try on my Mac, but I think that each call to Dir.tmpdir return the same folder.

Demo

Here a screenshot of when this short code runs (click to enlarge). You have to look closely to the timestamp of each action (specially the seconds) to understand what is happenning.

ruby_global_mutex ruby_global_mutex