thread_safe 0.3.5-java → 0.3.6-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.
@@ -1,449 +0,0 @@
1
- require 'thread'
2
- require 'thread_safe'
3
- require File.join(File.dirname(__FILE__), "test_helper")
4
-
5
- Thread.abort_on_exception = true
6
-
7
- class TestCacheTorture < Minitest::Test # this is not run unless RUBY_VERSION =~ /1\.8/ || ENV['TRAVIS'] (see the end of the file)
8
- THREAD_COUNT = 40
9
- KEY_COUNT = (((2**13) - 2) * 0.75).to_i # get close to the doubling cliff
10
- LOW_KEY_COUNT = (((2**8 ) - 2) * 0.75).to_i # get close to the doubling cliff
11
-
12
- INITIAL_VALUE_CACHE_SETUP = lambda do |options, keys|
13
- cache = ThreadSafe::Cache.new
14
- initial_value = options[:initial_value] || 0
15
- keys.each {|key| cache[key] = initial_value}
16
- cache
17
- end
18
- ZERO_VALUE_CACHE_SETUP = lambda do |options, keys|
19
- INITIAL_VALUE_CACHE_SETUP.call(options.merge(:initial_value => 0), keys)
20
- end
21
-
22
- DEFAULTS = {
23
- :key_count => KEY_COUNT,
24
- :thread_count => THREAD_COUNT,
25
- :loop_count => 1,
26
- :prelude => '',
27
- :cache_setup => lambda {|options, keys| ThreadSafe::Cache.new}
28
- }
29
-
30
- LOW_KEY_COUNT_OPTIONS = {:loop_count => 150, :key_count => LOW_KEY_COUNT}
31
- SINGLE_KEY_COUNT_OPTIONS = {:loop_count => 100_000, :key_count => 1}
32
-
33
- def test_concurrency
34
- code = <<-RUBY_EVAL
35
- cache[key]
36
- cache[key] = key
37
- cache[key]
38
- cache.delete(key)
39
- RUBY_EVAL
40
- do_thread_loop(__method__, code)
41
- end
42
-
43
- def test_put_if_absent
44
- do_thread_loop(__method__, 'acc += 1 unless cache.put_if_absent(key, key)', :key_count => 100_000) do |result, cache, options, keys|
45
- assert_standard_accumulator_test_result(result, cache, options, keys)
46
- end
47
- end
48
-
49
- def test_compute_if_absent
50
- code = 'cache.compute_if_absent(key) { acc += 1; key }'
51
- do_thread_loop(__method__, code) do |result, cache, options, keys|
52
- assert_standard_accumulator_test_result(result, cache, options, keys)
53
- end
54
- end
55
-
56
- def test_compute_put_if_absent
57
- code = <<-RUBY_EVAL
58
- if key.even?
59
- cache.compute_if_absent(key) { acc += 1; key }
60
- else
61
- acc += 1 unless cache.put_if_absent(key, key)
62
- end
63
- RUBY_EVAL
64
- do_thread_loop(__method__, code) do |result, cache, options, keys|
65
- assert_standard_accumulator_test_result(result, cache, options, keys)
66
- end
67
- end
68
-
69
- def test_compute_if_absent_and_present
70
- compute_if_absent_and_present
71
- compute_if_absent_and_present(LOW_KEY_COUNT_OPTIONS)
72
- compute_if_absent_and_present(SINGLE_KEY_COUNT_OPTIONS)
73
- end
74
-
75
- def test_add_remove_to_zero
76
- add_remove_to_zero
77
- add_remove_to_zero(LOW_KEY_COUNT_OPTIONS)
78
- add_remove_to_zero(SINGLE_KEY_COUNT_OPTIONS)
79
- end
80
-
81
- def test_add_remove_to_zero_via_merge_pair
82
- add_remove_to_zero_via_merge_pair
83
- add_remove_to_zero_via_merge_pair(LOW_KEY_COUNT_OPTIONS)
84
- add_remove_to_zero_via_merge_pair(SINGLE_KEY_COUNT_OPTIONS)
85
- end
86
-
87
- def test_add_remove
88
- add_remove
89
- add_remove(LOW_KEY_COUNT_OPTIONS)
90
- add_remove(SINGLE_KEY_COUNT_OPTIONS)
91
- end
92
-
93
- def test_add_remove_via_compute
94
- add_remove_via_compute
95
- add_remove_via_compute(LOW_KEY_COUNT_OPTIONS)
96
- add_remove_via_compute(SINGLE_KEY_COUNT_OPTIONS)
97
- end
98
-
99
- def add_remove_via_compute_if_absent_present
100
- add_remove_via_compute_if_absent_present
101
- add_remove_via_compute_if_absent_present(LOW_KEY_COUNT_OPTIONS)
102
- add_remove_via_compute_if_absent_present(SINGLE_KEY_COUNT_OPTIONS)
103
- end
104
-
105
- def test_add_remove_indiscriminate
106
- add_remove_indiscriminate
107
- add_remove_indiscriminate(LOW_KEY_COUNT_OPTIONS)
108
- add_remove_indiscriminate(SINGLE_KEY_COUNT_OPTIONS)
109
- end
110
-
111
- def test_count_up
112
- count_up
113
- count_up(LOW_KEY_COUNT_OPTIONS)
114
- count_up(SINGLE_KEY_COUNT_OPTIONS)
115
- end
116
-
117
- def test_count_up_via_compute
118
- count_up_via_compute
119
- count_up_via_compute(LOW_KEY_COUNT_OPTIONS)
120
- count_up_via_compute(SINGLE_KEY_COUNT_OPTIONS)
121
- end
122
-
123
- def test_count_up_via_merge_pair
124
- count_up_via_merge_pair
125
- count_up_via_merge_pair(LOW_KEY_COUNT_OPTIONS)
126
- count_up_via_merge_pair(SINGLE_KEY_COUNT_OPTIONS)
127
- end
128
-
129
- def test_count_race
130
- prelude = 'change = (rand(2) == 1) ? 1 : -1'
131
- code = <<-RUBY_EVAL
132
- v = cache[key]
133
- acc += change if cache.replace_pair(key, v, v + change)
134
- RUBY_EVAL
135
- do_thread_loop(__method__, code, :loop_count => 5, :prelude => prelude, :cache_setup => ZERO_VALUE_CACHE_SETUP) do |result, cache, options, keys|
136
- result_sum = sum(result)
137
- assert_equal(sum(keys.map {|key| cache[key]}), result_sum)
138
- assert_equal(sum(cache.values), result_sum)
139
- assert_equal(options[:key_count], cache.size)
140
- end
141
- end
142
-
143
- def test_get_and_set_new
144
- code = 'acc += 1 unless cache.get_and_set(key, key)'
145
- do_thread_loop(__method__, code) do |result, cache, options, keys|
146
- assert_standard_accumulator_test_result(result, cache, options, keys)
147
- end
148
- end
149
-
150
- def test_get_and_set_existing
151
- code = 'acc += 1 if cache.get_and_set(key, key) == -1'
152
- do_thread_loop(__method__, code, :cache_setup => INITIAL_VALUE_CACHE_SETUP, :initial_value => -1) do |result, cache, options, keys|
153
- assert_standard_accumulator_test_result(result, cache, options, keys)
154
- end
155
- end
156
-
157
- private
158
- def compute_if_absent_and_present(opts = {})
159
- prelude = 'on_present = rand(2) == 1'
160
- code = <<-RUBY_EVAL
161
- if on_present
162
- cache.compute_if_present(key) {|old_value| acc += 1; old_value + 1}
163
- else
164
- cache.compute_if_absent(key) { acc += 1; 1 }
165
- end
166
- RUBY_EVAL
167
- do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
168
- stored_sum = 0
169
- stored_key_count = 0
170
- keys.each do |k|
171
- if value = cache[k]
172
- stored_sum += value
173
- stored_key_count += 1
174
- end
175
- end
176
- assert_equal(stored_sum, sum(result))
177
- assert_equal(stored_key_count, cache.size)
178
- end
179
- end
180
-
181
- def add_remove(opts = {})
182
- prelude = 'do_add = rand(2) == 1'
183
- code = <<-RUBY_EVAL
184
- if do_add
185
- acc += 1 unless cache.put_if_absent(key, key)
186
- else
187
- acc -= 1 if cache.delete_pair(key, key)
188
- end
189
- RUBY_EVAL
190
- do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
191
- assert_all_key_mappings_exist(cache, keys, false)
192
- assert_equal(cache.size, sum(result))
193
- end
194
- end
195
-
196
- def add_remove_via_compute(opts = {})
197
- prelude = 'do_add = rand(2) == 1'
198
- code = <<-RUBY_EVAL
199
- cache.compute(key) do |old_value|
200
- if do_add
201
- acc += 1 unless old_value
202
- key
203
- else
204
- acc -= 1 if old_value
205
- nil
206
- end
207
- end
208
- RUBY_EVAL
209
- do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
210
- assert_all_key_mappings_exist(cache, keys, false)
211
- assert_equal(cache.size, sum(result))
212
- end
213
- end
214
-
215
- def add_remove_via_compute_if_absent_present(opts = {})
216
- prelude = 'do_add = rand(2) == 1'
217
- code = <<-RUBY_EVAL
218
- if do_add
219
- cache.compute_if_absent(key) { acc += 1; key }
220
- else
221
- cache.compute_if_present(key) { acc -= 1; nil }
222
- end
223
- RUBY_EVAL
224
- do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
225
- assert_all_key_mappings_exist(cache, keys, false)
226
- assert_equal(cache.size, sum(result))
227
- end
228
- end
229
-
230
- def add_remove_indiscriminate(opts = {})
231
- prelude = 'do_add = rand(2) == 1'
232
- code = <<-RUBY_EVAL
233
- if do_add
234
- acc += 1 unless cache.put_if_absent(key, key)
235
- else
236
- acc -= 1 if cache.delete(key)
237
- end
238
- RUBY_EVAL
239
- do_thread_loop(__method__, code, {:loop_count => 5, :prelude => prelude}.merge(opts)) do |result, cache, options, keys|
240
- assert_all_key_mappings_exist(cache, keys, false)
241
- assert_equal(cache.size, sum(result))
242
- end
243
- end
244
-
245
- def count_up(opts = {})
246
- code = <<-RUBY_EVAL
247
- v = cache[key]
248
- acc += 1 if cache.replace_pair(key, v, v + 1)
249
- RUBY_EVAL
250
- do_thread_loop(__method__, code, {:loop_count => 5, :cache_setup => ZERO_VALUE_CACHE_SETUP}.merge(opts)) do |result, cache, options, keys|
251
- assert_count_up(result, cache, options, keys)
252
- end
253
- end
254
-
255
- def count_up_via_compute(opts = {})
256
- code = <<-RUBY_EVAL
257
- cache.compute(key) do |old_value|
258
- acc += 1
259
- old_value ? old_value + 1 : 1
260
- end
261
- RUBY_EVAL
262
- do_thread_loop(__method__, code, {:loop_count => 5}.merge(opts)) do |result, cache, options, keys|
263
- assert_count_up(result, cache, options, keys)
264
- result.inject(nil) do |previous_value, next_value| # since compute guarantees atomicity all count ups should be equal
265
- assert_equal previous_value, next_value if previous_value
266
- next_value
267
- end
268
- end
269
- end
270
-
271
- def count_up_via_merge_pair(opts = {})
272
- code = <<-RUBY_EVAL
273
- cache.merge_pair(key, 1) {|old_value| old_value + 1}
274
- RUBY_EVAL
275
- do_thread_loop(__method__, code, {:loop_count => 5}.merge(opts)) do |result, cache, options, keys|
276
- all_match = true
277
- expected_value = options[:loop_count] * options[:thread_count]
278
- keys.each do |key|
279
- if expected_value != (value = cache[key])
280
- all_match = false
281
- break
282
- end
283
- end
284
- assert all_match
285
- end
286
- end
287
-
288
- def add_remove_to_zero(opts = {})
289
- code = <<-RUBY_EVAL
290
- acc += 1 unless cache.put_if_absent(key, key)
291
- acc -= 1 if cache.delete_pair(key, key)
292
- RUBY_EVAL
293
- do_thread_loop(__method__, code, {:loop_count => 5}.merge(opts)) do |result, cache, options, keys|
294
- assert_all_key_mappings_exist(cache, keys, false)
295
- assert_equal(cache.size, sum(result))
296
- end
297
- end
298
-
299
- def add_remove_to_zero_via_merge_pair(opts = {})
300
- code = <<-RUBY_EVAL
301
- acc += (cache.merge_pair(key, key) {}) ? 1 : -1
302
- RUBY_EVAL
303
- do_thread_loop(__method__, code, {:loop_count => 5}.merge(opts)) do |result, cache, options, keys|
304
- assert_all_key_mappings_exist(cache, keys, false)
305
- assert_equal(cache.size, sum(result))
306
- end
307
- end
308
-
309
- def do_thread_loop(name, code, options = {}, &block)
310
- options = DEFAULTS.merge(options)
311
- meth = define_loop name, code, options[:prelude]
312
- keys = to_keys_array(options[:key_count])
313
- run_thread_loop(meth, keys, options, &block)
314
-
315
- if options[:key_count] > 1
316
- options[:key_count] = (options[:key_count] / 40).to_i
317
- keys = to_hash_collision_keys_array(options[:key_count])
318
- run_thread_loop(meth, keys, options.merge(:loop_count => (options[:loop_count] * 5)), &block)
319
- end
320
- end
321
-
322
- def run_thread_loop(meth, keys, options)
323
- cache = options[:cache_setup].call(options, keys)
324
- barrier = ThreadSafe::Test::Barrier.new(options[:thread_count])
325
- result = (1..options[:thread_count]).map do
326
- Thread.new do
327
- setup_sync_and_start_loop(meth, cache, keys, barrier, options[:loop_count])
328
- end
329
- end.map(&:value)
330
- yield result, cache, options, keys if block_given?
331
- end
332
-
333
- def setup_sync_and_start_loop(meth, cache, keys, barrier, loop_count)
334
- my_keys = keys.shuffle
335
- barrier.await
336
- if my_keys.size == 1
337
- key = my_keys.first
338
- send("#{meth}_single_key", cache, key, loop_count)
339
- else
340
- send("#{meth}_multiple_keys", cache, my_keys, loop_count)
341
- end
342
- end
343
-
344
- def define_loop(name, body, prelude)
345
- inner_meth_name = :"_#{name}_loop_inner"
346
- outer_meth_name = :"_#{name}_loop_outer"
347
- # looping is splitted into the "loop methods" to trigger the JIT
348
- self.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
349
- def #{inner_meth_name}_multiple_keys(cache, keys, i, length, acc)
350
- #{prelude}
351
- target = i + length
352
- while i < target
353
- key = keys[i]
354
- #{body}
355
- i += 1
356
- end
357
- acc
358
- end unless method_defined?(:#{inner_meth_name}_multiple_keys)
359
- RUBY_EVAL
360
-
361
- self.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
362
- def #{inner_meth_name}_single_key(cache, key, i, length, acc)
363
- #{prelude}
364
- target = i + length
365
- while i < target
366
- #{body}
367
- i += 1
368
- end
369
- acc
370
- end unless method_defined?(:#{inner_meth_name}_single_key)
371
- RUBY_EVAL
372
-
373
- self.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
374
- def #{outer_meth_name}_multiple_keys(cache, keys, loop_count)
375
- total_length = keys.size
376
- acc = 0
377
- inc = 100
378
- loop_count.times do
379
- i = 0
380
- pre_loop_inc = total_length % inc
381
- acc = #{inner_meth_name}_multiple_keys(cache, keys, i, pre_loop_inc, acc)
382
- i += pre_loop_inc
383
- while i < total_length
384
- acc = #{inner_meth_name}_multiple_keys(cache, keys, i, inc, acc)
385
- i += inc
386
- end
387
- end
388
- acc
389
- end unless method_defined?(:#{outer_meth_name}_multiple_keys)
390
- RUBY_EVAL
391
-
392
- self.class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
393
- def #{outer_meth_name}_single_key(cache, key, loop_count)
394
- acc = 0
395
- i = 0
396
- inc = 100
397
-
398
- pre_loop_inc = loop_count % inc
399
- acc = #{inner_meth_name}_single_key(cache, key, i, pre_loop_inc, acc)
400
- i += pre_loop_inc
401
-
402
- while i < loop_count
403
- acc = #{inner_meth_name}_single_key(cache, key, i, inc, acc)
404
- i += inc
405
- end
406
- acc
407
- end unless method_defined?(:#{outer_meth_name}_single_key)
408
- RUBY_EVAL
409
- outer_meth_name
410
- end
411
-
412
- def to_keys_array(key_count)
413
- arr = []
414
- key_count.times {|i| arr << i}
415
- arr
416
- end
417
-
418
- def to_hash_collision_keys_array(key_count)
419
- to_keys_array(key_count).map {|key| ThreadSafe::Test::HashCollisionKey(key)}
420
- end
421
-
422
- def sum(result)
423
- result.inject(0) {|acc, i| acc + i}
424
- end
425
-
426
- def assert_standard_accumulator_test_result(result, cache, options, keys)
427
- assert_all_key_mappings_exist(cache, keys)
428
- assert_equal(options[:key_count], sum(result))
429
- assert_equal(options[:key_count], cache.size)
430
- end
431
-
432
- def assert_all_key_mappings_exist(cache, keys, all_must_exist = true)
433
- keys.each do |key|
434
- if (value = cache[key]) || all_must_exist
435
- assert_equal key, value unless key == value # don't do a bazzilion assertions unless necessary
436
- end
437
- end
438
- end
439
-
440
- def assert_count_up(result, cache, options, keys)
441
- keys.each do |key|
442
- unless value = cache[key]
443
- assert value
444
- end
445
- end
446
- assert_equal(sum(cache.values), sum(result))
447
- assert_equal(options[:key_count], cache.size)
448
- end
449
- end unless RUBY_VERSION =~ /1\.8/ || ENV['TRAVIS']
@@ -1,17 +0,0 @@
1
- require 'thread_safe'
2
- require File.join(File.dirname(__FILE__), "test_helper")
3
-
4
- class TestHash < Minitest::Test
5
- def test_concurrency
6
- hsh = ThreadSafe::Hash.new
7
- (1..THREADS).map do |i|
8
- Thread.new do
9
- 1000.times do |j|
10
- hsh[i*1000+j] = i
11
- hsh[i*1000+j]
12
- hsh.delete(i*1000+j)
13
- end
14
- end
15
- end.map(&:join)
16
- end
17
- end