redis-semaphore
Implements a mutex and semaphore using Redis and the neat BLPOP command.
The mutex and semaphore is blocking, not polling, and has a fair queue serving processes on a first-come, first-serve basis.
For more info see [Wikipedia](http://en.wikipedia.org/wiki/Semaphore_(programming\)).
Usage
First let's see how to create a mutex:
s = Redis::Semaphore.new(:semaphore_name, :connection => "localhost")
s.lock do
# We're now in a mutex protected area
# No matter how many processes are running this program,
# there will be only one running this code block at a time.
do_something_speshiul()
end
While our application is inside the code block given to s.lock
, other calls to use the mutex with the same name will block until our code block is finished. Once our mutex unlocks, the next process will unblock and be able to execute the code block. The blocking processes get unblocked in order of arrival, creating a fair queue.
You can also allow a set number of processes inside the semaphore-protected block:
s = Redis::Semaphore.new(:semaphore_name, 5, :connection => "localhost")
s.lock do
# Up to five processes at a time will be able to get inside this code
# block simultaneously.
do_something_less_speshiul()
end
You don't need to use code blocks, you can also use linear code:
s = Redis::Semaphore.new(:semaphore_name, :connection => "localhost")
s.lock
do_something_speshiul()
s.unlock # Don't forget this, or the mutex will be locked forever!
If you don't want to wait forever until the mutex or semaphore release, you can use a timeout, in seconds:
if s.lock(5) # This will only block for at most 5 seconds if the mutex stays locked.
do_something_speshiul()
s.unlock
else
puts "Aborted."
end
You can check if the mutex or semaphore already exists, or how many resources are left in the semaphore:
puts "Someone already initialized this mutex or semaphore!" if s.exists?
puts "There are #{s.available} resources available right now."
In the constructor you can pass in any arguments that you would pass to a regular Redis constructor. You can even pass in your custom Redis client:
r = Redis.new(:connection => "localhost", :db => 222)
s = Redis::Semaphore.new(:another_name, r)
#...
If an exception happens during a lock, the lock will automatically be released:
begin
s.lock do
raise Exception
end
rescue
s.locked? # false
end
Installation
$ gem install redis-semaphore
Testing
$ bundle install
$ rake