Created
August 23, 2012 17:47
-
-
Save tekwiz/3439373 to your computer and use it in GitHub Desktop.
Ruby Semaphore - in response to http://stackoverflow.com/questions/5478789/ruby-semaphores
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Semaphore | |
def initialize(limit) | |
@main, @internal = Mutex.new, Mutex.new | |
@mutexes = limit.times.collect { Mutex.new } | |
end | |
def synchronize | |
# create the mutex variable for scope | |
# grab the current thread for sleeping if necessary later | |
mutex, thread = nil, Thread.current | |
begin | |
# @main synchronizes the requests to the semaphore | |
@main.synchronize { | |
# try until we have a mutex | |
until mutex | |
# attempt to get a mutex and lock it so we can use it outside of the | |
# @main mutex | |
sync { | |
if mutex = get | |
# if we can't lock the mutex, something went very wrong | |
mutex.try_lock || raise("Can't obtain lock for %p"%[mutex]) | |
end | |
} | |
# try again in ~100ms (sleep isn't a guaranteed interval) | |
thread.sleep(0.1) unless mutex | |
end | |
# remove the mutex from the array so we can unlock it without something | |
# else getting it | |
sync { @mutexes.delete mutex } | |
} | |
# unlock the mutex so we can use it | |
mutex.unlock | |
mutex.synchronize { | |
# now that the mutex is locked again, we can put it back into the array | |
sync { @mutexes << mutex } | |
# now we can finally do what was originally requested | |
yield | |
} | |
ensure | |
# ensure that the mutex is back in the array | |
sync { | |
if mutex and [email protected]?(mutex) | |
# something went wrong and the mutex didn't get added back | |
@main.synchronize { | |
if mutex.locked? | |
# not sure how this could happen, but be prepared | |
warn(("%p lost %p, and it is still locked!"%[self, mutex]) + | |
" Dumping the Mutex and creating a new one.") | |
@mutexes << Mutex.new | |
else | |
@mutexes << mutex | |
end | |
} | |
end | |
} | |
end | |
end | |
# true if all Mutexes are locked | |
def all_locked? | |
!get | |
end | |
# true if any Mutexes are locked | |
def any_locked? | |
!!sync { @mutexes.detect(&:locked?) } | |
end | |
protected | |
# returns an available Mutex or nil if there aren't any available. | |
def get | |
sync { @mutexes.detect { |m| !m.locked? } } | |
end | |
# synchronize internal actions (mostly regarding the @mutexes array) | |
def sync | |
@internal.synchronize { yield } | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is in response to a stack overflow question: http://stackoverflow.com/questions/5478789/ruby-semaphores