Backport thread-local variables from Ruby 2.0

This commit is contained in:
Rafael Mendonça França 2012-12-11 17:45:27 -03:00
parent 48583f8bf7
commit a565f80bcb
2 changed files with 147 additions and 0 deletions

View File

@ -0,0 +1,70 @@
class Thread
LOCK = Mutex.new # :nodoc:
# Returns the value of a thread local variable that has been set. Note that
# these are different than fiber local values.
#
# Thread local values are carried along with threads, and do not respect
# fibers. For example:
#
# Thread.new {
# Thread.current.thread_variable_set("foo", "bar") # set a thread local
# Thread.current["foo"] = "bar" # set a fiber local
#
# Fiber.new {
# Fiber.yield [
# Thread.current.thread_variable_get("foo"), # get the thread local
# Thread.current["foo"], # get the fiber local
# ]
# }.resume
# }.join.value # => ['bar', nil]
#
# The value <tt>"bar"</tt> is returned for the thread local, where +nil+ is returned
# for the fiber local. The fiber is executed in the same thread, so the
# thread local values are available.
def thread_variable_get(key)
locals[key.to_sym]
end
# Sets a thread local with +key+ to +value+. Note that these are local to
# threads, and not to fibers. Please see Thread#thread_variable_get for
# more information.
def thread_variable_set(key, value)
locals[key.to_sym] = value
end
# Returns an an array of the names of the thread-local variables (as Symbols).
#
# thr = Thread.new do
# Thread.current.thread_variable_set(:cat, 'meow')
# Thread.current.thread_variable_set("dog", 'woof')
# end
# thr.join #=> #<Thread:0x401b3f10 dead>
# thr.thread_variables #=> [:dog, :cat]
#
# Note that these are not fiber local variables. Please see Thread#thread_variable_get
# for more details.
def thread_variables
locals.keys
end
# Returns <tt>true</tt> if the given string (or symbol) exists as a
# thread-local variable.
#
# me = Thread.current
# me.thread_variable_set(:oliver, "a")
# me.thread_variable?(:oliver) #=> true
# me.thread_variable?(:stanley) #=> false
#
# Note that these are not fiber local variables. Please see Thread#thread_variable_get
# for more details.
def thread_variable?(key)
locals.has_key?(key.to_sym)
end
private
def locals
@locals || LOCK.synchronize { @locals ||= {} }
end
end unless Thread.instance_methods.include?(:thread_variable_set)

View File

@ -0,0 +1,77 @@
require 'abstract_unit'
require 'active_support/core_ext/thread'
class ThreadExt < ActiveSupport::TestCase
def test_main_thread_variable_in_enumerator
assert_equal Thread.main, Thread.current
Thread.current.thread_variable_set :foo, "bar"
thread, value = Fiber.new {
Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
}.resume
assert_equal Thread.current, thread
assert_equal Thread.current.thread_variable_get(:foo), value
end
def test_thread_variable_in_enumerator
Thread.new {
Thread.current.thread_variable_set :foo, "bar"
thread, value = Fiber.new {
Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
}.resume
assert_equal Thread.current, thread
assert_equal Thread.current.thread_variable_get(:foo), value
}.join
end
def test_thread_variables
assert_equal [], Thread.new { Thread.current.thread_variables }.join.value
t = Thread.new {
Thread.current.thread_variable_set(:foo, "bar")
Thread.current.thread_variables
}
assert_equal [:foo], t.join.value
end
def test_thread_variable?
refute Thread.new { Thread.current.thread_variable?("foo") }.join.value
t = Thread.new {
Thread.current.thread_variable_set("foo", "bar")
}.join
assert t.thread_variable?("foo")
assert t.thread_variable?(:foo)
refute t.thread_variable?(:bar)
end
def test_thread_variable_strings_and_symbols_are_the_same_key
t = Thread.new {}.join
t.thread_variable_set("foo", "bar")
assert_equal "bar", t.thread_variable_get(:foo)
end
def test_thread_variable_frozen
t = Thread.new { }.join
t.freeze
assert_raises(RuntimeError) do
t.thread_variable_set(:foo, "bar")
end
end
def test_thread_variable_security
t = Thread.new { sleep }
assert_raises(SecurityError) do
Thread.new { $SAFE = 4; t.thread_variable_get(:foo) }.join
end
assert_raises(SecurityError) do
Thread.new { $SAFE = 4; t.thread_variable_set(:foo, :baz) }.join
end
end
end