thread_variables.patch

Aaron Patterson, 10/27/2012 02:40 AM

Download (9.43 KB)

View differences:

NEWS
81 81
      * added Struct#to_h returning values with keys corresponding to the
82 82
        instance variable names.
83 83

  
84
  * Thread
85
    * added method:
86
      * added Thread#thread_variable_get for getting thread local variables
87
        (these are different than Fiber local variables).
88
      * added Thread#thread_variable_set for setting thread local variables.
89
      * added Thread#thread_variables for getting a list of the thread local
90
        variable keys.
91
      * added Thread#thread_variable? for testing to see if a particular thread
92
        variable has been set.
93

  
84 94
  * Time
85 95
    * change return value:
86 96
      * Time#to_s returned encoding defaults to US-ASCII but automatically
test/ruby/test_thread.rb
27 27
    end
28 28
  end
29 29

  
30
  def test_main_thread_variable_in_enumerator
31
    assert_equal Thread.main, Thread.current
32

  
33
    Thread.current.thread_variable_set :foo, "bar"
34

  
35
    thread, value = Fiber.new {
36
      Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
37
    }.resume
38

  
39
    assert_equal Thread.current, thread
40
    assert_equal Thread.current.thread_variable_get(:foo), value
41
  end
42

  
43
  def test_thread_variable_in_enumerator
44
    Thread.new {
45
      Thread.current.thread_variable_set :foo, "bar"
46

  
47
      thread, value = Fiber.new {
48
        Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
49
      }.resume
50

  
51
      assert_equal Thread.current, thread
52
      assert_equal Thread.current.thread_variable_get(:foo), value
53
    }.join
54
  end
55

  
56
  def test_thread_variables
57
    assert_equal [], Thread.new { Thread.current.thread_variables }.join.value
58

  
59
    t = Thread.new {
60
      Thread.current.thread_variable_set(:foo, "bar")
61
      Thread.current.thread_variables
62
    }
63
    assert_equal [:foo], t.join.value
64
  end
65

  
66
  def test_thread_variable?
67
    refute Thread.new { Thread.current.thread_variable?("foo") }.join.value
68
    t = Thread.new {
69
      Thread.current.thread_variable_set("foo", "bar")
70
    }.join
71

  
72
    assert t.thread_variable?("foo")
73
    assert t.thread_variable?(:foo)
74
    refute t.thread_variable?(:bar)
75
  end
76

  
77
  def test_thread_variable_strings_and_symbols_are_the_same_key
78
    t = Thread.new {}.join
79
    t.thread_variable_set("foo", "bar")
80
    assert_equal "bar", t.thread_variable_get(:foo)
81
  end
82

  
83
  def test_thread_variable_frozen
84
    t = Thread.new { }.join
85
    t.freeze
86
    assert_raises(RuntimeError) do
87
      t.thread_variable_set(:foo, "bar")
88
    end
89
  end
90

  
91
  def test_thread_variable_security
92
    t = Thread.new { sleep }
93

  
94
    assert_raises(SecurityError) do
95
      Thread.new { $SAFE = 4; t.thread_variable_get(:foo) }.join
96
    end
97

  
98
    assert_raises(SecurityError) do
99
      Thread.new { $SAFE = 4; t.thread_variable_set(:foo, :baz) }.join
100
    end
101
  end
102

  
30 103
  def test_mutex_synchronize
31 104
    m = Mutex.new
32 105
    r = 0
thread.c
2525 2525
 *    #=> nil if fiber-local
2526 2526
 *    #=> 2 if thread-local (The value 2 is leaked to outside of meth method.)
2527 2527
 *
2528
 *  For thread-local variables, please see <code>Thread#thread_local_get</code>
2529
 *  and <code>Thread#thread_local_set</code>.
2530
 *
2528 2531
 */
2529 2532

  
2530 2533
static VALUE
......
2561 2564
 *      thr[sym] = obj   -> obj
2562 2565
 *
2563 2566
 *  Attribute Assignment---Sets or creates the value of a fiber-local variable,
2564
 *  using either a symbol or a string. See also <code>Thread#[]</code>.
2567
 *  using either a symbol or a string. See also <code>Thread#[]</code>.  For
2568
 *  thread-local variables, please see <code>Thread#thread_variable_set</code>
2569
 *  and <code>Thread#thread_variable_get</code>.
2565 2570
 */
2566 2571

  
2567 2572
static VALUE
......
2572 2577

  
2573 2578
/*
2574 2579
 *  call-seq:
2580
 *      thr.thread_variable_get(key)  -> obj or nil
2581
 *
2582
 *  Returns the value of a thread local variable that has been set.  Note that
2583
 *  these are different than fiber local values.  For fiber local values,
2584
 *  please see Thread#[] and Thread#[]=.
2585
 *
2586
 *  Thread local values are carried along with threads, and do not respect
2587
 *  fibers.  For example:
2588
 *
2589
 *    Thread.new {
2590
 *      Thread.current.thread_variable_set("foo", "bar") # set a thread local
2591
 *      Thread.current["foo"] = "bar"                    # set a fiber local
2592
 *
2593
 *      Fiber.new {
2594
 *        Fiber.yield [
2595
 *          Thread.current.thread_variable_get("foo"), # get the thread local
2596
 *          Thread.current["foo"],                     # get the fiber local
2597
 *        ]
2598
 *      }.resume
2599
 *    }.join.value # => ['bar', nil]
2600
 *
2601
 *  The value "bar" is returned for the thread local, where nil is returned
2602
 *  for the fiber local.  The fiber is executed in the same thread, so the
2603
 *  thread local values are available.
2604
 *
2605
 *  See also Thread#[]
2606
 */
2607

  
2608
static VALUE
2609
rb_thread_variable_get(VALUE thread, VALUE id)
2610
{
2611
    VALUE locals;
2612
    rb_thread_t *th;
2613

  
2614
    GetThreadPtr(thread, th);
2615

  
2616
    if (rb_safe_level() >= 4 && th != GET_THREAD()) {
2617
	rb_raise(rb_eSecurityError, "Insecure: can't modify thread locals");
2618
    }
2619

  
2620
    locals = rb_iv_get(thread, "locals");
2621
    return rb_hash_aref(locals, ID2SYM(rb_to_id(id)));
2622
}
2623

  
2624
/*
2625
 *  call-seq:
2626
 *      thr.thread_variable_set(key, value)
2627
 *
2628
 *  Sets a thread local with +key+ to +value+.  Note that these are local to
2629
 *  threads, and not to fibers.  Please see Thread#thread_variable_get and
2630
 *  Thread#[] for more information.
2631
 */
2632

  
2633
static VALUE
2634
rb_thread_variable_set(VALUE thread, VALUE id, VALUE val)
2635
{
2636
    VALUE locals;
2637
    rb_thread_t *th;
2638

  
2639
    GetThreadPtr(thread, th);
2640

  
2641
    if (rb_safe_level() >= 4 && th != GET_THREAD()) {
2642
	rb_raise(rb_eSecurityError, "Insecure: can't modify thread locals");
2643
    }
2644
    if (OBJ_FROZEN(thread)) {
2645
	rb_error_frozen("thread locals");
2646
    }
2647

  
2648
    locals = rb_iv_get(thread, "locals");
2649
    return rb_hash_aset(locals, ID2SYM(rb_to_id(id)), val);
2650
}
2651

  
2652
/*
2653
 *  call-seq:
2575 2654
 *     thr.key?(sym)   -> true or false
2576 2655
 *
2577 2656
 *  Returns <code>true</code> if the given string (or symbol) exists as a
......
2651 2730
    return ary;
2652 2731
}
2653 2732

  
2733
static int
2734
keys_i(VALUE key, VALUE value, VALUE ary)
2735
{
2736
    rb_ary_push(ary, key);
2737
    return ST_CONTINUE;
2738
}
2739

  
2740
/*
2741
 *  call-seq:
2742
 *     thr.thread_variables   -> array
2743
 *
2744
 *  Returns an an array of the names of the thread-local variables (as Symbols).
2745
 *
2746
 *     thr = Thread.new do
2747
 *       Thread.current.thread_variable_set(:cat, 'meow')
2748
 *       Thread.current.thread_variable_set("dog", 'woof')
2749
 *     end
2750
 *     thr.join               #=> #<Thread:0x401b3f10 dead>
2751
 *     thr.thread_variables   #=> [:dog, :cat]
2752
 *
2753
 *  Note that these are not fiber local variables.  Please see Thread#[] and
2754
 *  Thread#thread_variable_get for more details.
2755
 */
