[前][次][番号順一覧][スレッド一覧]

ruby-changes:25333

From: tenderlove <ko1@a...>
Date: Tue, 30 Oct 2012 02:23:12 +0900 (JST)
Subject: [ruby-changes:25333] tenderlove:r37384 (trunk): * thread.c: added Thread#thread_variable_(get|set),

tenderlove	2012-10-30 02:22:36 +0900 (Tue, 30 Oct 2012)

  New Revision: 37384

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=37384

  Log:
    * thread.c: added Thread#thread_variable_(get|set),
      Thread#thread_variable?, and Thread#thread_variables for operating
      on variables that are local to threads. [ruby-core:47790]
    
    * vm.c: ditto
    
    * test/ruby/test_thread.rb: tests for thread variables.

  Modified files:
    trunk/ChangeLog
    trunk/NEWS
    trunk/test/ruby/test_thread.rb
    trunk/thread.c
    trunk/vm.c

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 37383)
+++ ChangeLog	(revision 37384)
@@ -1,3 +1,13 @@
+Tue Oct 30 02:20:10 2012  Aaron Patterson <aaron@t...>
+
+	* thread.c: added Thread#thread_variable_(get|set),
+	  Thread#thread_variable?, and Thread#thread_variables for operating
+	  on variables that are local to threads. [ruby-core:47790]
+
+	* vm.c: ditto
+
+	* test/ruby/test_thread.rb: tests for thread variables.
+
 Mon Oct 29 18:22:58 2012  Nobuyoshi Nakada  <nobu@r...>
 
 	* ext/stringio/stringio.c (strio_close): close separatedly per each
Index: thread.c
===================================================================
--- thread.c	(revision 37383)
+++ thread.c	(revision 37384)
@@ -2525,6 +2525,9 @@
  *    #=> nil if fiber-local
  *    #=> 2 if thread-local (The value 2 is leaked to outside of meth method.)
  *
+ *  For thread-local variables, please see <code>Thread#thread_local_get</code>
+ *  and <code>Thread#thread_local_set</code>.
+ *
  */
 
 static VALUE
@@ -2561,7 +2564,9 @@
  *      thr[sym] = obj   -> obj
  *
  *  Attribute Assignment---Sets or creates the value of a fiber-local variable,
- *  using either a symbol or a string. See also <code>Thread#[]</code>.
+ *  using either a symbol or a string. See also <code>Thread#[]</code>.  For
+ *  thread-local variables, please see <code>Thread#thread_variable_set</code>
+ *  and <code>Thread#thread_variable_get</code>.
  */
 
 static VALUE
