Class: Philiprehberger::LockKit::ReadWriteLock
- Inherits:
-
Object
- Object
- Philiprehberger::LockKit::ReadWriteLock
- Defined in:
- lib/philiprehberger/lock_kit/read_write_lock.rb
Overview
Read-write lock supporting shared reads and exclusive writes
Read locks are shared — multiple readers can hold the lock concurrently. Write locks are exclusive — no readers or other writers are allowed. Uses a ‘.readers` counter file alongside the lock file to track state.
Instance Method Summary collapse
-
#acquire_read(timeout: nil) ⇒ true
Acquire a shared read lock.
-
#acquire_write(timeout: nil) ⇒ true
Acquire an exclusive write lock.
-
#initialize(path) ⇒ ReadWriteLock
constructor
A new instance of ReadWriteLock.
-
#reader_count ⇒ Integer
Return the current number of active readers.
-
#release_read ⇒ void
Release the shared read lock.
-
#release_write ⇒ void
Release the exclusive write lock.
Constructor Details
#initialize(path) ⇒ ReadWriteLock
Returns a new instance of ReadWriteLock.
12 13 14 15 16 17 |
# File 'lib/philiprehberger/lock_kit/read_write_lock.rb', line 12 def initialize(path) @path = path @write_lock_path = "#{path}.write" @readers_path = "#{path}.readers" @write_file = nil end |
Instance Method Details
#acquire_read(timeout: nil) ⇒ true
Acquire a shared read lock
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/philiprehberger/lock_kit/read_write_lock.rb', line 24 def acquire_read(timeout: nil) deadline = timeout ? Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout : nil loop do # Check if a write lock is held unless write_locked? increment_readers # Double-check no writer snuck in unless write_locked? return true end decrement_readers end raise Error, "Could not acquire read lock on #{@path}" unless deadline remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC) if remaining <= 0 raise Error, "Timeout acquiring read lock on #{@path} after #{timeout}s" end sleep [0.05, remaining].min end end |
#acquire_write(timeout: nil) ⇒ true
Acquire an exclusive write lock
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/philiprehberger/lock_kit/read_write_lock.rb', line 62 def acquire_write(timeout: nil) @write_file = File.open(@write_lock_path, File::CREAT | File::RDWR) deadline = timeout ? Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout : nil # First, acquire the write file lock loop do break if @write_file.flock(File::LOCK_EX | File::LOCK_NB) if deadline remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC) if remaining <= 0 close_write_file raise Error, "Timeout acquiring write lock on #{@path} after #{timeout}s" end sleep [0.05, remaining].min else close_write_file raise Error, "Could not acquire write lock on #{@path}" end end # Then, wait for all readers to finish loop do break if reader_count.zero? if deadline remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC) if remaining <= 0 @write_file.flock(File::LOCK_UN) close_write_file raise Error, "Timeout acquiring write lock on #{@path} after #{timeout}s — readers still active" end sleep [0.05, remaining].min else @write_file.flock(File::LOCK_UN) close_write_file raise Error, "Could not acquire write lock on #{@path} — readers still active" end end true end |
#reader_count ⇒ Integer
Return the current number of active readers
120 121 122 123 124 125 126 127 |
# File 'lib/philiprehberger/lock_kit/read_write_lock.rb', line 120 def reader_count return 0 unless File.exist?(@readers_path) count = File.read(@readers_path).strip.to_i count.negative? ? 0 : count rescue Errno::ENOENT 0 end |
#release_read ⇒ void
This method returns an undefined value.
Release the shared read lock
53 54 55 |
# File 'lib/philiprehberger/lock_kit/read_write_lock.rb', line 53 def release_read decrement_readers end |
#release_write ⇒ void
This method returns an undefined value.
Release the exclusive write lock
110 111 112 113 114 115 |
# File 'lib/philiprehberger/lock_kit/read_write_lock.rb', line 110 def release_write return unless @write_file @write_file.flock(File::LOCK_UN) close_write_file end |