2756

  
2757
static VALUE
2758
rb_thread_variables(VALUE thread)
2759
{
2760
    VALUE locals;
2761
    VALUE ary;
2762

  
2763
    locals = rb_iv_get(thread, "locals");
2764
    ary = rb_ary_new();
2765
    rb_hash_foreach(locals, keys_i, ary);
2766

  
2767
    return ary;
2768
}
2769

  
2770
/*
2771
 *  call-seq:
2772
 *     thr.thread_variable?(key)   -> true or false
2773
 *
2774
 *  Returns <code>true</code> if the given string (or symbol) exists as a
2775
 *  thread-local variable.
2776
 *
2777
 *     me = Thread.current
2778
 *     me.thread_variable_set(:oliver, "a")
2779
 *     me.thread_variable?(:oliver)    #=> true
2780
 *     me.thread_variable?(:stanley)   #=> false
2781
 *
2782
 *  Note that these are not fiber local variables.  Please see Thread#[] and
2783
 *  Thread#thread_variable_get for more details.
2784
 */
2785

  
2786
static VALUE
2787
rb_thread_variable_p(VALUE thread, VALUE key)
2788
{
2789
    VALUE locals;
2790

  
2791
    locals = rb_iv_get(thread, "locals");
2792

  
2793
    if (!RHASH(locals)->ntbl)
2794
        return Qfalse;
2795

  
2796
    if (st_lookup(RHASH(locals)->ntbl, ID2SYM(rb_to_id(key)), 0)) {
2797
	return Qtrue;
2798
    }
2799

  
2800
    return Qfalse;
2801
}
2802

  
2654 2803
/*
2655 2804
 *  call-seq:
2656 2805
 *     thr.priority   -> integer
......
4541 4690
    rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
4542 4691
    rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
4543 4692
    rb_define_method(rb_cThread, "status", rb_thread_status, 0);
4693
    rb_define_method(rb_cThread, "thread_variable_get", rb_thread_variable_get, 1);
4694
    rb_define_method(rb_cThread, "thread_variable_set", rb_thread_variable_set, 2);
4695
    rb_define_method(rb_cThread, "thread_variables", rb_thread_variables, 0);
4696
    rb_define_method(rb_cThread, "thread_variable?", rb_thread_variable_p, 1);
4544 4697
    rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
4545 4698
    rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
4546 4699
    rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
vm.c
1838 1838
    GetThreadPtr(self, th);
1839 1839

  
1840 1840
    th_init(th, self);
1841
    rb_iv_set(self, "locals", rb_hash_new());
1841 1842
    th->vm = vm;
1842 1843

  
1843 1844
    th->top_wrapper = 0;
......
2185 2186

  
2186 2187
	/* create main thread */
2187 2188
	th_self = th->self = TypedData_Wrap_Struct(rb_cThread, &thread_data_type, th);
2189
	rb_iv_set(th_self, "locals", rb_hash_new());
2188 2190
	vm->main_thread = th;
2189 2191
	vm->running_thread = th;
2190 2192
	th->vm = vm;