aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support')
-rw-r--r--activesupport/lib/active_support/concurrency/share_lock.rb20
-rw-r--r--activesupport/lib/active_support/dependencies.rb23
-rw-r--r--activesupport/lib/active_support/dependencies/interlock.rb8
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