|
1 | 1 | describe Concurrent::Edge::AtomicMarkableReference do
|
2 |
| - # use a number outside JRuby's fixnum cache range, to ensure identity is |
3 |
| - # preserved |
4 | 2 | subject { described_class.new 1000, true }
|
5 | 3 |
|
6 |
| - specify :test_construct do |
7 |
| - expect(subject.value).to eq 1000 |
8 |
| - expect(subject.marked?).to eq true |
9 |
| - end |
| 4 | + describe '.initialize' do |
| 5 | + it 'constructs the object' do |
| 6 | + expect(subject.value).to eq 1000 |
| 7 | + expect(subject.marked?).to eq true |
| 8 | + end |
| 9 | + |
| 10 | + it 'has sane defaults' do |
| 11 | + amr = described_class.new |
10 | 12 |
|
11 |
| - specify :test_set do |
12 |
| - val, mark = subject.set 1001, true |
| 13 | + expect(amr.value).to eq nil |
| 14 | + expect(amr.marked?).to eq false |
| 15 | + end |
| 16 | + end |
13 | 17 |
|
14 |
| - expect(subject.value).to eq 1001 |
15 |
| - expect(subject.marked?).to eq true |
| 18 | + describe '#set' do |
| 19 | + it 'sets the value and mark' do |
| 20 | + val, mark = subject.set 1001, true |
16 | 21 |
|
17 |
| - expect(val).to eq 1001 |
18 |
| - expect(mark).to eq true |
| 22 | + expect(subject.value).to eq 1001 |
| 23 | + expect(subject.marked?).to eq true |
| 24 | + expect(val).to eq 1001 |
| 25 | + expect(mark).to eq true |
| 26 | + end |
19 | 27 | end
|
20 | 28 |
|
21 |
| - specify :test_update do |
22 |
| - val, mark = subject.update { |v, m| [v + 1, !m] } |
| 29 | + describe '#try_update' do |
| 30 | + it 'updates the value and mark' do |
| 31 | + val, mark = subject.try_update { |v, m| [v + 1, !m] } |
23 | 32 |
|
24 |
| - expect(subject.value).to eq 1001 |
25 |
| - expect(subject.marked?).to eq false |
| 33 | + expect(subject.value).to eq 1001 |
| 34 | + expect(val).to eq 1001 |
| 35 | + expect(mark).to eq false |
| 36 | + end |
26 | 37 |
|
27 |
| - expect(val).to eq 1001 |
28 |
| - expect(mark).to eq false |
| 38 | + it 'raises ConcurrentUpdateError when attempting to set inside of block' do |
| 39 | + expect do |
| 40 | + subject.try_update do |v, m| |
| 41 | + subject.set(1001, false) |
| 42 | + [v + 1, !m] |
| 43 | + end |
| 44 | + end.to raise_error Concurrent::ConcurrentUpdateError |
| 45 | + end |
29 | 46 | end
|
30 | 47 |
|
31 |
| - specify :test_try_update do |
32 |
| - val, mark = subject.try_update { |v, m| [v + 1, !m] } |
| 48 | + describe '#update' do |
| 49 | + it 'updates the value and mark' do |
| 50 | + val, mark = subject.update { |v, m| [v + 1, !m] } |
33 | 51 |
|
34 |
| - expect(subject.value).to eq 1001 |
| 52 | + expect(subject.value).to eq 1001 |
| 53 | + expect(subject.marked?).to eq false |
35 | 54 |
|
36 |
| - expect(val).to eq 1001 |
37 |
| - expect(mark).to eq false |
38 |
| - end |
| 55 | + expect(val).to eq 1001 |
| 56 | + expect(mark).to eq false |
| 57 | + end |
39 | 58 |
|
40 |
| - specify :test_try_update_fails do |
41 |
| - expect do |
42 |
| - # assigning within block exploits implementation detail for test |
43 |
| - subject.try_update do |v, m| |
| 59 | + it 'retries until update succeeds' do |
| 60 | + tries = 0 |
| 61 | + |
| 62 | + subject.update do |v, m| |
| 63 | + tries += 1 |
44 | 64 | subject.set(1001, false)
|
45 | 65 | [v + 1, !m]
|
46 | 66 | end
|
47 |
| - end.to raise_error Concurrent::ConcurrentUpdateError |
48 |
| - end |
49 |
| - |
50 |
| - specify :test_update_retries do |
51 |
| - tries = 0 |
52 | 67 |
|
53 |
| - # assigning within block exploits implementation detail for test |
54 |
| - subject.update do |v, m| |
55 |
| - tries += 1 |
56 |
| - subject.set(1001, false) |
57 |
| - [v + 1, !m] |
| 68 | + expect(tries).to eq 2 |
58 | 69 | end
|
59 |
| - |
60 |
| - expect(tries).to eq 2 |
61 |
| - end |
62 |
| - |
63 |
| - specify :test_numeric_cas do |
64 |
| - # non-idempotent Float (JRuby, Rubinius, MRI < 2.0.0 or 32-bit) |
65 |
| - subject.set(1.0 + 0.1, true) |
66 |
| - expect(subject.compare_and_set(1.0 + 0.1, 1.2, true, false)) |
67 |
| - .to be_truthy, "CAS failed for (#{1.0 + 0.1}, true) => (1.2, false)" |
68 |
| - |
69 |
| - # Bignum |
70 |
| - subject.set(2**100, false) |
71 |
| - expect(subject.compare_and_set(2**100, 2**99, false, true)) |
72 |
| - .to be_truthy, "CAS failed for (#{2**100}, false) => (0, true)" |
73 |
| - |
74 |
| - # Rational |
75 |
| - require 'rational' unless ''.respond_to? :to_r |
76 |
| - subject.set(Rational(1, 3), true) |
77 |
| - expect(subject.compare_and_set(Rational(1, 3), Rational(3, 1), true, false)) |
78 |
| - .to be_truthy, "CAS failed for (#{Rational(1, 3)}, true) => (0, false)" |
79 |
| - |
80 |
| - # Complex |
81 |
| - require 'complex' unless ''.respond_to? :to_c |
82 |
| - subject.set(Complex(1, 2), false) |
83 |
| - expect(subject.compare_and_set(Complex(1, 2), Complex(1, 3), false, true)) |
84 |
| - .to be_truthy, "CAS failed for (#{Complex(1, 2)}, false) => (0, false)" |
85 | 70 | end
|
86 | 71 |
|
87 | 72 | describe '#compare_and_set' do
|
88 |
| - context 'objects have the same identity' do |
89 |
| - it 'is successful' do |
| 73 | + context 'when objects have the same identity' do |
| 74 | + it 'sets the value and mark' do |
90 | 75 | arr = [1, 2, 3]
|
91 | 76 | subject.set(arr, true)
|
92 | 77 | expect(subject.compare_and_set(arr, 1.2, true, false)).to be_truthy
|
93 | 78 | end
|
94 | 79 | end
|
95 | 80 |
|
96 |
| - context 'objects have the different identity' do |
97 |
| - it 'is not successful' do |
| 81 | + context 'when objects have the different identity' do |
| 82 | + it 'it does not set the value or mark' do |
98 | 83 | subject.set([1, 2, 3], true)
|
99 | 84 | expect(subject.compare_and_set([1, 2, 3], 1.2, true, false))
|
100 | 85 | .to be_falsey
|
101 | 86 | end
|
| 87 | + |
| 88 | + context 'when comparing Numeric objects' do |
| 89 | + context 'Non-idepotent Float' do |
| 90 | + it 'sets the value and mark' do |
| 91 | + subject.set(1.0 + 0.1, true) |
| 92 | + expect(subject.compare_and_set(1.0 + 0.1, 1.2, true, false)) |
| 93 | + .to be_truthy |
| 94 | + end |
| 95 | + end |
| 96 | + |
| 97 | + context 'BigNum' do |
| 98 | + it 'sets the value and mark' do |
| 99 | + subject.set(2**100, false) |
| 100 | + expect(subject.compare_and_set(2**100, 2**99, false, true)) |
| 101 | + .to be_truthy |
| 102 | + end |
| 103 | + end |
| 104 | + |
| 105 | + context 'Rational' do |
| 106 | + it 'sets the value and mark' do |
| 107 | + require 'rational' unless ''.respond_to? :to_r |
| 108 | + subject.set(Rational(1, 3), true) |
| 109 | + comp = subject.compare_and_set(Rational(1, 3), |
| 110 | + Rational(3, 1), |
| 111 | + true, |
| 112 | + false) |
| 113 | + expect(comp).to be_truthy |
| 114 | + end |
| 115 | + end |
| 116 | + end |
| 117 | + |
| 118 | + context 'Rational' do |
| 119 | + it 'is successful' do |
| 120 | + # Complex |
| 121 | + require 'complex' unless ''.respond_to? :to_c |
| 122 | + subject.set(Complex(1, 2), false) |
| 123 | + comp = subject.compare_and_set(Complex(1, 2), |
| 124 | + Complex(1, 3), |
| 125 | + false, |
| 126 | + true) |
| 127 | + expect(comp) |
| 128 | + .to be_truthy |
| 129 | + end |
| 130 | + end |
102 | 131 | end
|
103 | 132 | end
|
104 | 133 | end
|
0 commit comments