diff options
author | Matthew Draper <matthew@trebex.net> | 2015-07-09 04:33:14 +0930 |
---|---|---|
committer | Matthew Draper <matthew@trebex.net> | 2015-07-09 04:33:14 +0930 |
commit | 0b93c48bbe74857ead9a9ef56b35f87965edbb49 (patch) | |
tree | 1861b4f1710fda3a66a8eedd94ae8e6cec44fc18 | |
parent | 48a735aff7032afaf5534613b10c635acead042a (diff) | |
download | rails-0b93c48bbe74857ead9a9ef56b35f87965edbb49.tar.gz rails-0b93c48bbe74857ead9a9ef56b35f87965edbb49.tar.bz2 rails-0b93c48bbe74857ead9a9ef56b35f87965edbb49.zip |
Document ShareLock and the Interlock
3 files changed, 42 insertions, 9 deletions
diff --git a/activesupport/lib/active_support/concurrency/share_lock.rb b/activesupport/lib/active_support/concurrency/share_lock.rb index e03f2cfc7a..f1c6230084 100644 --- a/activesupport/lib/active_support/concurrency/share_lock.rb +++ b/activesupport/lib/active_support/concurrency/share_lock.rb @@ -3,6 +3,15 @@ require 'monitor' module ActiveSupport module Concurrency + # A share/exclusive lock, otherwise known as a read/write lock. + # + # https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock + #-- + # Note that a pending Exclusive lock attempt does not block incoming + # Share requests (i.e., we are "read-preferring"). That seems + # consistent with the behavior of +loose_upgrades+, but may be the + # wrong choice otherwise: it nominally reduces the possibility of + # deadlock by risking starvation instead. class ShareLock include MonitorMixin @@ -35,6 +44,9 @@ module ActiveSupport @exclusive_depth = 0 end + # Returns false if +no_wait+ is specified and the lock is not + # immediately available. Otherwise, returns true after the lock + # has been acquired. def start_exclusive(no_wait=false) synchronize do unless @exclusive_thread == Thread.current @@ -56,6 +68,8 @@ module ActiveSupport end end + # Relinquish the exclusive lock. Must only be called by the thread + # that called start_exclusive (and currently holds the lock). def stop_exclusive synchronize do raise "invalid unlock" if @exclusive_thread != Thread.current @@ -88,6 +102,10 @@ module ActiveSupport end end + # 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. def exclusive(no_wait=false) if start_exclusive(no_wait) begin @@ -98,6 +116,7 @@ module ActiveSupport end end + # Execute the supplied block while holding the Share lock. def sharing start_sharing begin @@ -109,6 +128,7 @@ module ActiveSupport private + # Must be called within synchronize def busy? (@exclusive_thread && @exclusive_thread != Thread.current) || @sharing.size > (@sharing[Thread.current] > 0 ? 1 : 0) diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 2a0e06495f..770c845435 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -22,6 +22,23 @@ module ActiveSupport #:nodoc: mattr_accessor :interlock self.interlock = Interlock.new + # :doc: + + # Execute the supplied block without interference from any + # concurrent loads + def self.run_interlock + Dependencies.interlock.running { yield } + end + + # Execute the supplied block while holding an exclusive lock, + # preventing any other thread from being inside a #run_interlock + # block at the same time + def self.load_interlock + Dependencies.interlock.loading { yield } + end + + # :nodoc: + # Should we turn on Ruby warnings on the first load of dependent files? mattr_accessor :warnings_on_first_load self.warnings_on_first_load = false @@ -238,7 +255,7 @@ module ActiveSupport #:nodoc: end def load_dependency(file) - Dependencies.interlock.loading do + Dependencies.load_interlock do if Dependencies.load? && ActiveSupport::Dependencies.constant_watch_stack.watching? Dependencies.new_constants_in(Object) { yield } else @@ -331,7 +348,7 @@ module ActiveSupport #:nodoc: def clear log_call - Dependencies.interlock.loading do + Dependencies.load_interlock do loaded.clear loading.clear remove_unloadable_constants! @@ -344,7 +361,7 @@ module ActiveSupport #:nodoc: expanded = File.expand_path(file_name) return if loaded.include?(expanded) - Dependencies.interlock.loading do + Dependencies.load_interlock do # Maybe it got loaded while we were waiting for our lock: return if loaded.include?(expanded) diff --git a/activesupport/lib/active_support/dependencies/interlock.rb b/activesupport/lib/active_support/dependencies/interlock.rb index 035802d680..148212c951 100644 --- a/activesupport/lib/active_support/dependencies/interlock.rb +++ b/activesupport/lib/active_support/dependencies/interlock.rb @@ -1,9 +1,9 @@ require 'active_support/concurrency/share_lock' -module ActiveSupport +module ActiveSupport #:nodoc: module Dependencies #:nodoc: class Interlock - def initialize + def initialize # :nodoc: @lock = ActiveSupport::Concurrency::ShareLock.new(true) end @@ -36,10 +36,6 @@ module ActiveSupport yield end end - - # Match the Mutex API, so we can be used by Rack::Lock - alias :lock :start_running - alias :unlock :done_running end end end |