thread_safe 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +24 -0
  3. data/README.md +19 -5
  4. data/Rakefile +13 -6
  5. data/examples/bench_cache.rb +1 -1
  6. data/ext/org/jruby/ext/thread_safe/JRubyCacheBackendLibrary.java +54 -15
  7. data/ext/org/jruby/ext/thread_safe/jsr166e/ConcurrentHashMap.java +28 -0
  8. data/ext/org/jruby/ext/thread_safe/jsr166e/ConcurrentHashMapV8.java +19 -10
  9. data/ext/org/jruby/ext/thread_safe/jsr166e/LongAdder.java +1 -2
  10. data/ext/org/jruby/ext/thread_safe/jsr166e/Striped64.java +1 -1
  11. data/ext/org/jruby/ext/thread_safe/jsr166e/nounsafe/ConcurrentHashMapV8.java +3788 -0
  12. data/ext/org/jruby/ext/thread_safe/jsr166e/nounsafe/LongAdder.java +204 -0
  13. data/ext/org/jruby/ext/thread_safe/jsr166e/nounsafe/Striped64.java +291 -0
  14. data/ext/org/jruby/ext/thread_safe/jsr166y/ThreadLocalRandom.java +1 -1
  15. data/ext/thread_safe/JrubyCacheBackendService.java +2 -2
  16. data/lib/thread_safe.rb +1 -1
  17. data/lib/thread_safe/atomic_reference_cache_backend.rb +1 -1
  18. data/lib/thread_safe/cache.rb +6 -3
  19. data/lib/thread_safe/mri_cache_backend.rb +2 -2
  20. data/lib/thread_safe/non_concurrent_cache_backend.rb +1 -1
  21. data/lib/thread_safe/synchronized_cache_backend.rb +1 -1
  22. data/lib/thread_safe/synchronized_delegator.rb +36 -19
  23. data/lib/thread_safe/util.rb +1 -1
  24. data/lib/thread_safe/util/adder.rb +1 -1
  25. data/lib/thread_safe/util/atomic_reference.rb +1 -1
  26. data/lib/thread_safe/util/cheap_lockable.rb +1 -1
  27. data/lib/thread_safe/util/power_of_two_tuple.rb +1 -1
  28. data/lib/thread_safe/util/striped64.rb +1 -1
  29. data/lib/thread_safe/util/volatile.rb +1 -1
  30. data/lib/thread_safe/util/volatile_tuple.rb +1 -1
  31. data/lib/thread_safe/util/xor_shift_random.rb +1 -1
  32. data/lib/thread_safe/version.rb +1 -1
  33. data/test/src/thread_safe/SecurityManager.java +21 -0
  34. data/test/test_array.rb +1 -1
  35. data/test/test_cache.rb +27 -10
  36. data/test/test_cache_loops.rb +377 -376
  37. data/test/test_hash.rb +1 -2
  38. data/test/test_helper.rb +33 -3
  39. data/test/test_synchronized_delegator.rb +67 -17
  40. data/thread_safe.gemspec +6 -3
  41. metadata +36 -10
@@ -196,4 +196,4 @@ public class ThreadLocalRandom extends Random {
196
196
  }
197
197
 
198
198
  private static final long serialVersionUID = -5851777807851030925L;
199
- }
199
+ }
@@ -1,7 +1,7 @@
1
1
  package thread_safe;
2
2
 
3
3
  import java.io.IOException;
4
-
4
+
5
5
  import org.jruby.Ruby;
6
6
  import org.jruby.ext.thread_safe.JRubyCacheBackendLibrary;
7
7
  import org.jruby.runtime.load.BasicLibraryService;
@@ -12,4 +12,4 @@ public class JrubyCacheBackendService implements BasicLibraryService {
12
12
  new JRubyCacheBackendLibrary().load(runtime, false);
13
13
  return true;
14
14
  }
15
- }
15
+ }
@@ -28,7 +28,7 @@ module ThreadSafe
28
28
  class Hash < ::Hash
29
29
  include JRuby::Synchronized
30
30
  end
31
- elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
31
+ elsif !defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby'
32
32
  # Because MRI never runs code in parallel, the existing
33
33
  # non-thread-safe structures should usually work fine.
34
34
  Array = ::Array
@@ -919,4 +919,4 @@ module ThreadSafe
919
919
  @counter.add(-by)
920
920
  end
921
921
  end
922
- end
922
+ end
@@ -7,8 +7,8 @@ module ThreadSafe
7
7
  autoload :AtomicReferenceCacheBackend, 'thread_safe/atomic_reference_cache_backend'
8
8
  autoload :SynchronizedCacheBackend, 'thread_safe/synchronized_cache_backend'
9
9
 
10
- ConcurrentCacheBackend =
11
- case defined?(RUBY_ENGINE) && RUBY_ENGINE
10
+ ConcurrentCacheBackend = if defined?(RUBY_ENGINE)
11
+ case RUBY_ENGINE
12
12
  when 'jruby'; JRubyCacheBackend
13
13
  when 'ruby'; MriCacheBackend
14
14
  when 'rbx'; AtomicReferenceCacheBackend
@@ -16,6 +16,9 @@ module ThreadSafe
16
16
  warn 'ThreadSafe: unsupported Ruby engine, using a fully synchronized ThreadSafe::Cache implementation' if $VERBOSE
17
17
  SynchronizedCacheBackend
18
18
  end
19
+ else
20
+ MriCacheBackend
21
+ end
19
22
 
20
23
  class Cache < ConcurrentCacheBackend
21
24
  KEY_ERROR = defined?(KeyError) ? KeyError : IndexError # there is no KeyError in 1.8 mode
@@ -137,4 +140,4 @@ module ThreadSafe
137
140
  end
138
141
  end
139
142
  end
140
- end
143
+ end
@@ -4,7 +4,7 @@ module ThreadSafe
4
4
  #
5
5
  # The previous implementation used `Thread.critical` on 1.8 MRI to implement the 4 composed atomic operations (`put_if_absent`, `replace_pair`,
6
6
  # `replace_if_exists`, `delete_pair`) this however doesn't work for `compute_if_absent` because on 1.8 the Mutex class is itself implemented
7
- # via `Thread.critical` and a call to `Mutex#lock` does not restore the previous `Thread.critical` value (thus any synchronisation clears the
7
+ # via `Thread.critical` and a call to `Mutex#lock` does not restore the previous `Thread.critical` value (thus any synchronisation clears the
8
8
  # `Thread.critical` flag and we loose control). This poses a problem as the provided block might use synchronisation on its own.
9
9
  #
10
10
  # NOTE: a neat idea of writing a c-ext to manually perform atomic put_if_absent, while relying on Ruby not releasing a GVL while calling
@@ -59,4 +59,4 @@ module ThreadSafe
59
59
  WRITE_LOCK.synchronize { super }
60
60
  end
61
61
  end
62
- end
62
+ end
@@ -130,4 +130,4 @@ module ThreadSafe
130
130
  end
131
131
  end
132
132
  end
133
- end
133
+ end
@@ -73,4 +73,4 @@ module ThreadSafe
73
73
  synchronize { super }
74
74
  end
75
75
  end
76
- end
76
+ end
@@ -1,35 +1,52 @@
1
+ require 'delegate'
2
+ require 'monitor'
3
+
1
4
  # This class provides a trivial way to synchronize all calls to a given object
2
- # by wrapping it with a Delegator that performs Mutex#lock/unlock calls around
3
- # the delegated #send. Example:
5
+ # by wrapping it with a `Delegator` that performs `Monitor#enter/exit` calls
6
+ # around the delegated `#send`. Example:
4
7
  #
5
8
  # array = [] # not thread-safe on many impls
6
- # array = MutexedDelegator.new(array) # thread-safe
9
+ # array = SynchronizedDelegator.new([]) # thread-safe
7
10
  #
8
- # A simple Mutex provides a very coarse-grained way to synchronize a given
11
+ # A simple `Monitor` provides a very coarse-grained way to synchronize a given
9
12
  # object, in that it will cause synchronization for methods that have no
10
13
  # need for it, but this is a trivial way to get thread-safety where none may
11
14
  # exist currently on some implementations.
12
15
  #
13
16
  # This class is currently being considered for inclusion into stdlib, via
14
17
  # https://bugs.ruby-lang.org/issues/8556
