thread_variables 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +38 -0
- data/Rakefile +14 -0
- data/lib/thread_variables.rb +31 -0
- data/lib/thread_variables/version.rb +3 -0
- data/test/colorized_test_output.rb +26 -0
- data/test/thread_variables_test.rb +130 -0
- data/thread_variables.gemspec +19 -0
- data/thread_variables.patch +319 -0
- metadata +77 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Stefan Kaes
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# ThreadVariables
|
2
|
+
|
3
|
+
This gem provides methods to access thread local variables with a ruby trunk compatible
|
4
|
+
API. It also provides an implementation for ruby 1.9, since ruby 1.9.3 made thread locals
|
5
|
+
fiber local.
|
6
|
+
|
7
|
+
See http://bugs.ruby-lang.org/issues/7097
|
8
|
+
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'thread_variables'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install thread_variables
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
require "thread_variables"
|
27
|
+
Thread.current.thread_variable_set :foo, 5
|
28
|
+
Thread.current.thread_variable_get :foo
|
29
|
+
Thread.current.thread_variable? :foo
|
30
|
+
Thread.current.thread_variables
|
31
|
+
|
32
|
+
## Contributing
|
33
|
+
|
34
|
+
1. Fork it
|
35
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
36
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
37
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
38
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "bundler/setup"
|
3
|
+
require "rake/testtask"
|
4
|
+
include Rake::DSL
|
5
|
+
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.test_files = FileList['test/**/*_test.rb']
|
9
|
+
t.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default do
|
13
|
+
Rake::Task[:test].invoke
|
14
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "thread_variables/version"
|
2
|
+
require "thread"
|
3
|
+
|
4
|
+
class Thread
|
5
|
+
if RUBY_VERSION < "1.9"
|
6
|
+
|
7
|
+
alias :thread_variable_get :[]
|
8
|
+
alias :thread_variable_set :[]=
|
9
|
+
alias :thread_variables :keys
|
10
|
+
alias :thread_variable? :key?
|
11
|
+
|
12
|
+
elsif !instance_methods.include?(:thread_variables)
|
13
|
+
|
14
|
+
def thread_variables
|
15
|
+
(locals = @locals) ? locals.keys : []
|
16
|
+
end
|
17
|
+
|
18
|
+
def thread_variable?(k)
|
19
|
+
(locals = @locals) && locals.include?(k.to_sym)
|
20
|
+
end
|
21
|
+
|
22
|
+
def thread_variable_get(k)
|
23
|
+
(locals = @locals) ? locals[k.to_sym] : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def thread_variable_set(k,v)
|
27
|
+
(@locals ||= {})[k.to_sym] = v
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# colorized output for Test::Unit / MiniTest
|
2
|
+
begin
|
3
|
+
if RUBY_VERSION < "1.9"
|
4
|
+
require 'redgreen'
|
5
|
+
else
|
6
|
+
require "test/unit/testcase"
|
7
|
+
class MiniTest::Unit::TestCase
|
8
|
+
alias :run_without_colors :run
|
9
|
+
require 'ansi/code'
|
10
|
+
ANSI_COLOR_MAPPING = Hash.new(:white).merge!('.' => :green, 'S' => :magenta, 'F' => :yellow, 'E' => :red )
|
11
|
+
def run(*args)
|
12
|
+
r = run_without_colors *args
|
13
|
+
ANSI.ansi(r, ANSI_COLOR_MAPPING[r])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
class MiniTest::Unit
|
17
|
+
def status(io = @@out)
|
18
|
+
format = "%d tests, %d assertions, %d failures, %d errors, %d skips"
|
19
|
+
color = (errors + failures) > 0 ? :red : :green
|
20
|
+
io.puts ANSI.ansi(format % [test_count, assertion_count, failures, errors, skips], color)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
rescue LoadError => e
|
25
|
+
# do nothing
|
26
|
+
end if $stdout.tty?
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'thread_variables'
|
2
|
+
require 'test/unit'
|
3
|
+
require File.expand_path('../colorized_test_output', __FILE__)
|
4
|
+
|
5
|
+
class ThreadVariablesTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def test_symbol_setter_and_getter
|
8
|
+
t = Thread.new { Thread.current.thread_variable_set :foo, 42 }
|
9
|
+
t.join
|
10
|
+
assert_equal 42, t.thread_variable_get(:foo)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_string_setter_and_getter
|
14
|
+
t = Thread.new { Thread.current.thread_variable_set "foo", 42 }
|
15
|
+
t.join
|
16
|
+
assert_equal 42, t.thread_variable_get("foo")
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_string_setter_and_smybol_getter
|
20
|
+
t = Thread.new { Thread.current.thread_variable_set "foo", 42 }
|
21
|
+
t.join
|
22
|
+
assert_equal 42, t.thread_variable_get(:foo)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_symbol_setter_and_string_getter
|
26
|
+
t = Thread.new { Thread.current.thread_variable_set :foo, 42 }
|
27
|
+
t.join
|
28
|
+
assert_equal 42, t.thread_variable_get("foo")
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_listing_keys
|
32
|
+
t = Thread.new do
|
33
|
+
Thread.current.thread_variable_set :foo, 42
|
34
|
+
Thread.current.thread_variable_set "bar", 815
|
35
|
+
end
|
36
|
+
t.join
|
37
|
+
vs = t.thread_variables
|
38
|
+
assert_equal 2, vs.size
|
39
|
+
assert vs.include?(:foo)
|
40
|
+
assert vs.include?(:bar)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_ckecking_keys
|
44
|
+
t = Thread.new do
|
45
|
+
Thread.current.thread_variable_set :foo, 42
|
46
|
+
Thread.current.thread_variable_set "bar", 815
|
47
|
+
end
|
48
|
+
t.join
|
49
|
+
assert t.thread_variable?(:foo)
|
50
|
+
assert t.thread_variable?(:bar)
|
51
|
+
end
|
52
|
+
|
53
|
+
# official tests
|
54
|
+
# see http://bugs.ruby-lang.org/attachments/3149/thread_variables.patch
|
55
|
+
|
56
|
+
def test_main_thread_variable_in_enumerator
|
57
|
+
assert_equal Thread.main, Thread.current
|
58
|
+
|
59
|
+
Thread.current.thread_variable_set :foo, "bar"
|
60
|
+
|
61
|
+
thread, value = Fiber.new {
|
62
|
+
Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
|
63
|
+
}.resume
|
64
|
+
|
65
|
+
assert_equal Thread.current, thread
|
66
|
+
assert_equal Thread.current.thread_variable_get(:foo), value
|
67
|
+
end if RUBY_VERSION >= "1.9"
|
68
|
+
|
69
|
+
def test_thread_variable_in_enumerator
|
70
|
+
Thread.new {
|
71
|
+
Thread.current.thread_variable_set :foo, "bar"
|
72
|
+
|
73
|
+
thread, value = Fiber.new {
|
74
|
+
Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
|
75
|
+
}.resume
|
76
|
+
|
77
|
+
assert_equal Thread.current, thread
|
78
|
+
assert_equal Thread.current.thread_variable_get(:foo), value
|
79
|
+
}.join
|
80
|
+
end if RUBY_VERSION >= "1.9"
|
81
|
+
|
82
|
+
def test_thread_variables
|
83
|
+
assert_equal [], Thread.new { Thread.current.thread_variables }.join.value
|
84
|
+
|
85
|
+
t = Thread.new {
|
86
|
+
Thread.current.thread_variable_set(:foo, "bar")
|
87
|
+
Thread.current.thread_variables
|
88
|
+
}
|
89
|
+
assert_equal [:foo], t.join.value
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_thread_variable?
|
93
|
+
assert !Thread.new { Thread.current.thread_variable?("foo") }.join.value
|
94
|
+
t = Thread.new {
|
95
|
+
Thread.current.thread_variable_set("foo", "bar")
|
96
|
+
}.join
|
97
|
+
|
98
|
+
assert t.thread_variable?("foo")
|
99
|
+
assert t.thread_variable?(:foo)
|
100
|
+
assert !t.thread_variable?(:bar)
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_thread_variable_strings_and_symbols_are_the_same_key
|
104
|
+
t = Thread.new {}.join
|
105
|
+
t.thread_variable_set("foo", "bar")
|
106
|
+
assert_equal "bar", t.thread_variable_get(:foo)
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_thread_variable_frozen
|
110
|
+
t = Thread.new { }.join
|
111
|
+
t.freeze
|
112
|
+
exception_class = RUBY_VERSION < "1.9" ? TypeError : RuntimeError
|
113
|
+
assert_raises(exception_class) do
|
114
|
+
t.thread_variable_set(:foo, "bar")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_thread_variable_security
|
119
|
+
t = Thread.new { sleep }
|
120
|
+
|
121
|
+
assert_raises(SecurityError) do
|
122
|
+
Thread.new { $SAFE = 4; t.thread_variable_get(:foo) }.join
|
123
|
+
end
|
124
|
+
|
125
|
+
assert_raises(SecurityError) do
|
126
|
+
Thread.new { $SAFE = 4; t.thread_variable_set(:foo, :baz) }.join
|
127
|
+
end
|
128
|
+
end if RUBY_VERSION > "1.9.3"
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'thread_variables/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "thread_variables"
|
8
|
+
gem.version = ThreadVariables::VERSION
|
9
|
+
gem.authors = ["Stefan Kaes"]
|
10
|
+
gem.email = ["stefan.kaes@xing.com"]
|
11
|
+
gem.description = %q{Provide thread local variables for ruby 1.9 and API for all ruby versions}
|
12
|
+
gem.summary = %q{Provide functionality compatible to latest trunk commit for all ruby version above 1.8}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
end
|
@@ -0,0 +1,319 @@
|
|
1
|
+
diff --git a/NEWS b/NEWS
|
2
|
+
index 0e4508e..b20cebb 100644
|
3
|
+
--- a/NEWS
|
4
|
+
+++ b/NEWS
|
5
|
+
@@ -81,6 +81,16 @@ with all sufficient information, see the ChangeLog file.
|
6
|
+
* added Struct#to_h returning values with keys corresponding to the
|
7
|
+
instance variable names.
|
8
|
+
|
9
|
+
+ * Thread
|
10
|
+
+ * added method:
|
11
|
+
+ * added Thread#thread_variable_get for getting thread local variables
|
12
|
+
+ (these are different than Fiber local variables).
|
13
|
+
+ * added Thread#thread_variable_set for setting thread local variables.
|
14
|
+
+ * added Thread#thread_variables for getting a list of the thread local
|
15
|
+
+ variable keys.
|
16
|
+
+ * added Thread#thread_variable? for testing to see if a particular thread
|
17
|
+
+ variable has been set.
|
18
|
+
+
|
19
|
+
* Time
|
20
|
+
* change return value:
|
21
|
+
* Time#to_s returned encoding defaults to US-ASCII but automatically
|
22
|
+
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
|
23
|
+
index c8081b4..145e021 100644
|
24
|
+
--- a/test/ruby/test_thread.rb
|
25
|
+
+++ b/test/ruby/test_thread.rb
|
26
|
+
@@ -27,6 +27,79 @@ class TestThread < Test::Unit::TestCase
|
27
|
+
end
|
28
|
+
end
|
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
|
+
+
|
103
|
+
def test_mutex_synchronize
|
104
|
+
m = Mutex.new
|
105
|
+
r = 0
|
106
|
+
diff --git a/thread.c b/thread.c
|
107
|
+
index b512566..b21a088 100644
|
108
|
+
--- a/thread.c
|
109
|
+
+++ b/thread.c
|
110
|
+
@@ -2525,6 +2525,9 @@ rb_thread_local_aref(VALUE thread, ID id)
|
111
|
+
* #=> nil if fiber-local
|
112
|
+
* #=> 2 if thread-local (The value 2 is leaked to outside of meth method.)
|
113
|
+
*
|
114
|
+
+ * For thread-local variables, please see <code>Thread#thread_local_get</code>
|
115
|
+
+ * and <code>Thread#thread_local_set</code>.
|
116
|
+
+ *
|
117
|
+
*/
|
118
|
+
|
119
|
+
static VALUE
|
120
|
+
@@ -2561,7 +2564,9 @@ rb_thread_local_aset(VALUE thread, ID id, VALUE val)
|
121
|
+
* thr[sym] = obj -> obj
|
122
|
+
*
|
123
|
+
* Attribute Assignment---Sets or creates the value of a fiber-local variable,
|
124
|
+
- * using either a symbol or a string. See also <code>Thread#[]</code>.
|
125
|
+
+ * using either a symbol or a string. See also <code>Thread#[]</code>. For
|
126
|
+
+ * thread-local variables, please see <code>Thread#thread_variable_set</code>
|
127
|
+
+ * and <code>Thread#thread_variable_get</code>.
|
128
|
+
*/
|
129
|
+
|
130
|
+
static VALUE
|
131
|
+
@@ -2572,6 +2577,80 @@ rb_thread_aset(VALUE self, VALUE id, VALUE val)
|
132
|
+
|
133
|
+
/*
|
134
|
+
* call-seq:
|
135
|
+
+ * thr.thread_variable_get(key) -> obj or nil
|
136
|
+
+ *
|
137
|
+
+ * Returns the value of a thread local variable that has been set. Note that
|
138
|
+
+ * these are different than fiber local values. For fiber local values,
|
139
|
+
+ * please see Thread#[] and Thread#[]=.
|
140
|
+
+ *
|
141
|
+
+ * Thread local values are carried along with threads, and do not respect
|
142
|
+
+ * fibers. For example:
|
143
|
+
+ *
|
144
|
+
+ * Thread.new {
|
145
|
+
+ * Thread.current.thread_variable_set("foo", "bar") # set a thread local
|
146
|
+
+ * Thread.current["foo"] = "bar" # set a fiber local
|
147
|
+
+ *
|
148
|
+
+ * Fiber.new {
|
149
|
+
+ * Fiber.yield [
|
150
|
+
+ * Thread.current.thread_variable_get("foo"), # get the thread local
|
151
|
+
+ * Thread.current["foo"], # get the fiber local
|
152
|
+
+ * ]
|
153
|
+
+ * }.resume
|
154
|
+
+ * }.join.value # => ['bar', nil]
|
155
|
+
+ *
|
156
|
+
+ * The value "bar" is returned for the thread local, where nil is returned
|
157
|
+
+ * for the fiber local. The fiber is executed in the same thread, so the
|
158
|
+
+ * thread local values are available.
|
159
|
+
+ *
|
160
|
+
+ * See also Thread#[]
|
161
|
+
+ */
|
162
|
+
+
|
163
|
+
+static VALUE
|
164
|
+
+rb_thread_variable_get(VALUE thread, VALUE id)
|
165
|
+
+{
|
166
|
+
+ VALUE locals;
|
167
|
+
+ rb_thread_t *th;
|
168
|
+
+
|
169
|
+
+ GetThreadPtr(thread, th);
|
170
|
+
+
|
171
|
+
+ if (rb_safe_level() >= 4 && th != GET_THREAD()) {
|
172
|
+
+ rb_raise(rb_eSecurityError, "Insecure: can't modify thread locals");
|
173
|
+
+ }
|
174
|
+
+
|
175
|
+
+ locals = rb_iv_get(thread, "locals");
|
176
|
+
+ return rb_hash_aref(locals, ID2SYM(rb_to_id(id)));
|
177
|
+
+}
|
178
|
+
+
|
179
|
+
+/*
|
180
|
+
+ * call-seq:
|
181
|
+
+ * thr.thread_variable_set(key, value)
|
182
|
+
+ *
|
183
|
+
+ * Sets a thread local with +key+ to +value+. Note that these are local to
|
184
|
+
+ * threads, and not to fibers. Please see Thread#thread_variable_get and
|
185
|
+
+ * Thread#[] for more information.
|
186
|
+
+ */
|
187
|
+
+
|
188
|
+
+static VALUE
|
189
|
+
+rb_thread_variable_set(VALUE thread, VALUE id, VALUE val)
|
190
|
+
+{
|
191
|
+
+ VALUE locals;
|
192
|
+
+ rb_thread_t *th;
|
193
|
+
+
|
194
|
+
+ GetThreadPtr(thread, th);
|
195
|
+
+
|
196
|
+
+ if (rb_safe_level() >= 4 && th != GET_THREAD()) {
|
197
|
+
+ rb_raise(rb_eSecurityError, "Insecure: can't modify thread locals");
|
198
|
+
+ }
|
199
|
+
+ if (OBJ_FROZEN(thread)) {
|
200
|
+
+ rb_error_frozen("thread locals");
|
201
|
+
+ }
|
202
|
+
+
|
203
|
+
+ locals = rb_iv_get(thread, "locals");
|
204
|
+
+ return rb_hash_aset(locals, ID2SYM(rb_to_id(id)), val);
|
205
|
+
+}
|
206
|
+
+
|
207
|
+
+/*
|
208
|
+
+ * call-seq:
|
209
|
+
* thr.key?(sym) -> true or false
|
210
|
+
*
|
211
|
+
* Returns <code>true</code> if the given string (or symbol) exists as a
|
212
|
+
@@ -2651,6 +2730,76 @@ rb_thread_keys(VALUE self)
|
213
|
+
return ary;
|
214
|
+
}
|
215
|
+
|
216
|
+
+static int
|
217
|
+
+keys_i(VALUE key, VALUE value, VALUE ary)
|
218
|
+
+{
|
219
|
+
+ rb_ary_push(ary, key);
|
220
|
+
+ return ST_CONTINUE;
|
221
|
+
+}
|
222
|
+
+
|
223
|
+
+/*
|
224
|
+
+ * call-seq:
|
225
|
+
+ * thr.thread_variables -> array
|
226
|
+
+ *
|
227
|
+
+ * Returns an an array of the names of the thread-local variables (as Symbols).
|
228
|
+
+ *
|
229
|
+
+ * thr = Thread.new do
|
230
|
+
+ * Thread.current.thread_variable_set(:cat, 'meow')
|
231
|
+
+ * Thread.current.thread_variable_set("dog", 'woof')
|
232
|
+
+ * end
|
233
|
+
+ * thr.join #=> #<Thread:0x401b3f10 dead>
|
234
|
+
+ * thr.thread_variables #=> [:dog, :cat]
|
235
|
+
+ *
|
236
|
+
+ * Note that these are not fiber local variables. Please see Thread#[] and
|
237
|
+
+ * Thread#thread_variable_get for more details.
|
238
|
+
+ */
|
239
|
+
+
|
240
|
+
+static VALUE
|
241
|
+
+rb_thread_variables(VALUE thread)
|
242
|
+
+{
|
243
|
+
+ VALUE locals;
|
244
|
+
+ VALUE ary;
|
245
|
+
+
|
246
|
+
+ locals = rb_iv_get(thread, "locals");
|
247
|
+
+ ary = rb_ary_new();
|
248
|
+
+ rb_hash_foreach(locals, keys_i, ary);
|
249
|
+
+
|
250
|
+
+ return ary;
|
251
|
+
+}
|
252
|
+
+
|
253
|
+
+/*
|
254
|
+
+ * call-seq:
|
255
|
+
+ * thr.thread_variable?(key) -> true or false
|
256
|
+
+ *
|
257
|
+
+ * Returns <code>true</code> if the given string (or symbol) exists as a
|
258
|
+
+ * thread-local variable.
|
259
|
+
+ *
|
260
|
+
+ * me = Thread.current
|
261
|
+
+ * me.thread_variable_set(:oliver, "a")
|
262
|
+
+ * me.thread_variable?(:oliver) #=> true
|
263
|
+
+ * me.thread_variable?(:stanley) #=> false
|
264
|
+
+ *
|
265
|
+
+ * Note that these are not fiber local variables. Please see Thread#[] and
|
266
|
+
+ * Thread#thread_variable_get for more details.
|
267
|
+
+ */
|
268
|
+
+
|
269
|
+
+static VALUE
|
270
|
+
+rb_thread_variable_p(VALUE thread, VALUE key)
|
271
|
+
+{
|
272
|
+
+ VALUE locals;
|
273
|
+
+
|
274
|
+
+ locals = rb_iv_get(thread, "locals");
|
275
|
+
+
|
276
|
+
+ if (!RHASH(locals)->ntbl)
|
277
|
+
+ return Qfalse;
|
278
|
+
+
|
279
|
+
+ if (st_lookup(RHASH(locals)->ntbl, ID2SYM(rb_to_id(key)), 0)) {
|
280
|
+
+ return Qtrue;
|
281
|
+
+ }
|
282
|
+
+
|
283
|
+
+ return Qfalse;
|
284
|
+
+}
|
285
|
+
+
|
286
|
+
/*
|
287
|
+
* call-seq:
|
288
|
+
* thr.priority -> integer
|
289
|
+
@@ -4541,6 +4690,10 @@ Init_Thread(void)
|
290
|
+
rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
|
291
|
+
rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
|
292
|
+
rb_define_method(rb_cThread, "status", rb_thread_status, 0);
|
293
|
+
+ rb_define_method(rb_cThread, "thread_variable_get", rb_thread_variable_get, 1);
|
294
|
+
+ rb_define_method(rb_cThread, "thread_variable_set", rb_thread_variable_set, 2);
|
295
|
+
+ rb_define_method(rb_cThread, "thread_variables", rb_thread_variables, 0);
|
296
|
+
+ rb_define_method(rb_cThread, "thread_variable?", rb_thread_variable_p, 1);
|
297
|
+
rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
|
298
|
+
rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
|
299
|
+
rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
|
300
|
+
diff --git a/vm.c b/vm.c
|
301
|
+
index c47b592..5269a50 100644
|
302
|
+
--- a/vm.c
|
303
|
+
+++ b/vm.c
|
304
|
+
@@ -1838,6 +1838,7 @@ ruby_thread_init(VALUE self)
|
305
|
+
GetThreadPtr(self, th);
|
306
|
+
|
307
|
+
th_init(th, self);
|
308
|
+
+ rb_iv_set(self, "locals", rb_hash_new());
|
309
|
+
th->vm = vm;
|
310
|
+
|
311
|
+
th->top_wrapper = 0;
|
312
|
+
@@ -2185,6 +2186,7 @@ Init_VM(void)
|
313
|
+
|
314
|
+
/* create main thread */
|
315
|
+
th_self = th->self = TypedData_Wrap_Struct(rb_cThread, &thread_data_type, th);
|
316
|
+
+ rb_iv_set(th_self, "locals", rb_hash_new());
|
317
|
+
vm->main_thread = th;
|
318
|
+
vm->running_thread = th;
|
319
|
+
th->vm = vm;
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: thread_variables
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Stefan Kaes
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-11-02 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Provide thread local variables for ruby 1.9 and API for all ruby versions
|
22
|
+
email:
|
23
|
+
- stefan.kaes@xing.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- .gitignore
|
32
|
+
- Gemfile
|
33
|
+
- LICENSE.txt
|
34
|
+
- README.md
|
35
|
+
- Rakefile
|
36
|
+
- lib/thread_variables.rb
|
37
|
+
- lib/thread_variables/version.rb
|
38
|
+
- test/colorized_test_output.rb
|
39
|
+
- test/thread_variables_test.rb
|
40
|
+
- thread_variables.gemspec
|
41
|
+
- thread_variables.patch
|
42
|
+
homepage: ""
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
hash: 3
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project:
|
71
|
+
rubygems_version: 1.8.24
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: Provide functionality compatible to latest trunk commit for all ruby version above 1.8
|
75
|
+
test_files:
|
76
|
+
- test/colorized_test_output.rb
|
77
|
+
- test/thread_variables_test.rb
|