concurrent-ruby 0.6.0.pre.2 → 0.6.0
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 +3 -8
- data/lib/concurrent.rb +2 -0
- data/lib/concurrent/actor/actor.rb +3 -3
- data/lib/concurrent/actor/postable.rb +1 -1
- data/lib/concurrent/actors.rb +0 -3
- data/lib/concurrent/actress.rb +75 -0
- data/lib/concurrent/actress/ad_hoc.rb +14 -0
- data/lib/concurrent/actress/context.rb +96 -0
- data/lib/concurrent/actress/core.rb +204 -0
- data/lib/concurrent/actress/core_delegations.rb +37 -0
- data/lib/concurrent/actress/doc.md +53 -0
- data/lib/concurrent/actress/envelope.rb +25 -0
- data/lib/concurrent/actress/errors.rb +14 -0
- data/lib/concurrent/actress/reference.rb +64 -0
- data/lib/concurrent/actress/type_check.rb +48 -0
- data/lib/concurrent/agent.rb +20 -11
- data/lib/concurrent/async.rb +54 -25
- data/lib/concurrent/atomic/atomic.rb +48 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +13 -13
- data/lib/concurrent/atomic/atomic_fixnum.rb +9 -17
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +7 -4
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +16 -14
- data/lib/concurrent/atomic/event.rb +11 -16
- data/lib/concurrent/atomics.rb +1 -0
- data/lib/concurrent/channel/channel.rb +4 -2
- data/lib/concurrent/collection/blocking_ring_buffer.rb +1 -1
- data/lib/concurrent/configuration.rb +59 -47
- data/lib/concurrent/delay.rb +28 -12
- data/lib/concurrent/dereferenceable.rb +6 -6
- data/lib/concurrent/errors.rb +30 -0
- data/lib/concurrent/executor/executor.rb +11 -4
- data/lib/concurrent/executor/immediate_executor.rb +1 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +4 -0
- data/lib/concurrent/executor/one_by_one.rb +24 -12
- data/lib/concurrent/executor/per_thread_executor.rb +1 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +2 -1
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +7 -2
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +3 -0
- data/lib/concurrent/executor/timer_set.rb +1 -1
- data/lib/concurrent/future.rb +0 -2
- data/lib/concurrent/ivar.rb +31 -6
- data/lib/concurrent/logging.rb +17 -0
- data/lib/concurrent/mvar.rb +45 -0
- data/lib/concurrent/obligation.rb +61 -20
- data/lib/concurrent/observable.rb +7 -0
- data/lib/concurrent/promise.rb +1 -0
- data/lib/concurrent/runnable.rb +2 -2
- data/lib/concurrent/supervisor.rb +1 -2
- data/lib/concurrent/timer_task.rb +17 -13
- data/lib/concurrent/tvar.rb +113 -73
- data/lib/concurrent/utility/processor_count.rb +141 -116
- data/lib/concurrent/utility/timeout.rb +4 -5
- data/lib/concurrent/version.rb +1 -1
- data/spec/concurrent/actor/postable_shared.rb +1 -1
- data/spec/concurrent/actress_spec.rb +191 -0
- data/spec/concurrent/async_spec.rb +35 -3
- data/spec/concurrent/atomic/atomic_boolean_spec.rb +1 -1
- data/spec/concurrent/atomic/atomic_fixnum_spec.rb +1 -1
- data/spec/concurrent/atomic/atomic_spec.rb +133 -0
- data/spec/concurrent/atomic/count_down_latch_spec.rb +1 -1
- data/spec/concurrent/collection/priority_queue_spec.rb +1 -1
- data/spec/concurrent/configuration_spec.rb +5 -2
- data/spec/concurrent/delay_spec.rb +14 -0
- data/spec/concurrent/exchanger_spec.rb +4 -9
- data/spec/concurrent/executor/java_cached_thread_pool_spec.rb +1 -1
- data/spec/concurrent/executor/java_fixed_thread_pool_spec.rb +1 -1
- data/spec/concurrent/executor/java_single_thread_executor_spec.rb +1 -1
- data/spec/concurrent/executor/java_thread_pool_executor_spec.rb +1 -1
- data/spec/concurrent/executor/ruby_thread_pool_executor_spec.rb +4 -4
- data/spec/concurrent/ivar_spec.rb +2 -2
- data/spec/concurrent/obligation_spec.rb +113 -24
- data/spec/concurrent/observable_shared.rb +4 -0
- data/spec/concurrent/observable_spec.rb +8 -3
- data/spec/concurrent/runnable_spec.rb +2 -2
- data/spec/concurrent/scheduled_task_spec.rb +1 -0
- data/spec/concurrent/supervisor_spec.rb +26 -11
- data/spec/concurrent/timer_task_spec.rb +36 -35
- data/spec/concurrent/tvar_spec.rb +1 -1
- data/spec/concurrent/utility/timer_spec.rb +8 -8
- data/spec/spec_helper.rb +8 -18
- data/spec/support/example_group_extensions.rb +48 -0
- metadata +23 -16
- data/lib/concurrent/actor/actor_context.rb +0 -77
- data/lib/concurrent/actor/actor_ref.rb +0 -67
- data/lib/concurrent/actor/simple_actor_ref.rb +0 -94
- data/lib/concurrent_ruby_ext.bundle +0 -0
- data/spec/concurrent/actor/actor_context_spec.rb +0 -29
- data/spec/concurrent/actor/actor_ref_shared.rb +0 -263
- data/spec/concurrent/actor/simple_actor_ref_spec.rb +0 -135
- data/spec/support/functions.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58bf31b4139ff60bd206c0771a4616653c77d710
|
4
|
+
data.tar.gz: 1fbbe6556817458272475deac127e92fc0c723a2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d701a724fe2bb49c0bfa6c8cd3ccb242af88983ac04d90ac622a40a00e3e8e39b21a635a090c6dd808124f82733c66b99fabf05504691dcbe5a4a1f97ab8f33
|
7
|
+
data.tar.gz: 6bc189a09d26d754131c4ca8f09ec45912f2c0a4bb22ef3a578daa5781103df38597c8ab86573a78f211aa88f1f635188673b3d0d51334f56588b5c3d7c3f841
|
data/README.md
CHANGED
@@ -18,6 +18,8 @@ and classic concurrency patterns.
|
|
18
18
|
<p>
|
19
19
|
The design goals of this gem are:
|
20
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>
|
21
23
|
<li>Stay true to the spirit of the languages providing inspiration</li>
|
22
24
|
<li>But implement in a way that makes sense for Ruby</li>
|
23
25
|
<li>Keep the semantics as idiomatic Ruby as possible</li>
|
@@ -126,14 +128,6 @@ task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('IN
|
|
126
128
|
task.state #=> :pending
|
127
129
|
sleep(3) # do other stuff
|
128
130
|
task.value #=> 25.96
|
129
|
-
|
130
|
-
# Async
|
131
|
-
ticker = Ticker.new
|
132
|
-
ticker.extend(Concurrent::Async)
|
133
|
-
hpq = ticker.async.get_year_end_closing('HPQ', 2013)
|
134
|
-
ibm = ticker.await.get_year_end_closing('IBM', 2013)
|
135
|
-
hpq.value #=> 27.98
|
136
|
-
ibm.value #=> 187.57
|
137
131
|
```
|
138
132
|
|
139
133
|
## Contributors
|
@@ -144,6 +138,7 @@ ibm.value #=> 187.57
|
|
144
138
|
* [Lucas Allan](https://github.com/lucasallan)
|
145
139
|
* [Petr Chalupa](https://github.com/pitr-ch)
|
146
140
|
* [Ravil Bayramgalin](https://github.com/brainopia)
|
141
|
+
* [Larry Lv](https://github.com/larrylv)
|
147
142
|
* [Giuseppe Capizzi](https://github.com/gcapizzi)
|
148
143
|
* [Brian Shirai](https://github.com/brixen)
|
149
144
|
* [Chip Miller](https://github.com/chip-miller)
|
data/lib/concurrent.rb
CHANGED
@@ -13,6 +13,7 @@ require 'concurrent/async'
|
|
13
13
|
require 'concurrent/dataflow'
|
14
14
|
require 'concurrent/delay'
|
15
15
|
require 'concurrent/dereferenceable'
|
16
|
+
require 'concurrent/errors'
|
16
17
|
require 'concurrent/exchanger'
|
17
18
|
require 'concurrent/future'
|
18
19
|
require 'concurrent/ivar'
|
@@ -27,6 +28,7 @@ require 'concurrent/stoppable'
|
|
27
28
|
require 'concurrent/supervisor'
|
28
29
|
require 'concurrent/timer_task'
|
29
30
|
require 'concurrent/tvar'
|
31
|
+
require 'concurrent/actress'
|
30
32
|
|
31
33
|
# Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell,
|
32
34
|
# F#, C#, Java, and classic concurrency patterns.
|
@@ -179,7 +179,7 @@ module Concurrent
|
|
179
179
|
#
|
180
180
|
# @deprecated +Actor+ is being replaced with a completely new framework prior to v1.0.0
|
181
181
|
def self.pool(count, *args, &block)
|
182
|
-
warn '[DEPRECATED] `Actor` is deprecated and will be replaced with `
|
182
|
+
warn '[DEPRECATED] `Actor` is deprecated and will be replaced with `Actress`.'
|
183
183
|
raise ArgumentError.new('count must be greater than zero') unless count > 0
|
184
184
|
mailbox = Queue.new
|
185
185
|
actors = count.times.collect do
|
@@ -212,7 +212,7 @@ module Concurrent
|
|
212
212
|
#
|
213
213
|
# @!visibility public
|
214
214
|
def act(*message)
|
215
|
-
warn '[DEPRECATED] `Actor` is deprecated and will be replaced with `
|
215
|
+
warn '[DEPRECATED] `Actor` is deprecated and will be replaced with `Actress`.'
|
216
216
|
raise NotImplementedError.new("#{self.class} does not implement #act")
|
217
217
|
end
|
218
218
|
|
@@ -220,7 +220,7 @@ module Concurrent
|
|
220
220
|
#
|
221
221
|
# @deprecated +Actor+ is being replaced with a completely new framework prior to v1.0.0
|
222
222
|
def on_run # :nodoc:
|
223
|
-
warn '[DEPRECATED] `Actor` is deprecated and will be replaced with `
|
223
|
+
warn '[DEPRECATED] `Actor` is deprecated and will be replaced with `Actress`.'
|
224
224
|
queue.clear
|
225
225
|
end
|
226
226
|
|
@@ -57,7 +57,7 @@ module Concurrent
|
|
57
57
|
|
58
58
|
def post!(seconds, *message)
|
59
59
|
raise ArgumentError.new('empty message') if message.empty?
|
60
|
-
raise Concurrent::
|
60
|
+
raise Concurrent::LifecycleError unless ready?
|
61
61
|
raise Concurrent::TimeoutError if seconds.to_f <= 0.0
|
62
62
|
event = Event.new
|
63
63
|
cback = Queue.new
|
data/lib/concurrent/actors.rb
CHANGED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'concurrent/configuration'
|
2
|
+
require 'concurrent/executor/one_by_one'
|
3
|
+
require 'concurrent/ivar'
|
4
|
+
require 'concurrent/logging'
|
5
|
+
|
6
|
+
module Concurrent
|
7
|
+
|
8
|
+
# {include:file:lib/concurrent/actress/doc.md}
|
9
|
+
module Actress
|
10
|
+
|
11
|
+
require 'concurrent/actress/type_check'
|
12
|
+
require 'concurrent/actress/errors'
|
13
|
+
require 'concurrent/actress/core_delegations'
|
14
|
+
require 'concurrent/actress/envelope'
|
15
|
+
require 'concurrent/actress/reference'
|
16
|
+
require 'concurrent/actress/core'
|
17
|
+
require 'concurrent/actress/context'
|
18
|
+
|
19
|
+
require 'concurrent/actress/ad_hoc'
|
20
|
+
|
21
|
+
# @return [Reference, nil] current executing actor if any
|
22
|
+
def self.current
|
23
|
+
Thread.current[:__current_actress__]
|
24
|
+
end
|
25
|
+
|
26
|
+
# implements ROOT
|
27
|
+
class Root
|
28
|
+
include Context
|
29
|
+
# to allow spawning of new actors, spawn needs to be called inside the parent Actor
|
30
|
+
def on_message(message)
|
31
|
+
if message.is_a?(Array) && message.first == :spawn
|
32
|
+
spawn message[1], &message[2]
|
33
|
+
else
|
34
|
+
# ignore
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# A root actor, a default parent of all actors spawned outside an actor
|
40
|
+
ROOT = Core.new(parent: nil, name: '/', class: Root).reference
|
41
|
+
|
42
|
+
# @param block for actress_class instantiation
|
43
|
+
# @param args see {.spawn_optionify}
|
44
|
+
def self.spawn(*args, &block)
|
45
|
+
warn '[EXPERIMENTAL] A full release of `Actress`, renamed `Actor`, is expected in the 0.7.0 release.'
|
46
|
+
if Actress.current
|
47
|
+
Core.new(spawn_optionify(*args).merge(parent: Actress.current), &block).reference
|
48
|
+
else
|
49
|
+
ROOT.ask([:spawn, spawn_optionify(*args), block]).value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# as {.spawn} but it'll raise when Actor not initialized properly
|
54
|
+
def self.spawn!(*args, &block)
|
55
|
+
warn '[EXPERIMENTAL] A full release of `Actress`, renamed `Actor`, is expected in the 0.7.0 release.'
|
56
|
+
spawn(spawn_optionify(*args).merge(initialized: ivar = IVar.new), &block).tap { ivar.no_error! }
|
57
|
+
end
|
58
|
+
|
59
|
+
# @overload spawn_optionify(actress_class, name, *args)
|
60
|
+
# @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
|
62
|
+
# @param args for actress_class instantiation
|
63
|
+
# @overload spawn_optionify(opts)
|
64
|
+
# see {Core#initialize} opts
|
65
|
+
def self.spawn_optionify(*args)
|
66
|
+
if args.size == 1 && args.first.is_a?(Hash)
|
67
|
+
args.first
|
68
|
+
else
|
69
|
+
{ class: args[0],
|
70
|
+
name: args[1],
|
71
|
+
args: args[2..-1] }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actress
|
3
|
+
class AdHoc
|
4
|
+
include Context
|
5
|
+
def initialize(*args, &initializer)
|
6
|
+
@on_message = Type! initializer.call(*args), Proc
|
7
|
+
end
|
8
|
+
|
9
|
+
def on_message(message)
|
10
|
+
instance_exec message, &@on_message
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actress
|
3
|
+
|
4
|
+
# module used to define actor behaviours
|
5
|
+
# @example ping
|
6
|
+
# class Ping
|
7
|
+
# include Context
|
8
|
+
# def on_message(message)
|
9
|
+
# message
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# Ping.spawn(:ping1).ask(:m).value #=> :m
|
14
|
+
module Context
|
15
|
+
include TypeCheck
|
16
|
+
include CoreDelegations
|
17
|
+
|
18
|
+
attr_reader :core
|
19
|
+
|
20
|
+
# @abstract override to define Actor's behaviour
|
21
|
+
# @param [Object] message
|
22
|
+
# @return [Object] a result which will be used to set the IVar supplied to Reference#ask
|
23
|
+
# @note self should not be returned (or sent to other actors), {#reference} should be used
|
24
|
+
# instead
|
25
|
+
def on_message(message)
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
|
29
|
+
def logger
|
30
|
+
core.logger
|
31
|
+
end
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
def on_envelope(envelope)
|
35
|
+
@envelope = envelope
|
36
|
+
on_message envelope.message
|
37
|
+
ensure
|
38
|
+
@envelope = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# @see Actress.spawn
|
42
|
+
def spawn(*args, &block)
|
43
|
+
Actress.spawn(*args, &block)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @see Core#children
|
47
|
+
def children
|
48
|
+
core.children
|
49
|
+
end
|
50
|
+
|
51
|
+
# @see Core#terminate!
|
52
|
+
def terminate!
|
53
|
+
core.terminate!
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# @api private
|
59
|
+
def initialize_core(core)
|
60
|
+
@core = Type! core, Core
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Envelope] current envelope, accessible inside #on_message processing
|
64
|
+
def envelope
|
65
|
+
@envelope or raise 'envelope not set'
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.included(base)
|
69
|
+
base.extend ClassMethods
|
70
|
+
super base
|
71
|
+
end
|
72
|
+
|
73
|
+
module ClassMethods
|
74
|
+
# behaves as {Actress.spawn} but class_name is omitted
|
75
|
+
def spawn(name_or_opts, *args, &block)
|
76
|
+
Actress.spawn spawn_optionify(name_or_opts, *args), &block
|
77
|
+
end
|
78
|
+
|
79
|
+
# behaves as {Actress.spawn!} but class_name is omitted
|
80
|
+
def spawn!(name_or_opts, *args, &block)
|
81
|
+
Actress.spawn! spawn_optionify(name_or_opts, *args), &block
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def spawn_optionify(name_or_opts, *args)
|
87
|
+
if name_or_opts.is_a? Hash
|
88
|
+
name_or_opts.merge class: self
|
89
|
+
else
|
90
|
+
{ class: self, name: name_or_opts, args: args }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actress
|
3
|
+
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
# Core of the actor
|
7
|
+
# @api private
|
8
|
+
# @note devel: core should not block on anything, e.g. it cannot wait on children to terminate
|
9
|
+
# that would eat up all threads in task pool and deadlock
|
10
|
+
class Core
|
11
|
+
include TypeCheck
|
12
|
+
include Concurrent::Logging
|
13
|
+
|
14
|
+
attr_reader :reference, :name, :path, :executor, :terminated
|
15
|
+
|
16
|
+
# @option opts [String] name
|
17
|
+
# @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
|
20
|
+
# @option opts [Executor] executor, default is `Concurrent.configuration.global_task_pool`
|
21
|
+
# @option opts [IVar, nil] initialized, if present it'll be set or failed after {Context} initialization
|
22
|
+
# @option opts [Proc, nil] logger a proc accepting (level, progname, message = nil, &block) params,
|
23
|
+
# can be used to hook actor instance to any logging system
|
24
|
+
# @param [Proc] block for class instantiation
|
25
|
+
def initialize(opts = {}, &block)
|
26
|
+
@mailbox = Array.new
|
27
|
+
@one_by_one = OneByOne.new
|
28
|
+
# 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
|
34
|
+
|
35
|
+
parent = opts[:parent]
|
36
|
+
@parent_core = (Type! parent, Reference, NilClass) && parent.send(:core)
|
37
|
+
if @parent_core.nil? && @name != '/'
|
38
|
+
raise 'only root has no parent'
|
39
|
+
end
|
40
|
+
|
41
|
+
@path = @parent_core ? File.join(@parent_core.path, @name) : @name
|
42
|
+
@logger = opts[:logger]
|
43
|
+
|
44
|
+
@parent_core.add_child reference if @parent_core
|
45
|
+
|
46
|
+
@actress_class = actress_class = Child! opts.fetch(:class), Context
|
47
|
+
args = opts.fetch(:args, [])
|
48
|
+
initialized = Type! opts[:initialized], IVar, NilClass
|
49
|
+
|
50
|
+
schedule_execution do
|
51
|
+
begin
|
52
|
+
@actress = actress_class.new *args, &block
|
53
|
+
@actress.send :initialize_core, self
|
54
|
+
initialized.set true if initialized
|
55
|
+
rescue => ex
|
56
|
+
log ERROR, ex
|
57
|
+
terminate!
|
58
|
+
initialized.fail ex if initialized
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Reference, nil] of parent actor
|
64
|
+
def parent
|
65
|
+
@parent_core && @parent_core.reference
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [Array<Reference>] of children actors
|
69
|
+
def children
|
70
|
+
guard!
|
71
|
+
@children.to_a
|
72
|
+
end
|
73
|
+
|
74
|
+
# @api private
|
75
|
+
def add_child(child)
|
76
|
+
guard!
|
77
|
+
Type! child, Reference
|
78
|
+
@children.add child
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
|
82
|
+
# @api private
|
83
|
+
def remove_child(child)
|
84
|
+
schedule_execution do
|
85
|
+
Type! child, Reference
|
86
|
+
@children.delete child
|
87
|
+
end
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
# is executed by Reference scheduling processing of new messages
|
92
|
+
# can be called from other alternative Reference implementations
|
93
|
+
# @param [Envelope] envelope
|
94
|
+
def on_envelope(envelope)
|
95
|
+
schedule_execution do
|
96
|
+
if terminated?
|
97
|
+
reject_envelope envelope
|
98
|
+
else
|
99
|
+
@mailbox.push envelope
|
100
|
+
end
|
101
|
+
process_envelopes?
|
102
|
+
end
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
|
106
|
+
# @note Actor rejects envelopes when terminated.
|
107
|
+
# @return [true, false] if actor is terminated
|
108
|
+
def terminated?
|
109
|
+
@terminated.set?
|
110
|
+
end
|
111
|
+
|
112
|
+
# Terminates the actor. Any Envelope received after termination is rejected.
|
113
|
+
# Terminates all its children, does not wait until they are terminated.
|
114
|
+
def terminate!
|
115
|
+
guard!
|
116
|
+
@children.each do |ch|
|
117
|
+
ch.send(:core).tap { |core| core.send(:schedule_execution) { core.terminate! } }
|
118
|
+
end
|
119
|
+
|
120
|
+
@terminated.set
|
121
|
+
|
122
|
+
@parent_core.remove_child reference if @parent_core
|
123
|
+
@mailbox.each do |envelope|
|
124
|
+
reject_envelope envelope
|
125
|
+
log DEBUG, "rejected #{envelope.message} from #{envelope.sender_path}"
|
126
|
+
end
|
127
|
+
@mailbox.clear
|
128
|
+
|
129
|
+
nil
|
130
|
+
end
|
131
|
+
|
132
|
+
# @api private
|
133
|
+
# ensures that we are inside of the executor
|
134
|
+
def guard!
|
135
|
+
unless Actress.current == reference
|
136
|
+
raise "can be called only inside actor #{reference} but was #{Actress.current}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
# Ensures that only one envelope processing is scheduled with #schedule_execution,
|
143
|
+
# this allows other scheduled blocks to be executed before next envelope processing.
|
144
|
+
# Simply put this ensures that Core is still responsive to internal calls (like add_child)
|
145
|
+
# even though the Actor is flooded with messages.
|
146
|
+
def process_envelopes?
|
147
|
+
unless @mailbox.empty? || @receive_envelope_scheduled
|
148
|
+
@receive_envelope_scheduled = true
|
149
|
+
schedule_execution { receive_envelope }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Processes single envelope, calls #process_envelopes? at the end to ensure next envelope
|
154
|
+
# scheduling.
|
155
|
+
def receive_envelope
|
156
|
+
envelope = @mailbox.shift
|
157
|
+
|
158
|
+
if terminated?
|
159
|
+
reject_envelope envelope
|
160
|
+
log FATAL, "this should not be happening #{caller[0]}"
|
161
|
+
end
|
162
|
+
|
163
|
+
log DEBUG, "received #{envelope.message} from #{envelope.sender_path}"
|
164
|
+
|
165
|
+
result = @actress.on_envelope envelope
|
166
|
+
envelope.ivar.set result unless envelope.ivar.nil?
|
167
|
+
|
168
|
+
nil
|
169
|
+
rescue => error
|
170
|
+
log ERROR, error
|
171
|
+
terminate!
|
172
|
+
envelope.ivar.fail error unless envelope.ivar.nil?
|
173
|
+
ensure
|
174
|
+
@receive_envelope_scheduled = false
|
175
|
+
process_envelopes?
|
176
|
+
end
|
177
|
+
|
178
|
+
# Schedules blocks to be executed on executor sequentially,
|
179
|
+
# sets Actress.current
|
180
|
+
def schedule_execution
|
181
|
+
@one_by_one.post(@executor) do
|
182
|
+
begin
|
183
|
+
Thread.current[:__current_actress__] = reference
|
184
|
+
yield
|
185
|
+
rescue => e
|
186
|
+
log FATAL, e
|
187
|
+
ensure
|
188
|
+
Thread.current[:__current_actress__] = nil
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
nil
|
193
|
+
end
|
194
|
+
|
195
|
+
def reject_envelope(envelope)
|
196
|
+
envelope.reject! ActressTerminated.new(reference)
|
197
|
+
end
|
198
|
+
|
199
|
+
def log(level, message = nil, &block)
|
200
|
+
super level, @path, message, &block
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|