thread_safe 0.1.1-java

Sign up to get free protection for your applications and to get access to all the features.
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