thread_safe 0.1.1-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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +144 -0
  5. data/README.md +34 -0
  6. data/Rakefile +36 -0
  7. data/examples/bench_cache.rb +35 -0
  8. data/ext/org/jruby/ext/thread_safe/JRubyCacheBackendLibrary.java +200 -0
  9. data/ext/org/jruby/ext/thread_safe/jsr166e/ConcurrentHashMapV8.java +3842 -0
  10. data/ext/org/jruby/ext/thread_safe/jsr166e/LongAdder.java +204 -0
  11. data/ext/org/jruby/ext/thread_safe/jsr166e/Striped64.java +342 -0
  12. data/ext/org/jruby/ext/thread_safe/jsr166y/ThreadLocalRandom.java +199 -0
  13. data/ext/thread_safe/JrubyCacheBackendService.java +15 -0
  14. data/lib/thread_safe.rb +65 -0
  15. data/lib/thread_safe/atomic_reference_cache_backend.rb +922 -0
  16. data/lib/thread_safe/cache.rb +137 -0
  17. data/lib/thread_safe/mri_cache_backend.rb +62 -0
  18. data/lib/thread_safe/non_concurrent_cache_backend.rb +133 -0
  19. data/lib/thread_safe/synchronized_cache_backend.rb +76 -0
  20. data/lib/thread_safe/synchronized_delegator.rb +35 -0
  21. data/lib/thread_safe/util.rb +16 -0
  22. data/lib/thread_safe/util/adder.rb +59 -0
  23. data/lib/thread_safe/util/atomic_reference.rb +12 -0
  24. data/lib/thread_safe/util/cheap_lockable.rb +105 -0
  25. data/lib/thread_safe/util/power_of_two_tuple.rb +26 -0
  26. data/lib/thread_safe/util/striped64.rb +226 -0
  27. data/lib/thread_safe/util/volatile.rb +62 -0
  28. data/lib/thread_safe/util/volatile_tuple.rb +46 -0
  29. data/lib/thread_safe/util/xor_shift_random.rb +39 -0
  30. data/lib/thread_safe/version.rb +3 -0
  31. data/test/test_array.rb +20 -0
  32. data/test/test_cache.rb +792 -0
  33. data/test/test_cache_loops.rb +453 -0
  34. data/test/test_hash.rb +20 -0
  35. data/test/test_helper.rb +73 -0
  36. data/test/test_synchronized_delegator.rb +42 -0
  37. data/thread_safe.gemspec +21 -0
  38. metadata +100 -0
@@ -0,0 +1,46 @@
1
+ module ThreadSafe
2
+ module Util
3
+ # A fixed size array with volatile volatile getters/setters.
4
+ # Usage:
5
+ # arr = VolatileTuple.new(16)
6
+ # arr.volatile_set(0, :foo)
7
+ # arr.volatile_get(0) # => :foo
8
+ # arr.cas(0, :foo, :bar) # => true
9
+ # arr.volatile_get(0) # => :bar
10
+ class VolatileTuple
11
+ include Enumerable
12
+
13
+ Tuple = defined?(Rubinius::Tuple) ? Rubinius::Tuple : Array
14
+
15
+ def initialize(size)
16
+ @tuple = tuple = Tuple.new(size)
17
+ i = 0
18
+ while i < size
19
+ tuple[i] = AtomicReference.new
20
+ i += 1
21
+ end
22
+ end
23
+
24
+ def volatile_get(i)
25
+ @tuple[i].get
26
+ end
27
+
28
+ def volatile_set(i, value)
29
+ @tuple[i].set(value)
30
+ end
31
+
32
+ def compare_and_set(i, old_value, new_value)
33
+ @tuple[i].compare_and_set(old_value, new_value)
34
+ end
35
+ alias_method :cas, :compare_and_set
36
+
37
+ def size
38
+ @tuple.size
39
+ end
40
+
41
+ def each
42
+ @tuple.each {|ref| yield ref.get}
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,39 @@
1
+ module ThreadSafe
2
+ module Util
3
+ # A xorshift random number (positive +Fixnum+s) generator, provides reasonably cheap way to generate thread local random numbers without contending for
4
+ # the global +Kernel.rand+.
5
+ # Usage:
6
+ # x = XorShiftRandom.get # uses Kernel.rand to generate an initial seed
7
+ # while true
8
+ # if (x = XorShiftRandom.xorshift).odd? # thread-localy generate a next random number
9
+ # do_something_at_random
10
+ # end
11
+ # end
12
+ module XorShiftRandom
13
+ extend self
14
+ MAX_XOR_SHIFTABLE_INT = MAX_INT - 1
15
+
16
+ # Generates an initial non-zero positive +Fixnum+ via +Kernel.rand+.
17
+ def get
18
+ Kernel.rand(MAX_XOR_SHIFTABLE_INT) + 1 # 0 can't be xorshifted
19
+ end
20
+
21
+ # xorshift based on: http://www.jstatsoft.org/v08/i14/paper
22
+ if 0.size == 4
23
+ # using the "yˆ=y>>a; yˆ=y<<b; yˆ=y>>c;" transform with the (a,b,c) tuple with values (3,1,14) to minimise Bignum overflows
24
+ def xorshift(x)
25
+ x ^= x >> 3
26
+ x ^= (x << 1) & MAX_INT # cut-off Bignum overflow
27
+ x ^= x >> 14
28
+ end
29
+ else
30
+ # using the "yˆ=y>>a; yˆ=y<<b; yˆ=y>>c;" transform with the (a,b,c) tuple with values (1,1,54) to minimise Bignum overflows
31
+ def xorshift(x)
32
+ x ^= x >> 1
33
+ x ^= (x << 1) & MAX_INT # cut-off Bignum overflow
34
+ x ^= x >> 54
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ module Threadsafe
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,20 @@
1
+ require 'test/unit'
2
+ require 'thread_safe'
3
+
4
+ class TestArray < Test::Unit::TestCase
5
+ def test_concurrency
6
+ ary = ThreadSafe::Array.new
7
+ assert_nothing_raised do
8
+ (1..100).map do |i|
9
+ Thread.new do
10
+ 1000.times do
11
+ ary << i
12
+ ary.each {|x| x * 2}
13
+ ary.shift
14
+ ary.last
15
+ end
16
+ end
17
+ end.map(&:join)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,792 @@
1
+ require 'test/unit'
2
+ require 'thread_safe'
3
+ require 'thread'
4
+ require File.join(File.dirname(__FILE__), "test_helper")
5
+
6
+ Thread.abort_on_exception = true
7
+
8
+ class TestCache < Test::Unit::TestCase
9
+ def setup
10
+ @cache = ThreadSafe::Cache.new
11
+ end
12
+
13
+ def test_concurrency
14
+ cache = @cache
15
+ assert_nothing_raised do
16
+ (1..100).map do |i|
17
+ Thread.new do
18
+ 1000.times do |j|
19
+ key = i*1000+j
20
+ cache[key] = i
21
+ cache[key]
22
+ cache.delete(key)
23
+ end
24
+ end
25
+ end.map(&:join)
26
+ end
27
+ end
28
+
29
+ def test_retrieval
30
+ assert_size_change 1 do
31
+ assert_equal nil, @cache[:a]
32
+ @cache[:a] = 1
33
+ assert_equal 1, @cache[:a]
34
+ end
35
+ end
36
+
37
+ def test_put_if_absent
38
+ with_or_without_default_proc do
39
+ assert_size_change 1 do
40
+ assert_equal nil, @cache.put_if_absent(:a, 1)
41
+ assert_equal 1, @cache.put_if_absent(:a, 1)
42
+ assert_equal 1, @cache.put_if_absent(:a, 2)
43
+ assert_equal 1, @cache[:a]
44
+ end
45
+ end
46
+ end
47
+
48
+ def test_compute_if_absent
49
+ with_or_without_default_proc do
50
+ assert_size_change 3 do
51
+ assert_equal(1, (@cache.compute_if_absent(:a) {1}))
52
+ assert_equal(1, (@cache.compute_if_absent(:a) {2}))
53
+ assert_equal 1, @cache[:a]
54
+ @cache[:b] = nil
55
+ assert_equal(nil, (@cache.compute_if_absent(:b) {1}))
56
+ assert_equal(nil, (@cache.compute_if_absent(:c) {}))
57
+ assert_equal nil, @cache[:c]
58
+ assert_equal true, @cache.key?(:c)
59
+ end
60
+ end
61
+ end
62
+
63
+ def test_compute_if_absent_with_return
64
+ with_or_without_default_proc { assert_handles_return_lambda(:compute_if_absent, :a) }
65
+ end
66
+
67
+ def test_compute_if_absent_exception
68
+ with_or_without_default_proc { assert_handles_exception(:compute_if_absent, :a) }
69
+ end
70
+
71
+ def test_compute_if_absent_atomicity
72
+ late_compute_threads_count = 10
73
+ late_put_if_absent_threads_count = 10
74
+ getter_threads_count = 5
75
+ compute_started = ThreadSafe::Test::Latch.new(1)
76
+ compute_proceed = ThreadSafe::Test::Latch.new(late_compute_threads_count + late_put_if_absent_threads_count + getter_threads_count)
77
+ block_until_compute_started = lambda do |name|
78
+ if (v = @cache[:a]) != nil
79
+ assert_equal nil, v
80
+ end
81
+ compute_proceed.release
82
+ compute_started.await
83
+ end
84
+
85
+ assert_size_change 1 do
86
+ late_compute_threads = Array.new(late_compute_threads_count) do
87
+ Thread.new do
88
+ block_until_compute_started.call('compute_if_absent')
89
+ assert_equal(1, (@cache.compute_if_absent(:a) { flunk }))
90
+ end
91
+ end
92
+
93
+ late_put_if_absent_threads = Array.new(late_put_if_absent_threads_count) do
94
+ Thread.new do
95
+ block_until_compute_started.call('put_if_absent')
96
+ assert_equal(1, @cache.put_if_absent(:a, 2))
97
+ end
98
+ end
99
+
100
+ getter_threads = Array.new(getter_threads_count) do
101
+ Thread.new do
102
+ block_until_compute_started.call('getter')
103
+ Thread.pass while @cache[:a].nil?
104
+ assert_equal 1, @cache[:a]
105
+ end
106
+ end
107
+
108
+ Thread.new do
109
+ @cache.compute_if_absent(:a) do
110
+ compute_started.release
111
+ compute_proceed.await
112
+ sleep(0.2)
113
+ 1
114
+ end
115
+ end.join
116
+ (late_compute_threads + late_put_if_absent_threads + getter_threads).each(&:join)
117
+ end
118
+ end
119
+
120
+ def test_compute_if_present
121
+ with_or_without_default_proc do
122
+ assert_no_size_change do
123
+ assert_equal(nil, @cache.compute_if_present(:a) {})
124
+ assert_equal(nil, @cache.compute_if_present(:a) {1})
125
+ assert_equal(nil, @cache.compute_if_present(:a) {flunk})
126
+ assert_equal false, @cache.key?(:a)
127
+ end
128
+
129
+ @cache[:a] = 1
130
+ assert_no_size_change do
131
+ assert_equal(1, @cache.compute_if_present(:a) {1})
132
+ assert_equal(1, @cache[:a])
133
+ assert_equal(2, @cache.compute_if_present(:a) {2})
134
+ assert_equal(2, @cache[:a])
135
+ assert_equal(false, @cache.compute_if_present(:a) {false})
136
+ assert_equal(false, @cache[:a])
137
+
138
+ @cache[:a] = 1
139
+ yielded = false
140
+ @cache.compute_if_present(:a) do |old_value|
141
+ yielded = true
142
+ assert_equal 1, old_value
143
+ 2
144
+ end
145
+ assert yielded
146
+ end
147
+
148
+ assert_size_change -1 do
149
+ assert_equal(nil, @cache.compute_if_present(:a) {})
150
+ assert_equal(false, @cache.key?(:a))
151
+ assert_equal(nil, @cache.compute_if_present(:a) {1})
152
+ assert_equal(false, @cache.key?(:a))
153
+ end
154
+ end
155
+ end
156
+
157
+ def test_compute_if_present_with_return
158
+ with_or_without_default_proc do
159
+ @cache[:a] = 1
160
+ assert_handles_return_lambda(:compute_if_present, :a)
161
+ end
162
+ end
163
+
164
+ def test_compute_if_present_exception
165
+ with_or_without_default_proc do
166
+ @cache[:a] = 1
167
+ assert_handles_exception(:compute_if_present, :a)
168
+ end
169
+ end
170
+
171
+ def test_compute
172
+ with_or_without_default_proc do
173
+ assert_no_size_change do
174
+ assert_compute(:a, nil, nil) {}
175
+ end
176
+
177
+ assert_size_change 1 do
178
+ assert_compute(:a, nil, 1) {1}
179
+ assert_compute(:a, 1, 2) {2}
180
+ assert_compute(:a, 2, false) {false}
181
+ assert_equal false, @cache[:a]
182
+ end
183
+
184
+ assert_size_change -1 do
185
+ assert_compute(:a, false, nil) {}
186
+ end
187
+ end
188
+ end
189
+
190
+ def test_compute_with_return
191
+ with_or_without_default_proc do
192
+ assert_handles_return_lambda(:compute, :a)
193
+ @cache[:a] = 1
194
+ assert_handles_return_lambda(:compute, :a)
195
+ end
196
+ end
197
+
198
+ def test_compute_exception
199
+ with_or_without_default_proc do
200
+ assert_handles_exception(:compute, :a)
201
+ @cache[:a] = 1
202
+ assert_handles_exception(:compute, :a)
203
+ end
204
+ end
205
+
206
+ def test_merge_pair
207
+ with_or_without_default_proc do
208
+ assert_size_change 1 do
209
+ assert_equal(nil, @cache.merge_pair(:a, nil) {flunk})
210
+ assert_equal true, @cache.key?(:a)
211
+ assert_equal nil, @cache[:a]
212
+ end
213
+
214
+ assert_no_size_change do
215
+ assert_merge_pair(:a, nil, nil, false) {false}
216
+ assert_merge_pair(:a, nil, false, 1) {1}
217
+ assert_merge_pair(:a, nil, 1, 2) {2}
218
+ end
219
+
220
+ assert_size_change -1 do
221
+ assert_merge_pair(:a, nil, 2, nil) {}
222
+ assert_equal false, @cache.key?(:a)
223
+ end
224
+ end
225
+ end
226
+
227
+ def test_merge_pair_with_return
228
+ with_or_without_default_proc do
229
+ @cache[:a] = 1
230
+ assert_handles_return_lambda(:merge_pair, :a, 2)
231
+ end
232
+ end
233
+
234
+ def test_merge_pair_exception
235
+ with_or_without_default_proc do
236
+ @cache[:a] = 1
237
+ assert_handles_exception(:merge_pair, :a, 2)
238
+ end
239
+ end
240
+
241
+ def test_updates_dont_block_reads
242
+ getters_count = 20
243
+ key_klass = ThreadSafe::Test::HashCollisionKey
244
+ keys = [key_klass.new(1, 100), key_klass.new(2, 100), key_klass.new(3, 100)] # hash colliding keys
245
+ inserted_keys = []
246
+
247
+ keys.each do |key, i|
248
+ compute_started = ThreadSafe::Test::Latch.new(1)
249
+ compute_finished = ThreadSafe::Test::Latch.new(1)
250
+ getters_started = ThreadSafe::Test::Latch.new(getters_count)
251
+ getters_finished = ThreadSafe::Test::Latch.new(getters_count)
252
+
253
+ computer_thread = Thread.new do
254
+ getters_started.await
255
+ @cache.compute_if_absent(key) do
256
+ compute_started.release
257
+ getters_finished.await
258
+ 1
259
+ end
260
+ compute_finished.release
261
+ end
262
+
263
+ getter_threads = (1..getters_count).map do
264
+ Thread.new do
265
+ getters_started.release
266
+ inserted_keys.each do |inserted_key|
267
+ assert_equal true, @cache.key?(inserted_key)
268
+ assert_equal 1, @cache[inserted_key]
269
+ end
270
+ assert_equal false, @cache.key?(key)
271
+ compute_started.await
272
+ inserted_keys.each do |inserted_key|
273
+ assert_equal true, @cache.key?(inserted_key)
274
+ assert_equal 1, @cache[inserted_key]
275
+ end
276
+ assert_equal false, @cache.key?(key)
277
+ assert_equal nil, @cache[key]
278
+ getters_finished.release
279
+ compute_finished.await
280
+ assert_equal true, @cache.key?(key)
281
+ assert_equal 1, @cache[key]
282
+ end
283
+ end
284
+
285
+ (getter_threads << computer_thread).map {|t| assert(t.join(2))} # asserting no deadlocks
286
+ inserted_keys << key
287
+ end
288
+ end
289
+
290
+ def test_collision_resistance
291
+ keys = (0..1000).map {|i| ThreadSafe::Test::HashCollisionKey.new(i, 1)}
292
+ keys.each {|k| @cache[k] = k.key}
293
+ 10.times do |i|
294
+ size = keys.size
295
+ while i < size
296
+ k = keys[i]
297
+ assert(k.key == @cache.delete(k) && !@cache.key?(k) && (@cache[k] = k.key; @cache[k] == k.key))
298
+ i += 10
299
+ end
300
+ end
301
+ assert(keys.all? {|k| @cache[k] == k.key})
302
+ end
303
+
304
+ def test_replace_pair
305
+ with_or_without_default_proc do
306
+ assert_no_size_change do
307
+ assert_equal false, @cache.replace_pair(:a, 1, 2)
308
+ assert_equal false, @cache.replace_pair(:a, nil, nil)
309
+ assert_equal false, @cache.key?(:a)
310
+ end
311
+
312
+ @cache[:a] = 1
313
+ assert_no_size_change do
314
+ assert_equal true, @cache.replace_pair(:a, 1, 2)
315
+ assert_equal false, @cache.replace_pair(:a, 1, 2)
316
+ assert_equal 2, @cache[:a]
317
+ assert_equal true, @cache.replace_pair(:a, 2, 2)
318
+ assert_equal 2, @cache[:a]
319
+ assert_equal true, @cache.replace_pair(:a, 2, nil)
320
+ assert_equal false, @cache.replace_pair(:a, 2, nil)
321
+ assert_equal nil, @cache[:a]
322
+ assert_equal true, @cache.key?(:a)
323
+ assert_equal true, @cache.replace_pair(:a, nil, nil)
324
+ assert_equal true, @cache.key?(:a)
325
+ assert_equal true, @cache.replace_pair(:a, nil, 1)
326
+ assert_equal 1, @cache[:a]
327
+ end
328
+ end
329
+ end
330
+
331
+ def test_replace_if_exists
332
+ with_or_without_default_proc do
333
+ assert_no_size_change do
334
+ assert_equal nil, @cache.replace_if_exists(:a, 1)
335
+ assert_equal false, @cache.key?(:a)
336
+ end
337
+
338
+ @cache[:a] = 1
339
+ assert_no_size_change do
340
+ assert_equal 1, @cache.replace_if_exists(:a, 2)
341
+ assert_equal 2, @cache[:a]
342
+ assert_equal 2, @cache.replace_if_exists(:a, nil)
343
+ assert_equal nil, @cache[:a]
344
+ assert_equal true, @cache.key?(:a)
345
+ assert_equal nil, @cache.replace_if_exists(:a, 1)
346
+ assert_equal 1, @cache[:a]
347
+ end
348
+ end
349
+ end
350
+
351
+ def test_get_and_set
352
+ with_or_without_default_proc do
353
+ assert_size_change 1 do
354
+ assert_equal nil, @cache.get_and_set(:a, 1)
355
+ assert_equal true, @cache.key?(:a)
356
+ assert_equal 1, @cache[:a]
357
+ assert_equal 1, @cache.get_and_set(:a, 2)
358
+ assert_equal 2, @cache.get_and_set(:a, nil)
359
+ assert_equal nil, @cache[:a]
360
+ assert_equal true, @cache.key?(:a)
361
+ assert_equal nil, @cache.get_and_set(:a, 1)
362
+ assert_equal 1, @cache[:a]
363
+ end
364
+ end
365
+ end
366
+
367
+ def test_key
368
+ with_or_without_default_proc do
369
+ assert_equal false, @cache.key?(:a)
370
+ @cache[:a] = 1
371
+ assert_equal true, @cache.key?(:a)
372
+ end
373
+ end
374
+
375
+ def test_value
376
+ with_or_without_default_proc do
377
+ assert_equal false, @cache.value?(1)
378
+ @cache[:a] = 1
379
+ assert_equal true, @cache.value?(1)
380
+ end
381
+ end
382
+
383
+ def test_delete
384
+ with_or_without_default_proc do |default_proc_set|
385
+ assert_no_size_change do
386
+ assert_equal nil, @cache.delete(:a)
387
+ end
388
+ @cache[:a] = 1
389
+ assert_size_change -1 do
390
+ assert_equal 1, @cache.delete(:a)
391
+ end
392
+ assert_no_size_change do
393
+ assert_equal nil, @cache[:a] unless default_proc_set
394
+
395
+ assert_equal false, @cache.key?(:a)
396
+ assert_equal nil, @cache.delete(:a)
397
+ end
398
+ end
399
+ end
400
+
401
+ def test_delete_pair
402
+ with_or_without_default_proc do
403
+ assert_no_size_change do
404
+ assert_equal false, @cache.delete_pair(:a, 2)
405
+ assert_equal false, @cache.delete_pair(:a, nil)
406
+ end
407
+ @cache[:a] = 1
408
+ assert_no_size_change do
409
+ assert_equal false, @cache.delete_pair(:a, 2)
410
+ end
411
+ assert_size_change -1 do
412
+ assert_equal 1, @cache[:a]
413
+ assert_equal true, @cache.delete_pair(:a, 1)
414
+ assert_equal false, @cache.delete_pair(:a, 1)
415
+ assert_equal false, @cache.key?(:a)
416
+ end
417
+ end
418
+ end
419
+
420
+ def test_default_proc
421
+ @cache = cache_with_default_proc(1)
422
+ assert_no_size_change do
423
+ assert_equal false, @cache.key?(:a)
424
+ end
425
+ assert_size_change 1 do
426
+ assert_equal 1, @cache[:a]
427
+ assert_equal true, @cache.key?(:a)
428
+ end
429
+ end
430
+
431
+ def test_falsy_default_proc
432
+ @cache = cache_with_default_proc(nil)
433
+ assert_no_size_change do
434
+ assert_equal false, @cache.key?(:a)
435
+ end
436
+ assert_size_change 1 do
437
+ assert_equal nil, @cache[:a]
438
+ assert_equal true, @cache.key?(:a)
439
+ end
440
+ end
441
+
442
+ def test_fetch
443
+ with_or_without_default_proc do |default_proc_set|
444
+ assert_no_size_change do
445
+ assert_equal 1, @cache.fetch(:a, 1)
446
+ assert_equal(1, (@cache.fetch(:a) {1}))
447
+ assert_equal false, @cache.key?(:a)
448
+
449
+ assert_equal nil, @cache[:a] unless default_proc_set
450
+ end
451
+
452
+ @cache[:a] = 1
453
+ assert_no_size_change do
454
+ assert_equal(1, (@cache.fetch(:a) {flunk}))
455
+ end
456
+
457
+ assert_raise(ThreadSafe::Cache::KEY_ERROR) do
458
+ @cache.fetch(:b)
459
+ end
460
+ end
461
+ end
462
+
463
+ def test_falsy_fetch
464
+ with_or_without_default_proc do
465
+ assert_equal false, @cache.key?(:a)
466
+
467
+ assert_no_size_change do
468
+ assert_equal(nil, @cache.fetch(:a, nil))
469
+ assert_equal(false, @cache.fetch(:a, false))
470
+ assert_equal(nil, (@cache.fetch(:a) {}))
471
+ assert_equal(false, (@cache.fetch(:a) {false}))
472
+ end
473
+
474
+ @cache[:a] = nil
475
+ assert_no_size_change do
476
+ assert_equal true, @cache.key?(:a)
477
+ assert_equal(nil, (@cache.fetch(:a) {flunk}))
478
+ end
479
+ end
480
+ end
481
+
482
+ def test_fetch_with_return
483
+ with_or_without_default_proc do
484
+ r = lambda do
485
+ @cache.fetch(:a) { return 10 }
486
+ end.call
487
+
488
+ assert_no_size_change do
489
+ assert_equal 10, r
490
+ assert_equal false, @cache.key?(:a)
491
+ end
492
+ end
493
+ end
494
+
495
+ def test_clear
496
+ @cache[:a] = 1
497
+ assert_size_change -1 do
498
+ assert_equal @cache, @cache.clear
499
+ assert_equal false, @cache.key?(:a)
500
+ assert_equal nil, @cache[:a]
501
+ end
502
+ end
503
+
504
+ def test_each_pair
505
+ @cache.each_pair {|k, v| flunk}
506
+ assert_equal(@cache, (@cache.each_pair {}))
507
+ @cache[:a] = 1
508
+
509
+ h = {}
510
+ @cache.each_pair {|k, v| h[k] = v}
511
+ assert_equal({:a => 1}, h)
512
+
513
+ @cache[:b] = 2
514
+ h = {}
515
+ @cache.each_pair {|k, v| h[k] = v}
516
+ assert_equal({:a => 1, :b => 2}, h)
517
+ end
518
+
519
+ def test_each_pair_iterator
520
+ @cache[:a] = 1
521
+ @cache[:b] = 2
522
+ i = 0
523
+ r = @cache.each_pair do |k, v|
524
+ if i == 0
525
+ i += 1
526
+ next
527
+ flunk
528
+ elsif i == 1
529
+ break :breaked
530
+ end
531
+ end
532
+
533
+ assert_equal :breaked, r
534
+ end
535
+
536
+ def test_each_pair_allows_modification
537
+ @cache[:a] = 1
538
+ @cache[:b] = 1
539
+ @cache[:c] = 1
540
+
541
+ assert_nothing_raised do
542
+ assert_size_change 1 do
543
+ @cache.each_pair do |k, v|
544
+ @cache[:z] = 1
545
+ end
546
+ end
547
+ end
548
+ end
549
+
550
+ def test_keys
551
+ assert_equal [], @cache.keys
552
+
553
+ @cache[1] = 1
554
+ assert_equal [1], @cache.keys
555
+
556
+ @cache[2] = 2
557
+ assert_equal [1, 2], @cache.keys.sort
558
+ end
559
+
560
+ def test_values
561
+ assert_equal [], @cache.values
562
+
563
+ @cache[1] = 1
564
+ assert_equal [1], @cache.values
565
+
566
+ @cache[2] = 2
567
+ assert_equal [1, 2], @cache.values.sort
568
+ end
569
+
570
+ def test_each_key
571
+ assert_equal(@cache, (@cache.each_key {flunk}))
572
+
573
+ @cache[1] = 1
574
+ arr = []
575
+ @cache.each_key {|k| arr << k}
576
+ assert_equal [1], arr
577
+
578
+ @cache[2] = 2
579
+ arr = []
580
+ @cache.each_key {|k| arr << k}
581
+ assert_equal [1, 2], arr.sort
582
+ end
583
+
584
+ def test_each_value
585
+ assert_equal(@cache, (@cache.each_value {flunk}))
586
+
587
+ @cache[1] = 1
588
+ arr = []
589
+ @cache.each_value {|k| arr << k}
590
+ assert_equal [1], arr
591
+
592
+ @cache[2] = 2
593
+ arr = []
594
+ @cache.each_value {|k| arr << k}
595
+ assert_equal [1, 2], arr.sort
596
+ end
597
+
598
+ def test_empty
599
+ assert_equal true, @cache.empty?
600
+ @cache[:a] = 1
601
+ assert_equal false, @cache.empty?
602
+ end
603
+
604
+ def test_options_validation
605
+ assert_valid_options(nil)
606
+ assert_valid_options({})
607
+ assert_valid_options(:foo => :bar)
608
+ end
609
+
610
+ def test_initial_capacity_options_validation
611
+ assert_valid_option(:initial_capacity, nil)
612
+ assert_valid_option(:initial_capacity, 1)
613
+ assert_invalid_option(:initial_capacity, '')
614
+ assert_invalid_option(:initial_capacity, 1.0)
615
+ assert_invalid_option(:initial_capacity, -1)
616
+ end
617
+
618
+ def test_load_factor_options_validation
619
+ assert_valid_option(:load_factor, nil)
620
+ assert_valid_option(:load_factor, 0.01)
621
+ assert_valid_option(:load_factor, 0.75)
622
+ assert_valid_option(:load_factor, 1)
623
+ assert_invalid_option(:load_factor, '')
624
+ assert_invalid_option(:load_factor, 0)
625
+ assert_invalid_option(:load_factor, 1.1)
626
+ assert_invalid_option(:load_factor, 2)
627
+ assert_invalid_option(:load_factor, -1)
628
+ end
629
+
630
+ def test_size
631
+ assert_equal 0, @cache.size
632
+ @cache[:a] = 1
633
+ assert_equal 1, @cache.size
634
+ @cache[:b] = 1
635
+ assert_equal 2, @cache.size
636
+ @cache.delete(:a)
637
+ assert_equal 1, @cache.size
638
+ @cache.delete(:b)
639
+ assert_equal 0, @cache.size
640
+ end
641
+
642
+ def test_get_or_default
643
+ with_or_without_default_proc do
644
+ assert_equal 1, @cache.get_or_default(:a, 1)
645
+ assert_equal nil, @cache.get_or_default(:a, nil)
646
+ assert_equal false, @cache.get_or_default(:a, false)
647
+ assert_equal false, @cache.key?(:a)
648
+
649
+ @cache[:a] = 1
650
+ assert_equal 1, @cache.get_or_default(:a, 2)
651
+ end
652
+ end
653
+
654
+ def test_dup_clone
655
+ [:dup, :clone].each do |meth|
656
+ cache = cache_with_default_proc(:default_value)
657
+ cache[:a] = 1
658
+ dupped = cache.send(meth)
659
+ assert_equal 1, dupped[:a]
660
+ assert_equal 1, dupped.size
661
+ assert_size_change 1, cache do
662
+ assert_no_size_change dupped do
663
+ cache[:b] = 1
664
+ end
665
+ end
666
+ assert_equal false, dupped.key?(:b)
667
+ assert_no_size_change cache do
668
+ assert_size_change -1, dupped do
669
+ dupped.delete(:a)
670
+ end
671
+ end
672
+ assert_equal false, dupped.key?(:a)
673
+ assert_equal true, cache.key?(:a)
674
+ # test default proc
675
+ assert_size_change 1, cache do
676
+ assert_no_size_change dupped do
677
+ assert_equal :default_value, cache[:c]
678
+ assert_equal false, dupped.key?(:c)
679
+ end
680
+ end
681
+ assert_no_size_change cache do
682
+ assert_size_change 1, dupped do
683
+ assert_equal :default_value, dupped[:d]
684
+ assert_equal false, cache.key?(:d)
685
+ end
686
+ end
687
+ end
688
+ end
689
+
690
+ def test_is_unfreezable
691
+ assert_raise(NoMethodError) { @cache.freeze }
692
+ end
693
+
694
+ def test_marshal_dump_load
695
+ assert_nothing_raised do
696
+ new_cache = Marshal.load(Marshal.dump(@cache))
697
+ assert_equal 0, new_cache.size
698
+ end
699
+ @cache[:a] = 1
700
+ new_cache = Marshal.load(Marshal.dump(@cache))
701
+ assert_equal 1, @cache[:a]
702
+ assert_equal 1, new_cache.size
703
+ end
704
+
705
+ def test_marshal_dump_doesnt_work_with_default_proc
706
+ assert_raise(TypeError) do
707
+ Marshal.dump(ThreadSafe::Cache.new {})
708
+ end
709
+ end
710
+
711
+ private
712
+ def with_or_without_default_proc
713
+ yield false
714
+ @cache = ThreadSafe::Cache.new {|h, k| h[k] = :default_value}
715
+ yield true
716
+ end
717
+
718
+ def cache_with_default_proc(default_value = 1)
719
+ ThreadSafe::Cache.new {|cache, k| cache[k] = default_value}
720
+ end
721
+
722
+ def assert_valid_option(option_name, value)
723
+ assert_valid_options(option_name => value)
724
+ end
725
+
726
+ def assert_valid_options(options)
727
+ assert_nothing_raised { ThreadSafe::Cache.new(options) }
728
+ end
729
+
730
+ def assert_invalid_option(option_name, value)
731
+ assert_invalid_options(option_name => value)
732
+ end
733
+
734
+ def assert_invalid_options(options)
735
+ assert_raise(ArgumentError) { ThreadSafe::Cache.new(options) }
736
+ end
737
+
738
+ def assert_size_change(change, cache = @cache)
739
+ start = cache.size
740
+ yield
741
+ assert_equal change, cache.size - start
742
+ end
743
+
744
+ def assert_no_size_change(cache = @cache, &block)
745
+ assert_size_change(0, cache, &block)
746
+ end
747
+
748
+ def assert_handles_return_lambda(method, key, *args)
749
+ before_had_key = @cache.key?(key)
750
+ before_had_value = before_had_key ? @cache[key] : nil
751
+
752
+ returning_lambda = lambda do
753
+ @cache.send(method, key, *args) { return :direct_return }
754
+ end
755
+
756
+ assert_no_size_change do
757
+ assert_equal(:direct_return, returning_lambda.call)
758
+ assert_equal before_had_key, @cache.key?(key)
759
+ assert_equal before_had_value, @cache[key] if before_had_value
760
+ end
761
+ end
762
+
763
+ class TestException < Exception; end
764
+ def assert_handles_exception(method, key, *args)
765
+ before_had_key = @cache.key?(key)
766
+ before_had_value = before_had_key ? @cache[key] : nil
767
+
768
+ assert_no_size_change do
769
+ assert_raise(TestException) do
770
+ @cache.send(method, key, *args) { raise TestException, '' }
771
+ end
772
+ assert_equal before_had_key, @cache.key?(key)
773
+ assert_equal before_had_value, @cache[key] if before_had_value
774
+ end
775
+ end
776
+
777
+ def assert_compute(key, expected_old_value, expected_result)
778
+ result = @cache.compute(:a) do |old_value|
779
+ assert_equal expected_old_value, old_value
780
+ yield
781
+ end
782
+ assert_equal expected_result, result
783
+ end
784
+
785
+ def assert_merge_pair(key, value, expected_old_value, expected_result)
786
+ result = @cache.merge_pair(key, value) do |old_value|
787
+ assert_equal expected_old_value, old_value
788
+ yield
789
+ end
790
+ assert_equal expected_result, result
791
+ end
792
+ end