philiprehberger-semaphore
Counting semaphore for concurrent access control with timeouts
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem "philiprehberger-semaphore"
Or install directly:
gem install philiprehberger-semaphore
Usage
require "philiprehberger/semaphore"
sem = Philiprehberger::Semaphore::Counter.new(permits: 3)
sem.acquire { do_work }
Timeout-Based Acquisition
require "philiprehberger/semaphore"
result = sem.try_acquire(timeout: 5) do
perform_database_query
end
# result is false if timeout expired
Weighted Permits
require "philiprehberger/semaphore"
sem = Philiprehberger::Semaphore::Counter.new(permits: 10)
sem.acquire(weight: 3) { heavy_operation }
FIFO Fairness
require "philiprehberger/semaphore"
sem = Philiprehberger::Semaphore::Counter.new(permits: 5, fair: true)
sem.acquire { do_work }
Dynamic Permit Adjustment
require "philiprehberger/semaphore"
sem = Philiprehberger::Semaphore::Counter.new(permits: 3)
sem.resize(5)
sem.permits # => 5
sem.available # => 5
Graceful Drain
require "philiprehberger/semaphore"
sem = Philiprehberger::Semaphore::Counter.new(permits: 3)
# Workers acquire permits in other threads...
# When shutting down, drain blocks until all permits are returned:
sem.drain
# After drain, new acquisitions are rejected:
sem.acquire # => raises Philiprehberger::Semaphore::Error
sem.try_acquire(timeout: 1) # => false
Observability
require "philiprehberger/semaphore"
sem = Philiprehberger::Semaphore::Counter.new(permits: 5)
sem.acquire
sem.acquire(weight: 2)
sem.acquired_count # => 3
sem.available # => 2
API
| Method | Description |
|---|---|
.new(permits:, fair: false) |
Create a semaphore with the given number of permits and optional FIFO fairness |
#acquire(weight: 1) { block } |
Acquire one or more permits, blocking until available |
#try_acquire(timeout:, weight: 1) { block } |
Try to acquire within timeout, returns false on expiry |
#release(weight: 1) |
Release one or more permits back to the semaphore |
#resize(new_permits) |
Change total permit count at runtime |
#drain |
Block until all permits are returned; reject new acquisitions |
#available |
Return the number of currently available permits |
#acquired_count |
Return the number of permits currently held (permits - available) |
#permits |
Return the total number of permits |
#fair? |
Return whether the semaphore uses FIFO fairness |
#draining? |
Return whether the semaphore is currently draining |
Development
bundle install
bundle exec rspec
bundle exec rubocop
Support
If you find this project useful: