Class: ActiveSupport::Concurrency::ShareLock
Relationships & Source Files | |
Super Chains via Extension / Inclusion / Inheritance | |
Instance Chain:
self,
MonitorMixin
|
|
Inherits: | Object |
Defined in: | activesupport/lib/active_support/concurrency/share_lock.rb |
Overview
A share/exclusive lock, otherwise known as a read/write lock.
Class Method Summary
- .new ⇒ ShareLock constructor
Instance Method Summary
-
#exclusive(purpose: nil, compatible: [], after_compatible: [], no_wait: false)
Execute the supplied block while holding the Exclusive lock.
-
#sharing
Execute the supplied block while holding the Share lock.
-
#start_exclusive(purpose: nil, compatible: [], no_wait: false)
Returns false if
no_wait
is set and the lock is not immediately available. - #start_sharing
-
#stop_exclusive(compatible: [])
Relinquish the exclusive lock.
- #stop_sharing
-
#yield_shares(purpose: nil, compatible: [], block_share: false)
Temporarily give up all held Share locks while executing the supplied block, allowing any
compatible
exclusive lock request to proceed.
Constructor Details
.new ⇒ ShareLock
# File 'activesupport/lib/active_support/concurrency/share_lock.rb', line 49
def initialize super() @cv = new_cond @sharing = Hash.new(0) @waiting = {} @sleeping = {} @exclusive_thread = nil @exclusive_depth = 0 end
Instance Method Details
#exclusive(purpose: nil, compatible: [], after_compatible: [], no_wait: false)
Execute the supplied block while holding the Exclusive lock. If no_wait
is set and the lock is not immediately available, returns nil
without yielding. Otherwise, returns the result of the block.
See #start_exclusive for other options.
# File 'activesupport/lib/active_support/concurrency/share_lock.rb', line 147
def exclusive(purpose: nil, compatible: [], after_compatible: [], no_wait: false) if start_exclusive(purpose: purpose, compatible: compatible, no_wait: no_wait) begin yield ensure stop_exclusive(compatible: after_compatible) end end end
#sharing
Execute the supplied block while holding the Share lock.
# File 'activesupport/lib/active_support/concurrency/share_lock.rb', line 158
def sharing start_sharing begin yield ensure stop_sharing end end
#start_exclusive(purpose: nil, compatible: [], no_wait: false)
Returns false if no_wait
is set and the lock is not immediately available. Otherwise, returns true after the lock has been acquired.
purpose
and compatible
work together; while this thread is waiting for the exclusive lock, it will yield its share (if any) to any other attempt whose purpose
appears in this attempt’s compatible
list. This allows a “loose” upgrade, which, being less strict, prevents some classes of deadlocks.
For many resources, loose upgrades are sufficient: if a thread is awaiting a lock, it is not running any other code. With purpose
matching, it is possible to yield only to other threads whose activity will not interfere.
# File 'activesupport/lib/active_support/concurrency/share_lock.rb', line 75
def start_exclusive(purpose: nil, compatible: [], no_wait: false) synchronize do unless @exclusive_thread == Thread.current if busy_for_exclusive?(purpose) return false if no_wait yield_shares(purpose: purpose, compatible: compatible, block_share: true) do wait_for(:start_exclusive) { busy_for_exclusive?(purpose) } end end @exclusive_thread = Thread.current end @exclusive_depth += 1 true end end
#start_sharing
[ GitHub ]# File 'activesupport/lib/active_support/concurrency/share_lock.rb', line 113
def start_sharing synchronize do if @sharing[Thread.current] > 0 || @exclusive_thread == Thread.current # We already hold a lock; nothing to wait for elsif @waiting[Thread.current] # We're nested inside a yield_shares call: we'll resume as # soon as there isn't an exclusive lock in our way wait_for(:start_sharing) { @exclusive_thread } else # This is an initial / outermost share call: any outstanding # requests for an exclusive lock get to go first wait_for(:start_sharing) { busy_for_sharing?(false) } end @sharing[Thread.current] += 1 end end
#stop_exclusive(compatible: [])
Relinquish the exclusive lock. Must only be called by the thread that called start_exclusive (and currently holds the lock).
# File 'activesupport/lib/active_support/concurrency/share_lock.rb', line 95
def stop_exclusive(compatible: []) synchronize do raise "invalid unlock" if @exclusive_thread != Thread.current @exclusive_depth -= 1 if @exclusive_depth == 0 @exclusive_thread = nil if eligible_waiters?(compatible) yield_shares(compatible: compatible, block_share: true) do wait_for(:stop_exclusive) { @exclusive_thread || eligible_waiters?(compatible) } end end @cv.broadcast end end end
#stop_sharing
[ GitHub ]# File 'activesupport/lib/active_support/concurrency/share_lock.rb', line 130
def stop_sharing synchronize do if @sharing[Thread.current] > 1 @sharing[Thread.current] -= 1 else @sharing.delete Thread.current @cv.broadcast end end end