18
+ class SynchronizedDelegator < SimpleDelegator
15
19
 
16
- require 'delegate'
20
+ def initialize(obj)
21
+ __setobj__(obj)
22
+ @monitor = Monitor.new
23
+ end
17
24
 
18
- unless defined?(SynchronizedDelegator)
19
- class SynchronizedDelegator < SimpleDelegator
20
- def initialize(*)
25
+ def method_missing(method, *args, &block)
26
+ monitor = @monitor
27
+ begin
28
+ monitor.enter
21
29
  super
22
- @mutex = Mutex.new
30
+ ensure
31
+ monitor.exit
23
32
  end
24
-
25
- def method_missing(m, *args, &block)
26
- begin
27
- mutex = @mutex
28
- mutex.lock
29
- super
30
- ensure
31
- mutex.unlock
33
+ end
34
+
35
+ # Work-around for 1.8 std-lib not passing block around to delegate.
36
+ # @private
37
+ def method_missing(method, *args, &block)
38
+ monitor = @monitor
39
+ begin
40
+ monitor.enter
41
+ target = self.__getobj__
42
+ if target.respond_to?(method)
43
+ target.__send__(method, *args, &block)
44
+ else
45
+ super(method, *args, &block)
32
46
  end
47
+ ensure
48
+ monitor.exit
33
49
  end
34
- end
35
- end
50
+ end if RUBY_VERSION[0, 3] == '1.8'
51
+
52
+ end unless defined?(SynchronizedDelegator)
@@ -13,4 +13,4 @@ module ThreadSafe
13
13
  autoload :VolatileTuple, 'thread_safe/util/volatile_tuple'
14
14
  autoload :XorShiftRandom, 'thread_safe/util/xor_shift_random'
15
15
  end
16
- end
16
+ end
@@ -56,4 +56,4 @@ module ThreadSafe
56
56
  end
57
57
  end
58
58
  end
59
- end
59
+ end
@@ -9,4 +9,4 @@ module ThreadSafe
9
9
  defined?(Atomic::InternalReference) ? Atomic::InternalReference : Atomic
10
10
  end
11
11
  end
12
- end
12
+ end
@@ -102,4 +102,4 @@ module ThreadSafe
102
102
  end
103
103
  end
104
104
  end
105
- end
105
+ end
@@ -23,4 +23,4 @@ module ThreadSafe
23
23
  end
24
24
  end
25
25
  end
26
- end
26
+ end
@@ -223,4 +223,4 @@ module ThreadSafe
223
223
  end
224
224
  end
225
225
  end
226
- end
226
+ end
@@ -59,4 +59,4 @@ module ThreadSafe
59
59
  end
60
60
  end
61
61
  end
62
- end
62
+ end
@@ -43,4 +43,4 @@ module ThreadSafe
43
43
  end
44
44
  end
45
45
  end
46
- end
46
+ end
@@ -36,4 +36,4 @@ module ThreadSafe
36
36
  end
37
37
  end
38
38
  end
39
- end
39
+ end
@@ -1,3 +1,3 @@
1
1
  module Threadsafe
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,21 @@
1
+ package thread_safe;
2
+
3
+ import java.security.Permission;
4
+ import java.util.ArrayList;
5
+ import java.util.List;
6
+
7
+ public class SecurityManager extends java.lang.SecurityManager {
8
+ private final List<Permission> deniedPermissions =
9
+ new ArrayList<Permission>();
10
+
11
+ @Override
12
+ public void checkPermission(Permission p) {
13
+ if (deniedPermissions.contains(p)) {
14
+ throw new SecurityException("Denied!");
15
+ }
16
+ }
17
+
18
+ public void deny(Permission p) {
19
+ deniedPermissions.add(p);
20
+ }
21
+ }
@@ -17,4 +17,4 @@ class TestArray < Test::Unit::TestCase
17
17
  end.map(&:join)
18
18
  end
19
19
  end
20
- end
20
+ end
@@ -290,17 +290,21 @@ class TestCache < Test::Unit::TestCase
290
290
  end
291
291
 
292
292
  def test_collision_resistance
