thread_variables.patch

Aaron Patterson, 10/26/2012 07:19 AM

Download (8.62 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
2572 2572

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

  
2603
static VALUE
2604
rb_thread_variable_get(VALUE thread, VALUE id)
2605
{
2606
    VALUE locals;
2607
    rb_thread_t *th;
2608

  
2609
    GetThreadPtr(thread, th);
2610

  
2611
    if (rb_safe_level() >= 4 && th != GET_THREAD()) {
2612
	rb_raise(rb_eSecurityError, "Insecure: can't modify thread locals");
2613
    }
2614

  
2615
    locals = rb_iv_get(thread, "locals");
2616
    return rb_hash_aref(locals, ID2SYM(rb_to_id(id)));
2617
}
2618

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

  
2628
static VALUE
2629
rb_thread_variable_set(VALUE thread, VALUE id, VALUE val)
2630
{
2631
    VALUE locals;
2632
    rb_thread_t *th;
2633

  
2634
    GetThreadPtr(thread, th);
2635

  
2636
    if (rb_safe_level() >= 4 && th != GET_THREAD()) {
2637
	rb_raise(rb_eSecurityError, "Insecure: can't modify thread locals");
2638
    }
2639
    if (OBJ_FROZEN(thread)) {
2640
	rb_error_frozen("thread locals");
2641
    }
2642

  
2643
    locals = rb_iv_get(thread, "locals");
2644
    return rb_hash_aset(locals, ID2SYM(rb_to_id(id)), val);
2645
}
2646

  
2647
/*
2648
 *  call-seq:
2575 2649
 *     thr.key?(sym)   -> true or false
2576 2650
 *
2577 2651
 *  Returns <code>true</code> if the given string (or symbol) exists as a
......
2651 2725
    return ary;
2652 2726
}
2653 2727

  
2728
static int
2729
keys_i(VALUE key, VALUE value, VALUE ary)
2730
{
2731
    rb_ary_push(ary, key);
2732
    return ST_CONTINUE;
2733
}
2734

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

  
2752
static VALUE
2753
rb_thread_variables(VALUE thread)
2754
{
2755
    VALUE locals;
2756
    VALUE ary;
2757

  
2758
    locals = rb_iv_get(thread, "locals");
2759
    ary = rb_ary_new();
2760
    rb_hash_foreach(locals, keys_i, ary);
2761

  
2762
    return ary;
2763
}
2764

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

  
2781
static VALUE
2782
rb_thread_variable_p(VALUE thread, VALUE key)
2783
{
2784
    VALUE locals;
2785

  
2786
    locals = rb_iv_get(thread, "locals");
2787

  
2788
    if (!RHASH(locals)->ntbl)
2789
        return Qfalse;
2790

  
2791
    if (st_lookup(RHASH(locals)->ntbl, ID2SYM(rb_to_id(key)), 0)) {
2792
	return Qtrue;
2793
    }
2794

  
2795
    return Qfalse;
2796
}
2797

  
2654 2798
/*
2655 2799
 *  call-seq:
2656 2800
 *     thr.priority   -> integer
......
4541 4685
    rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
4542 4686
    rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
4543 4687
    rb_define_method(rb_cThread, "status", rb_thread_status, 0);
4688
    rb_define_method(rb_cThread, "thread_variable_get", rb_thread_variable_get, 1);
4689
    rb_define_method(rb_cThread, "thread_variable_set", rb_thread_variable_set, 2);
4690
    rb_define_method(rb_cThread, "thread_variables", rb_thread_variables, 0);
4691
    rb_define_method(rb_cThread, "thread_variable?", rb_thread_variable_p, 1);
4544 4692
    rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
4545 4693
    rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
4546 4694
    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;