Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
redis-read-write-locks (0.5.0)
redis-read-write-locks (0.5.1)

GEM
remote: https://rubygems.org/
Expand Down
19 changes: 12 additions & 7 deletions lib/redis_read_write_locks/base_lock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ def acquire(retry_count: nil, retry_delay: DEFAULT_RETRY_DELAY)

def synchronize(retry_count: nil, retry_delay: DEFAULT_RETRY_DELAY, &block)
acquire_or_raise(retry_count: retry_count, retry_delay: retry_delay)
stopped = false
watchdog = start_watchdog(Thread.current, -> { stopped })
stop = Thread::Queue.new
watchdog = start_watchdog(Thread.current, stop)
begin
block.call
ensure
stopped = true
stop.close
watchdog.join
release
end
Expand All @@ -66,13 +66,18 @@ def acquire_or_raise(retry_count:, retry_delay:)
acquire || raise(LockNotAcquiredError, "Could not acquire #{lock_type} lock '#{@name}'")
end

def start_watchdog(main_thread, stopped)
# Waits on the queue rather than sleeping, so closing it on release wakes the
# watchdog at once. While it slept, every synchronize paid up to a full
# WATCHDOG_SLEEP_INTERVAL on the way out - far more than a short critical
# section takes, and callers that take many brief locks paid it every time.
def start_watchdog(main_thread, stop)
Thread.new do
elapsed = 0.0
until stopped.call
sleep WATCHDOG_SLEEP_INTERVAL
loop do
stop.pop(timeout: WATCHDOG_SLEEP_INTERVAL)
break if stop.closed?

elapsed += WATCHDOG_SLEEP_INTERVAL
next if stopped.call
next unless elapsed >= WATCHDOG_REFRESH_INTERVAL

elapsed = 0.0
Expand Down
2 changes: 1 addition & 1 deletion lib/redis_read_write_locks/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module RedisReadWriteLocks
VERSION = "0.5.0"
VERSION = "0.5.1"
end
10 changes: 10 additions & 0 deletions spec/redis_read_write_locks/write_lock_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@
expect { lock.synchronize {} }.to raise_error(RedisReadWriteLocks::LockNotAcquiredError)
end

it "releases without waiting out the watchdog's sleep interval" do
stub_const("RedisReadWriteLocks::BaseLock::WATCHDOG_SLEEP_INTERVAL", 5)

started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
lock.synchronize { sleep 0.05 }
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started

expect(elapsed).to be < 1
end

it "watchdog keeps lock alive beyond TTL" do
stub_const("RedisReadWriteLocks::BaseLock::WATCHDOG_REFRESH_INTERVAL", 0.1)
stub_const("RedisReadWriteLocks::BaseLock::WATCHDOG_SLEEP_INTERVAL", 0.05)
Expand Down
Loading