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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 58bf31b4139ff60bd206c0771a4616653c77d710
4
- data.tar.gz: 1fbbe6556817458272475deac127e92fc0c723a2
3
+ metadata.gz: edceac6c210ab7ac152c72f134a44e0b853d3dc8
4
+ data.tar.gz: f65c0a53c67c4cfbd9d4d9f1a170a2f5af2c2cb8
5
5
  SHA512:
6
- metadata.gz: 4d701a724fe2bb49c0bfa6c8cd3ccb242af88983ac04d90ac622a40a00e3e8e39b21a635a090c6dd808124f82733c66b99fabf05504691dcbe5a4a1f97ab8f33
7
- data.tar.gz: 6bc189a09d26d754131c4ca8f09ec45912f2c0a4bb22ef3a578daa5781103df38597c8ab86573a78f211aa88f1f635188673b3d0d51334f56588b5c3d7c3f841
6
+ metadata.gz: f256b59fa79c66e7e275b314b7e8b666350b1e79a00c81b0d9609e78920dc88c89053596cdd487d6fe375368332d8a6884b6c53112a3d8d2909eb6ea43b628d5
7
+ data.tar.gz: 68b74563a9db63470d244bb63a7ee21056aad2a08fb4a37a65bac1d088d0f06ec26bfcaeaeb5be00ed3ad6ce71665eab69267255a71295b39bafbb2ca61a86f0
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://secure.travis-ci.org/jdantonio/concurrent-ruby.png)](https://travis-ci.org/jdantonio/concurrent-ruby?branch=master) [![Coverage Status](https://coveralls.io/repos/jdantonio/concurrent-ruby/badge.png)](https://coveralls.io/r/jdantonio/concurrent-ruby) [![Code Climate](https://codeclimate.com/github/jdantonio/concurrent-ruby.png)](https://codeclimate.com/github/jdantonio/concurrent-ruby) [![Inline docs](http://inch-pages.github.io/github/jdantonio/concurrent-ruby.png)](http://inch-pages.github.io/github/jdantonio/concurrent-ruby) [![Dependency Status](https://gemnasium.com/jdantonio/concurrent-ruby.png)](https://gemnasium.com/jdantonio/concurrent-ruby)
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 'unopinionted' 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/jdantonio/concurrent-ruby/logo/concurrent-ruby-logo-300x300.png"/>
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
- ### Install
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
- *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.*
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/jdantonio/concurrent-ruby/wiki)
55
- or the [API documentation](http://rubydoc.info/github/jdantonio/concurrent-ruby/master/frames)
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/jdantonio/concurrent-ruby/wiki/Async),
63
- [Agent](https://github.com/jdantonio/concurrent-ruby/wiki/Agent),
64
- [Future](https://github.com/jdantonio/concurrent-ruby/wiki/Future),
65
- [Promise](https://github.com/jdantonio/concurrent-ruby/wiki/Promise),
66
- [ScheduledTask](https://github.com/jdantonio/concurrent-ruby/wiki/ScheduledTask),
67
- and [TimerTask](https://github.com/jdantonio/concurrent-ruby/wiki/TimerTask)
68
- * Erlang-inspired [Supervisor](https://github.com/jdantonio/concurrent-ruby/wiki/Supervisor) and other lifecycle classes/mixins
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/jdantonio/concurrent-ruby/wiki/MVar-(M-Structure)),
71
- [I-Structures](https://github.com/jdantonio/concurrent-ruby/wiki/IVar-(I-Structure)),
72
- [thread-local variables](https://github.com/jdantonio/concurrent-ruby/wiki/ThreadLocalVar),
73
- atomic counters, and [software transactional memory](https://github.com/jdantonio/concurrent-ruby/wiki/TVar-(STM))
74
- * Thread synchronization classes and algorithms including [dataflow](https://github.com/jdantonio/concurrent-ruby/wiki/Dataflow),
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/jdantonio/concurrent-ruby/wiki/Thread%20Pools)
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/jdantonio/concurrent-ruby/wiki/Logo)
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 &copy; 2014 [Jerry D'Antonio](https://twitter.com/jerrydantonio). All Rights Reserved.
@@ -1,11 +1,133 @@
1
1
  require 'concurrent/configuration'
2
- require 'concurrent/executor/one_by_one'
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
- # {include:file:lib/concurrent/actress/doc.md}
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[:__current_actress__]
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
- warn '[EXPERIMENTAL] A full release of `Actress`, renamed `Actor`, is expected in the 0.7.0 release.'
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 actor behaviours
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 omitted
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 omitted
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
- # @api private
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
- attr_reader :reference, :name, :path, :executor, :terminated
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] actress_class a class to be instantiated defining Actor's behaviour
19
- # @option opts [Array<Object>] args arguments for actress_class instantiation
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 = Array.new
27
- @one_by_one = OneByOne.new
42
+ @mailbox = Array.new
43
+ @serialized_execution = SerializedExecution.new
28
44
  # noinspection RubyArgCount
29
- @terminated = Event.new
30
- @executor = Type! opts.fetch(:executor, Concurrent.configuration.global_task_pool), Executor
31
- @children = Set.new
32
- @reference = Reference.new self
33
- @name = (Type! opts.fetch(:name), String, Symbol).to_s
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
- @actress_class = actress_class = Child! opts.fetch(:class), Context
47
- args = opts.fetch(:args, [])
48
- initialized = Type! opts[:initialized], IVar, NilClass
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
- @actress = actress_class.new *args, &block
53
- @actress.send :initialize_core, self
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 = @actress.on_envelope envelope
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
- @one_by_one.post(@executor) do
205
+ @serialized_execution.post(@executor) do
182
206
  begin
183
- Thread.current[:__current_actress__] = reference
207
+ Thread.current[:__current_actor__] = reference
184
208
  yield
185
209
  rescue => e
186
210
  log FATAL, e
187
211
  ensure
188
- Thread.current[:__current_actress__] = nil
212
+ Thread.current[:__current_actor__] = nil
189
213
  end
190
214
  end
191
215