concurrent-ruby 0.6.0.pre.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -8
- data/lib/concurrent.rb +2 -0
- data/lib/concurrent/actor/actor.rb +3 -3
- data/lib/concurrent/actor/postable.rb +1 -1
- data/lib/concurrent/actors.rb +0 -3
- data/lib/concurrent/actress.rb +75 -0
- data/lib/concurrent/actress/ad_hoc.rb +14 -0
- data/lib/concurrent/actress/context.rb +96 -0
- data/lib/concurrent/actress/core.rb +204 -0
- data/lib/concurrent/actress/core_delegations.rb +37 -0
- data/lib/concurrent/actress/doc.md +53 -0
- data/lib/concurrent/actress/envelope.rb +25 -0
- data/lib/concurrent/actress/errors.rb +14 -0
- data/lib/concurrent/actress/reference.rb +64 -0
- data/lib/concurrent/actress/type_check.rb +48 -0
- data/lib/concurrent/agent.rb +20 -11
- data/lib/concurrent/async.rb +54 -25
- data/lib/concurrent/atomic/atomic.rb +48 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +13 -13
- data/lib/concurrent/atomic/atomic_fixnum.rb +9 -17
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +7 -4
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +16 -14
- data/lib/concurrent/atomic/event.rb +11 -16
- data/lib/concurrent/atomics.rb +1 -0
- data/lib/concurrent/channel/channel.rb +4 -2
- data/lib/concurrent/collection/blocking_ring_buffer.rb +1 -1
- data/lib/concurrent/configuration.rb +59 -47
- data/lib/concurrent/delay.rb +28 -12
- data/lib/concurrent/dereferenceable.rb +6 -6
- data/lib/concurrent/errors.rb +30 -0
- data/lib/concurrent/executor/executor.rb +11 -4
- data/lib/concurrent/executor/immediate_executor.rb +1 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +4 -0
- data/lib/concurrent/executor/one_by_one.rb +24 -12
- data/lib/concurrent/executor/per_thread_executor.rb +1 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +2 -1
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +7 -2
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +3 -0
- data/lib/concurrent/executor/timer_set.rb +1 -1
- data/lib/concurrent/future.rb +0 -2
- data/lib/concurrent/ivar.rb +31 -6
- data/lib/concurrent/logging.rb +17 -0
- data/lib/concurrent/mvar.rb +45 -0
- data/lib/concurrent/obligation.rb +61 -20
- data/lib/concurrent/observable.rb +7 -0
- data/lib/concurrent/promise.rb +1 -0
- data/lib/concurrent/runnable.rb +2 -2
- data/lib/concurrent/supervisor.rb +1 -2
- data/lib/concurrent/timer_task.rb +17 -13
- data/lib/concurrent/tvar.rb +113 -73
- data/lib/concurrent/utility/processor_count.rb +141 -116
- data/lib/concurrent/utility/timeout.rb +4 -5
- data/lib/concurrent/version.rb +1 -1
- data/spec/concurrent/actor/postable_shared.rb +1 -1
- data/spec/concurrent/actress_spec.rb +191 -0
- data/spec/concurrent/async_spec.rb +35 -3
- data/spec/concurrent/atomic/atomic_boolean_spec.rb +1 -1
- data/spec/concurrent/atomic/atomic_fixnum_spec.rb +1 -1
- data/spec/concurrent/atomic/atomic_spec.rb +133 -0
- data/spec/concurrent/atomic/count_down_latch_spec.rb +1 -1
- data/spec/concurrent/collection/priority_queue_spec.rb +1 -1
- data/spec/concurrent/configuration_spec.rb +5 -2
- data/spec/concurrent/delay_spec.rb +14 -0
- data/spec/concurrent/exchanger_spec.rb +4 -9
- data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +1 -1
- data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +1 -1
- data/spec/concurrent/executor/java_single_thread_executor_spec.rb +1 -1
- data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +1 -1
- data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +4 -4
- data/spec/concurrent/ivar_spec.rb +2 -2
- data/spec/concurrent/obligation_spec.rb +113 -24
- data/spec/concurrent/observable_shared.rb +4 -0
- data/spec/concurrent/observable_spec.rb +8 -3
- data/spec/concurrent/runnable_spec.rb +2 -2
- data/spec/concurrent/scheduled_task_spec.rb +1 -0
- data/spec/concurrent/supervisor_spec.rb +26 -11
- data/spec/concurrent/timer_task_spec.rb +36 -35
- data/spec/concurrent/tvar_spec.rb +1 -1
- data/spec/concurrent/utility/timer_spec.rb +8 -8
- data/spec/spec_helper.rb +8 -18
- data/spec/support/example_group_extensions.rb +48 -0
- metadata +23 -16
- data/lib/concurrent/actor/actor_context.rb +0 -77
- data/lib/concurrent/actor/actor_ref.rb +0 -67
- data/lib/concurrent/actor/simple_actor_ref.rb +0 -94
- data/lib/concurrent_ruby_ext.bundle +0 -0
- data/spec/concurrent/actor/actor_context_spec.rb +0 -29
- data/spec/concurrent/actor/actor_ref_shared.rb +0 -263
- data/spec/concurrent/actor/simple_actor_ref_spec.rb +0 -135
- data/spec/support/functions.rb +0 -25
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
module TestHelpers
|
5
|
+
def delta(v1, v2)
|
6
|
+
if block_given?
|
7
|
+
v1 = yield(v1)
|
8
|
+
v2 = yield(v2)
|
9
|
+
end
|
10
|
+
return (v1 - v2).abs
|
11
|
+
end
|
12
|
+
|
13
|
+
def mri?
|
14
|
+
RbConfig::CONFIG['ruby_install_name']=~ /^ruby$/i
|
15
|
+
end
|
16
|
+
|
17
|
+
def jruby?
|
18
|
+
RbConfig::CONFIG['ruby_install_name']=~ /^jruby$/i
|
19
|
+
end
|
20
|
+
|
21
|
+
def rbx?
|
22
|
+
RbConfig::CONFIG['ruby_install_name']=~ /^rbx$/i
|
23
|
+
end
|
24
|
+
|
25
|
+
def reset_gem_configuration
|
26
|
+
Concurrent.instance_variable_set(:@configuration, Concurrent::Configuration.new)
|
27
|
+
end
|
28
|
+
|
29
|
+
extend self
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class RSpec::Core::ExampleGroup
|
34
|
+
def self.with_full_reset
|
35
|
+
before(:each) do
|
36
|
+
reset_gem_configuration
|
37
|
+
end
|
38
|
+
|
39
|
+
after(:each) do
|
40
|
+
Thread.list.each do |thread|
|
41
|
+
thread.kill unless thread == Thread.current
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
include Concurrent::TestHelpers
|
47
|
+
extend Concurrent::TestHelpers
|
48
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concurrent-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.0
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jerry D'Antonio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |2
|
14
14
|
Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more.
|
@@ -24,13 +24,21 @@ files:
|
|
24
24
|
- README.md
|
25
25
|
- lib/concurrent.rb
|
26
26
|
- lib/concurrent/actor/actor.rb
|
27
|
-
- lib/concurrent/actor/actor_context.rb
|
28
|
-
- lib/concurrent/actor/actor_ref.rb
|
29
27
|
- lib/concurrent/actor/postable.rb
|
30
|
-
- lib/concurrent/actor/simple_actor_ref.rb
|
31
28
|
- lib/concurrent/actors.rb
|
29
|
+
- lib/concurrent/actress.rb
|
30
|
+
- lib/concurrent/actress/ad_hoc.rb
|
31
|
+
- lib/concurrent/actress/context.rb
|
32
|
+
- lib/concurrent/actress/core.rb
|
33
|
+
- lib/concurrent/actress/core_delegations.rb
|
34
|
+
- lib/concurrent/actress/doc.md
|
35
|
+
- lib/concurrent/actress/envelope.rb
|
36
|
+
- lib/concurrent/actress/errors.rb
|
37
|
+
- lib/concurrent/actress/reference.rb
|
38
|
+
- lib/concurrent/actress/type_check.rb
|
32
39
|
- lib/concurrent/agent.rb
|
33
40
|
- lib/concurrent/async.rb
|
41
|
+
- lib/concurrent/atomic/atomic.rb
|
34
42
|
- lib/concurrent/atomic/atomic_boolean.rb
|
35
43
|
- lib/concurrent/atomic/atomic_fixnum.rb
|
36
44
|
- lib/concurrent/atomic/condition.rb
|
@@ -54,6 +62,7 @@ files:
|
|
54
62
|
- lib/concurrent/dataflow.rb
|
55
63
|
- lib/concurrent/delay.rb
|
56
64
|
- lib/concurrent/dereferenceable.rb
|
65
|
+
- lib/concurrent/errors.rb
|
57
66
|
- lib/concurrent/exchanger.rb
|
58
67
|
- lib/concurrent/executor/cached_thread_pool.rb
|
59
68
|
- lib/concurrent/executor/executor.rb
|
@@ -77,6 +86,7 @@ files:
|
|
77
86
|
- lib/concurrent/executors.rb
|
78
87
|
- lib/concurrent/future.rb
|
79
88
|
- lib/concurrent/ivar.rb
|
89
|
+
- lib/concurrent/logging.rb
|
80
90
|
- lib/concurrent/mvar.rb
|
81
91
|
- lib/concurrent/obligation.rb
|
82
92
|
- lib/concurrent/observable.rb
|
@@ -94,16 +104,14 @@ files:
|
|
94
104
|
- lib/concurrent/utility/timer.rb
|
95
105
|
- lib/concurrent/version.rb
|
96
106
|
- lib/concurrent_ruby.rb
|
97
|
-
- lib/concurrent_ruby_ext.bundle
|
98
|
-
- spec/concurrent/actor/actor_context_spec.rb
|
99
|
-
- spec/concurrent/actor/actor_ref_shared.rb
|
100
107
|
- spec/concurrent/actor/actor_spec.rb
|
101
108
|
- spec/concurrent/actor/postable_shared.rb
|
102
|
-
- spec/concurrent/
|
109
|
+
- spec/concurrent/actress_spec.rb
|
103
110
|
- spec/concurrent/agent_spec.rb
|
104
111
|
- spec/concurrent/async_spec.rb
|
105
112
|
- spec/concurrent/atomic/atomic_boolean_spec.rb
|
106
113
|
- spec/concurrent/atomic/atomic_fixnum_spec.rb
|
114
|
+
- spec/concurrent/atomic/atomic_spec.rb
|
107
115
|
- spec/concurrent/atomic/condition_spec.rb
|
108
116
|
- spec/concurrent/atomic/copy_on_notify_observer_set_spec.rb
|
109
117
|
- spec/concurrent/atomic/copy_on_write_observer_set_spec.rb
|
@@ -162,7 +170,7 @@ files:
|
|
162
170
|
- spec/concurrent/utility/timeout_spec.rb
|
163
171
|
- spec/concurrent/utility/timer_spec.rb
|
164
172
|
- spec/spec_helper.rb
|
165
|
-
- spec/support/
|
173
|
+
- spec/support/example_group_extensions.rb
|
166
174
|
- spec/support/less_than_or_equal_to_matcher.rb
|
167
175
|
homepage: http://www.concurrent-ruby.com
|
168
176
|
licenses:
|
@@ -179,9 +187,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
179
187
|
version: 1.9.3
|
180
188
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
189
|
requirements:
|
182
|
-
- - "
|
190
|
+
- - ">="
|
183
191
|
- !ruby/object:Gem::Version
|
184
|
-
version:
|
192
|
+
version: '0'
|
185
193
|
requirements: []
|
186
194
|
rubyforge_project:
|
187
195
|
rubygems_version: 2.2.2
|
@@ -190,15 +198,14 @@ specification_version: 4
|
|
190
198
|
summary: Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell,
|
191
199
|
F#, C#, Java, and classic concurrency patterns.
|
192
200
|
test_files:
|
193
|
-
- spec/concurrent/actor/actor_context_spec.rb
|
194
|
-
- spec/concurrent/actor/actor_ref_shared.rb
|
195
201
|
- spec/concurrent/actor/actor_spec.rb
|
196
202
|
- spec/concurrent/actor/postable_shared.rb
|
197
|
-
- spec/concurrent/
|
203
|
+
- spec/concurrent/actress_spec.rb
|
198
204
|
- spec/concurrent/agent_spec.rb
|
199
205
|
- spec/concurrent/async_spec.rb
|
200
206
|
- spec/concurrent/atomic/atomic_boolean_spec.rb
|
201
207
|
- spec/concurrent/atomic/atomic_fixnum_spec.rb
|
208
|
+
- spec/concurrent/atomic/atomic_spec.rb
|
202
209
|
- spec/concurrent/atomic/condition_spec.rb
|
203
210
|
- spec/concurrent/atomic/copy_on_notify_observer_set_spec.rb
|
204
211
|
- spec/concurrent/atomic/copy_on_write_observer_set_spec.rb
|
@@ -257,6 +264,6 @@ test_files:
|
|
257
264
|
- spec/concurrent/utility/timeout_spec.rb
|
258
265
|
- spec/concurrent/utility/timer_spec.rb
|
259
266
|
- spec/spec_helper.rb
|
260
|
-
- spec/support/
|
267
|
+
- spec/support/example_group_extensions.rb
|
261
268
|
- spec/support/less_than_or_equal_to_matcher.rb
|
262
269
|
has_rdoc:
|
@@ -1,77 +0,0 @@
|
|
1
|
-
require 'concurrent/actor/simple_actor_ref'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
# Actor-based concurrency is all the rage in some circles. Originally described in
|
6
|
-
# 1973, the actor model is a paradigm for creating asynchronous, concurrent objects
|
7
|
-
# that is becoming increasingly popular. Much has changed since actors were first
|
8
|
-
# written about four decades ago, which has led to a serious fragmentation within
|
9
|
-
# the actor community. There is *no* universally accepted, strict definition of
|
10
|
-
# "actor" and actor implementations differ widely between languages and libraries.
|
11
|
-
#
|
12
|
-
# A good definition of "actor" is:
|
13
|
-
#
|
14
|
-
# An independent, concurrent, single-purpose, computational entity that communicates exclusively via message passing.
|
15
|
-
#
|
16
|
-
# The actor framework in this library is heavily influenced by the Akka toolkit,
|
17
|
-
# with additional inspiration from Erlang and Scala. Unlike many of the abstractions
|
18
|
-
# in this library, `ActorContext` takes an *object-oriented* approach to asynchronous
|
19
|
-
# concurrency, rather than a *functional programming* approach.
|
20
|
-
#
|
21
|
-
# Creating an actor class is achieved by including the `ActorContext` module
|
22
|
-
# within a standard Ruby class. One `ActorContext` is mixed in, however, everything
|
23
|
-
# changes. Objects of the class can no longer be instanced with the `#new` method.
|
24
|
-
# Instead, the various factor methods, such as `#spawn`, must be used. These factory
|
25
|
-
# methods do not return direct references to the actor object. Instead they return
|
26
|
-
# objects of one of the `ActorRef` subclasses. The `ActorRef` objects encapsulate
|
27
|
-
# actor objects. This encapsulation is necessary to prevent synchronous method calls
|
28
|
-
# froom being made against the actors. It also allows the messaging and lifecycle
|
29
|
-
# behavior to be implemented within the `ActorRef` subclasses, allowing for better
|
30
|
-
# separation of responsibility.
|
31
|
-
#
|
32
|
-
# @see Concurrent::ActorRef
|
33
|
-
#
|
34
|
-
# @see http://akka.io/
|
35
|
-
# @see http://www.erlang.org/doc/getting_started/conc_prog.html
|
36
|
-
# @see http://www.scala-lang.org/api/current/index.html#scala.actors.Actor
|
37
|
-
#
|
38
|
-
# @see http://doc.akka.io/docs/akka/snapshot/general/supervision.html#What_Restarting_Means What Restarting Means
|
39
|
-
# @see http://doc.akka.io/docs/akka/snapshot/general/supervision.html#What_Lifecycle_Monitoring_Means What Lifecycle Monitoring Means
|
40
|
-
module ActorContext
|
41
|
-
|
42
|
-
# Callback method called by the `ActorRef` which encapsulates the actor instance.
|
43
|
-
def on_start
|
44
|
-
end
|
45
|
-
|
46
|
-
# Callback method called by the `ActorRef` which encapsulates the actor instance.
|
47
|
-
def on_shutdown
|
48
|
-
end
|
49
|
-
|
50
|
-
# Callback method called by the `ActorRef` which encapsulates the actor instance.
|
51
|
-
#
|
52
|
-
# @param [Time] time the date/time at which the error occurred
|
53
|
-
# @param [Array] message the message that caused the error
|
54
|
-
# @param [Exception] exception the exception object that was raised
|
55
|
-
def on_error(time, message, exception)
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.included(base)
|
59
|
-
|
60
|
-
class << base
|
61
|
-
|
62
|
-
# Create a single, unregistered actor. The actor will run on its own, dedicated
|
63
|
-
# thread. The thread will be started the first time a message is post to the actor.
|
64
|
-
# Should the thread ever die it will be restarted the next time a message is post.
|
65
|
-
#
|
66
|
-
# @param [Hash] opts the options defining actor behavior
|
67
|
-
# @option opts [Array] :args (`nil`) arguments to be passed to the actor constructor
|
68
|
-
#
|
69
|
-
# @return [SimpleActorRef] the `ActorRef` encapsulating the actor
|
70
|
-
def spawn(opts = {})
|
71
|
-
args = opts.fetch(:args, [])
|
72
|
-
Concurrent::SimpleActorRef.new(self.new(*args), opts)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
module Concurrent
|
2
|
-
|
3
|
-
# Base class for classes that encapsulate `ActorContext` objects.
|
4
|
-
#
|
5
|
-
# @see Concurrent::ActorContext
|
6
|
-
module ActorRef
|
7
|
-
|
8
|
-
# @!method post(*msg, &block)
|
9
|
-
#
|
10
|
-
# Send a message and return a future which will eventually be updated
|
11
|
-
# with the result of the operation. An optional callback block can be
|
12
|
-
# given which will be called once the operation is complete. Although
|
13
|
-
# it is possible to use a callback block and also interrogate the
|
14
|
-
# returned future it is a good practice to do one or the other.
|
15
|
-
#
|
16
|
-
# @param [Array] msg One or more elements of the message
|
17
|
-
#
|
18
|
-
# @yield a callback operation to be performed when the operation is complete.
|
19
|
-
# @yieldparam [Time] time the date/time at which the error occurred
|
20
|
-
# @yieldparam [Object] result the result of message processing or `nil` on error
|
21
|
-
# @yieldparam [Exception] exception the exception object that was raised or `nil` on success
|
22
|
-
#
|
23
|
-
# @return [IVar] a future that will eventually contain the result of message processing
|
24
|
-
|
25
|
-
# @!method post!(timeout, *msg)
|
26
|
-
# Send a message synchronously and block awaiting the response.
|
27
|
-
#
|
28
|
-
# @param [Integer] timeout the maximum number of seconds to block waiting
|
29
|
-
# for a response
|
30
|
-
# @param [Array] msg One or more elements of the message
|
31
|
-
#
|
32
|
-
# @return [Object] the result of successful message processing
|
33
|
-
#
|
34
|
-
# @raise [Concurrent::TimeoutError] if a timeout occurs
|
35
|
-
# @raise [Exception] an exception which occurred during message processing
|
36
|
-
|
37
|
-
# @!method running?()
|
38
|
-
# Is the actor running and processing messages?
|
39
|
-
# @return [Boolean] `true` if running else `false`
|
40
|
-
|
41
|
-
# @!method shutdown?()
|
42
|
-
# Is the actor shutdown and no longer processing messages?
|
43
|
-
# @return [Boolean] `true` if shutdown else `false`
|
44
|
-
|
45
|
-
# @!method shutdown()
|
46
|
-
# Shutdown the actor, gracefully exit all threads, and stop processing messages.
|
47
|
-
# @return [Boolean] `true` if shutdown is successful else `false`
|
48
|
-
|
49
|
-
# @!method join(limit = nil)
|
50
|
-
# Suspend the current thread until the actor has been shutdown
|
51
|
-
# @param [Integer] limit the maximum number of seconds to block waiting for the
|
52
|
-
# actor to shutdown. Block indefinitely when `nil` or not given
|
53
|
-
# @return [Boolean] `true` if the actor shutdown before the limit expired else `false`
|
54
|
-
# @see http://www.ruby-doc.org/core-2.1.1/Thread.html#method-i-join
|
55
|
-
|
56
|
-
# Send a message and return. It's a fire-and-forget interaction.
|
57
|
-
#
|
58
|
-
# @param [Object] message a single value representing the message to be sent
|
59
|
-
# to the actor or an array of multiple message components
|
60
|
-
#
|
61
|
-
# @return [ActorRef] self
|
62
|
-
def <<(message)
|
63
|
-
post(*message)
|
64
|
-
self
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
@@ -1,94 +0,0 @@
|
|
1
|
-
require 'concurrent/actor/actor_ref'
|
2
|
-
require 'concurrent/atomic/event'
|
3
|
-
require 'concurrent/executor/single_thread_executor'
|
4
|
-
require 'concurrent/ivar'
|
5
|
-
|
6
|
-
module Concurrent
|
7
|
-
|
8
|
-
class SimpleActorRef
|
9
|
-
include ActorRef
|
10
|
-
|
11
|
-
def initialize(actor, opts = {})
|
12
|
-
@actor = actor
|
13
|
-
@mutex = Mutex.new
|
14
|
-
@one_by_one = OneByOne.new
|
15
|
-
@executor = OptionsParser::get_executor_from(opts)
|
16
|
-
@stop_event = Event.new
|
17
|
-
@reset_on_error = opts.fetch(:reset_on_error, true)
|
18
|
-
@exception_class = opts.fetch(:rescue_exception, false) ? Exception : StandardError
|
19
|
-
@args = opts.fetch(:args, []) if @reset_on_error
|
20
|
-
|
21
|
-
@actor.define_singleton_method(:shutdown, &method(:set_stop_event))
|
22
|
-
@actor.on_start
|
23
|
-
end
|
24
|
-
|
25
|
-
def running?
|
26
|
-
not @stop_event.set?
|
27
|
-
end
|
28
|
-
|
29
|
-
def shutdown?
|
30
|
-
@stop_event.set?
|
31
|
-
end
|
32
|
-
|
33
|
-
def post(*msg, &block)
|
34
|
-
raise ArgumentError.new('message cannot be empty') if msg.empty?
|
35
|
-
ivar = IVar.new
|
36
|
-
@one_by_one.post(@executor, Message.new(msg, ivar, block), &method(:process_message))
|
37
|
-
ivar
|
38
|
-
end
|
39
|
-
|
40
|
-
def post!(timeout, *msg)
|
41
|
-
raise Concurrent::TimeoutError unless timeout.nil? || timeout >= 0
|
42
|
-
ivar = self.post(*msg)
|
43
|
-
ivar.value(timeout)
|
44
|
-
if ivar.incomplete?
|
45
|
-
raise Concurrent::TimeoutError
|
46
|
-
elsif ivar.reason
|
47
|
-
raise ivar.reason
|
48
|
-
end
|
49
|
-
ivar.value
|
50
|
-
end
|
51
|
-
|
52
|
-
def shutdown
|
53
|
-
@mutex.synchronize do
|
54
|
-
return if shutdown?
|
55
|
-
@actor.on_shutdown
|
56
|
-
@stop_event.set
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def join(limit = nil)
|
61
|
-
@stop_event.wait(limit)
|
62
|
-
end
|
63
|
-
|
64
|
-
private
|
65
|
-
|
66
|
-
Message = Struct.new(:payload, :ivar, :callback)
|
67
|
-
|
68
|
-
def set_stop_event
|
69
|
-
@stop_event.set
|
70
|
-
end
|
71
|
-
|
72
|
-
def process_message(message)
|
73
|
-
result = ex = nil
|
74
|
-
|
75
|
-
begin
|
76
|
-
result = @actor.receive(*message.payload)
|
77
|
-
rescue @exception_class => ex
|
78
|
-
@actor.on_error(Time.now, message.payload, ex)
|
79
|
-
if @reset_on_error
|
80
|
-
@mutex.synchronize{ @actor = @actor.class.new(*@args) }
|
81
|
-
end
|
82
|
-
ensure
|
83
|
-
now = Time.now
|
84
|
-
message.ivar.complete(ex.nil?, result, ex)
|
85
|
-
|
86
|
-
begin
|
87
|
-
message.callback.call(now, result, ex) if message.callback
|
88
|
-
rescue @exception_class => ex
|
89
|
-
# suppress
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
Binary file
|
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
describe ActorContext do
|
6
|
-
|
7
|
-
let(:described_class) do
|
8
|
-
Class.new do
|
9
|
-
include ActorContext
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
context 'callbacks' do
|
14
|
-
|
15
|
-
subject { described_class.send(:new) }
|
16
|
-
|
17
|
-
specify { subject.should respond_to :on_start }
|
18
|
-
|
19
|
-
specify { subject.should respond_to :on_shutdown }
|
20
|
-
end
|
21
|
-
|
22
|
-
context '#spawn' do
|
23
|
-
|
24
|
-
it 'returns an ActorRef' do
|
25
|
-
described_class.spawn.should be_a ActorRef
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,263 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
def create_actor_test_class
|
4
|
-
Class.new do
|
5
|
-
include Concurrent::ActorContext
|
6
|
-
|
7
|
-
attr_reader :argv
|
8
|
-
|
9
|
-
def initialize(*args)
|
10
|
-
@argv = args
|
11
|
-
end
|
12
|
-
|
13
|
-
def receive(*msg)
|
14
|
-
case msg.first
|
15
|
-
when :poison
|
16
|
-
raise StandardError
|
17
|
-
when :bullet
|
18
|
-
raise Exception
|
19
|
-
when :stop
|
20
|
-
shutdown
|
21
|
-
when :terminate
|
22
|
-
Thread.current.kill
|
23
|
-
when :sleep
|
24
|
-
sleep(msg.last)
|
25
|
-
when :check
|
26
|
-
msg[1].set(msg.last)
|
27
|
-
else
|
28
|
-
msg.first
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
share_examples_for :actor_ref do
|
35
|
-
|
36
|
-
after(:each) do
|
37
|
-
subject.shutdown
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'includes ActorRef' do
|
41
|
-
subject.should be_a Concurrent::ActorRef
|
42
|
-
end
|
43
|
-
|
44
|
-
context 'running and shutdown' do
|
45
|
-
|
46
|
-
specify { subject.should respond_to :shutdown }
|
47
|
-
|
48
|
-
specify { subject.should be_running }
|
49
|
-
|
50
|
-
specify { subject.should_not be_shutdown }
|
51
|
-
|
52
|
-
specify do
|
53
|
-
subject.shutdown
|
54
|
-
sleep(0.1)
|
55
|
-
subject.should be_shutdown
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'defines a shutdown method on the actor(s)' do
|
59
|
-
subject << :foo
|
60
|
-
subject << :stop
|
61
|
-
sleep(0.1)
|
62
|
-
subject.should be_shutdown
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
context '#post' do
|
67
|
-
|
68
|
-
it 'raises an exception when the message is empty' do
|
69
|
-
expect {
|
70
|
-
subject.post
|
71
|
-
}.to raise_error(ArgumentError)
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'returns an IVar' do
|
75
|
-
subject.post(:foo).should be_a Concurrent::IVar
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'fulfills the IVar when message is processed' do
|
79
|
-
ivar = subject.post(:foo)
|
80
|
-
sleep(0.1)
|
81
|
-
ivar.should be_fulfilled
|
82
|
-
ivar.value.should eq :foo
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'rejects the IVar when message processing fails' do
|
86
|
-
ivar = subject.post(:poison)
|
87
|
-
sleep(0.1)
|
88
|
-
ivar.should be_rejected
|
89
|
-
ivar.reason.should be_a StandardError
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
context '#<<' do
|
94
|
-
|
95
|
-
it 'posts the message' do
|
96
|
-
ivar = Concurrent::IVar.new
|
97
|
-
subject << [:check, ivar, :foo]
|
98
|
-
ivar.value(0.1).should eq :foo
|
99
|
-
end
|
100
|
-
|
101
|
-
it 'returns self' do
|
102
|
-
expected = subject
|
103
|
-
result = subject << [1,2,3,4]
|
104
|
-
result.should eq expected
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
context '#post with callback' do
|
109
|
-
|
110
|
-
specify 'on success calls the callback with time and value' do
|
111
|
-
expected_value = expected_reason = nil
|
112
|
-
subject.post(:foo) do |time, value, reason|
|
113
|
-
expected_value = value
|
114
|
-
expected_reason = reason
|
115
|
-
end
|
116
|
-
sleep(0.1)
|
117
|
-
|
118
|
-
expected_value.should eq :foo
|
119
|
-
expected_reason.should be_nil
|
120
|
-
end
|
121
|
-
|
122
|
-
specify 'on failure calls the callback with time and reason' do
|
123
|
-
expected_value = expected_reason = nil
|
124
|
-
subject.post(:poison) do |time, value, reason|
|
125
|
-
expected_value = value
|
126
|
-
expected_reason = reason
|
127
|
-
end
|
128
|
-
sleep(0.1)
|
129
|
-
|
130
|
-
expected_value.should be_nil
|
131
|
-
expected_reason.should be_a StandardError
|
132
|
-
end
|
133
|
-
|
134
|
-
it 'supresses exceptions thrown by the callback' do
|
135
|
-
expected = nil
|
136
|
-
subject.post(:foo){|time, value, reason| raise StandardError }
|
137
|
-
sleep(0.1)
|
138
|
-
|
139
|
-
subject.post(:bar){|time, value, reason| expected = value }
|
140
|
-
sleep(0.1)
|
141
|
-
|
142
|
-
expected.should eq :bar
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
context '#post!' do
|
147
|
-
|
148
|
-
it 'raises an exception when the message is empty' do
|
149
|
-
expect {
|
150
|
-
subject.post!(1)
|
151
|
-
}.to raise_error(ArgumentError)
|
152
|
-
end
|
153
|
-
|
154
|
-
it 'blocks for up to the given number of seconds' do
|
155
|
-
start = Time.now.to_f
|
156
|
-
begin
|
157
|
-
subject.post!(1, :sleep, 2)
|
158
|
-
rescue
|
159
|
-
end
|
160
|
-
delta = Time.now.to_f - start
|
161
|
-
delta.should >= 1
|
162
|
-
delta.should <= 2
|
163
|
-
end
|
164
|
-
|
165
|
-
it 'blocks forever when the timeout is nil' do
|
166
|
-
start = Time.now.to_f
|
167
|
-
subject.post!(nil, :sleep, 1)
|
168
|
-
delta = Time.now.to_f - start
|
169
|
-
delta.should > 1
|
170
|
-
end
|
171
|
-
|
172
|
-
it 'raises a TimeoutError when timeout is zero' do
|
173
|
-
expect {
|
174
|
-
subject.post!(0, :foo)
|
175
|
-
}.to raise_error(Concurrent::TimeoutError)
|
176
|
-
end
|
177
|
-
|
178
|
-
it 'raises a TimeoutError when the timeout is reached' do
|
179
|
-
expect {
|
180
|
-
subject.post!(1, :sleep, 10)
|
181
|
-
}.to raise_error(Concurrent::TimeoutError)
|
182
|
-
end
|
183
|
-
|
184
|
-
it 'returns the result of success processing' do
|
185
|
-
subject.post!(1, :foo).should eq :foo
|
186
|
-
end
|
187
|
-
|
188
|
-
it 'bubbles exceptions thrown during processing' do
|
189
|
-
expect {
|
190
|
-
subject.post!(1, :poison)
|
191
|
-
}.to raise_error(StandardError)
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
context '#join' do
|
196
|
-
|
197
|
-
it 'blocks until shutdown when no limit is given' do
|
198
|
-
start = Time.now
|
199
|
-
subject << :foo # start the actor's thread
|
200
|
-
Thread.new{ sleep(1); subject.shutdown }
|
201
|
-
subject.join
|
202
|
-
stop = Time.now
|
203
|
-
|
204
|
-
subject.should be_shutdown
|
205
|
-
stop.should >= start + 1
|
206
|
-
stop.should <= start + 2
|
207
|
-
end
|
208
|
-
|
209
|
-
it 'blocks for no more than the given number of seconds' do
|
210
|
-
start = Time.now
|
211
|
-
subject << :foo # start the actor's thread
|
212
|
-
Thread.new{ sleep(5); subject.shutdown }
|
213
|
-
subject.join(1)
|
214
|
-
stop = Time.now
|
215
|
-
|
216
|
-
stop.should >= start + 1
|
217
|
-
stop.should <= start + 2
|
218
|
-
end
|
219
|
-
|
220
|
-
it 'returns true when shutdown has completed before timeout' do
|
221
|
-
subject << :foo # start the actor's thread
|
222
|
-
Thread.new{ sleep(1); subject.shutdown }
|
223
|
-
subject.join.should be_true
|
224
|
-
end
|
225
|
-
|
226
|
-
it 'returns false on timeout' do
|
227
|
-
subject << :foo # start the actor's thread
|
228
|
-
Thread.new{ sleep(5); subject.shutdown }
|
229
|
-
subject.join(1).should be_false
|
230
|
-
end
|
231
|
-
|
232
|
-
it 'returns immediately when already shutdown' do
|
233
|
-
start = Time.now
|
234
|
-
subject << :foo # start the actor's thread
|
235
|
-
sleep(0.1)
|
236
|
-
subject.shutdown
|
237
|
-
sleep(0.1)
|
238
|
-
|
239
|
-
start = Time.now
|
240
|
-
subject.join
|
241
|
-
Time.now.should >= start
|
242
|
-
Time.now.should <= start + 0.1
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
context '#on_error' do
|
247
|
-
|
248
|
-
specify 'is not called on success' do
|
249
|
-
actor = subject.instance_variable_get(:@actor)
|
250
|
-
actor.should_not_receive(:on_error).with(any_args)
|
251
|
-
subject.post(:foo)
|
252
|
-
sleep(0.1)
|
253
|
-
end
|
254
|
-
|
255
|
-
specify 'is called when a message raises an exception' do
|
256
|
-
actor = subject.instance_variable_get(:@actor)
|
257
|
-
actor.should_receive(:on_error).
|
258
|
-
with(anything, [:poison], an_instance_of(StandardError))
|
259
|
-
subject.post(:poison)
|
260
|
-
sleep(0.1)
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|