Skip to content

Commit

Permalink
Merge 130c049 into ab03fae
Browse files Browse the repository at this point in the history
  • Loading branch information
Petr Chalupa committed May 24, 2014
2 parents ab03fae + 130c049 commit 6a272f3
Show file tree
Hide file tree
Showing 38 changed files with 923 additions and 88 deletions.
1 change: 1 addition & 0 deletions Gemfile
Expand Up @@ -2,6 +2,7 @@ source 'https://rubygems.org'

gemspec


group :development do
gem 'rake', '~> 10.2.2'
gem 'countloc', '~> 0.4.0', platforms: :mri
Expand Down
1 change: 1 addition & 0 deletions lib/concurrent.rb
Expand Up @@ -28,6 +28,7 @@
require 'concurrent/supervisor'
require 'concurrent/timer_task'
require 'concurrent/tvar'
require 'concurrent/actress'

# Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell,
# F#, C#, Java, and classic concurrency patterns.
Expand Down
82 changes: 82 additions & 0 deletions lib/concurrent/actress.rb
@@ -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
14 changes: 14 additions & 0 deletions lib/concurrent/actress/ad_hoc.rb
@@ -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
96 changes: 96 additions & 0 deletions lib/concurrent/actress/context.rb
@@ -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

0 comments on commit 6a272f3

Please sign in to comment.