concurrent-ruby 0.5.0 → 0.6.0.pre.1
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.
- checksums.yaml +4 -4
- data/README.md +88 -77
- data/lib/concurrent.rb +17 -2
- data/lib/concurrent/actor.rb +17 -0
- data/lib/concurrent/actor_context.rb +31 -0
- data/lib/concurrent/actor_ref.rb +39 -0
- data/lib/concurrent/agent.rb +12 -3
- data/lib/concurrent/async.rb +290 -0
- data/lib/concurrent/atomic.rb +5 -9
- data/lib/concurrent/cached_thread_pool.rb +39 -137
- data/lib/concurrent/channel/blocking_ring_buffer.rb +60 -0
- data/lib/concurrent/channel/buffered_channel.rb +83 -0
- data/lib/concurrent/channel/channel.rb +11 -0
- data/lib/concurrent/channel/probe.rb +19 -0
- data/lib/concurrent/channel/ring_buffer.rb +54 -0
- data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
- data/lib/concurrent/channel/waitable_list.rb +38 -0
- data/lib/concurrent/configuration.rb +92 -0
- data/lib/concurrent/dataflow.rb +9 -3
- data/lib/concurrent/delay.rb +88 -0
- data/lib/concurrent/exchanger.rb +31 -0
- data/lib/concurrent/fixed_thread_pool.rb +28 -122
- data/lib/concurrent/future.rb +10 -5
- data/lib/concurrent/immediate_executor.rb +3 -2
- data/lib/concurrent/ivar.rb +2 -1
- data/lib/concurrent/java_cached_thread_pool.rb +45 -0
- data/lib/concurrent/java_fixed_thread_pool.rb +37 -0
- data/lib/concurrent/java_thread_pool_executor.rb +194 -0
- data/lib/concurrent/per_thread_executor.rb +23 -0
- data/lib/concurrent/postable.rb +2 -0
- data/lib/concurrent/processor_count.rb +125 -0
- data/lib/concurrent/promise.rb +42 -18
- data/lib/concurrent/ruby_cached_thread_pool.rb +37 -0
- data/lib/concurrent/ruby_fixed_thread_pool.rb +31 -0
- data/lib/concurrent/ruby_thread_pool_executor.rb +268 -0
- data/lib/concurrent/ruby_thread_pool_worker.rb +69 -0
- data/lib/concurrent/simple_actor_ref.rb +124 -0
- data/lib/concurrent/thread_local_var.rb +1 -1
- data/lib/concurrent/thread_pool_executor.rb +30 -0
- data/lib/concurrent/timer_task.rb +13 -10
- data/lib/concurrent/tvar.rb +212 -0
- data/lib/concurrent/utilities.rb +1 -0
- data/lib/concurrent/version.rb +1 -1
- data/spec/concurrent/actor_context_spec.rb +37 -0
- data/spec/concurrent/actor_ref_shared.rb +313 -0
- data/spec/concurrent/actor_spec.rb +9 -1
- data/spec/concurrent/agent_spec.rb +97 -96
- data/spec/concurrent/async_spec.rb +320 -0
- data/spec/concurrent/cached_thread_pool_shared.rb +137 -0
- data/spec/concurrent/channel/blocking_ring_buffer_spec.rb +149 -0
- data/spec/concurrent/channel/buffered_channel_spec.rb +151 -0
- data/spec/concurrent/channel/channel_spec.rb +37 -0
- data/spec/concurrent/channel/probe_spec.rb +49 -0
- data/spec/concurrent/channel/ring_buffer_spec.rb +126 -0
- data/spec/concurrent/channel/unbuffered_channel_spec.rb +132 -0
- data/spec/concurrent/configuration_spec.rb +134 -0
- data/spec/concurrent/dataflow_spec.rb +109 -27
- data/spec/concurrent/delay_spec.rb +77 -0
- data/spec/concurrent/exchanger_spec.rb +66 -0
- data/spec/concurrent/fixed_thread_pool_shared.rb +136 -0
- data/spec/concurrent/future_spec.rb +60 -51
- data/spec/concurrent/global_thread_pool_shared.rb +33 -0
- data/spec/concurrent/immediate_executor_spec.rb +4 -25
- data/spec/concurrent/ivar_spec.rb +36 -23
- data/spec/concurrent/java_cached_thread_pool_spec.rb +64 -0
- data/spec/concurrent/java_fixed_thread_pool_spec.rb +64 -0
- data/spec/concurrent/java_thread_pool_executor_spec.rb +71 -0
- data/spec/concurrent/obligation_shared.rb +32 -20
- data/spec/concurrent/{global_thread_pool_spec.rb → per_thread_executor_spec.rb} +9 -13
- data/spec/concurrent/processor_count_spec.rb +20 -0
- data/spec/concurrent/promise_spec.rb +29 -41
- data/spec/concurrent/ruby_cached_thread_pool_spec.rb +69 -0
- data/spec/concurrent/ruby_fixed_thread_pool_spec.rb +39 -0
- data/spec/concurrent/ruby_thread_pool_executor_spec.rb +183 -0
- data/spec/concurrent/simple_actor_ref_spec.rb +219 -0
- data/spec/concurrent/thread_pool_class_cast_spec.rb +40 -0
- data/spec/concurrent/thread_pool_executor_shared.rb +155 -0
- data/spec/concurrent/thread_pool_shared.rb +98 -36
- data/spec/concurrent/tvar_spec.rb +137 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/functions.rb +4 -0
- metadata +85 -20
- data/lib/concurrent/cached_thread_pool/worker.rb +0 -91
- data/lib/concurrent/channel.rb +0 -63
- data/lib/concurrent/fixed_thread_pool/worker.rb +0 -54
- data/lib/concurrent/global_thread_pool.rb +0 -42
- data/spec/concurrent/cached_thread_pool_spec.rb +0 -101
- data/spec/concurrent/channel_spec.rb +0 -86
- data/spec/concurrent/fixed_thread_pool_spec.rb +0 -92
- data/spec/concurrent/uses_global_thread_pool_shared.rb +0 -64
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe BlockingRingBuffer do
|
6
|
+
|
7
|
+
let(:capacity) { 3 }
|
8
|
+
let(:buffer) { BlockingRingBuffer.new(capacity) }
|
9
|
+
|
10
|
+
def fill_buffer
|
11
|
+
capacity.times { buffer.put 3 }
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#capacity' do
|
15
|
+
it 'returns the value passed in constructor' do
|
16
|
+
buffer.capacity.should eq capacity
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#count' do
|
21
|
+
it 'is zero when created' do
|
22
|
+
buffer.count.should eq 0
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'increases when an element is added' do
|
26
|
+
buffer.put 5
|
27
|
+
buffer.count.should eq 1
|
28
|
+
|
29
|
+
buffer.put 1
|
30
|
+
buffer.count.should eq 2
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'decreases when an element is removed' do
|
34
|
+
buffer.put 10
|
35
|
+
|
36
|
+
buffer.take
|
37
|
+
|
38
|
+
buffer.count.should eq 0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#empty?' do
|
43
|
+
it 'is true when count is zero' do
|
44
|
+
buffer.empty?.should be_true
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'is false when count is not zero' do
|
48
|
+
buffer.put 82
|
49
|
+
buffer.empty?.should be_false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#full?' do
|
54
|
+
it 'is true when count is capacity' do
|
55
|
+
fill_buffer
|
56
|
+
buffer.full?.should be_true
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'is false when count is not capacity' do
|
60
|
+
buffer.full?.should be_false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#put' do
|
65
|
+
it 'block when buffer is full' do
|
66
|
+
fill_buffer
|
67
|
+
|
68
|
+
t = Thread.new { buffer.put 32 }
|
69
|
+
|
70
|
+
sleep(0.1)
|
71
|
+
|
72
|
+
t.status.should eq 'sleep'
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'continues when an element is removed' do
|
76
|
+
latch = CountDownLatch.new(1)
|
77
|
+
|
78
|
+
Thread.new { (capacity + 1).times { buffer.put 'hi' }; latch.count_down }
|
79
|
+
Thread.new { sleep(0.1); buffer.take }
|
80
|
+
|
81
|
+
latch.wait(0.2).should be_true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#take' do
|
86
|
+
it 'blocks when buffer is empty' do
|
87
|
+
t = Thread.new { buffer.take }
|
88
|
+
|
89
|
+
sleep(0.1)
|
90
|
+
|
91
|
+
t.status.should eq 'sleep'
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'continues when an element is added' do
|
95
|
+
latch = CountDownLatch.new(1)
|
96
|
+
|
97
|
+
Thread.new { buffer.take; latch.count_down }
|
98
|
+
Thread.new { sleep(0.1); buffer.put 3 }
|
99
|
+
|
100
|
+
latch.wait(0.2).should be_true
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'returns the first added value' do
|
104
|
+
buffer.put 'hi'
|
105
|
+
buffer.put 'foo'
|
106
|
+
buffer.put 'bar'
|
107
|
+
|
108
|
+
buffer.take.should eq 'hi'
|
109
|
+
buffer.take.should eq 'foo'
|
110
|
+
buffer.take.should eq 'bar'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '#peek' do
|
115
|
+
context 'buffer empty' do
|
116
|
+
it 'returns nil when buffer is empty' do
|
117
|
+
buffer.peek.should be_nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'not empty' do
|
122
|
+
|
123
|
+
before(:each) { buffer.put 'element' }
|
124
|
+
|
125
|
+
it 'returns the first value' do
|
126
|
+
buffer.peek.should eq 'element'
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'does not change buffer' do
|
130
|
+
buffer.peek
|
131
|
+
buffer.count.should eq 1
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'circular condition' do
|
137
|
+
it 'can filled many times' do
|
138
|
+
fill_buffer
|
139
|
+
capacity.times { buffer.take }
|
140
|
+
|
141
|
+
buffer.put 'hi'
|
142
|
+
|
143
|
+
buffer.take.should eq 'hi'
|
144
|
+
buffer.capacity.should eq capacity
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe BufferedChannel do
|
6
|
+
|
7
|
+
let(:size) { 2 }
|
8
|
+
let!(:channel) { BufferedChannel.new(size) }
|
9
|
+
let(:probe) { Probe.new }
|
10
|
+
|
11
|
+
context 'without timeout' do
|
12
|
+
|
13
|
+
describe '#push' do
|
14
|
+
it 'adds elements to buffer' do
|
15
|
+
channel.buffer_queue_size.should be 0
|
16
|
+
|
17
|
+
channel.push('a')
|
18
|
+
channel.push('a')
|
19
|
+
|
20
|
+
channel.buffer_queue_size.should be 2
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should block when buffer is full' do
|
24
|
+
channel.push 1
|
25
|
+
channel.push 2
|
26
|
+
|
27
|
+
t = Thread.new { channel.push 3 }
|
28
|
+
sleep(0.05)
|
29
|
+
t.status.should eq 'sleep'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'restarts thread when buffer is no more full' do
|
33
|
+
channel.push 'hi'
|
34
|
+
channel.push 'foo'
|
35
|
+
|
36
|
+
result = nil
|
37
|
+
|
38
|
+
Thread.new { channel.push 'bar'; result = 42 }
|
39
|
+
|
40
|
+
sleep(0.1)
|
41
|
+
|
42
|
+
channel.pop
|
43
|
+
|
44
|
+
sleep(0.1)
|
45
|
+
|
46
|
+
result.should eq 42
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should assign value to a probe if probe set is not empty' do
|
50
|
+
channel.select(probe)
|
51
|
+
Thread.new { sleep(0.1); channel.push 3 }
|
52
|
+
probe.value.should eq 3
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#pop' do
|
57
|
+
it 'should block if buffer is empty' do
|
58
|
+
t = Thread.new { channel.pop }
|
59
|
+
sleep(0.05)
|
60
|
+
t.status.should eq 'sleep'
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'returns value if buffer is not empty' do
|
64
|
+
channel.push 1
|
65
|
+
result = channel.pop
|
66
|
+
|
67
|
+
result.should eq 1
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'removes the first value from the buffer' do
|
71
|
+
channel.push 'a'
|
72
|
+
channel.push 'b'
|
73
|
+
|
74
|
+
channel.pop.should eq 'a'
|
75
|
+
channel.buffer_queue_size.should eq 1
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
describe 'select' do
|
82
|
+
|
83
|
+
it 'does not block' do
|
84
|
+
t = Thread.new { channel.select(probe) }
|
85
|
+
|
86
|
+
sleep(0.05)
|
87
|
+
|
88
|
+
t.status.should eq false
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'gets notified by writer thread' do
|
92
|
+
channel.select(probe)
|
93
|
+
|
94
|
+
Thread.new { channel.push 82 }
|
95
|
+
|
96
|
+
probe.value.should eq 82
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'already set probes' do
|
102
|
+
context 'empty buffer' do
|
103
|
+
it 'discards already set probes' do
|
104
|
+
probe.set('set value')
|
105
|
+
|
106
|
+
channel.select(probe)
|
107
|
+
|
108
|
+
channel.push 27
|
109
|
+
|
110
|
+
channel.buffer_queue_size.should eq 1
|
111
|
+
channel.probe_set_size.should eq 0
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'empty probe set' do
|
116
|
+
it 'discards set probe' do
|
117
|
+
probe.set('set value')
|
118
|
+
|
119
|
+
channel.push 82
|
120
|
+
|
121
|
+
channel.select(probe)
|
122
|
+
|
123
|
+
channel.buffer_queue_size.should eq 1
|
124
|
+
|
125
|
+
channel.pop.should eq 82
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe 'probe set' do
|
132
|
+
|
133
|
+
it 'has size zero after creation' do
|
134
|
+
channel.probe_set_size.should eq 0
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'increases size after a select' do
|
138
|
+
channel.select(probe)
|
139
|
+
channel.probe_set_size.should eq 1
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'decreases size after a removal' do
|
143
|
+
channel.select(probe)
|
144
|
+
channel.remove_probe(probe)
|
145
|
+
channel.probe_set_size.should eq 0
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe Channel do
|
6
|
+
|
7
|
+
describe '.select' do
|
8
|
+
|
9
|
+
context 'without timeout' do
|
10
|
+
it 'returns the first value available on a channel' do
|
11
|
+
channels = [ UnbufferedChannel.new, UnbufferedChannel.new]
|
12
|
+
|
13
|
+
Thread.new { channels[1].push 77 }
|
14
|
+
|
15
|
+
value = Channel.select(*channels)
|
16
|
+
|
17
|
+
value.should eq 77
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'cleans up' do
|
21
|
+
channels = [ UnbufferedChannel.new, UnbufferedChannel.new]
|
22
|
+
channels.each { |ch| ch.stub(:remove_probe).with( an_instance_of(Probe) )}
|
23
|
+
|
24
|
+
Thread.new { channels[1].push 77 }
|
25
|
+
|
26
|
+
value = Channel.select(*channels)
|
27
|
+
|
28
|
+
value.should eq 77
|
29
|
+
|
30
|
+
channels.each { |ch| expect(ch).to have_received(:remove_probe).with( an_instance_of(Probe) ) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe Probe do
|
6
|
+
|
7
|
+
let(:probe) { Probe.new }
|
8
|
+
|
9
|
+
describe '#set_unless_assigned' do
|
10
|
+
context 'empty probe' do
|
11
|
+
it 'assigns the value' do
|
12
|
+
probe.set_unless_assigned(32)
|
13
|
+
probe.value.should eq 32
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns true' do
|
17
|
+
probe.set_unless_assigned('hi').should eq true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'fulfilled probe' do
|
22
|
+
before(:each) { probe.set(27) }
|
23
|
+
|
24
|
+
it 'does not assign the value' do
|
25
|
+
probe.set_unless_assigned(88)
|
26
|
+
probe.value.should eq 27
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns false' do
|
30
|
+
probe.set_unless_assigned('hello').should eq false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'rejected probe' do
|
35
|
+
before(:each) { probe.fail }
|
36
|
+
|
37
|
+
it 'does not assign the value' do
|
38
|
+
probe.set_unless_assigned(88)
|
39
|
+
probe.should be_rejected
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'returns false' do
|
43
|
+
probe.set_unless_assigned('hello').should eq false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
describe RingBuffer do
|
6
|
+
|
7
|
+
let(:capacity) { 3 }
|
8
|
+
let(:buffer) { RingBuffer.new(capacity) }
|
9
|
+
|
10
|
+
def fill_buffer
|
11
|
+
capacity.times { buffer.offer 3 }
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#capacity' do
|
15
|
+
it 'returns the value passed in constructor' do
|
16
|
+
buffer.capacity.should eq capacity
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#count' do
|
21
|
+
it 'is zero when created' do
|
22
|
+
buffer.count.should eq 0
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'increases when an element is added' do
|
26
|
+
buffer.offer 5
|
27
|
+
buffer.count.should eq 1
|
28
|
+
|
29
|
+
buffer.offer 1
|
30
|
+
buffer.count.should eq 2
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'decreases when an element is removed' do
|
34
|
+
buffer.offer 10
|
35
|
+
buffer.poll
|
36
|
+
|
37
|
+
buffer.count.should eq 0
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#empty?' do
|
42
|
+
it 'is true when count is zero' do
|
43
|
+
buffer.empty?.should be_true
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'is false when count is not zero' do
|
47
|
+
buffer.offer 82
|
48
|
+
buffer.empty?.should be_false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#full?' do
|
53
|
+
it 'is true when count is capacity' do
|
54
|
+
fill_buffer
|
55
|
+
buffer.full?.should be_true
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'is false when count is not capacity' do
|
59
|
+
buffer.full?.should be_false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#offer' do
|
64
|
+
it 'returns false when buffer is full' do
|
65
|
+
fill_buffer
|
66
|
+
buffer.offer(3).should be_false
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns true when the buffer is not full' do
|
70
|
+
buffer.offer(5).should be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#poll' do
|
76
|
+
it 'returns the first added value' do
|
77
|
+
buffer.offer 'hi'
|
78
|
+
buffer.offer 'foo'
|
79
|
+
buffer.offer 'bar'
|
80
|
+
|
81
|
+
buffer.poll.should eq 'hi'
|
82
|
+
buffer.poll.should eq 'foo'
|
83
|
+
buffer.poll.should eq 'bar'
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'returns nil when buffer is empty' do
|
87
|
+
buffer.poll.should be_nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#peek' do
|
92
|
+
context 'buffer empty' do
|
93
|
+
it 'returns nil when buffer is empty' do
|
94
|
+
buffer.peek.should be_nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'not empty' do
|
99
|
+
|
100
|
+
before(:each) { buffer.offer 'element' }
|
101
|
+
|
102
|
+
it 'returns the first value' do
|
103
|
+
buffer.peek.should eq 'element'
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'does not change buffer' do
|
107
|
+
buffer.peek
|
108
|
+
buffer.count.should eq 1
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'circular condition' do
|
114
|
+
it 'can filled many times' do
|
115
|
+
fill_buffer
|
116
|
+
capacity.times { buffer.poll }
|
117
|
+
|
118
|
+
buffer.offer 'hi'
|
119
|
+
|
120
|
+
buffer.poll.should eq 'hi'
|
121
|
+
buffer.capacity.should eq capacity
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|