293
- keys = (0..1000).map {|i| ThreadSafe::Test::HashCollisionKey.new(i, 1)}
294
- keys.each {|k| @cache[k] = k.key}
295
- 10.times do |i|
296
- size = keys.size
297
- while i < size
298
- k = keys[i]
299
- assert(k.key == @cache.delete(k) && !@cache.key?(k) && (@cache[k] = k.key; @cache[k] == k.key))
300
- i += 10
293
+ assert_collision_resistance((0..1000).map {|i| ThreadSafe::Test::HashCollisionKey(i, 1)})
294
+ end
295
+
296
+ def test_collision_resistance_with_arrays
297
+ special_array_class = Class.new(Array) do
298
+ def key # assert_collision_resistance expects to be able to call .key to get the "real" key
299
+ first.key
301
300
  end
302
301
  end
303
- assert(keys.all? {|k| @cache[k] == k.key})
302
+ # Test collision resistance with a keys that say they responds_to <=>, but then raise exceptions
303
+ # when actually called (ie: an Array filled with non-comparable keys).
304
+ # See https://github.com/headius/thread_safe/issues/19 for more info.
305
+ assert_collision_resistance((0..100).map do |i|
306
+ special_array_class.new([ThreadSafe::Test::HashCollisionKeyNonComparable.new(i, 1)])
307
+ end)
304
308
  end
305
309
 
306
310
  def test_replace_pair
@@ -791,4 +795,17 @@ class TestCache < Test::Unit::TestCase
791
795
  end
792
796
  assert_equal expected_result, result
793
797
  end
794
- end
798
+
799
+ def assert_collision_resistance(keys)
800
+ keys.each {|k| @cache[k] = k.key}
801
+ 10.times do |i|
802
+ size = keys.size
803
+ while i < size
804
+ k = keys[i]
805
+ assert(k.key == @cache.delete(k) && !@cache.key?(k) && (@cache[k] = k.key; @cache[k] == k.key))
806
+ i += 10
807
+ end
808
+ end
809
+ assert(keys.all? {|k| @cache[k] == k.key})
810
+ end
811
+ end
@@ -5,449 +5,450 @@ require File.join(File.dirname(__FILE__), "test_helper")
5
5
 
6
6
  Thread.abort_on_exception = true
7
7
 
8
- class TestCacheTorture < Test::Unit::TestCase
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
22
-
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(:concurrency, code)
42
- end
43
-
44
- def test_put_if_absent
45
- do_thread_loop(:put_if_absent, '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)
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)
47
22
  end
48
- end
49
23
 
50
- def test_compute_if_absent
51
- code = 'cache.compute_if_absent(key) { acc += 1; key }'
52
- do_thread_loop(:compute_if_absent, code) do |result, cache, options, keys|
53
- assert_standard_accumulator_test_result(result, cache, options, keys)
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)
54
43
  end
55
- end
56
44
 
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)
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)
63
48
  end
64
- RUBY_EVAL
65
- do_thread_loop(:compute_put_if_absent, code) do |result, cache, options, keys|
66
- assert_standard_accumulator_test_result(result, cache, options, keys)
67
49
  end
68
- end
69
50
 
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
75
-
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
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
56
+ end
81
57
 
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
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)
68
+ end
69
+ end
87
70
 
88
- def test_add_remove
89
- add_remove
90
- add_remove(LOW_KEY_COUNT_OPTIONS)
91
- add_remove(SINGLE_KEY_COUNT_OPTIONS)
92
- end
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
93
76
 
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
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
99
82
 
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
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
105
88
 
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
89
+ def test_add_remove
90
+ add_remove
91
+ add_remove(LOW_KEY_COUNT_OPTIONS)
92
+ add_remove(SINGLE_KEY_COUNT_OPTIONS)
93
+ end
111
94
 
112
- def test_count_up
113
- count_up
114
- count_up(LOW_KEY_COUNT_OPTIONS)
115
- count_up(SINGLE_KEY_COUNT_OPTIONS)
116
- end
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
117
100
 
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
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
123
106
 
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
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
129
112
 
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(:count_race, 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)
113
+ def test_count_up
114
+ count_up
115
+ count_up(LOW_KEY_COUNT_OPTIONS)
116
+ count_up(SINGLE_KEY_COUNT_OPTIONS)
141
117
  end
142
- end
143
118
 
144
- def test_get_and_set_new
145
- code = 'acc += 1 unless cache.get_and_set(key, key)'
146
- do_thread_loop(:get_and_set_new, code) do |result, cache, options, keys|
147
- assert_standard_accumulator_test_result(result, cache, options, keys)
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)
148
123
  end
149
- end
150
124
 
151
- def test_get_and_set_existing
152
- code = 'acc += 1 if cache.get_and_set(key, key) == -1'
153
- do_thread_loop(:get_and_set_existing, 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)
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)
155
129
  end
156
- end
157
130
 
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 }
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)
166
142
  end
167
- RUBY_EVAL
168
- do_thread_loop(:compute_if_absent_and_present, 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
143
+ end
144
+
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)
176
149
  end
177
- assert_equal(stored_sum, sum(result))
178
- assert_equal(stored_key_count, cache.size)
179
150
  end
180
- end
181
151
 
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)
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)
189
156
  end
190
- RUBY_EVAL
191
- do_thread_loop(:add_remove, 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))
194
157
  end
195
- end
196
158
 
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
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)
208
180
  end
209
- RUBY_EVAL
210
- do_thread_loop(:add_remove_via_compute, 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))
213
181
  end
214
- end
215
182
 
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 }
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))
223
195
  end
224
- RUBY_EVAL
225
- do_thread_loop(:add_remove_via_compute_if_absent_present, 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))
228
196
  end
229
- end
230
197
 
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)
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))
238
214
  end
239
- RUBY_EVAL
240
- do_thread_loop(:add_remove, 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))
243
215
  end
244
- end
245
216
 
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(:count_up, 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)
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))
229
+ end
253
230
  end
254
- end
255
231
 
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
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))
261
244
  end
262
- RUBY_EVAL
263
- do_thread_loop(:count_up_via_compute, 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
245
+ end
246
+
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)
268
254
  end
269
255
  end
270
- end
271
256
 
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(:count_up_via_merge_pair, 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
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
284
270
  end
285
- assert all_match
286
271
  end
287
- end
288
272
 
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(:add_remove_to_zero, 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))
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
287
+ end
297
288
  end
298
- end
299
289
 
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(:add_remove_to_zero_via_merge_pair, 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))
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))
298
+ end
307
299
  end
308
- end
309
300
 
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)
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))
321
308
  end
322
309
  end
323
- end
324
310
 
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
- t = Time.now
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])
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
332
323
  end
333
- end.map(&:value)
334
- yield result, cache, options, keys if block_given?
335
- end
324
+ end
336
325
 
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)
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?
345
335
  end
346
- end
347
336
 
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
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)
345
+ end
346
+ end
415
347
 
416
- def to_keys_array(key_count)
417
- arr = []
418
- key_count.times {|i| arr << i}
419
- arr
420
- end
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
421
415
 
422
- def to_hash_collision_keys_array(key_count)
423
- to_keys_array(key_count).map {|key| ThreadSafe::Test::HashCollisionKey(key)}
424
- end
416
+ def to_keys_array(key_count)
417
+ arr = []
418
+ key_count.times {|i| arr << i}
419
+ arr
420
+ end
425
421
 
426
- def sum(result)
427
- result.inject(0) {|acc, i| acc + i}
428
- end
422
+ def to_hash_collision_keys_array(key_count)
423
+ to_keys_array(key_count).map {|key| ThreadSafe::Test::HashCollisionKey(key)}
424
+ end
429
425
 
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
426
+ def sum(result)
427
+ result.inject(0) {|acc, i| acc + i}
428
+ end
429
+
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
435
435
 
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
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
440
441
  end
441
442
  end
442
- end
443
443
 
444
- def assert_count_up(result, cache, options, keys)
445
- keys.each do |key|
446
- unless value = cache[key]
447
- assert value
444
+ def assert_count_up(result, cache, options, keys)
445
+ keys.each do |key|
446
+ unless value = cache[key]
447
+ assert value
448
+ end
448
449
  end
450
+ assert_equal(sum(cache.values), sum(result))
451
+ assert_equal(options[:key_count], cache.size)
449
452
  end
450
- assert_equal(sum(cache.values), sum(result))
451
- assert_equal(options[:key_count], cache.size)
452
453
  end
453
454
  end