philiprehberger-semaphore

Tests Gem Version Last updated

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:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT