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
         |