@@ -2572,6 +2577,80 @@
 
 /*
  *  call-seq:
+ *      thr.thread_variable_get(key)  -> obj or nil
+ *
+ *  Returns the value of a thread local variable that has been set.  Note that
+ *  these are different than fiber local values.  For fiber local values,
+ *  please see Thread#[] and Thread#[]=.
+ *
+ *  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 "bar" 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.
+ *
+ *  See also Thread#[]
+ */
+
+static VALUE
+rb_thread_variable_get(VALUE thread, VALUE id)
+{
+    VALUE locals;
+    rb_thread_t *th;
+
+    GetThreadPtr(thread, th);
+
+    if (rb_safe_level() >= 4 && th != GET_THREAD()) {
+	rb_raise(rb_eSecurityError, "Insecure: can't modify thread locals");
+    }
+
+    locals = rb_iv_get(thread, "locals");
+    return rb_hash_aref(locals, ID2SYM(rb_to_id(id)));
+}
+
+/*
+ *  call-seq:
+ *      thr.thread_variable_set(key, value)
+ *
+ *  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 and
+ *  Thread#[] for more information.
+ */
+
+static VALUE
+rb_thread_variable_set(VALUE thread, VALUE id, VALUE val)
+{
+    VALUE locals;
+    rb_thread_t *th;
+
+    GetThreadPtr(thread, th);
+
+    if (rb_safe_level() >= 4 && th != GET_THREAD()) {
+	rb_raise(rb_eSecurityError, "Insecure: can't modify thread locals");
+    }
+    if (OBJ_FROZEN(thread)) {
+	rb_error_frozen("thread locals");
+    }
+
+    locals = rb_iv_get(thread, "locals");
+    return rb_hash_aset(locals, ID2SYM(rb_to_id(id)), val);
+}
+
+/*
+ *  call-seq:
  *     thr.key?(sym)   -> true or false
  *
  *  Returns <code>true</code> if the given string (or symbol) exists as a
@@ -2651,8 +2730,78 @@
     return ary;
 }
 
+static int
+keys_i(VALUE key, VALUE value, VALUE ary)
+{
+    rb_ary_push(ary, key);
+    return ST_CONTINUE;
+}
+
 /*
  *  call-seq:
+ *     thr.thread_variables   -> array
+ *
+ *  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#[] and
+ *  Thread#thread_variable_get for more details.
+ */
+
+static VALUE
+rb_thread_variables(VALUE thread)
+{
+    VALUE locals;
+    VALUE ary;
+
+    locals = rb_iv_get(thread, "locals");
+    ary = rb_ary_new();
+    rb_hash_foreach(locals, keys_i, ary);
+
+    return ary;
+}
+
+/*
+ *  call-seq:
+ *     thr.thread_variable?(key)   -> true or false
+ *
+ *  Returns <code>true</code> 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#[] and
+ *  Thread#thread_variable_get for more details.
+ */
+
+static VALUE
+rb_thread_variable_p(VALUE thread, VALUE key)
+{
+    VALUE locals;
+
+    locals = rb_iv_get(thread, "locals");
+
+    if (!RHASH(locals)->ntbl)
+        return Qfalse;
+
+    if (st_lookup(RHASH(locals)->ntbl, ID2SYM(rb_to_id(key)), 0)) {
+	return Qtrue;
+    }
+
+    return Qfalse;
+}
+
+/*
+ *  call-seq:
  *     thr.priority   -> integer
  *
  *  Returns the priority of <i>thr</i>. Default is inherited from the
@@ -4541,6 +4690,10 @@
     rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
     rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
     rb_define_method(rb_cThread, "status", rb_thread_status, 0);
+    rb_define_method(rb_cThread, "thread_variable_get", rb_thread_variable_get, 1);
+    rb_define_method(rb_cThread, "thread_variable_set", rb_thread_variable_set, 2);
+    rb_define_method(rb_cThread, "thread_variables", rb_thread_variables, 0);
+    rb_define_method(rb_cThread, "thread_variable?", rb_thread_variable_p, 1);
     rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
     rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
     rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
Index: NEWS
===================================================================
--- NEWS	(revision 37383)
+++ NEWS	(revision 37384)
@@ -83,6 +83,16 @@
       * added Struct#to_h returning values with keys corresponding to the
         instance variable names.
 
+  * Thread
+    * added method:
+      * added Thread#thread_variable_get for getting thread local variables
+        (these are different than Fiber local variables).
+      * added Thread#thread_variable_set for setting thread local variables.
+      * added Thread#thread_variables for getting a list of the thread local
+        variable keys.
+      * added Thread#thread_variable? for testing to see if a particular thread
+        variable has been set.
+
   * Time
     * change return value:
       * Time#to_s returned encoding defaults to US-ASCII but automatically
Index: vm.c
===================================================================
--- vm.c	(revision 37383)
+++ vm.c	(revision 37384)
@@ -1838,6 +1838,7 @@
     GetThreadPtr(self, th);
 
     th_init(th, self);
+    rb_iv_set(self, "locals", rb_hash_new());
     th->vm = vm;
 
     th->top_wrapper = 0;
@@ -2178,6 +2179,7 @@
 
 	/* create main thread */
 	th_self = th->self = TypedData_Wrap_Struct(rb_cThread, &thread_data_type, th);
+	rb_iv_set(th_self, "locals", rb_hash_new());
 	vm->main_thread = th;
 	vm->running_thread = th;
 	th->vm = vm;
Index: test/ruby/test_thread.rb
===================================================================
--- test/ruby/test_thread.rb	(revision 37383)
+++ test/ruby/test_thread.rb	(revision 37384)
@@ -27,6 +27,79 @@
     end
   end
 
+  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
+
   def test_mutex_synchronize
     m = Mutex.new
     r = 0

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]