concurrent-ruby 0.6.0 → 0.7.0.rc0
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 +55 -51
- data/lib/concurrent/actress/ad_hoc.rb +6 -0
- data/lib/concurrent/actress/context.rb +10 -8
- data/lib/concurrent/actress/core.rb +44 -20
- data/lib/concurrent/actress/core_delegations.rb +5 -0
- data/lib/concurrent/actress/envelope.rb +21 -5
- data/lib/concurrent/actress/reference.rb +5 -5
- data/lib/concurrent/actress.rb +152 -6
- data/lib/concurrent/agent.rb +9 -9
- data/lib/concurrent/atomic.rb +46 -0
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
- data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
- data/lib/concurrent/atomic_reference/jruby.rb +8 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
- data/lib/concurrent/atomic_reference/rbx.rb +16 -0
- data/lib/concurrent/atomic_reference/ruby.rb +16 -0
- data/lib/concurrent/atomics.rb +1 -1
- data/lib/concurrent/configuration.rb +7 -2
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +0 -1
- data/lib/concurrent/executor/{one_by_one.rb → serialized_execution.rb} +21 -8
- data/lib/concurrent/executor/timer_set.rb +6 -1
- data/lib/concurrent/executors.rb +1 -1
- data/lib/concurrent/logging.rb +1 -1
- data/lib/concurrent/observable.rb +1 -1
- data/lib/concurrent/supervisor.rb +1 -1
- data/lib/concurrent/timer_task.rb +0 -36
- data/lib/concurrent/version.rb +1 -1
- data/lib/concurrent.rb +3 -4
- data/lib/concurrent_ruby_ext.so +0 -0
- data/lib/extension_helper.rb +9 -0
- metadata +19 -152
- data/lib/concurrent/actor/actor.rb +0 -270
- data/lib/concurrent/actor/postable.rb +0 -102
- data/lib/concurrent/actors.rb +0 -2
- data/lib/concurrent/actress/doc.md +0 -53
- data/lib/concurrent/atomic/atomic.rb +0 -48
- data/lib/concurrent/runnable.rb +0 -90
- data/lib/concurrent/stoppable.rb +0 -20
- data/spec/concurrent/actor/actor_spec.rb +0 -376
- data/spec/concurrent/actor/postable_shared.rb +0 -218
- data/spec/concurrent/actress_spec.rb +0 -191
- data/spec/concurrent/agent_spec.rb +0 -500
- data/spec/concurrent/async_spec.rb +0 -352
- data/spec/concurrent/atomic/atomic_boolean_spec.rb +0 -172
- data/spec/concurrent/atomic/atomic_fixnum_spec.rb +0 -186
- data/spec/concurrent/atomic/atomic_spec.rb +0 -133
- data/spec/concurrent/atomic/condition_spec.rb +0 -171
- data/spec/concurrent/atomic/copy_on_notify_observer_set_spec.rb +0 -10
- data/spec/concurrent/atomic/copy_on_write_observer_set_spec.rb +0 -10
- data/spec/concurrent/atomic/count_down_latch_spec.rb +0 -151
- data/spec/concurrent/atomic/cyclic_barrier_spec.rb +0 -248
- data/spec/concurrent/atomic/event_spec.rb +0 -200
- data/spec/concurrent/atomic/observer_set_shared.rb +0 -242
- data/spec/concurrent/atomic/thread_local_var_spec.rb +0 -113
- data/spec/concurrent/channel/buffered_channel_spec.rb +0 -151
- data/spec/concurrent/channel/channel_spec.rb +0 -39
- data/spec/concurrent/channel/probe_spec.rb +0 -77
- data/spec/concurrent/channel/unbuffered_channel_spec.rb +0 -132
- data/spec/concurrent/collection/blocking_ring_buffer_spec.rb +0 -149
- data/spec/concurrent/collection/priority_queue_spec.rb +0 -317
- data/spec/concurrent/collection/ring_buffer_spec.rb +0 -126
- data/spec/concurrent/configuration_spec.rb +0 -71
- data/spec/concurrent/dataflow_spec.rb +0 -242
- data/spec/concurrent/delay_spec.rb +0 -91
- data/spec/concurrent/dereferenceable_shared.rb +0 -146
- data/spec/concurrent/exchanger_spec.rb +0 -66
- data/spec/concurrent/executor/cached_thread_pool_shared.rb +0 -115
- data/spec/concurrent/executor/fixed_thread_pool_shared.rb +0 -136
- data/spec/concurrent/executor/global_thread_pool_shared.rb +0 -35
- data/spec/concurrent/executor/immediate_executor_spec.rb +0 -12
- data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +0 -44
- data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +0 -64
- data/spec/concurrent/executor/java_single_thread_executor_spec.rb +0 -21
- data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +0 -71
- data/spec/concurrent/executor/per_thread_executor_spec.rb +0 -57
- data/spec/concurrent/executor/ruby_cached_thread_pool_spec.rb +0 -69
- data/spec/concurrent/executor/ruby_fixed_thread_pool_spec.rb +0 -39
- data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +0 -18
- data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +0 -171
- data/spec/concurrent/executor/safe_task_executor_spec.rb +0 -103
- data/spec/concurrent/executor/thread_pool_class_cast_spec.rb +0 -52
- data/spec/concurrent/executor/thread_pool_executor_shared.rb +0 -155
- data/spec/concurrent/executor/thread_pool_shared.rb +0 -239
- data/spec/concurrent/executor/timer_set_spec.rb +0 -183
- data/spec/concurrent/future_spec.rb +0 -329
- data/spec/concurrent/ivar_spec.rb +0 -215
- data/spec/concurrent/mvar_spec.rb +0 -380
- data/spec/concurrent/obligation_shared.rb +0 -102
- data/spec/concurrent/obligation_spec.rb +0 -282
- data/spec/concurrent/observable_shared.rb +0 -177
- data/spec/concurrent/observable_spec.rb +0 -56
- data/spec/concurrent/options_parser_spec.rb +0 -71
- data/spec/concurrent/promise_spec.rb +0 -367
- data/spec/concurrent/runnable_shared.rb +0 -68
- data/spec/concurrent/runnable_spec.rb +0 -235
- data/spec/concurrent/scheduled_task_spec.rb +0 -342
- data/spec/concurrent/stoppable_shared.rb +0 -37
- data/spec/concurrent/supervisor_spec.rb +0 -1148
- data/spec/concurrent/timer_task_spec.rb +0 -258
- data/spec/concurrent/tvar_spec.rb +0 -137
- data/spec/concurrent/utility/processor_count_spec.rb +0 -20
- data/spec/concurrent/utility/timeout_spec.rb +0 -50
- data/spec/concurrent/utility/timer_spec.rb +0 -52
- data/spec/spec_helper.rb +0 -31
- data/spec/support/example_group_extensions.rb +0 -48
- data/spec/support/less_than_or_equal_to_matcher.rb +0 -5
- /data/{LICENSE → LICENSE.txt} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a37030c113bbaf5da04499b31efd48b49c8244e6
|
|
4
|
+
data.tar.gz: 794a39695eadb43c80668f28f8c9eb367fbd8dc3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c99611c6311e743524a1769f6c1a663be7cee748813d772c5a5b5e7f9bdcd344c58d696e7d74dcae1c7e285fb7caa2d3e0cef0150a125e546e12474d9d1a3184
|
|
7
|
+
data.tar.gz: 3678784b9161c7b0f59ff608950fc2eb17887281be9ffbb842f4bde178303b0ab347d2e1b82111fd43604d4a4419354277524abe55c20222370464ad5735c85b
|
data/README.md
CHANGED
|
@@ -1,79 +1,82 @@
|
|
|
1
1
|
# Concurrent Ruby
|
|
2
|
-
[](http://badge.fury.io/rb/concurrent-ruby) [](http://badge.fury.io/rb/concurrent-ruby) [](https://travis-ci.org/ruby-concurrency/concurrent-ruby) [](https://coveralls.io/r/ruby-concurrency/concurrent-ruby) [](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby) [](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby) [](https://gemnasium.com/ruby-concurrency/concurrent-ruby)
|
|
3
3
|
|
|
4
4
|
<table>
|
|
5
|
-
<tr>
|
|
6
|
-
<td align="left" valign="top">
|
|
7
|
-
<p>
|
|
8
|
-
Modern concurrency tools for Ruby. Inspired by
|
|
9
|
-
<a href="http://www.erlang.org/doc/reference_manual/processes.html">Erlang</a>,
|
|
10
|
-
<a href="http://clojure.org/concurrent_programming">Clojure</a>,
|
|
11
|
-
<a href="http://
|
|
12
|
-
<a href="http://www.haskell.org/haskellwiki/Applications_and_libraries/Concurrency_and_parallelism#Concurrent_Haskell">Haskell</a>,
|
|
13
|
-
<a href="http://blogs.msdn.com/b/dsyme/archive/2010/02/15/async-and-parallel-design-patterns-in-f-part-3-agents.aspx">F#</a>,
|
|
14
|
-
<a href="http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx">C#</a>,
|
|
15
|
-
<a href="http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html">Java</a>,
|
|
16
|
-
and classic concurrency patterns.
|
|
17
|
-
</p>
|
|
18
|
-
<p>
|
|
19
|
-
The design goals of this gem are:
|
|
20
|
-
<ul>
|
|
21
|
-
<li>Be an '
|
|
22
|
-
<li>Remain free of external gem dependencies</li>
|
|
23
|
-
<li>Stay true to the spirit of the languages providing inspiration</li>
|
|
24
|
-
<li>But implement in a way that makes sense for Ruby</li>
|
|
25
|
-
<li>Keep the semantics as idiomatic Ruby as possible</li>
|
|
26
|
-
<li>Support features that make sense in Ruby</li>
|
|
27
|
-
<li>Exclude features that don't make sense in Ruby</li>
|
|
28
|
-
<li>Be small, lean, and loosely coupled</li>
|
|
29
|
-
</ul>
|
|
30
|
-
</p>
|
|
31
|
-
</td>
|
|
32
|
-
<td align="right" valign="top">
|
|
33
|
-
<img src="https://raw.githubusercontent.com/wiki/
|
|
34
|
-
</td>
|
|
35
|
-
</tr>
|
|
5
|
+
<tr>
|
|
6
|
+
<td align="left" valign="top">
|
|
7
|
+
<p>
|
|
8
|
+
Modern concurrency tools for Ruby. Inspired by
|
|
9
|
+
<a href="http://www.erlang.org/doc/reference_manual/processes.html">Erlang</a>,
|
|
10
|
+
<a href="http://clojure.org/concurrent_programming">Clojure</a>,
|
|
11
|
+
<a href="http://akka.io/">Scala</a>,
|
|
12
|
+
<a href="http://www.haskell.org/haskellwiki/Applications_and_libraries/Concurrency_and_parallelism#Concurrent_Haskell">Haskell</a>,
|
|
13
|
+
<a href="http://blogs.msdn.com/b/dsyme/archive/2010/02/15/async-and-parallel-design-patterns-in-f-part-3-agents.aspx">F#</a>,
|
|
14
|
+
<a href="http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx">C#</a>,
|
|
15
|
+
<a href="http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html">Java</a>,
|
|
16
|
+
and classic concurrency patterns.
|
|
17
|
+
</p>
|
|
18
|
+
<p>
|
|
19
|
+
The design goals of this gem are:
|
|
20
|
+
<ul>
|
|
21
|
+
<li>Be an 'unopinionated' toolbox that provides useful utilities without debating which is better or why</li>
|
|
22
|
+
<li>Remain free of external gem dependencies</li>
|
|
23
|
+
<li>Stay true to the spirit of the languages providing inspiration</li>
|
|
24
|
+
<li>But implement in a way that makes sense for Ruby</li>
|
|
25
|
+
<li>Keep the semantics as idiomatic Ruby as possible</li>
|
|
26
|
+
<li>Support features that make sense in Ruby</li>
|
|
27
|
+
<li>Exclude features that don't make sense in Ruby</li>
|
|
28
|
+
<li>Be small, lean, and loosely coupled</li>
|
|
29
|
+
</ul>
|
|
30
|
+
</p>
|
|
31
|
+
</td>
|
|
32
|
+
<td align="right" valign="top">
|
|
33
|
+
<img src="https://raw.githubusercontent.com/wiki/ruby-concurrency/concurrent-ruby/logo/concurrent-ruby-logo-300x300.png"/>
|
|
34
|
+
</td>
|
|
35
|
+
</tr>
|
|
36
36
|
</table>
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
## Install
|
|
39
39
|
|
|
40
40
|
```shell
|
|
41
41
|
gem install concurrent-ruby
|
|
42
42
|
```
|
|
43
|
+
|
|
43
44
|
or add the following line to Gemfile:
|
|
44
45
|
|
|
45
46
|
```ruby
|
|
46
47
|
gem 'concurrent-ruby'
|
|
47
48
|
```
|
|
49
|
+
|
|
48
50
|
and run `bundle install` from your shell.
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
_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._
|
|
51
53
|
|
|
52
54
|
## Features & Documentation
|
|
53
55
|
|
|
54
|
-
Please see the [Concurrent Ruby Wiki](https://github.com/
|
|
55
|
-
or the [API documentation](http://
|
|
56
|
+
Please see the [Concurrent Ruby Wiki](https://github.com/ruby-concurrency/concurrent-ruby/wiki)
|
|
57
|
+
or the [API documentation](http://ruby-concurrency.github.io/concurrent-ruby/frames.html))
|
|
56
58
|
for more information or join our [mailing list](http://groups.google.com/group/concurrent-ruby).
|
|
57
59
|
|
|
58
60
|
There are many concurrency abstractions in this library. These abstractions can be broadly categorized
|
|
59
61
|
into several general groups:
|
|
60
62
|
|
|
61
63
|
* Asynchronous concurrency abstractions including
|
|
62
|
-
[Async](https://github.com/
|
|
63
|
-
[Agent](https://github.com/
|
|
64
|
-
[Future](https://github.com/
|
|
65
|
-
[Promise](https://github.com/
|
|
66
|
-
[ScheduledTask](https://github.com/
|
|
67
|
-
and [TimerTask](https://github.com/
|
|
68
|
-
* Erlang-inspired [Supervisor](https://github.com/
|
|
64
|
+
[Async](https://github.com/ruby-concurrency/concurrent-ruby/wiki/Async),
|
|
65
|
+
[Agent](https://github.com/ruby-concurrency/concurrent-ruby/wiki/Agent),
|
|
66
|
+
[Future](https://github.com/ruby-concurrency/concurrent-ruby/wiki/Future),
|
|
67
|
+
[Promise](https://github.com/ruby-concurrency/concurrent-ruby/wiki/Promise),
|
|
68
|
+
[ScheduledTask](https://github.com/ruby-concurrency/concurrent-ruby/wiki/ScheduledTask),
|
|
69
|
+
and [TimerTask](https://github.com/ruby-concurrency/concurrent-ruby/wiki/TimerTask)
|
|
70
|
+
* Erlang-inspired [Supervisor](https://github.com/ruby-concurrency/concurrent-ruby/wiki/Supervisor) and other lifecycle classes/mixins
|
|
69
71
|
for managing long-running threads
|
|
70
|
-
* Thread-safe variables including [M-Structures](https://github.com/
|
|
71
|
-
[I-Structures](https://github.com/
|
|
72
|
-
[thread-local variables](https://github.com/
|
|
73
|
-
atomic counters, and [software transactional memory](https://github.com/
|
|
74
|
-
* Thread synchronization classes and algorithms including [dataflow](https://github.com/
|
|
72
|
+
* Thread-safe variables including [M-Structures](https://github.com/ruby-concurrency/concurrent-ruby/wiki/MVar-(M-Structure)),
|
|
73
|
+
[I-Structures](https://github.com/ruby-concurrency/concurrent-ruby/wiki/IVar-(I-Structure)),
|
|
74
|
+
[thread-local variables](https://github.com/ruby-concurrency/concurrent-ruby/wiki/ThreadLocalVar),
|
|
75
|
+
atomic counters, and [software transactional memory](https://github.com/ruby-concurrency/concurrent-ruby/wiki/TVar-(STM))
|
|
76
|
+
* Thread synchronization classes and algorithms including [dataflow](https://github.com/ruby-concurrency/concurrent-ruby/wiki/Dataflow),
|
|
75
77
|
timeout, condition, countdown latch, dependency counter, and event
|
|
76
|
-
* Java-inspired [thread pools](https://github.com/
|
|
78
|
+
* Java-inspired [thread pools](https://github.com/ruby-concurrency/concurrent-ruby/wiki/Thread%20Pools)
|
|
79
|
+
* New fast light-weighted [Actor model](http://ruby-concurrency.github.io/concurrent-ruby/frames.html#!Concurrent/Actress.html) implementation.
|
|
77
80
|
* And many more...
|
|
78
81
|
|
|
79
82
|
### Semantic Versioning
|
|
@@ -91,7 +94,7 @@ It should be fully compatible with any interpreter that is compliant with Ruby 1
|
|
|
91
94
|
Many more code examples can be found in the documentation for each class (linked above).
|
|
92
95
|
This one simple example shows some of the power of this gem.
|
|
93
96
|
|
|
94
|
-
```ruby
|
|
97
|
+
```ruby
|
|
95
98
|
require 'concurrent'
|
|
96
99
|
require 'thread' # for Queue
|
|
97
100
|
require 'open-uri' # for open(uri)
|
|
@@ -140,6 +143,7 @@ task.value #=> 25.96
|
|
|
140
143
|
* [Ravil Bayramgalin](https://github.com/brainopia)
|
|
141
144
|
* [Larry Lv](https://github.com/larrylv)
|
|
142
145
|
* [Giuseppe Capizzi](https://github.com/gcapizzi)
|
|
146
|
+
* [Bill Dueber](https://github.com/billdueber)
|
|
143
147
|
* [Brian Shirai](https://github.com/brixen)
|
|
144
148
|
* [Chip Miller](https://github.com/chip-miller)
|
|
145
149
|
* [Jamie Hodge](https://github.com/jamiehodge)
|
|
@@ -157,6 +161,6 @@ task.value #=> 25.96
|
|
|
157
161
|
|
|
158
162
|
*Concurrent Ruby* is free software released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
|
159
163
|
|
|
160
|
-
The *Concurrent Ruby* [logo](https://github.com/
|
|
164
|
+
The *Concurrent Ruby* [logo](https://github.com/ruby-concurrency/concurrent-ruby/wiki/Logo)
|
|
161
165
|
was designed by [David Jones](https://twitter.com/zombyboy).
|
|
162
166
|
It is Copyright © 2014 [Jerry D'Antonio](https://twitter.com/jerrydantonio). All Rights Reserved.
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
module Concurrent
|
|
2
2
|
module Actress
|
|
3
|
+
# Allows quick creation of actors with behaviour defined by blocks.
|
|
4
|
+
# @example ping
|
|
5
|
+
# AdHoc.spawn :forward, an_actor do |where|
|
|
6
|
+
# # this block has to return proc defining #on_message behaviour
|
|
7
|
+
# -> message { where.tell message }
|
|
8
|
+
# end
|
|
3
9
|
class AdHoc
|
|
4
10
|
include Context
|
|
5
11
|
def initialize(*args, &initializer)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
module Concurrent
|
|
2
2
|
module Actress
|
|
3
3
|
|
|
4
|
-
# module used to define
|
|
4
|
+
# This module is used to define actors. It can be included in any class,
|
|
5
|
+
# only requirement is to override {Context#on_message} method.
|
|
5
6
|
# @example ping
|
|
6
7
|
# class Ping
|
|
7
8
|
# include Context
|
|
@@ -26,10 +27,6 @@ module Concurrent
|
|
|
26
27
|
raise NotImplementedError
|
|
27
28
|
end
|
|
28
29
|
|
|
29
|
-
def logger
|
|
30
|
-
core.logger
|
|
31
|
-
end
|
|
32
|
-
|
|
33
30
|
# @api private
|
|
34
31
|
def on_envelope(envelope)
|
|
35
32
|
@envelope = envelope
|
|
@@ -53,9 +50,14 @@ module Concurrent
|
|
|
53
50
|
core.terminate!
|
|
54
51
|
end
|
|
55
52
|
|
|
53
|
+
# delegates to core.log
|
|
54
|
+
# @see Logging#log
|
|
55
|
+
def log(level, progname, message = nil, &block)
|
|
56
|
+
core.log(level, progname, message, &block)
|
|
57
|
+
end
|
|
58
|
+
|
|
56
59
|
private
|
|
57
60
|
|
|
58
|
-
# @api private
|
|
59
61
|
def initialize_core(core)
|
|
60
62
|
@core = Type! core, Core
|
|
61
63
|
end
|
|
@@ -71,12 +73,12 @@ module Concurrent
|
|
|
71
73
|
end
|
|
72
74
|
|
|
73
75
|
module ClassMethods
|
|
74
|
-
# behaves as {Actress.spawn} but class_name is
|
|
76
|
+
# behaves as {Concurrent::Actress.spawn} but class_name is auto-inserted based on receiver
|
|
75
77
|
def spawn(name_or_opts, *args, &block)
|
|
76
78
|
Actress.spawn spawn_optionify(name_or_opts, *args), &block
|
|
77
79
|
end
|
|
78
80
|
|
|
79
|
-
# behaves as {Actress.spawn!} but class_name is
|
|
81
|
+
# behaves as {Concurrent::Actress.spawn!} but class_name is auto-inserted based on receiver
|
|
80
82
|
def spawn!(name_or_opts, *args, &block)
|
|
81
83
|
Actress.spawn! spawn_optionify(name_or_opts, *args), &block
|
|
82
84
|
end
|
|
@@ -4,33 +4,50 @@ module Concurrent
|
|
|
4
4
|
require 'set'
|
|
5
5
|
|
|
6
6
|
# Core of the actor
|
|
7
|
-
# @
|
|
7
|
+
# @note Whole class should be considered private. An user should use {Context}s and {Reference}s only.
|
|
8
8
|
# @note devel: core should not block on anything, e.g. it cannot wait on children to terminate
|
|
9
9
|
# that would eat up all threads in task pool and deadlock
|
|
10
10
|
class Core
|
|
11
11
|
include TypeCheck
|
|
12
12
|
include Concurrent::Logging
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
# @!attribute [r] reference
|
|
15
|
+
# @return [Reference] reference to this actor which can be safely passed around
|
|
16
|
+
# @!attribute [r] name
|
|
17
|
+
# @return [String] the name of this instance, it should be uniq (not enforced right now)
|
|
18
|
+
# @!attribute [r] path
|
|
19
|
+
# @return [String] a path of this actor. It is used for easier orientation and logging.
|
|
20
|
+
# Path is constructed recursively with: `parent.path + self.name` up to a {Actress::ROOT},
|
|
21
|
+
# e.g. `/an_actor/its_child`.
|
|
22
|
+
# (It will also probably form a supervision path (failures will be reported up to parents)
|
|
23
|
+
# in future versions.)
|
|
24
|
+
# @!attribute [r] executor
|
|
25
|
+
# @return [Executor] which is used to process messages
|
|
26
|
+
# @!attribute [r] terminated
|
|
27
|
+
# @return [Event] event which will become set when actor is terminated.
|
|
28
|
+
# @!attribute [r] actor_class
|
|
29
|
+
# @return [Context] a class including {Context} representing Actor's behaviour
|
|
30
|
+
attr_reader :reference, :name, :path, :executor, :terminated, :actor_class
|
|
15
31
|
|
|
16
32
|
# @option opts [String] name
|
|
17
33
|
# @option opts [Reference, nil] parent of an actor spawning this one
|
|
18
|
-
# @option opts [Context]
|
|
19
|
-
# @option opts [Array<Object>] args arguments for
|
|
34
|
+
# @option opts [Context] actor_class a class to be instantiated defining Actor's behaviour
|
|
35
|
+
# @option opts [Array<Object>] args arguments for actor_class instantiation
|
|
20
36
|
# @option opts [Executor] executor, default is `Concurrent.configuration.global_task_pool`
|
|
21
37
|
# @option opts [IVar, nil] initialized, if present it'll be set or failed after {Context} initialization
|
|
22
38
|
# @option opts [Proc, nil] logger a proc accepting (level, progname, message = nil, &block) params,
|
|
23
39
|
# can be used to hook actor instance to any logging system
|
|
24
40
|
# @param [Proc] block for class instantiation
|
|
25
41
|
def initialize(opts = {}, &block)
|
|
26
|
-
@mailbox
|
|
27
|
-
@
|
|
42
|
+
@mailbox = Array.new
|
|
43
|
+
@serialized_execution = SerializedExecution.new
|
|
28
44
|
# noinspection RubyArgCount
|
|
29
|
-
@terminated
|
|
30
|
-
@executor
|
|
31
|
-
@children
|
|
32
|
-
@reference
|
|
33
|
-
@name
|
|
45
|
+
@terminated = Event.new
|
|
46
|
+
@executor = Type! opts.fetch(:executor, Concurrent.configuration.global_task_pool), Executor
|
|
47
|
+
@children = Set.new
|
|
48
|
+
@reference = Reference.new self
|
|
49
|
+
@name = (Type! opts.fetch(:name), String, Symbol).to_s
|
|
50
|
+
@actor = Concurrent::Atomic.new
|
|
34
51
|
|
|
35
52
|
parent = opts[:parent]
|
|
36
53
|
@parent_core = (Type! parent, Reference, NilClass) && parent.send(:core)
|
|
@@ -43,14 +60,14 @@ module Concurrent
|
|
|
43
60
|
|
|
44
61
|
@parent_core.add_child reference if @parent_core
|
|
45
62
|
|
|
46
|
-
@
|
|
47
|
-
args
|
|
48
|
-
initialized
|
|
63
|
+
@actor_class = actor_class = Child! opts.fetch(:class), Context
|
|
64
|
+
args = opts.fetch(:args, [])
|
|
65
|
+
initialized = Type! opts[:initialized], IVar, NilClass
|
|
49
66
|
|
|
50
67
|
schedule_execution do
|
|
51
68
|
begin
|
|
52
|
-
@
|
|
53
|
-
|
|
69
|
+
@actor.value = actor_class.new(*args, &block).
|
|
70
|
+
tap { |a| a.send :initialize_core, self }
|
|
54
71
|
initialized.set true if initialized
|
|
55
72
|
rescue => ex
|
|
56
73
|
log ERROR, ex
|
|
@@ -113,6 +130,8 @@ module Concurrent
|
|
|
113
130
|
# Terminates all its children, does not wait until they are terminated.
|
|
114
131
|
def terminate!
|
|
115
132
|
guard!
|
|
133
|
+
return nil if terminated?
|
|
134
|
+
|
|
116
135
|
@children.each do |ch|
|
|
117
136
|
ch.send(:core).tap { |core| core.send(:schedule_execution) { core.terminate! } }
|
|
118
137
|
end
|
|
@@ -150,6 +169,11 @@ module Concurrent
|
|
|
150
169
|
end
|
|
151
170
|
end
|
|
152
171
|
|
|
172
|
+
# @return [Context]
|
|
173
|
+
def actor
|
|
174
|
+
@actor.value
|
|
175
|
+
end
|
|
176
|
+
|
|
153
177
|
# Processes single envelope, calls #process_envelopes? at the end to ensure next envelope
|
|
154
178
|
# scheduling.
|
|
155
179
|
def receive_envelope
|
|
@@ -162,7 +186,7 @@ module Concurrent
|
|
|
162
186
|
|
|
163
187
|
log DEBUG, "received #{envelope.message} from #{envelope.sender_path}"
|
|
164
188
|
|
|
165
|
-
result =
|
|
189
|
+
result = actor.on_envelope envelope
|
|
166
190
|
envelope.ivar.set result unless envelope.ivar.nil?
|
|
167
191
|
|
|
168
192
|
nil
|
|
@@ -178,14 +202,14 @@ module Concurrent
|
|
|
178
202
|
# Schedules blocks to be executed on executor sequentially,
|
|
179
203
|
# sets Actress.current
|
|
180
204
|
def schedule_execution
|
|
181
|
-
@
|
|
205
|
+
@serialized_execution.post(@executor) do
|
|
182
206
|
begin
|
|
183
|
-
Thread.current[:
|
|
207
|
+
Thread.current[:__current_actor__] = reference
|
|
184
208
|
yield
|
|
185
209
|
rescue => e
|
|
186
210
|
log FATAL, e
|
|
187
211
|
ensure
|
|
188
|
-
Thread.current[:
|
|
212
|
+
Thread.current[:__current_actor__] = nil
|
|
189
213
|
end
|
|
190
214
|
end
|
|
191
215
|
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
module Concurrent
|
|
2
2
|
module Actress
|
|
3
|
-
Envelope
|
|
3
|
+
class Envelope
|
|
4
4
|
include TypeCheck
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
# @!attribute [r] message
|
|
7
|
+
# @return [Object] a message
|
|
8
|
+
# @!attribute [r] ivar
|
|
9
|
+
# @return [IVar] an ivar which becomes resolved after message is processed
|
|
10
|
+
# @!attribute [r] sender
|
|
11
|
+
# @return [Reference, Thread] an actor or thread sending the message
|
|
12
|
+
# @!attribute [r] address
|
|
13
|
+
# @return [Reference] where this message will be delivered
|
|
14
|
+
|
|
15
|
+
attr_reader :message, :ivar, :sender, :address
|
|
16
|
+
|
|
17
|
+
def initialize(message, ivar, sender, address)
|
|
18
|
+
@message = message
|
|
19
|
+
@ivar = Type! ivar, IVar, NilClass
|
|
20
|
+
@sender = Type! sender, Reference, Thread
|
|
21
|
+
@address = Type! address, Reference
|
|
10
22
|
end
|
|
11
23
|
|
|
12
24
|
def sender_path
|
|
@@ -17,6 +29,10 @@ module Concurrent
|
|
|
17
29
|
end
|
|
18
30
|
end
|
|
19
31
|
|
|
32
|
+
def address_path
|
|
33
|
+
address.path
|
|
34
|
+
end
|
|
35
|
+
|
|
20
36
|
def reject!(error)
|
|
21
37
|
ivar.fail error unless ivar.nil?
|
|
22
38
|
end
|
|
@@ -2,8 +2,8 @@ module Concurrent
|
|
|
2
2
|
module Actress
|
|
3
3
|
|
|
4
4
|
# Reference is public interface of Actor instances. It is used for sending messages and can
|
|
5
|
-
# be freely passed around the program. It also provides some basic information about the actor
|
|
6
|
-
# see {CoreDelegations}
|
|
5
|
+
# be freely passed around the program. It also provides some basic information about the actor,
|
|
6
|
+
# see {CoreDelegations}.
|
|
7
7
|
class Reference
|
|
8
8
|
include TypeCheck
|
|
9
9
|
include CoreDelegations
|
|
@@ -43,14 +43,14 @@ module Concurrent
|
|
|
43
43
|
ask(message, ivar).value!
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
# behaves as #tell when no ivar and as #ask when ivar
|
|
46
|
+
# behaves as {#tell} when no ivar and as {#ask} when ivar
|
|
47
47
|
def message(message, ivar = nil)
|
|
48
|
-
core.on_envelope Envelope.new(message, ivar, Actress.current || Thread.current)
|
|
48
|
+
core.on_envelope Envelope.new(message, ivar, Actress.current || Thread.current, self)
|
|
49
49
|
return ivar || self
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def to_s
|
|
53
|
-
"#<#{self.class} #{path}>"
|
|
53
|
+
"#<#{self.class} #{path} (#{actor_class})>"
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
alias_method :inspect, :to_s
|
data/lib/concurrent/actress.rb
CHANGED
|
@@ -1,11 +1,133 @@
|
|
|
1
1
|
require 'concurrent/configuration'
|
|
2
|
-
require 'concurrent/executor/
|
|
2
|
+
require 'concurrent/executor/serialized_execution'
|
|
3
3
|
require 'concurrent/ivar'
|
|
4
4
|
require 'concurrent/logging'
|
|
5
5
|
|
|
6
6
|
module Concurrent
|
|
7
7
|
|
|
8
|
-
#
|
|
8
|
+
# # Actor model
|
|
9
|
+
#
|
|
10
|
+
# - Light-weighted.
|
|
11
|
+
# - Inspired by Akka and Erlang.
|
|
12
|
+
#
|
|
13
|
+
# Actors are sharing a thread-pool by default which makes them very cheap to create and discard.
|
|
14
|
+
# Thousands of actors can be created allowing to brake the program to small maintainable pieces
|
|
15
|
+
# without breaking single responsibility principles.
|
|
16
|
+
#
|
|
17
|
+
# ## What is an actor model?
|
|
18
|
+
#
|
|
19
|
+
# [Wiki](http://en.wikipedia.org/wiki/Actor_model) says:
|
|
20
|
+
# The actor model in computer science is a mathematical model of concurrent computation
|
|
21
|
+
# that treats _actors_ as the universal primitives of concurrent digital computation:
|
|
22
|
+
# in response to a message that it receives, an actor can make local decisions,
|
|
23
|
+
# create more actors, send more messages, and determine how to respond to the next
|
|
24
|
+
# message received.
|
|
25
|
+
#
|
|
26
|
+
# ## Why?
|
|
27
|
+
#
|
|
28
|
+
# Concurrency is hard this is one of many ways how to simplify the problem.
|
|
29
|
+
# It is simpler to reason about actors then about locks (and all their possible states).
|
|
30
|
+
#
|
|
31
|
+
# ## How to use it
|
|
32
|
+
#
|
|
33
|
+
# {include:file:doc/actress/quick.out.rb}
|
|
34
|
+
#
|
|
35
|
+
# ## Messaging
|
|
36
|
+
#
|
|
37
|
+
# Messages are processed in same order as they are sent by a sender. It may interleaved with
|
|
38
|
+
# messages form other senders though. There is also a contract in actor model that
|
|
39
|
+
# messages sent between actors should be immutable. Gems like
|
|
40
|
+
#
|
|
41
|
+
# - [Algebrick](https://github.com/pitr-ch/algebrick) - Typed struct on steroids based on
|
|
42
|
+
# algebraic types and pattern matching
|
|
43
|
+
# - [Hamster](https://github.com/hamstergem/hamster) - Efficient, Immutable, Thread-Safe
|
|
44
|
+
# Collection classes for Ruby
|
|
45
|
+
#
|
|
46
|
+
# are very useful.
|
|
47
|
+
#
|
|
48
|
+
# ## Architecture
|
|
49
|
+
#
|
|
50
|
+
# Actors are running on shared thread poll which allows user to create many actors cheaply.
|
|
51
|
+
# Downside is that these actors cannot be directly used to do IO or other blocking operations.
|
|
52
|
+
# Blocking operations could starve the `default_task_pool`. However there are two options:
|
|
53
|
+
#
|
|
54
|
+
# - Create an regular actor which will schedule blocking operations in `global_operation_pool`
|
|
55
|
+
# (which is intended for blocking operations) sending results back to self in messages.
|
|
56
|
+
# - Create an actor using `global_operation_pool` instead of `global_task_pool`, e.g.
|
|
57
|
+
# `AnIOActor.spawn name: :blocking, executor: Concurrent.configuration.global_operation_pool`.
|
|
58
|
+
#
|
|
59
|
+
# Each actor is composed from 3 objects:
|
|
60
|
+
#
|
|
61
|
+
# ### {Reference}
|
|
62
|
+
# {include:Actress::Reference}
|
|
63
|
+
#
|
|
64
|
+
# ### {Core}
|
|
65
|
+
# {include:Actress::Core}
|
|
66
|
+
#
|
|
67
|
+
# ### {Context}
|
|
68
|
+
# {include:Actress::Context}
|
|
69
|
+
#
|
|
70
|
+
# ## Speed
|
|
71
|
+
#
|
|
72
|
+
# Simple benchmark Actress vs Celluloid, the numbers are looking good
|
|
73
|
+
# but you know how it is with benchmarks. Source code is in
|
|
74
|
+
# `examples/actress/celluloid_benchmark.rb`. It sends numbers between x actors
|
|
75
|
+
# and adding 1 until certain limit is reached.
|
|
76
|
+
#
|
|
77
|
+
# Benchmark legend:
|
|
78
|
+
#
|
|
79
|
+
# - mes. - number of messages send between the actors
|
|
80
|
+
# - act. - number of actors exchanging the messages
|
|
81
|
+
# - impl. - which gem is used
|
|
82
|
+
#
|
|
83
|
+
# ### JRUBY
|
|
84
|
+
#
|
|
85
|
+
# Rehearsal --------------------------------------------------------
|
|
86
|
+
# 50000 2 actress 24.110000 0.800000 24.910000 ( 7.728000)
|
|
87
|
+
# 50000 2 celluloid 28.510000 4.780000 33.290000 ( 14.782000)
|
|
88
|
+
# 50000 500 actress 13.700000 0.280000 13.980000 ( 4.307000)
|
|
89
|
+
# 50000 500 celluloid 14.520000 11.740000 26.260000 ( 12.258000)
|
|
90
|
+
# 50000 1000 actress 10.890000 0.220000 11.110000 ( 3.760000)
|
|
91
|
+
# 50000 1000 celluloid 15.600000 21.690000 37.290000 ( 18.512000)
|
|
92
|
+
# 50000 1500 actress 10.580000 0.270000 10.850000 ( 3.646000)
|
|
93
|
+
# 50000 1500 celluloid 14.490000 29.790000 44.280000 ( 26.043000)
|
|
94
|
+
# --------------------------------------------- total: 201.970000sec
|
|
95
|
+
#
|
|
96
|
+
# mes. act. impl. user system total real
|
|
97
|
+
# 50000 2 actress 9.820000 0.510000 10.330000 ( 5.735000)
|
|
98
|
+
# 50000 2 celluloid 10.390000 4.030000 14.420000 ( 7.494000)
|
|
99
|
+
# 50000 500 actress 9.880000 0.200000 10.080000 ( 3.310000)
|
|
100
|
+
# 50000 500 celluloid 12.430000 11.310000 23.740000 ( 11.727000)
|
|
101
|
+
# 50000 1000 actress 10.590000 0.190000 10.780000 ( 4.029000)
|
|
102
|
+
# 50000 1000 celluloid 14.950000 23.260000 38.210000 ( 20.841000)
|
|
103
|
+
# 50000 1500 actress 10.710000 0.250000 10.960000 ( 3.892000)
|
|
104
|
+
# 50000 1500 celluloid 13.280000 30.030000 43.310000 ( 24.620000) (1)
|
|
105
|
+
#
|
|
106
|
+
# ### MRI 2.1.0
|
|
107
|
+
#
|
|
108
|
+
# Rehearsal --------------------------------------------------------
|
|
109
|
+
# 50000 2 actress 4.640000 0.080000 4.720000 ( 4.852390)
|
|
110
|
+
# 50000 2 celluloid 6.110000 2.300000 8.410000 ( 7.898069)
|
|
111
|
+
# 50000 500 actress 6.260000 2.210000 8.470000 ( 7.400573)
|
|
112
|
+
# 50000 500 celluloid 10.250000 4.930000 15.180000 ( 14.174329)
|
|
113
|
+
# 50000 1000 actress 6.300000 1.860000 8.160000 ( 7.303162)
|
|
114
|
+
# 50000 1000 celluloid 12.300000 7.090000 19.390000 ( 17.962621)
|
|
115
|
+
# 50000 1500 actress 7.410000 2.610000 10.020000 ( 8.887396)
|
|
116
|
+
# 50000 1500 celluloid 14.850000 10.690000 25.540000 ( 24.489796)
|
|
117
|
+
# ---------------------------------------------- total: 99.890000sec
|
|
118
|
+
#
|
|
119
|
+
# mes. act. impl. user system total real
|
|
120
|
+
# 50000 2 actress 4.190000 0.070000 4.260000 ( 4.306386)
|
|
121
|
+
# 50000 2 celluloid 6.490000 2.210000 8.700000 ( 8.280051)
|
|
122
|
+
# 50000 500 actress 7.060000 2.520000 9.580000 ( 8.518707)
|
|
123
|
+
# 50000 500 celluloid 10.550000 4.980000 15.530000 ( 14.699962)
|
|
124
|
+
# 50000 1000 actress 6.440000 1.870000 8.310000 ( 7.571059)
|
|
125
|
+
# 50000 1000 celluloid 12.340000 7.510000 19.850000 ( 18.793591)
|
|
126
|
+
# 50000 1500 actress 6.720000 2.160000 8.880000 ( 7.929630)
|
|
127
|
+
# 50000 1500 celluloid 14.140000 10.130000 24.270000 ( 22.775288) (1)
|
|
128
|
+
#
|
|
129
|
+
# *Note (1):* Celluloid is using thread per actor so this bench is creating about 1500
|
|
130
|
+
# native threads. Actress is using constant number of threads.
|
|
9
131
|
module Actress
|
|
10
132
|
|
|
11
133
|
require 'concurrent/actress/type_check'
|
|
@@ -20,7 +142,7 @@ module Concurrent
|
|
|
20
142
|
|
|
21
143
|
# @return [Reference, nil] current executing actor if any
|
|
22
144
|
def self.current
|
|
23
|
-
Thread.current[:
|
|
145
|
+
Thread.current[:__current_actor__]
|
|
24
146
|
end
|
|
25
147
|
|
|
26
148
|
# implements ROOT
|
|
@@ -39,10 +161,26 @@ module Concurrent
|
|
|
39
161
|
# A root actor, a default parent of all actors spawned outside an actor
|
|
40
162
|
ROOT = Core.new(parent: nil, name: '/', class: Root).reference
|
|
41
163
|
|
|
164
|
+
# Spawns a new actor.
|
|
165
|
+
#
|
|
166
|
+
# @example simple
|
|
167
|
+
# Actress.spawn(AdHoc, :ping1) { -> message { message } }
|
|
168
|
+
#
|
|
169
|
+
# @example complex
|
|
170
|
+
# Actress.spawn name: :ping3,
|
|
171
|
+
# class: AdHoc,
|
|
172
|
+
# args: [1]
|
|
173
|
+
# executor: Concurrent.configuration.global_task_pool do |add|
|
|
174
|
+
# lambda { |number| number + add }
|
|
175
|
+
# end
|
|
176
|
+
#
|
|
42
177
|
# @param block for actress_class instantiation
|
|
43
178
|
# @param args see {.spawn_optionify}
|
|
179
|
+
# @return [Reference] never the actual actor
|
|
44
180
|
def self.spawn(*args, &block)
|
|
45
|
-
|
|
181
|
+
experimental_acknowledged? or
|
|
182
|
+
warn '[EXPERIMENTAL] A full release of `Actress`, renamed `Actor`, is expected in the 0.7.0 release.'
|
|
183
|
+
|
|
46
184
|
if Actress.current
|
|
47
185
|
Core.new(spawn_optionify(*args).merge(parent: Actress.current), &block).reference
|
|
48
186
|
else
|
|
@@ -52,13 +190,12 @@ module Concurrent
|
|
|
52
190
|
|
|
53
191
|
# as {.spawn} but it'll raise when Actor not initialized properly
|
|
54
192
|
def self.spawn!(*args, &block)
|
|
55
|
-
warn '[EXPERIMENTAL] A full release of `Actress`, renamed `Actor`, is expected in the 0.7.0 release.'
|
|
56
193
|
spawn(spawn_optionify(*args).merge(initialized: ivar = IVar.new), &block).tap { ivar.no_error! }
|
|
57
194
|
end
|
|
58
195
|
|
|
59
196
|
# @overload spawn_optionify(actress_class, name, *args)
|
|
60
197
|
# @param [Context] actress_class to be spawned
|
|
61
|
-
# @param [String, Symbol] name of the instance, it's used to generate the path of the actor
|
|
198
|
+
# @param [String, Symbol] name of the instance, it's used to generate the {Core#path} of the actor
|
|
62
199
|
# @param args for actress_class instantiation
|
|
63
200
|
# @overload spawn_optionify(opts)
|
|
64
201
|
# see {Core#initialize} opts
|
|
@@ -71,5 +208,14 @@ module Concurrent
|
|
|
71
208
|
args: args[2..-1] }
|
|
72
209
|
end
|
|
73
210
|
end
|
|
211
|
+
|
|
212
|
+
# call this to disable experimental warning
|
|
213
|
+
def self.i_know_it_is_experimental!
|
|
214
|
+
@experimental_acknowledged = true
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def self.experimental_acknowledged?
|
|
218
|
+
!!@experimental_acknowledged
|
|
219
|
+
end
|
|
74
220
|
end
|
|
75
221
|
end
|