concurrent-ruby 0.3.2 → 0.4.0
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 +76 -225
- data/lib/concurrent.rb +18 -2
- data/lib/concurrent/actor.rb +180 -72
- data/lib/concurrent/channel.rb +28 -0
- data/lib/concurrent/dereferenceable.rb +21 -1
- data/lib/concurrent/event.rb +33 -1
- data/lib/concurrent/postable.rb +98 -0
- data/lib/concurrent/stoppable.rb +20 -0
- data/lib/concurrent/timer_task.rb +213 -8
- data/lib/concurrent/utilities.rb +12 -1
- data/lib/concurrent/version.rb +1 -1
- data/md/actor.md +2 -2
- data/md/agent.md +1 -1
- data/md/channel.md +40 -0
- data/md/dereferenceable.md +7 -9
- data/md/future.md +2 -2
- data/md/promise.md +1 -1
- data/md/scheduled_task.md +1 -1
- data/md/timer_task.md +1 -1
- data/spec/concurrent/actor_spec.rb +48 -255
- data/spec/concurrent/channel_spec.rb +86 -0
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +0 -18
- data/spec/concurrent/event_spec.rb +38 -3
- data/spec/concurrent/global_thread_pool_spec.rb +0 -19
- data/spec/concurrent/postable_shared.rb +222 -0
- data/spec/concurrent/stoppable_shared.rb +37 -0
- data/spec/concurrent/timer_task_spec.rb +37 -1
- metadata +22 -15
- data/lib/concurrent/goroutine.rb +0 -25
- data/md/goroutine.md +0 -54
- data/spec/concurrent/goroutine_spec.rb +0 -52
data/lib/concurrent/utilities.rb
CHANGED
@@ -2,8 +2,20 @@ require 'thread'
|
|
2
2
|
|
3
3
|
module Concurrent
|
4
4
|
|
5
|
+
# Error raised when an operations times out.
|
5
6
|
TimeoutError = Class.new(StandardError)
|
6
7
|
|
8
|
+
# Wait the given number of seconds for the block operation to complete.
|
9
|
+
#
|
10
|
+
# @param [Integer] seconds The number of seconds to wait
|
11
|
+
#
|
12
|
+
# @return The result of the block operation
|
13
|
+
#
|
14
|
+
# @raise Concurrent::TimeoutError when the block operation does not complete
|
15
|
+
# in the allotted number of seconds.
|
16
|
+
#
|
17
|
+
# @note This method is intended to be a simpler and more reliable replacement
|
18
|
+
# to the Ruby standard library `Timeout::timeout` method.
|
7
19
|
def timeout(seconds)
|
8
20
|
|
9
21
|
thread = Thread.new do
|
@@ -20,5 +32,4 @@ module Concurrent
|
|
20
32
|
Thread.kill(thread) unless thread.nil?
|
21
33
|
end
|
22
34
|
module_function :timeout
|
23
|
-
|
24
35
|
end
|
data/lib/concurrent/version.rb
CHANGED
data/md/actor.md
CHANGED
@@ -29,7 +29,7 @@ Actors are defined by subclassing the `Concurrent::Actor` class and overriding t
|
|
29
29
|
`#act` method. The `#act` method can have any signature/arity but
|
30
30
|
|
31
31
|
```ruby
|
32
|
-
def act(*args
|
32
|
+
def act(*args)
|
33
33
|
```
|
34
34
|
|
35
35
|
is the most flexible and least error-prone signature. The `#act` method is called in
|
@@ -146,7 +146,7 @@ to confusion and difficult debugging.
|
|
146
146
|
### Observation
|
147
147
|
|
148
148
|
The `Actor` superclass mixes in the Ruby standard library
|
149
|
-
[Observable](http://ruby-doc.org/stdlib-
|
149
|
+
[Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html)
|
150
150
|
module to provide consistent callbacks upon message processing completion. The normal
|
151
151
|
`Observable` methods, including `#add_observer` behave normally. Once an observer
|
152
152
|
is added to an `Actor` it will be notified of all messages processed *after*
|
data/md/agent.md
CHANGED
@@ -22,7 +22,7 @@ seacrhed in order until one matching the current exception is found. That `rescu
|
|
22
22
|
then be called an passed the exception object. If no matching `rescue` block is found, or none
|
23
23
|
were configured, then the exception will be suppressed.
|
24
24
|
|
25
|
-
`Agent`s also implement Ruby's [Observable](http://ruby-doc.org/stdlib-
|
25
|
+
`Agent`s also implement Ruby's [Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html).
|
26
26
|
Code that observes an `Agent` will receive a callback with the new value any time the value
|
27
27
|
is changed.
|
28
28
|
|
data/md/channel.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Change the dang channel!
|
2
|
+
|
3
|
+
`Channel` is a functional programming variation of `Actor`, based very loosely on the
|
4
|
+
[MailboxProcessor](http://blogs.msdn.com/b/dsyme/archive/2010/02/15/async-and-parallel-design-patterns-in-f-part-3-agents.aspx)
|
5
|
+
agent in [F#](http://msdn.microsoft.com/en-us/library/ee370357.aspx).
|
6
|
+
The `Actor` is used to create objects that receive messages from other
|
7
|
+
threads then processes those messages based on the behavior of the class. `Channel`
|
8
|
+
creates objects that receive messages and processe them using the block given
|
9
|
+
at construction. `Channel` is implemented as a subclass of
|
10
|
+
[Actor](https://github.com/jdantonio/concurrent-ruby/blob/master/md/actor.md)
|
11
|
+
and supports all message-passing methods of that class. `Channel` also supports pools
|
12
|
+
with a shared mailbox.
|
13
|
+
|
14
|
+
See the [Actor](https://github.com/jdantonio/concurrent-ruby/blob/master/md/actor.md)
|
15
|
+
documentation for more detail.
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
require 'concurrent'
|
21
|
+
|
22
|
+
channel = Concurrent::Channel.new do |msg|
|
23
|
+
sleep(1)
|
24
|
+
puts "#{msg}\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
channel.run! => #<Thread:0x007fa123d95fc8 sleep>
|
28
|
+
|
29
|
+
channel.post("Hello, World!") => 1
|
30
|
+
# wait...
|
31
|
+
=> Hello, World!
|
32
|
+
|
33
|
+
future = channel.post? "Don't Panic." => #<Concurrent::Contract:0x007fa123d6d9d8 @state=:pending...
|
34
|
+
future.pending? => true
|
35
|
+
# wait...
|
36
|
+
=> "Don't Panic."
|
37
|
+
future.fulfilled? => true
|
38
|
+
|
39
|
+
channel.stop => true
|
40
|
+
```
|
data/md/dereferenceable.md
CHANGED
@@ -2,18 +2,16 @@
|
|
2
2
|
|
3
3
|
Object references in Ruby are mutable. This can lead to serious problems when
|
4
4
|
the `#value` of a concurrent object is a mutable reference. Which is always the
|
5
|
-
case unless the value is a `Fixnum`, `Symbol`, or similar "
|
5
|
+
case unless the value is a `Fixnum`, `Symbol`, or similar "primitive" data type.
|
6
6
|
Most classes in this library that expose a `#value` getter method do so using
|
7
|
-
the Dereferenceable mixin module.
|
7
|
+
the `Dereferenceable` mixin module.
|
8
8
|
|
9
|
+
Objects with this mixin can be configured with a few options that can help protect
|
10
|
+
the program from potentially dangerous operations.
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
* `:dup_on_deref` when true the `Agent` will call the `#dup` method on the
|
15
|
-
`value` object every time the `#value` methid is called (default: false)
|
16
|
-
* `:freeze_on_deref` when true the `Agent` will call the `#freeze` method on the
|
12
|
+
* `:dup_on_deref` when true will call the `#dup` method on the
|
13
|
+
`value` object every time the `#value` method is called (default: false)
|
14
|
+
* `:freeze_on_deref` when true will call the `#freeze` method on the
|
17
15
|
`value` object every time the `#value` method is called (default: false)
|
18
16
|
* `:copy_on_deref` when given a `Proc` object the `Proc` will be run every time
|
19
17
|
the `#value` method is called. The `Proc` will be given the current `value` as
|
data/md/future.md
CHANGED
@@ -22,7 +22,7 @@ block indefinitely. If `0` the call will not block. Any other integer or float v
|
|
22
22
|
maximum number of seconds to block.
|
23
23
|
|
24
24
|
The `Future` class also includes the Ruby standard library
|
25
|
-
[Observable](http://ruby-doc.org/stdlib-
|
25
|
+
[Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html) module. On fulfillment
|
26
26
|
or rejection all observers will be notified according to the normal `Observable` behavior. The observer
|
27
27
|
callback function will be called with three parameters: the `Time` of fulfillment/rejection, the
|
28
28
|
final `value`, and the final `reason`. Observers added after fulfillment/rejection will still be
|
@@ -35,7 +35,7 @@ A fulfilled example:
|
|
35
35
|
```ruby
|
36
36
|
require 'concurrent'
|
37
37
|
|
38
|
-
count = Concurrent::Future{ sleep(10); 10 }
|
38
|
+
count = Concurrent::Future.new{ sleep(10); 10 }
|
39
39
|
count.state #=> :pending
|
40
40
|
count.pending? #=> true
|
41
41
|
|
data/md/promise.md
CHANGED
@@ -15,7 +15,7 @@ upon resolution. When a promise is rejected all its children will be summarily r
|
|
15
15
|
|
16
16
|
Promises have three possible states: *pending*, *rejected*, and *fulfilled*. When a promise is created it is set
|
17
17
|
to *pending* and will remain in that state until processing is complete. A completed promise is either *rejected*,
|
18
|
-
indicating that an exception was thrown during processing, or *fulfilled*, indicating
|
18
|
+
indicating that an exception was thrown during processing, or *fulfilled*, indicating it succeeded. If a promise is
|
19
19
|
*fulfilled* its `value` will be updated to reflect the result of the operation. If *rejected* the `reason` will
|
20
20
|
be updated with a reference to the thrown exception. The predicate methods `pending?`, `rejected`, and `fulfilled?`
|
21
21
|
can be called at any time to obtain the state of the promise, as can the `state` method, which returns a symbol.
|
data/md/scheduled_task.md
CHANGED
@@ -45,7 +45,7 @@ The result of a `ScheduledTask` can be obtained either synchronously or asynchro
|
|
45
45
|
`ScheduledTask` mixes in both the
|
46
46
|
[Obligation](https://github.com/jdantonio/concurrent-ruby/blob/master/md/obligation.md)
|
47
47
|
module and the
|
48
|
-
[Observable](http://ruby-doc.org/stdlib-
|
48
|
+
[Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html)
|
49
49
|
module from the Ruby standard library. With one exception `ScheduledTask` behaves
|
50
50
|
identically to
|
51
51
|
[Concurrent::Future](https://github.com/jdantonio/concurrent-ruby/blob/master/md/future.md)
|
data/md/timer_task.md
CHANGED
@@ -31,7 +31,7 @@ This class is based on the Java class
|
|
31
31
|
## Observation
|
32
32
|
|
33
33
|
`TimerTask` supports notification through the Ruby standard library
|
34
|
-
[Observable](http://ruby-doc.org/stdlib-
|
34
|
+
[Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html)
|
35
35
|
module. On execution the `TimerTask` will notify the observers with thress arguments:
|
36
36
|
time of execution, the result of the block (or nil on failure), and any raised
|
37
37
|
exceptions (or nil on success). If the timeout interval is exceeded the observer
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require_relative 'postable_shared'
|
2
3
|
require_relative 'runnable_shared'
|
3
4
|
|
4
5
|
module Concurrent
|
@@ -19,242 +20,30 @@ module Concurrent
|
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
23
|
+
## :runnable
|
22
24
|
subject { Class.new(actor_class).new }
|
23
|
-
|
24
25
|
it_should_behave_like :runnable
|
25
26
|
|
26
|
-
|
27
|
-
subject.stop
|
28
|
-
@thread.kill unless @thread.nil?
|
29
|
-
sleep(0.1)
|
30
|
-
end
|
31
|
-
|
32
|
-
context '#post' do
|
33
|
-
|
34
|
-
it 'returns false when not running' do
|
35
|
-
subject.post.should be_false
|
36
|
-
end
|
27
|
+
## :postable
|
37
28
|
|
38
|
-
|
39
|
-
@expected = false
|
40
|
-
actor = actor_class.new{|msg| @expected = msg }
|
41
|
-
@thread = Thread.new{ actor.run }
|
42
|
-
@thread.join(0.1)
|
43
|
-
actor.post(true)
|
44
|
-
@thread.join(0.1)
|
45
|
-
@expected.should be_true
|
46
|
-
actor.stop
|
47
|
-
end
|
29
|
+
let!(:postable_class){ actor_class }
|
48
30
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
@thread.join(0.1)
|
57
|
-
actor.post(true).should == 2
|
58
|
-
actor.stop
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'is aliased a <<' do
|
62
|
-
@expected = false
|
63
|
-
actor = actor_class.new{|msg| @expected = msg }
|
64
|
-
@thread = Thread.new{ actor.run }
|
65
|
-
@thread.join(0.1)
|
66
|
-
actor << true
|
67
|
-
@thread.join(0.1)
|
68
|
-
@expected.should be_true
|
69
|
-
actor.stop
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
context '#post?' do
|
74
|
-
|
75
|
-
it 'returns nil when not running' do
|
76
|
-
subject.post?.should be_false
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'returns an Obligation' do
|
80
|
-
actor = actor_class.new
|
81
|
-
@thread = Thread.new{ actor.run }
|
82
|
-
@thread.join(0.1)
|
83
|
-
obligation = actor.post?(nil)
|
84
|
-
obligation.should be_a(Obligation)
|
85
|
-
actor.stop
|
86
|
-
end
|
87
|
-
|
88
|
-
it 'fulfills the obligation on success' do
|
89
|
-
actor = actor_class.new{|msg| @expected = msg }
|
90
|
-
@thread = Thread.new{ actor.run }
|
91
|
-
@thread.join(0.1)
|
92
|
-
obligation = actor.post?(42)
|
93
|
-
@thread.join(0.1)
|
94
|
-
obligation.should be_fulfilled
|
95
|
-
obligation.value.should == 42
|
96
|
-
actor.stop
|
97
|
-
end
|
98
|
-
|
99
|
-
it 'rejects the obligation on failure' do
|
100
|
-
actor = actor_class.new{|msg| raise StandardError.new('Boom!') }
|
101
|
-
@thread = Thread.new{ actor.run }
|
102
|
-
@thread.join(0.1)
|
103
|
-
obligation = actor.post?(42)
|
104
|
-
@thread.join(0.1)
|
105
|
-
obligation.should be_rejected
|
106
|
-
obligation.reason.should be_a(StandardError)
|
107
|
-
actor.stop
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
context '#post!' do
|
112
|
-
|
113
|
-
it 'raises Concurrent::Runnable::LifecycleError when not running' do
|
114
|
-
expect {
|
115
|
-
subject.post!(1)
|
116
|
-
}.to raise_error(Concurrent::Runnable::LifecycleError)
|
117
|
-
end
|
118
|
-
|
119
|
-
it 'blocks for up to the given number of seconds' do
|
120
|
-
actor = actor_class.new{|msg| sleep }
|
121
|
-
@thread = Thread.new{ actor.run }
|
122
|
-
@thread.join(0.1)
|
123
|
-
start = Time.now.to_i
|
124
|
-
expect {
|
125
|
-
actor.post!(2, nil)
|
126
|
-
}.to raise_error
|
127
|
-
elapsed = Time.now.to_i - start
|
128
|
-
elapsed.should >= 2
|
129
|
-
actor.stop
|
130
|
-
end
|
131
|
-
|
132
|
-
it 'raises Concurrent::TimeoutError when seconds is zero' do
|
133
|
-
actor = actor_class.new{|msg| 42 }
|
134
|
-
@thread = Thread.new{ actor.run }
|
135
|
-
@thread.join(0.1)
|
136
|
-
expect {
|
137
|
-
actor.post!(0, nil)
|
138
|
-
}.to raise_error(Concurrent::TimeoutError)
|
139
|
-
actor.stop
|
140
|
-
end
|
141
|
-
|
142
|
-
it 'raises Concurrent::TimeoutError on timeout' do
|
143
|
-
actor = actor_class.new{|msg| sleep }
|
144
|
-
@thread = Thread.new{ actor.run }
|
145
|
-
@thread.join(0.1)
|
146
|
-
expect {
|
147
|
-
actor.post!(1, nil)
|
148
|
-
}.to raise_error(Concurrent::TimeoutError)
|
149
|
-
actor.stop
|
150
|
-
end
|
151
|
-
|
152
|
-
it 'bubbles the exception on error' do
|
153
|
-
actor = actor_class.new{|msg| raise StandardError.new('Boom!') }
|
154
|
-
@thread = Thread.new{ actor.run }
|
155
|
-
@thread.join(0.1)
|
156
|
-
expect {
|
157
|
-
actor.post!(1, nil)
|
158
|
-
}.to raise_error(StandardError)
|
159
|
-
actor.stop
|
160
|
-
end
|
161
|
-
|
162
|
-
it 'returns the result on success' do
|
163
|
-
actor = actor_class.new{|msg| 42 }
|
164
|
-
@thread = Thread.new{ actor.run }
|
165
|
-
@thread.join(0.1)
|
166
|
-
expected = actor.post!(1, nil)
|
167
|
-
expected.should == 42
|
168
|
-
actor.stop
|
169
|
-
end
|
170
|
-
|
171
|
-
it 'attempts to cancel the operation on timeout' do
|
172
|
-
@expected = 0
|
173
|
-
actor = actor_class.new{|msg| sleep(0.5); @expected += 1 }
|
174
|
-
@thread = Thread.new{ actor.run }
|
175
|
-
@thread.join(0.1)
|
176
|
-
actor.post(nil) # block the actor
|
177
|
-
expect {
|
178
|
-
actor.post!(0.1, nil)
|
179
|
-
}.to raise_error(Concurrent::TimeoutError)
|
180
|
-
sleep(1.5)
|
181
|
-
@expected.should == 1
|
182
|
-
actor.stop
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
context '#forward' do
|
187
|
-
|
188
|
-
let(:sender_clazz) do
|
189
|
-
Class.new(Actor) do
|
190
|
-
def act(*message)
|
191
|
-
if message.first.is_a?(Exception)
|
192
|
-
raise message.first
|
193
|
-
else
|
194
|
-
return message.first
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
let(:receiver_clazz) do
|
201
|
-
Class.new(Actor) do
|
202
|
-
attr_reader :result
|
203
|
-
def act(*message)
|
204
|
-
@result = message.first
|
31
|
+
let(:sender_class) do
|
32
|
+
Class.new(Actor) do
|
33
|
+
def act(*message)
|
34
|
+
if message.first.is_a?(Exception)
|
35
|
+
raise message.first
|
36
|
+
else
|
37
|
+
return message.first
|
205
38
|
end
|
206
39
|
end
|
207
40
|
end
|
41
|
+
end
|
208
42
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
let(:observer) { double('observer') }
|
213
|
-
|
214
|
-
before(:each) do
|
215
|
-
@sender = Thread.new{ sender.run }
|
216
|
-
@receiver = Thread.new{ receiver.run }
|
217
|
-
sleep(0.1)
|
218
|
-
end
|
219
|
-
|
220
|
-
after(:each) do
|
221
|
-
sender.stop
|
222
|
-
receiver.stop
|
223
|
-
sleep(0.1)
|
224
|
-
@sender.kill unless @sender.nil?
|
225
|
-
@receiver.kill unless @receiver.nil?
|
226
|
-
end
|
227
|
-
|
228
|
-
it 'returns false when sender not running' do
|
229
|
-
sender_clazz.new.forward(receiver).should be_false
|
230
|
-
end
|
231
|
-
|
232
|
-
it 'forwards the result to the receiver on success' do
|
233
|
-
sender.forward(receiver, 42)
|
234
|
-
sleep(0.1)
|
235
|
-
receiver.result.should eq 42
|
236
|
-
end
|
43
|
+
let(:sender) { sender_class.new }
|
44
|
+
let(:receiver) { postable_class.new }
|
237
45
|
|
238
|
-
|
239
|
-
sender.forward(receiver, StandardError.new)
|
240
|
-
sleep(0.1)
|
241
|
-
receiver.result.should be_nil
|
242
|
-
end
|
243
|
-
|
244
|
-
it 'notifies observers on success' do
|
245
|
-
observer.should_receive(:update).with(any_args())
|
246
|
-
sender.add_observer(observer)
|
247
|
-
sender.forward(receiver, 42)
|
248
|
-
sleep(0.1)
|
249
|
-
end
|
250
|
-
|
251
|
-
it 'notifies observers on exception' do
|
252
|
-
observer.should_not_receive(:update).with(any_args())
|
253
|
-
sender.add_observer(observer)
|
254
|
-
sender.forward(receiver, StandardError.new)
|
255
|
-
sleep(0.1)
|
256
|
-
end
|
257
|
-
end
|
46
|
+
it_should_behave_like :postable
|
258
47
|
|
259
48
|
context '#run' do
|
260
49
|
|
@@ -394,65 +183,69 @@ module Concurrent
|
|
394
183
|
}.to raise_error(ArgumentError)
|
395
184
|
end
|
396
185
|
|
397
|
-
it 'creates the requested number of
|
398
|
-
mailbox,
|
399
|
-
|
186
|
+
it 'creates the requested number of pool' do
|
187
|
+
mailbox, pool = clazz.pool(5)
|
188
|
+
pool.size.should == 5
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'passes all optional arguments to the individual constructors' do
|
192
|
+
clazz.should_receive(:new).with(1, 2, 3).exactly(5).times
|
193
|
+
clazz.pool(5, 1, 2, 3)
|
400
194
|
end
|
401
195
|
|
402
|
-
it 'passes the block to each actor' do
|
196
|
+
it 'passes a duplicate of the given block to each actor in the pool' do
|
403
197
|
block = proc{ nil }
|
404
|
-
|
405
|
-
|
198
|
+
block.should_receive(:dup).exactly(5).times.and_return(proc{ nil })
|
199
|
+
mailbox, pool = Channel.pool(5, &block)
|
406
200
|
end
|
407
201
|
|
408
|
-
it 'gives all
|
409
|
-
mailbox,
|
410
|
-
mbox1 =
|
411
|
-
mbox2 =
|
202
|
+
it 'gives all pool the same mailbox' do
|
203
|
+
mailbox, pool = clazz.pool(2)
|
204
|
+
mbox1 = pool.first.instance_variable_get(:@queue)
|
205
|
+
mbox2 = pool.last.instance_variable_get(:@queue)
|
412
206
|
mbox1.should eq mbox2
|
413
207
|
end
|
414
208
|
|
415
209
|
it 'returns a Poolbox as the first retval' do
|
416
|
-
mailbox,
|
210
|
+
mailbox, pool = clazz.pool(2)
|
417
211
|
mailbox.should be_a(Actor::Poolbox)
|
418
212
|
end
|
419
213
|
|
420
|
-
it 'gives the Poolbox the same mailbox as the
|
421
|
-
mailbox,
|
214
|
+
it 'gives the Poolbox the same mailbox as the pool' do
|
215
|
+
mailbox, pool = clazz.pool(1)
|
422
216
|
mbox1 = mailbox.instance_variable_get(:@queue)
|
423
|
-
mbox2 =
|
217
|
+
mbox2 = pool.first.instance_variable_get(:@queue)
|
424
218
|
mbox1.should eq mbox2
|
425
219
|
end
|
426
220
|
|
427
|
-
it 'returns an array of
|
428
|
-
mailbox,
|
429
|
-
|
221
|
+
it 'returns an array of pool as the second retval' do
|
222
|
+
mailbox, pool = clazz.pool(2)
|
223
|
+
pool.each do |actor|
|
430
224
|
actor.should be_a(clazz)
|
431
225
|
end
|
432
226
|
end
|
433
227
|
|
434
228
|
it 'posts to the mailbox with Poolbox#post' do
|
435
|
-
|
436
|
-
|
437
|
-
@thread = Thread.new{ actors.first.run }
|
229
|
+
mailbox, pool = clazz.pool(1)
|
230
|
+
@thread = Thread.new{ pool.first.run }
|
438
231
|
sleep(0.1)
|
439
232
|
mailbox.post(42)
|
440
233
|
sleep(0.1)
|
441
|
-
|
234
|
+
pool.first.last_message.should eq [42]
|
235
|
+
pool.first.stop
|
442
236
|
@thread.kill
|
443
|
-
@expected.should be_true
|
444
237
|
end
|
445
238
|
|
446
239
|
it 'posts to the mailbox with Poolbox#<<' do
|
447
240
|
@expected = false
|
448
|
-
mailbox,
|
449
|
-
@thread = Thread.new{
|
241
|
+
mailbox, pool = clazz.pool(1)
|
242
|
+
@thread = Thread.new{ pool.first.run }
|
450
243
|
sleep(0.1)
|
451
244
|
mailbox << 42
|
452
245
|
sleep(0.1)
|
453
|
-
|
246
|
+
pool.first.last_message.should eq [42]
|
247
|
+
pool.first.stop
|
454
248
|
@thread.kill
|
455
|
-
@expected.should be_true
|
456
249
|
end
|
457
250
|
end
|
458
251
|
|
@@ -464,10 +257,10 @@ module Concurrent
|
|
464
257
|
|
465
258
|
context '#pool' do
|
466
259
|
|
467
|
-
it 'creates
|
260
|
+
it 'creates pool of the appropriate subclass' do
|
468
261
|
actor = Class.new(actor_class)
|
469
|
-
mailbox,
|
470
|
-
|
262
|
+
mailbox, pool = actor.pool(1)
|
263
|
+
pool.first.should be_a(actor)
|
471
264
|
end
|
472
265
|
end
|
473
266
|
|