concurrent-ruby 0.6.0.pre.1 → 0.6.0.pre.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -0
- data/lib/concurrent.rb +9 -29
- data/lib/concurrent/{actor.rb → actor/actor.rb} +3 -3
- data/lib/concurrent/actor/actor_context.rb +77 -0
- data/lib/concurrent/actor/actor_ref.rb +67 -0
- data/lib/concurrent/{postable.rb → actor/postable.rb} +1 -1
- data/lib/concurrent/actor/simple_actor_ref.rb +94 -0
- data/lib/concurrent/actors.rb +5 -0
- data/lib/concurrent/agent.rb +81 -47
- data/lib/concurrent/async.rb +35 -35
- data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +170 -0
- data/lib/concurrent/{condition.rb → atomic/condition.rb} +0 -0
- data/lib/concurrent/{copy_on_notify_observer_set.rb → atomic/copy_on_notify_observer_set.rb} +48 -13
- data/lib/concurrent/{copy_on_write_observer_set.rb → atomic/copy_on_write_observer_set.rb} +41 -20
- data/lib/concurrent/atomic/count_down_latch.rb +116 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
- data/lib/concurrent/atomic/event.rb +103 -0
- data/lib/concurrent/{thread_local_var.rb → atomic/thread_local_var.rb} +0 -0
- data/lib/concurrent/atomics.rb +9 -0
- data/lib/concurrent/channel/buffered_channel.rb +6 -4
- data/lib/concurrent/channel/channel.rb +30 -2
- data/lib/concurrent/channel/unbuffered_channel.rb +2 -2
- data/lib/concurrent/channel/waitable_list.rb +3 -1
- data/lib/concurrent/channels.rb +5 -0
- data/lib/concurrent/{channel → collection}/blocking_ring_buffer.rb +16 -5
- data/lib/concurrent/collection/priority_queue.rb +305 -0
- data/lib/concurrent/{channel → collection}/ring_buffer.rb +6 -1
- data/lib/concurrent/collections.rb +3 -0
- data/lib/concurrent/configuration.rb +68 -19
- data/lib/concurrent/dataflow.rb +9 -9
- data/lib/concurrent/delay.rb +21 -13
- data/lib/concurrent/dereferenceable.rb +40 -33
- data/lib/concurrent/exchanger.rb +3 -0
- data/lib/concurrent/{cached_thread_pool.rb → executor/cached_thread_pool.rb} +8 -9
- data/lib/concurrent/executor/executor.rb +222 -0
- data/lib/concurrent/{fixed_thread_pool.rb → executor/fixed_thread_pool.rb} +6 -7
- data/lib/concurrent/{immediate_executor.rb → executor/immediate_executor.rb} +5 -5
- data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
- data/lib/concurrent/{java_fixed_thread_pool.rb → executor/java_fixed_thread_pool.rb} +7 -11
- data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
- data/lib/concurrent/{java_thread_pool_executor.rb → executor/java_thread_pool_executor.rb} +66 -77
- data/lib/concurrent/executor/one_by_one.rb +65 -0
- data/lib/concurrent/{per_thread_executor.rb → executor/per_thread_executor.rb} +4 -4
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
- data/lib/concurrent/{ruby_fixed_thread_pool.rb → executor/ruby_fixed_thread_pool.rb} +5 -4
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +72 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +282 -0
- data/lib/concurrent/{ruby_thread_pool_worker.rb → executor/ruby_thread_pool_worker.rb} +6 -6
- data/lib/concurrent/{safe_task_executor.rb → executor/safe_task_executor.rb} +20 -13
- data/lib/concurrent/executor/single_thread_executor.rb +35 -0
- data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
- data/lib/concurrent/executor/timer_set.rb +138 -0
- data/lib/concurrent/executors.rb +9 -0
- data/lib/concurrent/future.rb +39 -40
- data/lib/concurrent/ivar.rb +22 -15
- data/lib/concurrent/mvar.rb +2 -1
- data/lib/concurrent/obligation.rb +9 -3
- data/lib/concurrent/observable.rb +33 -0
- data/lib/concurrent/options_parser.rb +46 -0
- data/lib/concurrent/promise.rb +23 -24
- data/lib/concurrent/scheduled_task.rb +21 -45
- data/lib/concurrent/timer_task.rb +204 -126
- data/lib/concurrent/tvar.rb +1 -1
- data/lib/concurrent/utilities.rb +3 -36
- data/lib/concurrent/{processor_count.rb → utility/processor_count.rb} +1 -1
- data/lib/concurrent/utility/timeout.rb +36 -0
- data/lib/concurrent/utility/timer.rb +21 -0
- data/lib/concurrent/version.rb +1 -1
- data/lib/concurrent_ruby_ext.bundle +0 -0
- data/spec/concurrent/{actor_context_spec.rb → actor/actor_context_spec.rb} +0 -8
- data/spec/concurrent/{actor_ref_shared.rb → actor/actor_ref_shared.rb} +9 -59
- data/spec/concurrent/{actor_spec.rb → actor/actor_spec.rb} +43 -41
- data/spec/concurrent/{postable_shared.rb → actor/postable_shared.rb} +0 -0
- data/spec/concurrent/actor/simple_actor_ref_spec.rb +135 -0
- data/spec/concurrent/agent_spec.rb +160 -71
- data/spec/concurrent/atomic/atomic_boolean_spec.rb +172 -0
- data/spec/concurrent/atomic/atomic_fixnum_spec.rb +186 -0
- data/spec/concurrent/{condition_spec.rb → atomic/condition_spec.rb} +2 -2
- data/spec/concurrent/{copy_on_notify_observer_set_spec.rb → atomic/copy_on_notify_observer_set_spec.rb} +0 -0
- data/spec/concurrent/{copy_on_write_observer_set_spec.rb → atomic/copy_on_write_observer_set_spec.rb} +0 -0
- data/spec/concurrent/atomic/count_down_latch_spec.rb +151 -0
- data/spec/concurrent/atomic/cyclic_barrier_spec.rb +248 -0
- data/spec/concurrent/{event_spec.rb → atomic/event_spec.rb} +18 -3
- data/spec/concurrent/{observer_set_shared.rb → atomic/observer_set_shared.rb} +15 -6
- data/spec/concurrent/{thread_local_var_spec.rb → atomic/thread_local_var_spec.rb} +0 -0
- data/spec/concurrent/channel/buffered_channel_spec.rb +1 -1
- data/spec/concurrent/channel/channel_spec.rb +6 -4
- data/spec/concurrent/channel/probe_spec.rb +37 -9
- data/spec/concurrent/channel/unbuffered_channel_spec.rb +2 -2
- data/spec/concurrent/{channel → collection}/blocking_ring_buffer_spec.rb +0 -0
- data/spec/concurrent/collection/priority_queue_spec.rb +317 -0
- data/spec/concurrent/{channel → collection}/ring_buffer_spec.rb +0 -0
- data/spec/concurrent/configuration_spec.rb +4 -70
- data/spec/concurrent/dereferenceable_shared.rb +5 -4
- data/spec/concurrent/exchanger_spec.rb +10 -5
- data/spec/concurrent/{cached_thread_pool_shared.rb → executor/cached_thread_pool_shared.rb} +15 -37
- data/spec/concurrent/{fixed_thread_pool_shared.rb → executor/fixed_thread_pool_shared.rb} +0 -0
- data/spec/concurrent/{global_thread_pool_shared.rb → executor/global_thread_pool_shared.rb} +10 -8
- data/spec/concurrent/{immediate_executor_spec.rb → executor/immediate_executor_spec.rb} +0 -0
- data/spec/concurrent/{java_cached_thread_pool_spec.rb → executor/java_cached_thread_pool_spec.rb} +1 -21
- data/spec/concurrent/{java_fixed_thread_pool_spec.rb → executor/java_fixed_thread_pool_spec.rb} +0 -0
- data/spec/concurrent/executor/java_single_thread_executor_spec.rb +21 -0
- data/spec/concurrent/{java_thread_pool_executor_spec.rb → executor/java_thread_pool_executor_spec.rb} +0 -0
- data/spec/concurrent/{per_thread_executor_spec.rb → executor/per_thread_executor_spec.rb} +0 -4
- data/spec/concurrent/{ruby_cached_thread_pool_spec.rb → executor/ruby_cached_thread_pool_spec.rb} +1 -1
- data/spec/concurrent/{ruby_fixed_thread_pool_spec.rb → executor/ruby_fixed_thread_pool_spec.rb} +0 -0
- data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +18 -0
- data/spec/concurrent/{ruby_thread_pool_executor_spec.rb → executor/ruby_thread_pool_executor_spec.rb} +12 -24
- data/spec/concurrent/executor/safe_task_executor_spec.rb +103 -0
- data/spec/concurrent/{thread_pool_class_cast_spec.rb → executor/thread_pool_class_cast_spec.rb} +12 -0
- data/spec/concurrent/{thread_pool_executor_shared.rb → executor/thread_pool_executor_shared.rb} +0 -0
- data/spec/concurrent/{thread_pool_shared.rb → executor/thread_pool_shared.rb} +84 -119
- data/spec/concurrent/executor/timer_set_spec.rb +183 -0
- data/spec/concurrent/future_spec.rb +12 -0
- data/spec/concurrent/ivar_spec.rb +11 -1
- data/spec/concurrent/observable_shared.rb +173 -0
- data/spec/concurrent/observable_spec.rb +51 -0
- data/spec/concurrent/options_parser_spec.rb +71 -0
- data/spec/concurrent/runnable_shared.rb +6 -0
- data/spec/concurrent/scheduled_task_spec.rb +60 -40
- data/spec/concurrent/timer_task_spec.rb +130 -144
- data/spec/concurrent/{processor_count_spec.rb → utility/processor_count_spec.rb} +0 -0
- data/spec/concurrent/{utilities_spec.rb → utility/timeout_spec.rb} +0 -0
- data/spec/concurrent/utility/timer_spec.rb +52 -0
- metadata +147 -108
- data/lib/concurrent/actor_context.rb +0 -31
- data/lib/concurrent/actor_ref.rb +0 -39
- data/lib/concurrent/atomic.rb +0 -121
- data/lib/concurrent/channel/probe.rb +0 -19
- data/lib/concurrent/count_down_latch.rb +0 -60
- data/lib/concurrent/event.rb +0 -80
- data/lib/concurrent/java_cached_thread_pool.rb +0 -45
- data/lib/concurrent/ruby_cached_thread_pool.rb +0 -37
- data/lib/concurrent/ruby_thread_pool_executor.rb +0 -268
- data/lib/concurrent/simple_actor_ref.rb +0 -124
- data/lib/concurrent/thread_pool_executor.rb +0 -30
- data/spec/concurrent/atomic_spec.rb +0 -201
- data/spec/concurrent/count_down_latch_spec.rb +0 -125
- data/spec/concurrent/safe_task_executor_spec.rb +0 -58
- data/spec/concurrent/simple_actor_ref_spec.rb +0 -219
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 873e9e671b06398c80e8c918e5e2ba7c0d75564e
|
4
|
+
data.tar.gz: 69963957bf5e06a7a5b7ca21a678039496874c35
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92bb87ece26412064c4d9697b1d92ce1cc4963d74b6f9c32b32db091d216ff8655bbd1961322db8618666a571f4f9559cf7823aa9db1fa33687b2c45db1bb57a
|
7
|
+
data.tar.gz: 0d2645e119b50b88748b6131bcc984e127399bb9f5988fdfaa095fe915149ad03e60ab530523736006d3eb71a1b86821b6a889c032e9bd3c5d6a04a821d8ca2d
|
data/README.md
CHANGED
@@ -33,6 +33,20 @@ The design goals of this gem are:
|
|
33
33
|
</tr>
|
34
34
|
</table>
|
35
35
|
|
36
|
+
### Install
|
37
|
+
|
38
|
+
```shell
|
39
|
+
gem install concurrent-ruby
|
40
|
+
```
|
41
|
+
or add the following line to Gemfile:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
gem 'concurrent-ruby'
|
45
|
+
```
|
46
|
+
and run `bundle install` from your shell.
|
47
|
+
|
48
|
+
*NOTE: There is an old gem from 2007 called "concurrent" that does not appear to be under active development. That isn't us. Please do not run* `gem install concurrent`*. It is not the droid you are looking for.*
|
49
|
+
|
36
50
|
## Features & Documentation
|
37
51
|
|
38
52
|
Please see the [Concurrent Ruby Wiki](https://github.com/jdantonio/concurrent-ruby/wiki)
|
@@ -128,6 +142,8 @@ ibm.value #=> 187.57
|
|
128
142
|
* [Michele Della Torre](https://github.com/mighe)
|
129
143
|
* [Chris Seaton](https://github.com/chrisseaton)
|
130
144
|
* [Lucas Allan](https://github.com/lucasallan)
|
145
|
+
* [Petr Chalupa](https://github.com/pitr-ch)
|
146
|
+
* [Ravil Bayramgalin](https://github.com/brainopia)
|
131
147
|
* [Giuseppe Capizzi](https://github.com/gcapizzi)
|
132
148
|
* [Brian Shirai](https://github.com/brixen)
|
133
149
|
* [Chip Miller](https://github.com/chip-miller)
|
data/lib/concurrent.rb
CHANGED
@@ -1,52 +1,32 @@
|
|
1
1
|
require 'concurrent/version'
|
2
2
|
require 'concurrent/configuration'
|
3
3
|
|
4
|
-
require 'concurrent/
|
5
|
-
require 'concurrent/
|
6
|
-
require 'concurrent/
|
7
|
-
require 'concurrent/
|
8
|
-
require 'concurrent/
|
9
|
-
require 'concurrent/
|
10
|
-
require 'concurrent/ivar'
|
4
|
+
require 'concurrent/atomics'
|
5
|
+
require 'concurrent/actors'
|
6
|
+
require 'concurrent/channels'
|
7
|
+
require 'concurrent/collections'
|
8
|
+
require 'concurrent/executors'
|
9
|
+
require 'concurrent/utilities'
|
11
10
|
|
12
|
-
require 'concurrent/actor'
|
13
11
|
require 'concurrent/agent'
|
14
12
|
require 'concurrent/async'
|
15
13
|
require 'concurrent/dataflow'
|
16
14
|
require 'concurrent/delay'
|
17
15
|
require 'concurrent/dereferenceable'
|
18
|
-
require 'concurrent/event'
|
19
16
|
require 'concurrent/exchanger'
|
20
17
|
require 'concurrent/future'
|
18
|
+
require 'concurrent/ivar'
|
21
19
|
require 'concurrent/mvar'
|
22
20
|
require 'concurrent/obligation'
|
23
|
-
require 'concurrent/
|
24
|
-
require 'concurrent/
|
21
|
+
require 'concurrent/observable'
|
22
|
+
require 'concurrent/options_parser'
|
25
23
|
require 'concurrent/promise'
|
26
24
|
require 'concurrent/runnable'
|
27
25
|
require 'concurrent/scheduled_task'
|
28
26
|
require 'concurrent/stoppable'
|
29
27
|
require 'concurrent/supervisor'
|
30
|
-
require 'concurrent/thread_local_var'
|
31
28
|
require 'concurrent/timer_task'
|
32
29
|
require 'concurrent/tvar'
|
33
|
-
require 'concurrent/utilities'
|
34
|
-
|
35
|
-
require 'concurrent/channel/probe'
|
36
|
-
require 'concurrent/channel/channel'
|
37
|
-
require 'concurrent/channel/unbuffered_channel'
|
38
|
-
require 'concurrent/channel/buffered_channel'
|
39
|
-
require 'concurrent/channel/ring_buffer'
|
40
|
-
require 'concurrent/channel/blocking_ring_buffer'
|
41
|
-
|
42
|
-
require 'concurrent/actor_context'
|
43
|
-
require 'concurrent/simple_actor_ref'
|
44
|
-
|
45
|
-
require 'concurrent/cached_thread_pool'
|
46
|
-
require 'concurrent/fixed_thread_pool'
|
47
|
-
require 'concurrent/immediate_executor'
|
48
|
-
require 'concurrent/per_thread_executor'
|
49
|
-
require 'concurrent/thread_pool_executor'
|
50
30
|
|
51
31
|
# Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell,
|
52
32
|
# F#, C#, Java, and classic concurrency patterns.
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'observer'
|
3
3
|
|
4
|
-
require 'concurrent/
|
4
|
+
require 'concurrent/actor/postable'
|
5
|
+
require 'concurrent/atomic/event'
|
5
6
|
require 'concurrent/obligation'
|
6
|
-
require 'concurrent/postable'
|
7
7
|
require 'concurrent/runnable'
|
8
8
|
|
9
9
|
module Concurrent
|
@@ -123,7 +123,7 @@ module Concurrent
|
|
123
123
|
#
|
124
124
|
# @see http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html
|
125
125
|
class Actor
|
126
|
-
include Observable
|
126
|
+
include ::Observable
|
127
127
|
include Postable
|
128
128
|
include Runnable
|
129
129
|
|
@@ -0,0 +1,77 @@
|
|
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
|
@@ -0,0 +1,67 @@
|
|
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
|
@@ -0,0 +1,94 @@
|
|
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
|
data/lib/concurrent/agent.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
require 'thread'
|
2
|
-
require 'observer'
|
3
2
|
|
4
|
-
require 'concurrent/configuration'
|
5
3
|
require 'concurrent/dereferenceable'
|
6
|
-
require 'concurrent/
|
4
|
+
require 'concurrent/observable'
|
5
|
+
require 'concurrent/options_parser'
|
6
|
+
require 'concurrent/utility/timeout'
|
7
7
|
|
8
8
|
module Concurrent
|
9
9
|
|
10
10
|
# An agent is a single atomic value that represents an identity. The current value
|
11
|
-
# of the agent can be requested at any time (
|
12
|
-
# the global thread pool. Consumers can
|
11
|
+
# of the agent can be requested at any time (`#deref`). Each agent has a work queue and operates on
|
12
|
+
# the global thread pool. Consumers can `#post` code blocks to the agent. The code block (function)
|
13
13
|
# will receive the current value of the agent as its sole parameter. The return value of the block
|
14
14
|
# will become the new value of the agent. Agents support two error handling modes: fail and continue.
|
15
15
|
# A good example of an agent is a shared incrementing counter, such as the score in a video game.
|
@@ -34,13 +34,13 @@ module Concurrent
|
|
34
34
|
# @return [Fixnum] the maximum number of seconds before an update is cancelled
|
35
35
|
class Agent
|
36
36
|
include Dereferenceable
|
37
|
-
include
|
37
|
+
include Concurrent::Observable
|
38
38
|
|
39
39
|
# The default timeout value (in seconds); used when no timeout option
|
40
40
|
# is given at initialization
|
41
41
|
TIMEOUT = 5
|
42
42
|
|
43
|
-
attr_reader :timeout
|
43
|
+
attr_reader :timeout, :task_executor, :operation_executor
|
44
44
|
|
45
45
|
# Initialize a new Agent with the given initial value and provided options.
|
46
46
|
#
|
@@ -49,32 +49,34 @@ module Concurrent
|
|
49
49
|
#
|
50
50
|
# @option opts [Fixnum] :timeout (TIMEOUT) maximum number of seconds before an update is cancelled
|
51
51
|
#
|
52
|
-
# @option opts [Boolean] :operation (false) when
|
53
|
-
# operation pool (for long-running operations), when
|
52
|
+
# @option opts [Boolean] :operation (false) when `true` will execute the future on the global
|
53
|
+
# operation pool (for long-running operations), when `false` will execute the future on the
|
54
54
|
# global task pool (for short-running tasks)
|
55
55
|
# @option opts [object] :executor when provided will run all operations on
|
56
56
|
# this executor rather than the global thread pool (overrides :operation)
|
57
57
|
#
|
58
|
-
# @option opts [String] :dup_on_deref (false) call
|
59
|
-
# @option opts [String] :freeze_on_deref (false) call
|
60
|
-
# @option opts [String] :copy_on_deref (nil) call the given
|
58
|
+
# @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
|
59
|
+
# @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
|
60
|
+
# @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
|
61
61
|
# returning the value returned from the proc
|
62
62
|
def initialize(initial, opts = {})
|
63
|
-
@value
|
64
|
-
@rescuers
|
65
|
-
@validator
|
66
|
-
@timeout
|
67
|
-
|
68
|
-
@
|
63
|
+
@value = initial
|
64
|
+
@rescuers = []
|
65
|
+
@validator = Proc.new { |result| true }
|
66
|
+
@timeout = opts.fetch(:timeout, TIMEOUT).freeze
|
67
|
+
self.observers = CopyOnWriteObserverSet.new
|
68
|
+
@one_by_one = OneByOne.new
|
69
|
+
@task_executor = OptionsParser.get_task_executor_from(opts)
|
70
|
+
@operation_executor = OptionsParser.get_operation_executor_from(opts)
|
69
71
|
init_mutex
|
70
72
|
set_deref_options(opts)
|
71
73
|
end
|
72
74
|
|
73
75
|
# Specifies a block operation to be performed when an update operation raises
|
74
76
|
# an exception. Rescue blocks will be checked in order they were added. The first
|
75
|
-
# block for which the raised exception "is-a" subclass of the given
|
76
|
-
# be called. If no
|
77
|
-
# This behavior is intended to be identical to Ruby's
|
77
|
+
# block for which the raised exception "is-a" subclass of the given `clazz` will
|
78
|
+
# be called. If no `clazz` is given the block will match any caught exception.
|
79
|
+
# This behavior is intended to be identical to Ruby's `begin/rescue/end` behavior.
|
78
80
|
# Any number of rescue handlers can be added. If no rescue handlers are added then
|
79
81
|
# caught exceptions will be suppressed.
|
80
82
|
#
|
@@ -111,53 +113,78 @@ module Concurrent
|
|
111
113
|
# @yieldparam [Object] value the result of the last update operation
|
112
114
|
# @yieldreturn [Boolean] true if the value is valid else false
|
113
115
|
def validate(&block)
|
114
|
-
|
116
|
+
unless block.nil?
|
117
|
+
mutex.lock
|
118
|
+
@validator = block
|
119
|
+
mutex.unlock
|
120
|
+
end
|
115
121
|
self
|
116
122
|
end
|
117
123
|
alias_method :validates, :validate
|
118
124
|
alias_method :validate_with, :validate
|
119
125
|
alias_method :validates_with, :validate
|
120
126
|
|
121
|
-
# Update the current value with the result of the given block operation
|
127
|
+
# Update the current value with the result of the given block operation,
|
128
|
+
# block should not do blocking calls, use #post_off for blocking calls
|
122
129
|
#
|
123
130
|
# @yield the operation to be performed with the current value in order to calculate
|
124
131
|
# the new value
|
125
132
|
# @yieldparam [Object] value the current value
|
126
133
|
# @yieldreturn [Object] the new value
|
134
|
+
# @return [true, nil] nil when no block is given
|
127
135
|
def post(&block)
|
128
|
-
@
|
136
|
+
post_on(@task_executor, &block)
|
129
137
|
end
|
130
138
|
|
131
|
-
# Update the current value with the result of the given block operation
|
139
|
+
# Update the current value with the result of the given block operation,
|
140
|
+
# block can do blocking calls
|
141
|
+
#
|
142
|
+
# @yield the operation to be performed with the current value in order to calculate
|
143
|
+
# the new value
|
144
|
+
# @yieldparam [Object] value the current value
|
145
|
+
# @yieldreturn [Object] the new value
|
146
|
+
# @return [true, nil] nil when no block is given
|
147
|
+
def post_off(&block)
|
148
|
+
post_on(@operation_executor, &block)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Update the current value with the result of the given block operation,
|
152
|
+
# block should not do blocking calls, use #post_off for blocking calls
|
132
153
|
#
|
133
154
|
# @yield the operation to be performed with the current value in order to calculate
|
134
155
|
# the new value
|
135
156
|
# @yieldparam [Object] value the current value
|
136
157
|
# @yieldreturn [Object] the new value
|
137
158
|
def <<(block)
|
138
|
-
|
159
|
+
post(&block)
|
139
160
|
self
|
140
161
|
end
|
141
162
|
|
142
|
-
|
143
|
-
|
163
|
+
# Waits/blocks until all the updates sent before this call are done.
|
164
|
+
#
|
165
|
+
# @param [Numeric] timeout the maximum time in second to wait.
|
166
|
+
# @return [Boolean] false on timeout, true otherwise
|
167
|
+
def await(timeout = nil)
|
168
|
+
done = Event.new
|
169
|
+
post { |val| done.set; val }
|
170
|
+
done.wait timeout
|
144
171
|
end
|
145
172
|
|
146
|
-
|
173
|
+
private
|
147
174
|
|
148
|
-
def
|
149
|
-
|
175
|
+
def post_on(executor, &block)
|
176
|
+
return nil if block.nil?
|
177
|
+
@one_by_one.post(executor) { work(&block) }
|
178
|
+
true
|
150
179
|
end
|
151
180
|
|
152
|
-
private
|
153
|
-
|
154
181
|
# @!visibility private
|
155
182
|
Rescuer = Struct.new(:clazz, :block) # :nodoc:
|
156
183
|
|
157
184
|
# @!visibility private
|
158
185
|
def try_rescue(ex) # :nodoc:
|
159
186
|
rescuer = mutex.synchronize do
|
160
|
-
@rescuers.find{|r| ex.is_a?(r.clazz) }
|
187
|
+
@rescuers.find { |r| ex.is_a?(r.clazz) }
|
161
188
|
end
|
162
189
|
rescuer.block.call(ex) if rescuer
|
163
190
|
rescue Exception => ex
|
@@ -166,24 +193,31 @@ module Concurrent
|
|
166
193
|
|
167
194
|
# @!visibility private
|
168
195
|
def work(&handler) # :nodoc:
|
196
|
+
validator, value = mutex.synchronize { [@validator, @value] }
|
197
|
+
|
169
198
|
begin
|
199
|
+
# FIXME creates second thread
|
200
|
+
result, valid = Concurrent::timeout(@timeout) do
|
201
|
+
result = handler.call(value)
|
202
|
+
[result, validator.call(result)]
|
203
|
+
end
|
204
|
+
rescue Exception => ex
|
205
|
+
exception = ex
|
206
|
+
end
|
170
207
|
|
171
|
-
|
208
|
+
mutex.lock
|
209
|
+
should_notify = if !exception && valid
|
210
|
+
@value = result
|
211
|
+
true
|
212
|
+
end
|
213
|
+
mutex.unlock
|
172
214
|
|
173
|
-
|
174
|
-
result = Concurrent::timeout(@timeout) do
|
175
|
-
handler.call(@value)
|
176
|
-
end
|
177
|
-
if @validator.call(result)
|
178
|
-
@value = result
|
179
|
-
should_notify = true
|
180
|
-
end
|
181
|
-
end
|
215
|
+
if should_notify
|
182
216
|
time = Time.now
|
183
|
-
|
184
|
-
rescue Exception => ex
|
185
|
-
try_rescue(ex)
|
217
|
+
observers.notify_observers { [time, self.value] }
|
186
218
|
end
|
219
|
+
|
220
|
+
try_rescue(exception)
|
187
221
|
end
|
188
222
|
end
|
189
223
|
end
|