concurrent-ruby 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{LICENSE → LICENSE.txt} +0 -0
- data/README.md +55 -51
- data/lib/concurrent/actress.rb +152 -6
- 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/agent.rb +9 -9
- 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/version.rb +1 -1
- data/spec/concurrent/actress_spec.rb +84 -64
- data/spec/concurrent/agent_spec.rb +2 -2
- data/spec/concurrent/configuration_spec.rb +0 -2
- data/spec/concurrent/executor/thread_pool_shared.rb +30 -0
- data/spec/concurrent/scheduled_task_spec.rb +2 -4
- data/spec/concurrent/supervisor_spec.rb +2 -1
- data/spec/concurrent/timer_task_spec.rb +0 -2
- data/spec/spec_helper.rb +10 -0
- data/spec/support/example_group_extensions.rb +14 -10
- metadata +5 -6
- data/lib/concurrent/actress/doc.md +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: edceac6c210ab7ac152c72f134a44e0b853d3dc8
|
4
|
+
data.tar.gz: f65c0a53c67c4cfbd9d4d9f1a170a2f5af2c2cb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f256b59fa79c66e7e275b314b7e8b666350b1e79a00c81b0d9609e78920dc88c89053596cdd487d6fe375368332d8a6884b6c53112a3d8d2909eb6ea43b628d5
|
7
|
+
data.tar.gz: 68b74563a9db63470d244bb63a7ee21056aad2a08fb4a37a65bac1d088d0f06ec26bfcaeaeb5be00ed3ad6ce71665eab69267255a71295b39bafbb2ca61a86f0
|
data/{LICENSE → LICENSE.txt}
RENAMED
File without changes
|
data/README.md
CHANGED
@@ -1,79 +1,82 @@
|
|
1
1
|
# Concurrent Ruby
|
2
|
-
[![Gem Version](https://badge.fury.io/rb/concurrent-ruby.png)](http://badge.fury.io/rb/concurrent-ruby) [![Build Status](https://
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/concurrent-ruby.png)](http://badge.fury.io/rb/concurrent-ruby) [![Build Status](https://travis-ci.org/ruby-concurrency/concurrent-ruby.svg?branch=master)](https://travis-ci.org/ruby-concurrency/concurrent-ruby) [![Coverage Status](https://coveralls.io/repos/ruby-concurrency/concurrent-ruby/badge.png)](https://coveralls.io/r/ruby-concurrency/concurrent-ruby) [![Code Climate](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby.png)](https://codeclimate.com/github/ruby-concurrency/concurrent-ruby) [![Inline docs](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby.png)](http://inch-ci.org/github/ruby-concurrency/concurrent-ruby) [![Dependency Status](https://gemnasium.com/ruby-concurrency/concurrent-ruby.png)](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://www.scala-lang.org/api/current/index.html#scala.actors.Actor">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 '
|
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://www.scala-lang.org/api/current/index.html#scala.actors.Actor">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.
|
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
|
@@ -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
|
|