Skip to content

Instantly share code, notes, and snippets.

@tekwiz
Created August 23, 2012 17:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tekwiz/3439373 to your computer and use it in GitHub Desktop.
Save tekwiz/3439373 to your computer and use it in GitHub Desktop.
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 !@mutexes.include?(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
@tekwiz
Copy link
Author

tekwiz commented Aug 24, 2012

This is in response to a stack overflow question: http://stackoverflow.com/questions/5478789/ruby-semaphores

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment