thread_safe 0.3.1-java → 0.3.2-java

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bd78f6716a19a528d4f244f47944740468398978
4
- data.tar.gz: 92d44fadb74cb932282e682967b11c532ad7a678
3
+ metadata.gz: 011bc3a02316bba0bb69f2aeaaf95d72d14c25a5
4
+ data.tar.gz: 27e578528177719a9eb1ac72727c3f84c053c484
5
5
  SHA512:
6
- metadata.gz: 50cd42d6dfaa8f703d9fe3034150b3012317d1af47690c88a1c906196760ccd3eafd4afa1f925c946a8842f026cb7ccec00056fdb3bcaff3ed00657fa9db8508
7
- data.tar.gz: dddfe11774caf6f080292540c11cf795d9e8867d6917da1e2ff9d907fbb9e2eafa3b47ca6e9dcf5e07f2498c53bf238c6771df9e79540f310ae6ac3ef30bbd19
6
+ metadata.gz: 1e821f7904a0bac53fc7d0a5854ee442daffc75aebed26e216f4f58f3c21a87e6b71bac97fbea8ecf074725ef8c3b0dc3eda741999580539e570afa3857a0c30
7
+ data.tar.gz: 4b49dc4c12fd055deefe80e21ee38567dbdfb74dd36b51e85bdf80c252465cadbab8aec826dcea71b23c1bf7f120ba45d6bf8243015e573272893be450fd93ab
@@ -16,6 +16,14 @@ require 'monitor'
16
16
  # This class is currently being considered for inclusion into stdlib, via
17
17
  # https://bugs.ruby-lang.org/issues/8556
18
18
  class SynchronizedDelegator < SimpleDelegator
19
+ def setup
20
+ @old_abort = Thread.abort_on_exception
21
+ Thread.abort_on_exception = true
22
+ end
23
+
24
+ def teardown
25
+ Thread.abort_on_exception = @old_abort
26
+ end
19
27
 
20
28
  def initialize(obj)
21
29
  __setobj__(obj)
@@ -1,12 +1,45 @@
1
1
  module ThreadSafe
2
2
  module Util
3
- # An overhead-less atomic reference.
4
3
  AtomicReference =
5
4
  if defined?(Rubinius::AtomicReference)
5
+ # An overhead-less atomic reference.
6
6
  Rubinius::AtomicReference
7
7
  else
8
- require 'atomic'
9
- defined?(Atomic::InternalReference) ? Atomic::InternalReference : Atomic
8
+ begin
9
+ require 'atomic'
10
+ defined?(Atomic::InternalReference) ? Atomic::InternalReference : Atomic
11
+ rescue NameError
12
+ require 'thread' # get Mutex on 1.8
13
+ class FullLockingAtomicReference
14
+ def initialize(value = nil)
15
+ @___mutex = Mutex.new
16
+ @___value = value
17
+ end
18
+
19
+ def get
20
+ @___mutex.synchronize { @___value }
21
+ end
22
+ alias_method :value, :get
23
+
24
+ def set(new_value)
25
+ @___mutex.synchronize { @___value = new_value }
26
+ end
27
+ alias_method :value=, :set
28
+
29
+ def compare_and_set(old_value, new_value)
30
+ return false unless @___mutex.try_lock
31
+ begin
32
+ return false unless @___value.equal? old_value
33
+ @___value = new_value
34
+ ensure
35
+ @___mutex.unlock
36
+ end
37
+ true
38
+ end
39
+ end
40
+
41
+ FullLockingAtomicReference
42
+ end
10
43
  end
11
44
  end
12
45
  end
@@ -1,5 +1,5 @@
1
1
  module ThreadSafe
2
- VERSION = "0.3.1"
2
+ VERSION = "0.3.2"
3
3
  end
4
4
 
5
5
  # NOTE: <= 0.2.0 used Threadsafe::VERSION
@@ -5,450 +5,448 @@ require File.join(File.dirname(__FILE__), "test_helper")
5
5
 
6
6
  Thread.abort_on_exception = true
7
7
 
8
- unless RUBY_VERSION =~ /1\.8/ || ENV['TRAVIS']
9
- class TestCacheTorture < Test::Unit::TestCase
10
- THREAD_COUNT = 40
11
- KEY_COUNT = (((2**13) - 2) * 0.75).to_i # get close to the doubling cliff
12
- LOW_KEY_COUNT = (((2**8 ) - 2) * 0.75).to_i # get close to the doubling cliff
13
-
14
- INITIAL_VALUE_CACHE_SETUP = lambda do |options, keys|
15
- cache = ThreadSafe::Cache.new
16
- initial_value = options[:initial_value] || 0
17
- keys.each {|key| cache[key] = initial_value}
18
- cache
19
- end
20
- ZERO_VALUE_CACHE_SETUP = lambda do |options, keys|
21
- INITIAL_VALUE_CACHE_SETUP.call(options.merge(:initial_value => 0), keys)
22
- end
8
+ class TestCacheTorture < Test::Unit::TestCase # this is not run unless RUBY_VERSION =~ /1\.8/ || ENV['TRAVIS'] (see the end of the file)
9
+ THREAD_COUNT = 40
10
+ KEY_COUNT = (((2**13) - 2) * 0.75).to_i # get close to the doubling cliff
11
+ LOW_KEY_COUNT = (((2**8 ) - 2) * 0.75).to_i # get close to the doubling cliff
12
+
13
+ INITIAL_VALUE_CACHE_SETUP = lambda do |options, keys|
14
+ cache = ThreadSafe::Cache.new
15
+ initial_value = options[:initial_value] || 0
16
+ keys.each {|key| cache[key] = initial_value}
17
+ cache
18
+ end
19
+ ZERO_VALUE_CACHE_SETUP = lambda do |options, keys|
20
+ INITIAL_VALUE_CACHE_SETUP.call(options.merge(:initial_value => 0), keys)
21
+ end
23
22
 
24
- DEFAULTS = {
25
- :key_count => KEY_COUNT,
26
- :thread_count => THREAD_COUNT,
27
- :loop_count => 1,
28
- :prelude => '',
29
- :cache_setup => lambda {|options, keys| ThreadSafe::Cache.new}
30
- }
31
-
32
- LOW_KEY_COUNT_OPTIONS = {:loop_count => 150, :key_count => LOW_KEY_COUNT}
33
- SINGLE_KEY_COUNT_OPTIONS = {:loop_count => 100_000, :key_count => 1}
34
-
35
- def test_concurrency
36
- code = <<-RUBY_EVAL
37
- cache[key]
38
- cache[key] = key
39
- cache[key]
40
- cache.delete(key)
41
- RUBY_EVAL
42
- do_thread_loop(:concurrency, code)
43
- end
23
+ DEFAULTS = {
24
+ :key_count => KEY_COUNT,
25
+ :thread_count => THREAD_COUNT,
26
+ :loop_count => 1,
27
+ :prelude => '',
28
+ :cache_setup => lambda {|options, keys| ThreadSafe::Cache.new}
29
+ }
30
+
31
+ LOW_KEY_COUNT_OPTIONS = {:loop_count => 150, :key_count => LOW_KEY_COUNT}
32
+ SINGLE_KEY_COUNT_OPTIONS = {:loop_count => 100_000, :key_count => 1}
33
+
34
+ def test_concurrency
35
+ code = <<-RUBY_EVAL
36
+ cache[key]
37
+ cache[key] = key
38
+ cache[key]
39
+ cache.delete(key)
40
+ RUBY_EVAL
41
+ do_thread_loop(__method__, code)
42
+ end
44
43
 
45
- def test_put_if_absent
46
- do_thread_loop(:put_if_absent, 'acc += 1 unless cache.put_if_absent(key, key)', :key_count => 100_000) do |result, cache, options, keys|
47
- assert_standard_accumulator_test_result(result, cache, options, keys)
48
- end
44
+ def test_put_if_absent
45
+ do_thread_loop(__method__, 'acc += 1 unless cache.put_if_absent(key, key)', :key_count => 100_000) do |result, cache, options, keys|
46
+ assert_standard_accumulator_test_result(result, cache, options, keys)
49
47
  end
48
+ end
50
49
 
51
- def test_compute_if_absent
52
- code = 'cache.compute_if_absent(key) { acc += 1; key }'
53
- do_thread_loop(:compute_if_absent, code) do |result, cache, options, keys|
54
- assert_standard_accumulator_test_result(result, cache, options, keys)
55
- end
50
+ def test_compute_if_absent
51
+ code = 'cache.compute_if_absent(key) { acc += 1; key }'
52
+ do_thread_loop(__method__, code) do |result, cache, options, keys|
53
+ assert_standard_accumulator_test_result(result, cache, options, keys)
56
54
  end
55
+ end
57
56
 
58
- def test_compute_put_if_absent
59
- code = <<-RUBY_EVAL
60
- if key.even?
61
- cache.compute_if_absent(key) { acc += 1; key }
62
- else
63
- acc += 1 unless cache.put_if_absent(key, key)
64
- end
65
- RUBY_EVAL
66
- do_thread_loop(:compute_put_if_absent, code) do |result, cache, options, keys|
67
- assert_standard_accumulator_test_result(result, cache, options, keys)
57
+ def test_compute_put_if_absent
58
+ code = <<-RUBY_EVAL
59
+ if key.even?
60
+ cache.compute_if_absent(key) { acc += 1; key }
61
+ else
62
+ acc += 1 unless cache.put_if_absent(key, key)
68
63
  end
64
+ RUBY_EVAL
65
+ do_thread_loop(__method__, code) do |result, cache, options, keys|
66
+ assert_standard_accumulator_test_result(result, cache, options, keys)
69
67
  end
68
+ end
70
69
 
71
- def test_compute_if_absent_and_present
72
- compute_if_absent_and_present
73
- compute_if_absent_and_present(LOW_KEY_COUNT_OPTIONS)
74
- compute_if_absent_and_present(SINGLE_KEY_COUNT_OPTIONS)
75
- end
70
+ def test_compute_if_absent_and_present
71
+ compute_if_absent_and_present
72
+ compute_if_absent_and_present(LOW_KEY_COUNT_OPTIONS)
73
+ compute_if_absent_and_present(SINGLE_KEY_COUNT_OPTIONS)
74
+ end
76
75
 
77
- def test_add_remove_to_zero
78
- add_remove_to_zero
79
- add_remove_to_zero(LOW_KEY_COUNT_OPTIONS)
80
- add_remove_to_zero(SINGLE_KEY_COUNT_OPTIONS)
81
- end
76
+ def test_add_remove_to_zero
77
+ add_remove_to_zero
78
+ add_remove_to_zero(LOW_KEY_COUNT_OPTIONS)
79
+ add_remove_to_zero(SINGLE_KEY_COUNT_OPTIONS)
80
+ end
82
81
 
83
- def test_add_remove_to_zero_via_merge_pair
84
- add_remove_to_zero_via_merge_pair
85
- add_remove_to_zero_via_merge_pair(LOW_KEY_COUNT_OPTIONS)
86
- add_remove_to_zero_via_merge_pair(SINGLE_KEY_COUNT_OPTIONS)
87
- end
82
+ def test_add_remove_to_zero_via_merge_pair
83
+ add_remove_to_zero_via_merge_pair
84
+ add_remove_to_zero_via_merge_pair(LOW_KEY_COUNT_OPTIONS)
85
+ add_remove_to_zero_via_merge_pair(SINGLE_KEY_COUNT_OPTIONS)
86
+ end
88
87
 
89
- def test_add_remove
90
- add_remove
91
- add_remove(LOW_KEY_COUNT_OPTIONS)
92
- add_remove(SINGLE_KEY_COUNT_OPTIONS)
93
- end
88
+ def test_add_remove
89
+ add_remove
90
+ add_remove(LOW_KEY_COUNT_OPTIONS)
91
+ add_remove(SINGLE_KEY_COUNT_OPTIONS)
92
+ end
94
93
 
95
- def test_add_remove_via_compute
96
- add_remove_via_compute
97
- add_remove_via_compute(LOW_KEY_COUNT_OPTIONS)
98
- add_remove_via_compute(SINGLE_KEY_COUNT_OPTIONS)
99
- end
94
+ def test_add_remove_via_compute
95
+ add_remove_via_compute
96
+ add_remove_via_compute(LOW_KEY_COUNT_OPTIONS)
97
+ add_remove_via_compute(SINGLE_KEY_COUNT_OPTIONS)
98
+ end
100
99
 
101
- def add_remove_via_compute_if_absent_present
102
- add_remove_via_compute_if_absent_present
103
- add_remove_via_compute_if_absent_present(LOW_KEY_COUNT_OPTIONS)
104
- add_remove_via_compute_if_absent_present(SINGLE_KEY_COUNT_OPTIONS)
105
- end
100
+ def add_remove_via_compute_if_absent_present
101
+ add_remove_via_compute_if_absent_present
102
+ add_remove_via_compute_if_absent_present(LOW_KEY_COUNT_OPTIONS)
103
+ add_remove_via_compute_if_absent_present(SINGLE_KEY_COUNT_OPTIONS)
104
+ end
106
105
 
107
- def test_add_remove_indiscriminate
108
- add_remove_indiscriminate
109
- add_remove_indiscriminate(LOW_KEY_COUNT_OPTIONS)
110
- add_remove_indiscriminate(SINGLE_KEY_COUNT_OPTIONS)
111
- end
106
+ def test_add_remove_indiscriminate
107
+ add_remove_indiscriminate
108
+ add_remove_indiscriminate(LOW_KEY_COUNT_OPTIONS)
109
+ add_remove_indiscriminate(SINGLE_KEY_COUNT_OPTIONS)
110
+ end
112
111
 
113
- def test_count_up
114
- count_up
115
- count_up(LOW_KEY_COUNT_OPTIONS)
116
- count_up(SINGLE_KEY_COUNT_OPTIONS)
117
- end
112
+ def test_count_up
113
+ count_up
114
+ count_up(LOW_KEY_COUNT_OPTIONS)
115
+ count_up(SINGLE_KEY_COUNT_OPTIONS)
116
+ end
118
117
 
119
- def test_count_up_via_compute
120
- count_up_via_compute
121
- count_up_via_compute(LOW_KEY_COUNT_OPTIONS)
122
- count_up_via_compute(SINGLE_KEY_COUNT_OPTIONS)
123
- end
118
+ def test_count_up_via_compute
119
+ count_up_via_compute
120
+ count_up_via_compute(LOW_KEY_COUNT_OPTIONS)
121
+ count_up_via_compute(SINGLE_KEY_COUNT_OPTIONS)
122
+ end
124
123
 
125
- def test_count_up_via_merge_pair
126
- count_up_via_merge_pair
127
- count_up_via_merge_pair(LOW_KEY_COUNT_OPTIONS)
128
- count_up_via_merge_pair(SINGLE_KEY_COUNT_OPTIONS)
129
- end
124
+ def test_count_up_via_merge_pair
125
+ count_up_via_merge_pair
126
+ count_up_via_merge_pair(LOW_KEY_COUNT_OPTIONS)
127
+ count_up_via_merge_pair(SINGLE_KEY_COUNT_OPTIONS)
128
+ end
130
129
 
131
- def test_count_race
132
- prelude = 'change = (rand(2) == 1) ? 1 : -1'
133
- code = <<-RUBY_EVAL
134
- v = cache[key]
135
- acc += change if cache.replace_pair(key, v, v + change)
136
- RUBY_EVAL
137
- do_thread_loop(:count_race, code, :loop_count => 5, :prelude => prelude, :cache_setup => ZERO_VALUE_CACHE_SETUP) do |result, cache, options, keys|
138
- result_sum = sum(result)
139
- assert_equal(sum(keys.map {|key| cache[key]}), result_sum)
140
- assert_equal(sum(cache.values), result_sum)
141
- assert_equal(options[:key_count], cache.size)
142
- end
130
+ def test_count_race
131
+ prelude = 'change = (rand(2) == 1) ? 1 : -1'
132
+ code = <<-RUBY_EVAL
133
+ v = cache[key]
134
+ acc += change if cache.replace_pair(key, v, v + change)
135
+ RUBY_EVAL
136
+ do_thread_loop(__method__, code, :loop_count => 5, :prelude => prelude, :cache_setup => ZERO_VALUE_CACHE_SETUP) do |result, cache, options, keys|
137
+ result_sum = sum(result)
138
+ assert_equal(sum(keys.map {|key| cache[key]}), result_sum)
139
+ assert_equal(sum(cache.values), result_sum)
140
+ assert_equal(options[:key_count], cache.size)
143
141
  end
142
+ end
144
143
 
145
- def test_get_and_set_new
146
- code = 'acc += 1 unless cache.get_and_set(key, key)'
147
- do_thread_loop(:get_and_set_new, code) do |result, cache, options, keys|
148
- assert_standard_accumulator_test_result(result, cache, options, keys)
149
- end
144
+ def test_get_and_set_new
145
+ code = 'acc += 1 unless cache.get_and_set(key, key)'
146
+ do_thread_loop(__method__, code) do |result, cache, options, keys|
147
+ assert_standard_accumulator_test_result(result, cache, options, keys)
150
148
  end
149
+ end
151
150
 
152
- def test_get_and_set_existing
153
- code = 'acc += 1 if cache.get_and_set(key, key) == -1'
154
- do_thread_loop(:get_and_set_existing, code, :cache_setup => INITIAL_VALUE_CACHE_SETUP, :initial_value => -1) do |result, cache, options, keys|
155
- assert_standard_accumulator_test_result(result, cache, options, keys)
156
- end
151
+ def test_get_and_set_existing
152
+ code = 'acc += 1 if cache.get_and_set(key, key) == -1'
153
+ do_thread_loop(__method__, code, :cache_setup => INITIAL_VALUE_CACHE_SETUP, :initial_value => -1) do |result, cache, options, keys|
154
+ assert_standard_accumulator_test_result(result, cache, options, keys)
157
155
  end
156
+ end
158
157
 
159
- private
160
- def compute_if_absent_and_present(opts = {})
161
- prelude = 'on_present = rand(2) == 1'
162
- code = <<-RUBY_EVAL
163
- if on_present
164
- cache.compute_if_present(key) {|old_value| acc += 1; old_value + 1}
165
- else
166
- cache.compute_if_absent(key) { acc += 1; 1 }
167
- end
168
- RUBY_EVAL
169
- do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
170
- stored_sum = 0
171
- stored_key_count = 0
172
- keys.each do |k|
173
- if value = cache[k]
174
- stored_sum += value
175
- stored_key_count += 1
176
- end
177
- end
178
- assert_equal(stored_sum, sum(result))
179
- assert_equal(stored_key_count, cache.size)
158
+ private
159
+ def compute_if_absent_and_present(opts = {})
160
+ prelude = 'on_present = rand(2) == 1'
161
+ code = <<-RUBY_EVAL
162
+ if on_present
163
+ cache.compute_if_present(key) {|old_value| acc += 1; old_value + 1}
164
+ else
165
+ cache.compute_if_absent(key) { acc += 1; 1 }
180
166
  end
181
- end
182
-
183
- def add_remove(opts = {})
184
- prelude = 'do_add = rand(2) == 1'
185
- code = <<-RUBY_EVAL
186
- if do_add
187
- acc += 1 unless cache.put_if_absent(key, key)
188
- else
189
- acc -= 1 if cache.delete_pair(key, key)
190
- end
191
- RUBY_EVAL
192
- do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
193
- assert_all_key_mappings_exist(cache, keys, false)
194
- assert_equal(cache.size, sum(result))
167
+ RUBY_EVAL
168
+ do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
169
+ stored_sum = 0
170
+ stored_key_count = 0
171
+ keys.each do |k|
172
+ if value = cache[k]
173
+ stored_sum += value
174
+ stored_key_count += 1
175
+ end
195
176
  end
177
+ assert_equal(stored_sum, sum(result))
178
+ assert_equal(stored_key_count, cache.size)
196
179
  end
180
+ end
197
181
 
198
- def add_remove_via_compute(opts = {})
199
- prelude = 'do_add = rand(2) == 1'
200
- code = <<-RUBY_EVAL
201
- cache.compute(key) do |old_value|
202
- if do_add
203
- acc += 1 unless old_value
204
- key
205
- else
206
- acc -= 1 if old_value
207
- nil
208
- end
209
- end
210
- RUBY_EVAL
211
- do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
212
- assert_all_key_mappings_exist(cache, keys, false)
213
- assert_equal(cache.size, sum(result))
182
+ def add_remove(opts = {})
183
+ prelude = 'do_add = rand(2) == 1'
184
+ code = <<-RUBY_EVAL
185
+ if do_add
186
+ acc += 1 unless cache.put_if_absent(key, key)
187
+ else
188
+ acc -= 1 if cache.delete_pair(key, key)
214
189
  end
190
+ RUBY_EVAL
191
+ do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
192
+ assert_all_key_mappings_exist(cache, keys, false)
193
+ assert_equal(cache.size, sum(result))
215
194
  end
195
+ end
216
196
 
217
- def add_remove_via_compute_if_absent_present(opts = {})
218
- prelude = 'do_add = rand(2) == 1'
219
- code = <<-RUBY_EVAL
220
- if do_add
221
- cache.compute_if_absent(key) { acc += 1; key }
222
- else
223
- cache.compute_if_present(key) { acc -= 1; nil }
224
- end
225
- RUBY_EVAL
226
- do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
227
- assert_all_key_mappings_exist(cache, keys, false)
228
- assert_equal(cache.size, sum(result))
197
+ def add_remove_via_compute(opts = {})
198
+ prelude = 'do_add = rand(2) == 1'
199
+ code = <<-RUBY_EVAL
200
+ cache.compute(key) do |old_value|
201
+ if do_add
202
+ acc += 1 unless old_value
203
+ key
204
+ else
205
+ acc -= 1 if old_value
206
+ nil
207
+ end
229
208
  end
209
+ RUBY_EVAL
210
+ do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
211
+ assert_all_key_mappings_exist(cache, keys, false)
212
+ assert_equal(cache.size, sum(result))
230
213
  end
214
+ end
231
215
 
232
- def add_remove_indiscriminate(opts = {})
233
- prelude = 'do_add = rand(2) == 1'
234
- code = <<-RUBY_EVAL
235
- if do_add
236
- acc += 1 unless cache.put_if_absent(key, key)
237
- else
238
- acc -= 1 if cache.delete(key)
239
- end
240
- RUBY_EVAL
241
- do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
242
- assert_all_key_mappings_exist(cache, keys, false)
243
- assert_equal(cache.size, sum(result))
216
+ def add_remove_via_compute_if_absent_present(opts = {})
217
+ prelude = 'do_add = rand(2) == 1'
218
+ code = <<-RUBY_EVAL
219
+ if do_add
220
+ cache.compute_if_absent(key) { acc += 1; key }
221
+ else
222
+ cache.compute_if_present(key) { acc -= 1; nil }
244
223
  end
224
+ RUBY_EVAL
225
+ do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
226
+ assert_all_key_mappings_exist(cache, keys, false)
227
+ assert_equal(cache.size, sum(result))
245
228
  end
229
+ end
246
230
 
247
- def count_up(opts = {})
248
- code = <<-RUBY_EVAL
249
- v = cache[key]
250
- acc += 1 if cache.replace_pair(key, v, v + 1)
251
- RUBY_EVAL
252
- do_thread_loop(__method__, code, {:loop_count => 5, :cache_setup => ZERO_VALUE_CACHE_SETUP}.merge(opts)) do |result, cache, options, keys|
253
- assert_count_up(result, cache, options, keys)
231
+ def add_remove_indiscriminate(opts = {})
232
+ prelude = 'do_add = rand(2) == 1'
233
+ code = <<-RUBY_EVAL
234
+ if do_add
235
+ acc += 1 unless cache.put_if_absent(key, key)
236
+ else
237
+ acc -= 1 if cache.delete(key)
254
238
  end
239
+ RUBY_EVAL
240
+ do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
241
+ assert_all_key_mappings_exist(cache, keys, false)
242
+ assert_equal(cache.size, sum(result))
255
243
  end
244
+ end
256
245
 
257
- def count_up_via_compute(opts = {})
258
- code = <<-RUBY_EVAL
259
- cache.compute(key) do |old_value|
260
- acc += 1
261
- old_value ? old_value + 1 : 1
262
- end
263
- RUBY_EVAL
264
- do_thread_loop(:count_up_via_compute, code, {:loop_count => 5}.merge(opts)) do |result, cache, options, keys|
265
- assert_count_up(result, cache, options, keys)
266
- result.inject(nil) do |previous_value, next_value| # since compute guarantees atomicity all count ups should be equal
267
- assert_equal previous_value, next_value if previous_value
268
- next_value
269
- end
270
- end
246
+ def count_up(opts = {})
247
+ code = <<-RUBY_EVAL
248
+ v = cache[key]
249
+ acc += 1 if cache.replace_pair(key, v, v + 1)
250
+ RUBY_EVAL
251
+ do_thread_loop(__method__, code, {:loop_count => 5, :cache_setup => ZERO_VALUE_CACHE_SETUP}.merge(opts)) do |result, cache, options, keys|
252
+ assert_count_up(result, cache, options, keys)
271
253
  end
254
+ end
272
255
 
273
- def count_up_via_merge_pair(opts = {})
274
- code = <<-RUBY_EVAL
275
- cache.merge_pair(key, 1) {|old_value| old_value + 1}
276
- RUBY_EVAL
277
- do_thread_loop(:count_up_via_merge_pair, code, {:loop_count => 5}.merge(opts)) do |result, cache, options, keys|
278
- all_match = true
279
- expected_value = options[:loop_count] * options[:thread_count]
280
- keys.each do |key|
281
- if expected_value != (value = cache[key])
282
- all_match = false
283
- break
284
- end
285
- end
286
- assert all_match
256
+ def count_up_via_compute(opts = {})
257
+ code = <<-RUBY_EVAL
258
+ cache.compute(key) do |old_value|
259
+ acc += 1
260
+ old_value ? old_value + 1 : 1
287
261
  end
288
- end
289
-
290
- def add_remove_to_zero(opts = {})
291
- code = <<-RUBY_EVAL
292
- acc += 1 unless cache.put_if_absent(key, key)
293
- acc -= 1 if cache.delete_pair(key, key)
294
- RUBY_EVAL
295
- do_thread_loop(:add_remove_to_zero, code, {:loop_count => 5}.merge(opts)) do |result, cache, options, keys|
296
- assert_all_key_mappings_exist(cache, keys, false)
297
- assert_equal(cache.size, sum(result))
262
+ RUBY_EVAL
263
+ do_thread_loop(__method__, code, {:loop_count => 5}.merge(opts)) do |result, cache, options, keys|
264
+ assert_count_up(result, cache, options, keys)
265
+ result.inject(nil) do |previous_value, next_value| # since compute guarantees atomicity all count ups should be equal
266
+ assert_equal previous_value, next_value if previous_value
267
+ next_value
298
268
  end
299
269
  end
270
+ end
300
271
 
301
- def add_remove_to_zero_via_merge_pair(opts = {})
302
- code = <<-RUBY_EVAL
303
- acc += (cache.merge_pair(key, key) {}) ? -1 : 1
304
- RUBY_EVAL
305
- do_thread_loop(:add_remove_to_zero_via_merge_pair, code, {:loop_count => 5}.merge(opts)) do |result, cache, options, keys|
306
- assert_all_key_mappings_exist(cache, keys, false)
307
- assert_equal(cache.size, sum(result))
272
+ def count_up_via_merge_pair(opts = {})
273
+ code = <<-RUBY_EVAL
274
+ cache.merge_pair(key, 1) {|old_value| old_value + 1}
275
+ RUBY_EVAL
276
+ do_thread_loop(__method__, code, {:loop_count => 5}.merge(opts)) do |result, cache, options, keys|
277
+ all_match = true
278
+ expected_value = options[:loop_count] * options[:thread_count]
279
+ keys.each do |key|
280
+ if expected_value != (value = cache[key])
281
+ all_match = false
282
+ break
283
+ end
308
284
  end
285
+ assert all_match
309
286
  end
287
+ end
310
288
 
311
- def do_thread_loop(name, code, options = {}, &block)
312
- options = DEFAULTS.merge(options)
313
- meth = define_loop name, code, options[:prelude]
314
- assert_nothing_raised do
315
- keys = to_keys_array(options[:key_count])
316
- run_thread_loop(meth, keys, options, &block)
317
-
318
- if options[:key_count] > 1
319
- options[:key_count] = (options[:key_count] / 40).to_i
320
- keys = to_hash_collision_keys_array(options[:key_count])
321
- run_thread_loop(meth, keys, options.merge(:loop_count => (options[:loop_count] * 5)), &block)
322
- end
323
- end
289
+ def add_remove_to_zero(opts = {})
290
+ code = <<-RUBY_EVAL
291
+ acc += 1 unless cache.put_if_absent(key, key)
292
+ acc -= 1 if cache.delete_pair(key, key)
293
+ RUBY_EVAL
294
+ do_thread_loop(__method__, code, {:loop_count => 5}.merge(opts)) do |result, cache, options, keys|
295
+ assert_all_key_mappings_exist(cache, keys, false)
296
+ assert_equal(cache.size, sum(result))
324
297
  end
298
+ end
325
299
 
326
- def run_thread_loop(meth, keys, options)
327
- cache = options[:cache_setup].call(options, keys)
328
- barrier = ThreadSafe::Test::Barrier.new(options[:thread_count])
329
- result = (1..options[:thread_count]).map do
330
- Thread.new do
331
- setup_sync_and_start_loop(meth, cache, keys, barrier, options[:loop_count])
332
- end
333
- end.map(&:value)
334
- yield result, cache, options, keys if block_given?
300
+ def add_remove_to_zero_via_merge_pair(opts = {})
301
+ code = <<-RUBY_EVAL
302
+ acc += (cache.merge_pair(key, key) {}) ? 1 : -1
303
+ RUBY_EVAL
304
+ do_thread_loop(__method__, code, {:loop_count => 5}.merge(opts)) do |result, cache, options, keys|
305
+ assert_all_key_mappings_exist(cache, keys, false)
306
+ assert_equal(cache.size, sum(result))
335
307
  end
308
+ end
336
309
 
337
- def setup_sync_and_start_loop(meth, cache, keys, barrier, loop_count)
338
- my_keys = keys.shuffle
339
- barrier.await
340
- if my_keys.size == 1
341
- key = my_keys.first
342
- send("#{meth}_single_key", cache, key, loop_count)
343
- else
344
- send("#{meth}_multiple_keys", cache, my_keys, loop_count)
310
+ def do_thread_loop(name, code, options = {}, &block)
311
+ options = DEFAULTS.merge(options)
312
+ meth = define_loop name, code, options[:prelude]
313
+ assert_nothing_raised do
314
+ keys = to_keys_array(options[:key_count])
315
+ run_thread_loop(meth, keys, options, &block)
316
+
317
+ if options[:key_count] > 1
318
+ options[:key_count] = (options[:key_count] / 40).to_i
319
+ keys = to_hash_collision_keys_array(options[:key_count])
320
+ run_thread_loop(meth, keys, options.merge(:loop_count => (options[:loop_count] * 5)), &block)
345
321
  end
346
322
  end
323
+ end
347
324
 
348
- def define_loop(name, body, prelude)
349
- inner_meth_name = :"_#{name}_loop_inner"
350
- outer_meth_name = :"_#{name}_loop_outer"
351
- # looping is splitted into the "loop methods" to trigger the JIT
352
- self.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
353
- def #{inner_meth_name}_multiple_keys(cache, keys, i, length, acc)
354
- #{prelude}
355
- target = i + length
356
- while i < target
357
- key = keys[i]
358
- #{body}
359
- i += 1
360
- end
361
- acc
362
- end unless method_defined?(:#{inner_meth_name}_multiple_keys)
363
- RUBY_EVAL
364
-
365
- self.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
366
- def #{inner_meth_name}_single_key(cache, key, i, length, acc)
367
- #{prelude}
368
- target = i + length
369
- while i < target
370
- #{body}
371
- i += 1
372
- end
373
- acc
374
- end unless method_defined?(:#{inner_meth_name}_single_key)
375
- RUBY_EVAL
376
-
377
- self.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
378
- def #{outer_meth_name}_multiple_keys(cache, keys, loop_count)
379
- total_length = keys.size
380
- acc = 0
381
- inc = 100
382
- loop_count.times do
383
- i = 0
384
- pre_loop_inc = total_length % inc
385
- acc = #{inner_meth_name}_multiple_keys(cache, keys, i, pre_loop_inc, acc)
386
- i += pre_loop_inc
387
- while i < total_length
388
- acc = #{inner_meth_name}_multiple_keys(cache, keys, i, inc, acc)
389
- i += inc
390
- end
391
- end
392
- acc
393
- end unless method_defined?(:#{outer_meth_name}_multiple_keys)
394
- RUBY_EVAL
395
-
396
- self.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
397
- def #{outer_meth_name}_single_key(cache, key, loop_count)
398
- acc = 0
399
- i = 0
400
- inc = 100
401
-
402
- pre_loop_inc = loop_count % inc
403
- acc = #{inner_meth_name}_single_key(cache, key, i, pre_loop_inc, acc)
404
- i += pre_loop_inc
405
-
406
- while i < loop_count
407
- acc = #{inner_meth_name}_single_key(cache, key, i, inc, acc)
408
- i += inc
409
- end
410
- acc
411
- end unless method_defined?(:#{outer_meth_name}_single_key)
412
- RUBY_EVAL
413
- outer_meth_name
414
- end
325
+ def run_thread_loop(meth, keys, options)
326
+ cache = options[:cache_setup].call(options, keys)
327
+ barrier = ThreadSafe::Test::Barrier.new(options[:thread_count])
328
+ result = (1..options[:thread_count]).map do
329
+ Thread.new do
330
+ setup_sync_and_start_loop(meth, cache, keys, barrier, options[:loop_count])
331
+ end
332
+ end.map(&:value)
333
+ yield result, cache, options, keys if block_given?
334
+ end
415
335
 
416
- def to_keys_array(key_count)
417
- arr = []
418
- key_count.times {|i| arr << i}
419
- arr
336
+ def setup_sync_and_start_loop(meth, cache, keys, barrier, loop_count)
337
+ my_keys = keys.shuffle
338
+ barrier.await
339
+ if my_keys.size == 1
340
+ key = my_keys.first
341
+ send("#{meth}_single_key", cache, key, loop_count)
342
+ else
343
+ send("#{meth}_multiple_keys", cache, my_keys, loop_count)
420
344
  end
345
+ end
421
346
 
422
- def to_hash_collision_keys_array(key_count)
423
- to_keys_array(key_count).map {|key| ThreadSafe::Test::HashCollisionKey(key)}
424
- end
347
+ def define_loop(name, body, prelude)
348
+ inner_meth_name = :"_#{name}_loop_inner"
349
+ outer_meth_name = :"_#{name}_loop_outer"
350
+ # looping is splitted into the "loop methods" to trigger the JIT
351
+ self.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
352
+ def #{inner_meth_name}_multiple_keys(cache, keys, i, length, acc)
353
+ #{prelude}
354
+ target = i + length
355
+ while i < target
356
+ key = keys[i]
357
+ #{body}
358
+ i += 1
359
+ end
360
+ acc
361
+ end unless method_defined?(:#{inner_meth_name}_multiple_keys)
362
+ RUBY_EVAL
363
+
364
+ self.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
365
+ def #{inner_meth_name}_single_key(cache, key, i, length, acc)
366
+ #{prelude}
367
+ target = i + length
368
+ while i < target
369
+ #{body}
370
+ i += 1
371
+ end
372
+ acc
373
+ end unless method_defined?(:#{inner_meth_name}_single_key)
374
+ RUBY_EVAL
375
+
376
+ self.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
377
+ def #{outer_meth_name}_multiple_keys(cache, keys, loop_count)
378
+ total_length = keys.size
379
+ acc = 0
380
+ inc = 100
381
+ loop_count.times do
382
+ i = 0
383
+ pre_loop_inc = total_length % inc
384
+ acc = #{inner_meth_name}_multiple_keys(cache, keys, i, pre_loop_inc, acc)
385
+ i += pre_loop_inc
386
+ while i < total_length
387
+ acc = #{inner_meth_name}_multiple_keys(cache, keys, i, inc, acc)
388
+ i += inc
389
+ end
390
+ end
391
+ acc
392
+ end unless method_defined?(:#{outer_meth_name}_multiple_keys)
393
+ RUBY_EVAL
394
+
395
+ self.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
396
+ def #{outer_meth_name}_single_key(cache, key, loop_count)
397
+ acc = 0
398
+ i = 0
399
+ inc = 100
400
+
401
+ pre_loop_inc = loop_count % inc
402
+ acc = #{inner_meth_name}_single_key(cache, key, i, pre_loop_inc, acc)
403
+ i += pre_loop_inc
404
+
405
+ while i < loop_count
406
+ acc = #{inner_meth_name}_single_key(cache, key, i, inc, acc)
407
+ i += inc
408
+ end
409
+ acc
410
+ end unless method_defined?(:#{outer_meth_name}_single_key)
411
+ RUBY_EVAL
412
+ outer_meth_name
413
+ end
425
414
 
426
- def sum(result)
427
- result.inject(0) {|acc, i| acc + i}
428
- end
415
+ def to_keys_array(key_count)
416
+ arr = []
417
+ key_count.times {|i| arr << i}
418
+ arr
419
+ end
429
420
 
430
- def assert_standard_accumulator_test_result(result, cache, options, keys)
431
- assert_all_key_mappings_exist(cache, keys)
432
- assert_equal(options[:key_count], sum(result))
433
- assert_equal(options[:key_count], cache.size)
434
- end
421
+ def to_hash_collision_keys_array(key_count)
422
+ to_keys_array(key_count).map {|key| ThreadSafe::Test::HashCollisionKey(key)}
423
+ end
435
424
 
436
- def assert_all_key_mappings_exist(cache, keys, all_must_exist = true)
437
- keys.each do |key|
438
- if (value = cache[key]) || all_must_exist
439
- assert_equal key, value unless key == value # don't do a bazzilion assertions unless necessary
440
- end
425
+ def sum(result)
426
+ result.inject(0) {|acc, i| acc + i}
427
+ end
428
+
429
+ def assert_standard_accumulator_test_result(result, cache, options, keys)
430
+ assert_all_key_mappings_exist(cache, keys)
431
+ assert_equal(options[:key_count], sum(result))
432
+ assert_equal(options[:key_count], cache.size)
433
+ end
434
+
435
+ def assert_all_key_mappings_exist(cache, keys, all_must_exist = true)
436
+ keys.each do |key|
437
+ if (value = cache[key]) || all_must_exist
438
+ assert_equal key, value unless key == value # don't do a bazzilion assertions unless necessary
441
439
  end
442
440
  end
441
+ end
443
442
 
444
- def assert_count_up(result, cache, options, keys)
445
- keys.each do |key|
446
- unless value = cache[key]
447
- assert value
448
- end
443
+ def assert_count_up(result, cache, options, keys)
444
+ keys.each do |key|
445
+ unless value = cache[key]
446
+ assert value
449
447
  end
450
- assert_equal(sum(cache.values), sum(result))
451
- assert_equal(options[:key_count], cache.size)
452
448
  end
449
+ assert_equal(sum(cache.values), sum(result))
450
+ assert_equal(options[:key_count], cache.size)
453
451
  end
454
- end
452
+ end unless RUBY_VERSION =~ /1\.8/ || ENV['TRAVIS']
@@ -2,14 +2,6 @@ require 'test/unit'
2
2
  require 'thread_safe/synchronized_delegator.rb'
3
3
 
4
4
  class TestSynchronizedDelegator < Test::Unit::TestCase
5
- def setup
6
- @old_abort = Thread.abort_on_exception
7
- Thread.abort_on_exception = true
8
- end
9
-
10
- def teardown
11
- Thread.abort_on_exception = @old_abort
12
- end
13
5
 
14
6
  def test_wraps_array
15
7
  sync_array = SynchronizedDelegator.new(array = [])
@@ -19,6 +19,6 @@ Gem::Specification.new do |gem|
19
19
  gem.version = ThreadSafe::VERSION
20
20
  gem.license = "Apache-2.0"
21
21
 
22
- gem.add_dependency 'atomic', ['>= 1.1.7', '< 2']
22
+ gem.add_development_dependency 'atomic', ['>= 1.1.7', '< 2']
23
23
  gem.add_development_dependency 'rake'
24
24
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thread_safe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: java
6
6
  authors:
7
7
  - Charles Oliver Nutter
@@ -9,10 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-03-21 00:00:00.000000000 Z
12
+ date: 2014-04-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- requirement: !ruby/object:Gem::Requirement
15
+ name: atomic
16
+ version_requirements: !ruby/object:Gem::Requirement
16
17
  requirements:
17
18
  - - '>='
18
19
  - !ruby/object:Gem::Version
@@ -20,10 +21,7 @@ dependencies:
20
21
  - - <
21
22
  - !ruby/object:Gem::Version
22
23
  version: '2'
23
- name: atomic
24
- prerelease: false
25
- type: :runtime
26
- version_requirements: !ruby/object:Gem::Requirement
24
+ requirement: !ruby/object:Gem::Requirement
27
25
  requirements:
28
26
  - - '>='
29
27
  - !ruby/object:Gem::Version
@@ -31,20 +29,22 @@ dependencies:
31
29
  - - <
32
30
  - !ruby/object:Gem::Version
33
31
  version: '2'
32
+ prerelease: false
33
+ type: :development
34
34
  - !ruby/object:Gem::Dependency
35
- requirement: !ruby/object:Gem::Requirement
35
+ name: rake
36
+ version_requirements: !ruby/object:Gem::Requirement
36
37
  requirements:
37
38
  - - '>='
38
39
  - !ruby/object:Gem::Version
39
40
  version: '0'
40
- name: rake
41
- prerelease: false
42
- type: :development
43
- version_requirements: !ruby/object:Gem::Requirement
41
+ requirement: !ruby/object:Gem::Requirement
44
42
  requirements:
45
43
  - - '>='
46
44
  - !ruby/object:Gem::Version
47
45
  version: '0'
46
+ prerelease: false
47
+ type: :development
48
48
  description: Thread-safe collections and utilities for Ruby
49
49
  email:
50
50
  - headius@headius.com
@@ -73,6 +73,7 @@ files:
73
73
  - lib/thread_safe.rb
74
74
  - lib/thread_safe/atomic_reference_cache_backend.rb
75
75
  - lib/thread_safe/cache.rb
76
+ - lib/thread_safe/jruby_cache_backend.jar
76
77
  - lib/thread_safe/mri_cache_backend.rb
77
78
  - lib/thread_safe/non_concurrent_cache_backend.rb
78
79
  - lib/thread_safe/synchronized_cache_backend.rb
@@ -95,7 +96,6 @@ files:
95
96
  - test/test_helper.rb
96
97
  - test/test_synchronized_delegator.rb
97
98
  - thread_safe.gemspec
98
- - lib/thread_safe/jruby_cache_backend.jar
99
99
  homepage: https://github.com/headius/thread_safe
100
100
  licenses:
101
101
  - Apache-2.0
@@ -116,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
116
  version: '0'
117
117
  requirements: []
118
118
  rubyforge_project:
119
- rubygems_version: 2.1.9
119
+ rubygems_version: 2.2.2
120
120
  signing_key:
121
121
  specification_version: 4
122
122
  summary: A collection of data structures and utilities to make thread-safe programming in Ruby easier