diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 2075d79bd1..34961f22e1 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -17,7 +17,7 @@ class Date
# If Date.beginning_of_week has not been set for the current request, returns the week start specified in config.beginning_of_week.
# If no config.beginning_of_week was specified, returns :monday.
def beginning_of_week
- Thread.current[:beginning_of_week] || beginning_of_week_default || :monday
+ ::ActiveSupport::IsolatedExecutionState[:beginning_of_week] || beginning_of_week_default || :monday
end
# Sets Date.beginning_of_week to a week start (e.g. :monday) for current request/thread.
@@ -25,7 +25,7 @@ class Date
# This method accepts any of the following day symbols:
# :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday
def beginning_of_week=(week_start)
- Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start)
+ ::ActiveSupport::IsolatedExecutionState[:beginning_of_week] = find_beginning_of_week!(week_start)
end
# Returns week start day symbol (e.g. :monday), or raises an +ArgumentError+ for invalid day symbol.
diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb
index 4443405a85..76d8639063 100644
--- a/activesupport/lib/active_support/core_ext/time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/time/zones.rb
@@ -12,7 +12,7 @@ class Time
# Returns the TimeZone for the current request, if this has been set (via Time.zone=).
# If Time.zone has not been set for the current request, returns the TimeZone specified in config.time_zone.
def zone
- Thread.current[:time_zone] || zone_default
+ ::ActiveSupport::IsolatedExecutionState[:time_zone] || zone_default
end
# Sets Time.zone to a TimeZone object for the current request/thread.
@@ -39,7 +39,7 @@ class Time
# end
# end
def zone=(time_zone)
- Thread.current[:time_zone] = find_zone!(time_zone)
+ ::ActiveSupport::IsolatedExecutionState[:time_zone] = find_zone!(time_zone)
end
# Allows override of Time.zone locally inside supplied block;
diff --git a/activesupport/lib/active_support/execution_context.rb b/activesupport/lib/active_support/execution_context.rb
index 01d7742e9a..1c95188bae 100644
--- a/activesupport/lib/active_support/execution_context.rb
+++ b/activesupport/lib/active_support/execution_context.rb
@@ -46,7 +46,7 @@ module ActiveSupport
private
def store
- Thread.current[:active_support_execution_context] ||= {}
+ IsolatedExecutionState[:active_support_execution_context] ||= {}
end
end
end
diff --git a/activesupport/lib/active_support/execution_wrapper.rb b/activesupport/lib/active_support/execution_wrapper.rb
index 36e80b23c7..0c11087d26 100644
--- a/activesupport/lib/active_support/execution_wrapper.rb
+++ b/activesupport/lib/active_support/execution_wrapper.rb
@@ -113,11 +113,11 @@ module ActiveSupport
self.active = Concurrent::Hash.new
def self.active? # :nodoc:
- @active[Thread.current]
+ @active[IsolatedExecutionState.unique_id]
end
def run! # :nodoc:
- self.class.active[Thread.current] = true
+ self.class.active[IsolatedExecutionState.unique_id] = true
run
end
@@ -132,7 +132,7 @@ module ActiveSupport
def complete!
complete
ensure
- self.class.active.delete Thread.current
+ self.class.active.delete(IsolatedExecutionState.unique_id)
end
def complete # :nodoc:
diff --git a/activesupport/lib/active_support/isolated_execution_state.rb b/activesupport/lib/active_support/isolated_execution_state.rb
index 5d972a4acd..7e14b9b4f6 100644
--- a/activesupport/lib/active_support/isolated_execution_state.rb
+++ b/activesupport/lib/active_support/isolated_execution_state.rb
@@ -25,6 +25,10 @@ module ActiveSupport
end
end
+ def unique_id
+ self[:__id__] ||= Object.new
+ end
+
def [](key)
current[key]
end
diff --git a/activesupport/lib/active_support/logger_thread_safe_level.rb b/activesupport/lib/active_support/logger_thread_safe_level.rb
index 4f860939e7..042f484f82 100644
--- a/activesupport/lib/active_support/logger_thread_safe_level.rb
+++ b/activesupport/lib/active_support/logger_thread_safe_level.rb
@@ -18,8 +18,7 @@ module ActiveSupport
end
def local_level
- # Note: Thread#[] is fiber-local
- Thread.current[:logger_thread_safe_level]
+ IsolatedExecutionState[:logger_thread_safe_level]
end
def local_level=(level)
@@ -31,7 +30,7 @@ module ActiveSupport
else
raise ArgumentError, "Invalid log level: #{level.inspect}"
end
- Thread.current[:logger_thread_safe_level] = level
+ IsolatedExecutionState[:logger_thread_safe_level] = level
end
def level
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index 4935a8ffcf..0759d3a086 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -226,12 +226,12 @@ module ActiveSupport
end
def start(name, id, payload)
- timestack = Thread.current[:_timestack] ||= []
+ timestack = IsolatedExecutionState[:_timestack] ||= []
timestack.push Time.now
end
def finish(name, id, payload)
- timestack = Thread.current[:_timestack]
+ timestack = IsolatedExecutionState[:_timestack]
started = timestack.pop
@delegate.call(name, started, Time.now, id, payload)
end
@@ -243,12 +243,12 @@ module ActiveSupport
end
def start(name, id, payload)
- timestack = Thread.current[:_timestack_monotonic] ||= []
+ timestack = IsolatedExecutionState[:_timestack_monotonic] ||= []
timestack.push Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
def finish(name, id, payload)
- timestack = Thread.current[:_timestack_monotonic]
+ timestack = IsolatedExecutionState[:_timestack_monotonic]
started = timestack.pop
@delegate.call(name, started, Process.clock_gettime(Process::CLOCK_MONOTONIC), id, payload)
end
@@ -256,14 +256,14 @@ module ActiveSupport
class EventObject < Evented
def start(name, id, payload)
- stack = Thread.current[:_event_stack] ||= []
+ stack = IsolatedExecutionState[:_event_stack] ||= []
event = build_event name, id, payload
event.start!
stack.push event
end
def finish(name, id, payload)
- stack = Thread.current[:_event_stack]
+ stack = IsolatedExecutionState[:_event_stack]
event = stack.pop
event.payload = payload
event.finish!
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index 0bc4a21401..26852e590a 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -57,7 +57,7 @@ module ActiveSupport
def current_tags
# We use our object ID here to avoid conflicting with other instances
thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
- Thread.current[thread_key] ||= []
+ IsolatedExecutionState[thread_key] ||= []
end
def tags_text
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index 1930c417fd..e2b52daa65 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -181,11 +181,11 @@ module ActiveSupport
end
def current_thread_backend
- Thread.current[:xml_mini_backend]
+ IsolatedExecutionState[:xml_mini_backend]
end
def current_thread_backend=(name)
- Thread.current[:xml_mini_backend] = name && cast_backend_name_to_module(name)
+ IsolatedExecutionState[:xml_mini_backend] = name && cast_backend_name_to_module(name)
end
def cast_backend_name_to_module(name)
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index cb28444bba..c328234a39 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -1205,13 +1205,11 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase
def test_time_zone_setter_is_thread_safe
Time.use_zone "Paris" do
- t1 = Thread.new { Time.zone = "Alaska" }.join
- t2 = Thread.new { Time.zone = "Hawaii" }.join
- assert t1.stop?, "Thread 1 did not finish running"
- assert t2.stop?, "Thread 2 did not finish running"
+ t1 = Thread.new { Time.zone = "Alaska"; Time.zone }
+ t2 = Thread.new { Time.zone = "Hawaii"; Time.zone }
assert_equal ActiveSupport::TimeZone["Paris"], Time.zone
- assert_equal ActiveSupport::TimeZone["Alaska"], t1[:time_zone]
- assert_equal ActiveSupport::TimeZone["Hawaii"], t2[:time_zone]
+ assert_equal ActiveSupport::TimeZone["Alaska"], t1.value
+ assert_equal ActiveSupport::TimeZone["Hawaii"], t2.value
end
end
diff --git a/activesupport/test/logger_test.rb b/activesupport/test/logger_test.rb
index 23e77ad270..2461b48bc6 100644
--- a/activesupport/test/logger_test.rb
+++ b/activesupport/test/logger_test.rb
@@ -272,6 +272,9 @@ class LoggerTest < ActiveSupport::TestCase
end
def test_logger_level_main_fiber_safety
+ previous_isolation_level = ActiveSupport::IsolatedExecutionState.isolation_level
+ ActiveSupport::IsolatedExecutionState.isolation_level = :fiber
+
@logger.level = Logger::INFO
assert_level(Logger::INFO)
@@ -283,9 +286,14 @@ class LoggerTest < ActiveSupport::TestCase
assert_level(Logger::ERROR)
fiber.resume
end
+ ensure
+ ActiveSupport::IsolatedExecutionState.isolation_level = previous_isolation_level
end
def test_logger_level_local_fiber_safety
+ previous_isolation_level = ActiveSupport::IsolatedExecutionState.isolation_level
+ ActiveSupport::IsolatedExecutionState.isolation_level = :fiber
+
@logger.level = Logger::INFO
assert_level(Logger::INFO)
@@ -313,6 +321,25 @@ class LoggerTest < ActiveSupport::TestCase
end.resume
assert_level(Logger::INFO)
+ ensure
+ ActiveSupport::IsolatedExecutionState.isolation_level = previous_isolation_level
+ end
+
+ def test_logger_level_thread_safety
+ previous_isolation_level = ActiveSupport::IsolatedExecutionState.isolation_level
+ ActiveSupport::IsolatedExecutionState.isolation_level = :thread
+
+ @logger.level = Logger::INFO
+ assert_level(Logger::INFO)
+
+ enumerator = Enumerator.new do |yielder|
+ @logger.level = Logger::DEBUG
+ yielder.yield @logger.level
+ end
+ assert_equal Logger::DEBUG, enumerator.next
+ assert_level(Logger::DEBUG)
+ ensure
+ ActiveSupport::IsolatedExecutionState.isolation_level = previous_isolation_level
end
def test_temporarily_logging_at_a_noisier_level