Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
38 changed files
with
923 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
require 'logger' | ||
|
||
require 'concurrent/configuration' | ||
require 'concurrent/executor/one_by_one' | ||
require 'concurrent/ivar' | ||
require 'concurrent/logging' | ||
|
||
module Concurrent | ||
|
||
# @example ping | ||
# class Ping | ||
# include Context | ||
# def on_message(message) | ||
# message | ||
# end | ||
# end | ||
# Ping.spawn(:ping1).ask(:m).value #=> :m | ||
module Actress | ||
|
||
require 'concurrent/actress/type_check' | ||
require 'concurrent/actress/errors' | ||
require 'concurrent/actress/core_delegations' | ||
require 'concurrent/actress/envelope' | ||
require 'concurrent/actress/reference' | ||
require 'concurrent/actress/core' | ||
require 'concurrent/actress/context' | ||
|
||
require 'concurrent/actress/ad_hoc' | ||
|
||
# @return [Reference, nil] current executing actor if any | ||
def self.current | ||
Thread.current[:__current_actress__] | ||
end | ||
|
||
# implements ROOT | ||
class Root | ||
include Context | ||
# to allow spawning of new actors, spawn needs to be called inside the parent Actor | ||
def on_message(message) | ||
if message.is_a?(Array) && message.first == :spawn | ||
spawn message[1], &message[2] | ||
else | ||
# ignore | ||
end | ||
end | ||
end | ||
|
||
# A root actor, a default parent of all actors spawned outside an actor | ||
ROOT = Core.new(parent: nil, name: '/', class: Root).reference | ||
|
||
# @param block for actress_class instantiation | ||
# @param args see {#spawn_optionify} | ||
def self.spawn(*args, &block) | ||
if Actress.current | ||
Core.new(spawn_optionify(*args).merge(parent: Actress.current), &block).reference | ||
else | ||
ROOT.ask([:spawn, spawn_optionify(*args), block]).value | ||
end | ||
end | ||
|
||
# as {#spawn} but it'll raise when Actor not initialized properly | ||
def self.spawn!(*args, &block) | ||
spawn(spawn_optionify(*args).merge(initialized: ivar = IVar.new), &block).tap { ivar.no_error! } | ||
end | ||
|
||
# @overload spawn_optionify(actress_class, name, *args) | ||
# @param [Context] actress_class to be spawned | ||
# @param [String, Symbol] name of the instance, it's used to generate the path of the actor | ||
# @param args for actress_class instantiation | ||
# @overload spawn_optionify(opts) | ||
# see {Core.new} opts | ||
def self.spawn_optionify(*args) | ||
if args.size == 1 && args.first.is_a?(Hash) | ||
args.first | ||
else | ||
{ class: args[0], | ||
name: args[1], | ||
args: args[2..-1] } | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
module Concurrent | ||
module Actress | ||
class AdHoc | ||
include Context | ||
def initialize(*args, &initializer) | ||
@on_message = Type! initializer.call(*args), Proc | ||
end | ||
|
||
def on_message(message) | ||
instance_exec message, &@on_message | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
module Concurrent | ||
module Actress | ||
|
||
# module used to define actor behaviours | ||
# @example ping | ||
# class Ping | ||
# include Context | ||
# def on_message(message) | ||
# message | ||
# end | ||
# end | ||
# | ||
# Ping.spawn(:ping1).ask(:m).value #=> :m | ||
module Context | ||
include TypeCheck | ||
include CoreDelegations | ||
|
||
attr_reader :core | ||
|
||
# @abstract override to define Actor's behaviour | ||
# @param [Object] message | ||
# @return [Object] a result which will be used to set the IVar supplied to Reference#ask | ||
# @note self should not be returned (or sent to other actors), {#reference} should be used | ||
# instead | ||
def on_message(message) | ||
raise NotImplementedError | ||
end | ||
|
||
def logger | ||
core.logger | ||
end | ||
|
||
# @api private | ||
def on_envelope(envelope) | ||
@envelope = envelope | ||
on_message envelope.message | ||
ensure | ||
@envelope = nil | ||
end | ||
|
||
# @see Actress.spawn | ||
def spawn(*args, &block) | ||
Actress.spawn(*args, &block) | ||
end | ||
|
||
# @see Core#children | ||
def children | ||
core.children | ||
end | ||
|
||
# @see Core#terminate! | ||
def terminate! | ||
core.terminate! | ||
end | ||
|
||
private | ||
|
||
# @api private | ||
def initialize_core(core) | ||
@core = Type! core, Core | ||
end | ||
|
||
# @return [Envelope] current envelope, accessible inside #on_message processing | ||
def envelope | ||
@envelope or raise 'envelope not set' | ||
end | ||
|
||
def self.included(base) | ||
base.extend ClassMethods | ||
super base | ||
end | ||
|
||
module ClassMethods | ||
# behaves as {Actress.spawn} but class_name is omitted | ||
def spawn(name_or_opts, *args, &block) | ||
Actress.spawn spawn_optionify(name_or_opts, *args), &block | ||
end | ||
|
||
# behaves as {Actress.spawn!} but class_name is omitted | ||
def spawn!(name_or_opts, *args, &block) | ||
Actress.spawn! spawn_optionify(name_or_opts, *args), &block | ||
end | ||
|
||
private | ||
|
||
def spawn_optionify(name_or_opts, *args) | ||
if name_or_opts.is_a? Hash | ||
name_or_opts.merge class: self | ||
else | ||
{ class: self, name: name_or_opts, args: args } | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.