concurrent-ruby 0.7.0.rc0-x86-solaris-2.11
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 +7 -0
 - data/LICENSE.txt +21 -0
 - data/README.md +166 -0
 - data/ext/concurrent_ruby_ext/atomic_reference.c +78 -0
 - data/ext/concurrent_ruby_ext/atomic_reference.h +12 -0
 - data/ext/concurrent_ruby_ext/extconf.rb +59 -0
 - data/ext/concurrent_ruby_ext/rb_concurrent.c +28 -0
 - data/lib/concurrent.rb +45 -0
 - data/lib/concurrent/actress.rb +221 -0
 - data/lib/concurrent/actress/ad_hoc.rb +20 -0
 - data/lib/concurrent/actress/context.rb +98 -0
 - data/lib/concurrent/actress/core.rb +228 -0
 - data/lib/concurrent/actress/core_delegations.rb +42 -0
 - data/lib/concurrent/actress/envelope.rb +41 -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 +232 -0
 - data/lib/concurrent/async.rb +319 -0
 - data/lib/concurrent/atomic.rb +46 -0
 - data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
 - data/lib/concurrent/atomic/atomic_fixnum.rb +162 -0
 - data/lib/concurrent/atomic/condition.rb +67 -0
 - data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +118 -0
 - data/lib/concurrent/atomic/copy_on_write_observer_set.rb +117 -0
 - data/lib/concurrent/atomic/count_down_latch.rb +116 -0
 - data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
 - data/lib/concurrent/atomic/event.rb +98 -0
 - data/lib/concurrent/atomic/thread_local_var.rb +117 -0
 - data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
 - data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
 - data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
 - data/lib/concurrent/atomic_reference/jruby.rb +8 -0
 - data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
 - data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
 - data/lib/concurrent/atomic_reference/rbx.rb +16 -0
 - data/lib/concurrent/atomic_reference/ruby.rb +16 -0
 - data/lib/concurrent/atomics.rb +10 -0
 - data/lib/concurrent/channel/buffered_channel.rb +85 -0
 - data/lib/concurrent/channel/channel.rb +41 -0
 - data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
 - data/lib/concurrent/channel/waitable_list.rb +40 -0
 - data/lib/concurrent/channels.rb +5 -0
 - data/lib/concurrent/collection/blocking_ring_buffer.rb +71 -0
 - data/lib/concurrent/collection/priority_queue.rb +305 -0
 - data/lib/concurrent/collection/ring_buffer.rb +59 -0
 - data/lib/concurrent/collections.rb +3 -0
 - data/lib/concurrent/configuration.rb +158 -0
 - data/lib/concurrent/dataflow.rb +91 -0
 - data/lib/concurrent/delay.rb +112 -0
 - data/lib/concurrent/dereferenceable.rb +101 -0
 - data/lib/concurrent/errors.rb +30 -0
 - data/lib/concurrent/exchanger.rb +34 -0
 - data/lib/concurrent/executor/cached_thread_pool.rb +44 -0
 - data/lib/concurrent/executor/executor.rb +229 -0
 - data/lib/concurrent/executor/fixed_thread_pool.rb +33 -0
 - data/lib/concurrent/executor/immediate_executor.rb +16 -0
 - data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
 - data/lib/concurrent/executor/java_fixed_thread_pool.rb +33 -0
 - data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
 - data/lib/concurrent/executor/java_thread_pool_executor.rb +187 -0
 - data/lib/concurrent/executor/per_thread_executor.rb +24 -0
 - data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
 - data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +32 -0
 - data/lib/concurrent/executor/ruby_single_thread_executor.rb +73 -0
 - data/lib/concurrent/executor/ruby_thread_pool_executor.rb +286 -0
 - data/lib/concurrent/executor/ruby_thread_pool_worker.rb +72 -0
 - data/lib/concurrent/executor/safe_task_executor.rb +35 -0
 - data/lib/concurrent/executor/serialized_execution.rb +90 -0
 - data/lib/concurrent/executor/single_thread_executor.rb +35 -0
 - data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
 - data/lib/concurrent/executor/timer_set.rb +143 -0
 - data/lib/concurrent/executors.rb +9 -0
 - data/lib/concurrent/future.rb +124 -0
 - data/lib/concurrent/ivar.rb +111 -0
 - data/lib/concurrent/logging.rb +17 -0
 - data/lib/concurrent/mvar.rb +200 -0
 - data/lib/concurrent/obligation.rb +171 -0
 - data/lib/concurrent/observable.rb +40 -0
 - data/lib/concurrent/options_parser.rb +46 -0
 - data/lib/concurrent/promise.rb +169 -0
 - data/lib/concurrent/scheduled_task.rb +78 -0
 - data/lib/concurrent/supervisor.rb +343 -0
 - data/lib/concurrent/timer_task.rb +341 -0
 - data/lib/concurrent/tvar.rb +252 -0
 - data/lib/concurrent/utilities.rb +3 -0
 - data/lib/concurrent/utility/processor_count.rb +150 -0
 - data/lib/concurrent/utility/timeout.rb +35 -0
 - data/lib/concurrent/utility/timer.rb +21 -0
 - data/lib/concurrent/version.rb +3 -0
 - data/lib/concurrent_ruby.rb +1 -0
 - data/lib/concurrent_ruby_ext.so +0 -0
 - data/lib/extension_helper.rb +9 -0
 - metadata +140 -0
 
| 
         @@ -0,0 +1,169 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'thread'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'concurrent/obligation'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'concurrent/options_parser'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Concurrent
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              class Promise
         
     | 
| 
      
 9 
     | 
    
         
            +
                include Obligation
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                # Initialize a new Promise with the provided options.
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @param [Hash] opts the options used to define the behavior at update and deref
         
     | 
| 
      
 14 
     | 
    
         
            +
                #
         
     | 
| 
      
 15 
     | 
    
         
            +
                # @option opts [Promise] :parent the parent `Promise` when building a chain/tree
         
     | 
| 
      
 16 
     | 
    
         
            +
                # @option opts [Proc] :on_fulfill fulfillment handler
         
     | 
| 
      
 17 
     | 
    
         
            +
                # @option opts [Proc] :on_reject rejection handler
         
     | 
| 
      
 18 
     | 
    
         
            +
                #
         
     | 
| 
      
 19 
     | 
    
         
            +
                # @option opts [Boolean] :operation (false) when `true` will execute the future on the global
         
     | 
| 
      
 20 
     | 
    
         
            +
                #   operation pool (for long-running operations), when `false` will execute the future on the
         
     | 
| 
      
 21 
     | 
    
         
            +
                #   global task pool (for short-running tasks)
         
     | 
| 
      
 22 
     | 
    
         
            +
                # @option opts [object] :executor when provided will run all operations on
         
     | 
| 
      
 23 
     | 
    
         
            +
                #   this executor rather than the global thread pool (overrides :operation)
         
     | 
| 
      
 24 
     | 
    
         
            +
                #
         
     | 
| 
      
 25 
     | 
    
         
            +
                # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
         
     | 
| 
      
 26 
     | 
    
         
            +
                # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
         
     | 
| 
      
 27 
     | 
    
         
            +
                # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
         
     | 
| 
      
 28 
     | 
    
         
            +
                #   returning the value returned from the proc
         
     | 
| 
      
 29 
     | 
    
         
            +
                #
         
     | 
| 
      
 30 
     | 
    
         
            +
                # @see http://wiki.commonjs.org/wiki/Promises/A
         
     | 
| 
      
 31 
     | 
    
         
            +
                # @see http://promises-aplus.github.io/promises-spec/
         
     | 
| 
      
 32 
     | 
    
         
            +
                def initialize(opts = {}, &block)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  opts.delete_if { |k, v| v.nil? }
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  @executor = OptionsParser::get_executor_from(opts)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  @parent = opts.fetch(:parent) { nil }
         
     | 
| 
      
 37 
     | 
    
         
            +
                  @on_fulfill = opts.fetch(:on_fulfill) { Proc.new { |result| result } }
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @on_reject = opts.fetch(:on_reject) { Proc.new { |reason| raise reason } }
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  @promise_body = block || Proc.new { |result| result }
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @state = :unscheduled
         
     | 
| 
      
 42 
     | 
    
         
            +
                  @children = []
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  init_obligation
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                # @return [Promise]
         
     | 
| 
      
 48 
     | 
    
         
            +
                def self.fulfill(value, opts = {})
         
     | 
| 
      
 49 
     | 
    
         
            +
                  Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, true, value, nil) }
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                # @return [Promise]
         
     | 
| 
      
 54 
     | 
    
         
            +
                def self.reject(reason, opts = {})
         
     | 
| 
      
 55 
     | 
    
         
            +
                  Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, false, nil, reason) }
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                # @return [Promise]
         
     | 
| 
      
 59 
     | 
    
         
            +
                # @since 0.5.0
         
     | 
| 
      
 60 
     | 
    
         
            +
                def execute
         
     | 
| 
      
 61 
     | 
    
         
            +
                  if root?
         
     | 
| 
      
 62 
     | 
    
         
            +
                    if compare_and_set_state(:pending, :unscheduled)
         
     | 
| 
      
 63 
     | 
    
         
            +
                      set_pending
         
     | 
| 
      
 64 
     | 
    
         
            +
                      realize(@promise_body)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
                  else
         
     | 
| 
      
 67 
     | 
    
         
            +
                    @parent.execute
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
                  self
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                # @since 0.5.0
         
     | 
| 
      
 73 
     | 
    
         
            +
                def self.execute(opts = {}, &block)
         
     | 
| 
      
 74 
     | 
    
         
            +
                  new(opts, &block).execute
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                # @return [Promise] the new promise
         
     | 
| 
      
 78 
     | 
    
         
            +
                def then(rescuer = nil, &block)
         
     | 
| 
      
 79 
     | 
    
         
            +
                  raise ArgumentError.new('rescuers and block are both missing') if rescuer.nil? && !block_given?
         
     | 
| 
      
 80 
     | 
    
         
            +
                  block = Proc.new { |result| result } if block.nil?
         
     | 
| 
      
 81 
     | 
    
         
            +
                  child = Promise.new(
         
     | 
| 
      
 82 
     | 
    
         
            +
                    parent: self,
         
     | 
| 
      
 83 
     | 
    
         
            +
                    executor: @executor,
         
     | 
| 
      
 84 
     | 
    
         
            +
                    on_fulfill: block,
         
     | 
| 
      
 85 
     | 
    
         
            +
                    on_reject: rescuer
         
     | 
| 
      
 86 
     | 
    
         
            +
                  )
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  mutex.synchronize do
         
     | 
| 
      
 89 
     | 
    
         
            +
                    child.state = :pending if @state == :pending
         
     | 
| 
      
 90 
     | 
    
         
            +
                    child.on_fulfill(apply_deref_options(@value)) if @state == :fulfilled
         
     | 
| 
      
 91 
     | 
    
         
            +
                    child.on_reject(@reason) if @state == :rejected
         
     | 
| 
      
 92 
     | 
    
         
            +
                    @children << child
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                  child
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                # @return [Promise]
         
     | 
| 
      
 99 
     | 
    
         
            +
                def on_success(&block)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  raise ArgumentError.new('no block given') unless block_given?
         
     | 
| 
      
 101 
     | 
    
         
            +
                  self.then &block
         
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                # @return [Promise]
         
     | 
| 
      
 105 
     | 
    
         
            +
                def rescue(&block)
         
     | 
| 
      
 106 
     | 
    
         
            +
                  self.then(block)
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                alias_method :catch, :rescue
         
     | 
| 
      
 110 
     | 
    
         
            +
                alias_method :on_error, :rescue
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                protected
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                def set_pending
         
     | 
| 
      
 115 
     | 
    
         
            +
                  mutex.synchronize do
         
     | 
| 
      
 116 
     | 
    
         
            +
                    @state = :pending
         
     | 
| 
      
 117 
     | 
    
         
            +
                    @children.each { |c| c.set_pending }
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
      
 122 
     | 
    
         
            +
                def root? # :nodoc:
         
     | 
| 
      
 123 
     | 
    
         
            +
                  @parent.nil?
         
     | 
| 
      
 124 
     | 
    
         
            +
                end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
      
 127 
     | 
    
         
            +
                def on_fulfill(result)
         
     | 
| 
      
 128 
     | 
    
         
            +
                  realize Proc.new { @on_fulfill.call(result) }
         
     | 
| 
      
 129 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 130 
     | 
    
         
            +
                end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
      
 133 
     | 
    
         
            +
                def on_reject(reason)
         
     | 
| 
      
 134 
     | 
    
         
            +
                  realize Proc.new { @on_reject.call(reason) }
         
     | 
| 
      
 135 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 136 
     | 
    
         
            +
                end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                def notify_child(child)
         
     | 
| 
      
 139 
     | 
    
         
            +
                  if_state(:fulfilled) { child.on_fulfill(apply_deref_options(@value)) }
         
     | 
| 
      
 140 
     | 
    
         
            +
                  if_state(:rejected) { child.on_reject(@reason) }
         
     | 
| 
      
 141 
     | 
    
         
            +
                end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
      
 144 
     | 
    
         
            +
                def realize(task)
         
     | 
| 
      
 145 
     | 
    
         
            +
                  @executor.post do
         
     | 
| 
      
 146 
     | 
    
         
            +
                    success, value, reason = SafeTaskExecutor.new(task).execute
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                    children_to_notify = mutex.synchronize do
         
     | 
| 
      
 149 
     | 
    
         
            +
                      set_state!(success, value, reason)
         
     | 
| 
      
 150 
     | 
    
         
            +
                      @children.dup
         
     | 
| 
      
 151 
     | 
    
         
            +
                    end
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                    children_to_notify.each { |child| notify_child(child) }
         
     | 
| 
      
 154 
     | 
    
         
            +
                  end
         
     | 
| 
      
 155 
     | 
    
         
            +
                end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                def set_state!(success, value, reason)
         
     | 
| 
      
 158 
     | 
    
         
            +
                  set_state(success, value, reason)
         
     | 
| 
      
 159 
     | 
    
         
            +
                  event.set
         
     | 
| 
      
 160 
     | 
    
         
            +
                end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                def synchronized_set_state!(success, value, reason)
         
     | 
| 
      
 163 
     | 
    
         
            +
                  mutex.lock
         
     | 
| 
      
 164 
     | 
    
         
            +
                  set_state!(success, value, reason)
         
     | 
| 
      
 165 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 166 
     | 
    
         
            +
                  mutex.unlock
         
     | 
| 
      
 167 
     | 
    
         
            +
                end
         
     | 
| 
      
 168 
     | 
    
         
            +
              end
         
     | 
| 
      
 169 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,78 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'concurrent/ivar'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'concurrent/utility/timer'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'concurrent/executor/safe_task_executor'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Concurrent
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              class ScheduledTask < IVar
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                attr_reader :schedule_time
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(intended_time, opts = {}, &block)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  raise ArgumentError.new('no block given') unless block_given?
         
     | 
| 
      
 13 
     | 
    
         
            +
                  TimerSet.calculate_schedule_time(intended_time) # raises exceptons
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  super(NO_VALUE, opts)
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  self.observers = CopyOnNotifyObserverSet.new
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @intended_time =  intended_time
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @state = :unscheduled
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @task = block
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                # @since 0.5.0
         
     | 
| 
      
 24 
     | 
    
         
            +
                def execute
         
     | 
| 
      
 25 
     | 
    
         
            +
                  if compare_and_set_state(:pending, :unscheduled)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @schedule_time = TimerSet.calculate_schedule_time(@intended_time)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    Concurrent::timer(@schedule_time.to_f - Time.now.to_f, &method(:process_task))
         
     | 
| 
      
 28 
     | 
    
         
            +
                    self
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                # @since 0.5.0
         
     | 
| 
      
 33 
     | 
    
         
            +
                def self.execute(intended_time, opts = {}, &block)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  return ScheduledTask.new(intended_time, opts, &block).execute
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def cancelled?
         
     | 
| 
      
 38 
     | 
    
         
            +
                  state == :cancelled
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def in_progress?
         
     | 
| 
      
 42 
     | 
    
         
            +
                  state == :in_progress
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def cancel
         
     | 
| 
      
 46 
     | 
    
         
            +
                  if_state(:unscheduled, :pending) do
         
     | 
| 
      
 47 
     | 
    
         
            +
                    @state = :cancelled
         
     | 
| 
      
 48 
     | 
    
         
            +
                    event.set
         
     | 
| 
      
 49 
     | 
    
         
            +
                    true
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
                alias_method :stop, :cancel
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def add_observer(*args, &block)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  if_state(:unscheduled, :pending, :in_progress) do
         
     | 
| 
      
 56 
     | 
    
         
            +
                    observers.add_observer(*args, &block)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                protected :set, :fail, :complete
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                private
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                def process_task
         
     | 
| 
      
 65 
     | 
    
         
            +
                  if compare_and_set_state(:in_progress, :pending)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    success, val, reason = SafeTaskExecutor.new(@task).execute
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    mutex.synchronize do
         
     | 
| 
      
 69 
     | 
    
         
            +
                      set_state(success, val, reason)
         
     | 
| 
      
 70 
     | 
    
         
            +
                      event.set
         
     | 
| 
      
 71 
     | 
    
         
            +
                    end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                    time = Time.now
         
     | 
| 
      
 74 
     | 
    
         
            +
                    observers.notify_and_delete_observers{ [time, self.value, reason] }
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
              end
         
     | 
| 
      
 78 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,343 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'thread'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'concurrent/errors'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Concurrent
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              class Supervisor
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                DEFAULT_MONITOR_INTERVAL = 1
         
     | 
| 
      
 10 
     | 
    
         
            +
                RESTART_STRATEGIES = [:one_for_one, :one_for_all, :rest_for_one]
         
     | 
| 
      
 11 
     | 
    
         
            +
                DEFAULT_MAX_RESTART = 5
         
     | 
| 
      
 12 
     | 
    
         
            +
                DEFAULT_MAX_TIME = 60
         
     | 
| 
      
 13 
     | 
    
         
            +
                WORKER_API = {run: 0, stop: 0, running?: 0}
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                CHILD_TYPES = [:worker, :supervisor]
         
     | 
| 
      
 16 
     | 
    
         
            +
                CHILD_RESTART_OPTIONS = [:permanent, :transient, :temporary]
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                WorkerContext = Struct.new(:worker, :type, :restart) do
         
     | 
| 
      
 19 
     | 
    
         
            +
                  attr_accessor :thread
         
     | 
| 
      
 20 
     | 
    
         
            +
                  attr_accessor :terminated
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  def alive?() return thread && thread.alive?; end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def needs_restart?
         
     | 
| 
      
 25 
     | 
    
         
            +
                    return false if thread && thread.alive?
         
     | 
| 
      
 26 
     | 
    
         
            +
                    return false if terminated
         
     | 
| 
      
 27 
     | 
    
         
            +
                    case self.restart
         
     | 
| 
      
 28 
     | 
    
         
            +
                    when :permanent
         
     | 
| 
      
 29 
     | 
    
         
            +
                      return true
         
     | 
| 
      
 30 
     | 
    
         
            +
                    when :transient
         
     | 
| 
      
 31 
     | 
    
         
            +
                      return thread.nil? || thread.status.nil?
         
     | 
| 
      
 32 
     | 
    
         
            +
                    else #when :temporary
         
     | 
| 
      
 33 
     | 
    
         
            +
                      return false
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                WorkerCounts = Struct.new(:specs, :supervisors, :workers) do
         
     | 
| 
      
 39 
     | 
    
         
            +
                  attr_accessor :status
         
     | 
| 
      
 40 
     | 
    
         
            +
                  def add(context)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    self.specs += 1
         
     | 
| 
      
 42 
     | 
    
         
            +
                    self.supervisors += 1 if context.type == :supervisor
         
     | 
| 
      
 43 
     | 
    
         
            +
                    self.workers += 1 if context.type == :worker
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                  def active() sleeping + running + aborting end
         
     | 
| 
      
 46 
     | 
    
         
            +
                  def sleeping() @status.reduce(0){|x, s| x += (s == 'sleep' ? 1 : 0) } end
         
     | 
| 
      
 47 
     | 
    
         
            +
                  def running() @status.reduce(0){|x, s| x += (s == 'run' ? 1 : 0) } end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  def aborting() @status.reduce(0){|x, s| x += (s == 'aborting' ? 1 : 0) } end
         
     | 
| 
      
 49 
     | 
    
         
            +
                  def stopped() @status.reduce(0){|x, s| x += (s == false ? 1 : 0) } end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  def abend() @status.reduce(0){|x, s| x += (s.nil? ? 1 : 0) } end
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                attr_reader :monitor_interval
         
     | 
| 
      
 54 
     | 
    
         
            +
                attr_reader :restart_strategy
         
     | 
| 
      
 55 
     | 
    
         
            +
                attr_reader :max_restart
         
     | 
| 
      
 56 
     | 
    
         
            +
                attr_reader :max_time
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                alias_method :strategy, :restart_strategy
         
     | 
| 
      
 59 
     | 
    
         
            +
                alias_method :max_r, :max_restart
         
     | 
| 
      
 60 
     | 
    
         
            +
                alias_method :max_t, :max_time
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                def initialize(opts = {})
         
     | 
| 
      
 63 
     | 
    
         
            +
                  warn '[EXPERIMENTAL] Supervisor is being completely rewritten and will change soon.'
         
     | 
| 
      
 64 
     | 
    
         
            +
                  @restart_strategy = opts[:restart_strategy] || opts[:strategy] || :one_for_one
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @monitor_interval = (opts[:monitor_interval] || DEFAULT_MONITOR_INTERVAL).to_f
         
     | 
| 
      
 66 
     | 
    
         
            +
                  @max_restart = (opts[:max_restart] || opts[:max_r] || DEFAULT_MAX_RESTART).to_i
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @max_time = (opts[:max_time] || opts[:max_t] || DEFAULT_MAX_TIME).to_i
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  raise ArgumentError.new(":#{@restart_strategy} is not a valid restart strategy") unless RESTART_STRATEGIES.include?(@restart_strategy)
         
     | 
| 
      
 70 
     | 
    
         
            +
                  raise ArgumentError.new(':monitor_interval must be greater than zero') unless @monitor_interval > 0.0
         
     | 
| 
      
 71 
     | 
    
         
            +
                  raise ArgumentError.new(':max_restart must be greater than zero') unless @max_restart > 0
         
     | 
| 
      
 72 
     | 
    
         
            +
                  raise ArgumentError.new(':max_time must be greater than zero') unless @max_time > 0
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  @running = false
         
     | 
| 
      
 75 
     | 
    
         
            +
                  @mutex = Mutex.new
         
     | 
| 
      
 76 
     | 
    
         
            +
                  @workers = []
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @monitor = nil
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  @count = WorkerCounts.new(0, 0, 0)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  @restart_times = []
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  add_worker(opts[:worker]) unless opts[:worker].nil?
         
     | 
| 
      
 83 
     | 
    
         
            +
                  add_workers(opts[:workers]) unless opts[:workers].nil?
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                def run!
         
     | 
| 
      
 87 
     | 
    
         
            +
                  @mutex.synchronize do
         
     | 
| 
      
 88 
     | 
    
         
            +
                    raise StandardError.new('already running') if @running
         
     | 
| 
      
 89 
     | 
    
         
            +
                    @running = true
         
     | 
| 
      
 90 
     | 
    
         
            +
                    @monitor = Thread.new do
         
     | 
| 
      
 91 
     | 
    
         
            +
                      Thread.current.abort_on_exception = false
         
     | 
| 
      
 92 
     | 
    
         
            +
                      monitor
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
                  Thread.pass
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                def run
         
     | 
| 
      
 99 
     | 
    
         
            +
                  @mutex.synchronize do
         
     | 
| 
      
 100 
     | 
    
         
            +
                    raise StandardError.new('already running') if @running
         
     | 
| 
      
 101 
     | 
    
         
            +
                    @running = true
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
                  monitor
         
     | 
| 
      
 104 
     | 
    
         
            +
                  true
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                def stop
         
     | 
| 
      
 108 
     | 
    
         
            +
                  @mutex.synchronize do
         
     | 
| 
      
 109 
     | 
    
         
            +
                    return true unless @running
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                    @running = false
         
     | 
| 
      
 112 
     | 
    
         
            +
                    unless @monitor.nil?
         
     | 
| 
      
 113 
     | 
    
         
            +
                      @monitor.run if @monitor.status == 'sleep'
         
     | 
| 
      
 114 
     | 
    
         
            +
                      if @monitor.join(0.1).nil?
         
     | 
| 
      
 115 
     | 
    
         
            +
                        @monitor.kill
         
     | 
| 
      
 116 
     | 
    
         
            +
                      end
         
     | 
| 
      
 117 
     | 
    
         
            +
                      @monitor = nil
         
     | 
| 
      
 118 
     | 
    
         
            +
                    end
         
     | 
| 
      
 119 
     | 
    
         
            +
                    @restart_times.clear
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                    @workers.length.times do |i|
         
     | 
| 
      
 122 
     | 
    
         
            +
                      context = @workers[-1-i]
         
     | 
| 
      
 123 
     | 
    
         
            +
                      terminate_worker(context)
         
     | 
| 
      
 124 
     | 
    
         
            +
                    end
         
     | 
| 
      
 125 
     | 
    
         
            +
                    prune_workers
         
     | 
| 
      
 126 
     | 
    
         
            +
                  end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                  true
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                def running?
         
     | 
| 
      
 132 
     | 
    
         
            +
                  @mutex.synchronize { @running }
         
     | 
| 
      
 133 
     | 
    
         
            +
                end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                def length
         
     | 
| 
      
 136 
     | 
    
         
            +
                  @mutex.synchronize { @workers.length }
         
     | 
| 
      
 137 
     | 
    
         
            +
                end
         
     | 
| 
      
 138 
     | 
    
         
            +
                alias_method :size, :length
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                def current_restart_count
         
     | 
| 
      
 141 
     | 
    
         
            +
                  @restart_times.length
         
     | 
| 
      
 142 
     | 
    
         
            +
                end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                def count
         
     | 
| 
      
 145 
     | 
    
         
            +
                  @mutex.synchronize do
         
     | 
| 
      
 146 
     | 
    
         
            +
                    @count.status = @workers.collect{|w| w.thread ? w.thread.status : false }
         
     | 
| 
      
 147 
     | 
    
         
            +
                    @count.dup.freeze
         
     | 
| 
      
 148 
     | 
    
         
            +
                  end
         
     | 
| 
      
 149 
     | 
    
         
            +
                end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                def add_worker(worker, opts = {})
         
     | 
| 
      
 152 
     | 
    
         
            +
                  return nil if worker.nil? || ! behaves_as_worker?(worker)
         
     | 
| 
      
 153 
     | 
    
         
            +
                  @mutex.synchronize {
         
     | 
| 
      
 154 
     | 
    
         
            +
                    restart = opts[:restart] || :permanent
         
     | 
| 
      
 155 
     | 
    
         
            +
                    type = opts[:type] || (worker.is_a?(Supervisor) ? :supervisor : nil) || :worker
         
     | 
| 
      
 156 
     | 
    
         
            +
                    raise ArgumentError.new(":#{restart} is not a valid restart option") unless CHILD_RESTART_OPTIONS.include?(restart)
         
     | 
| 
      
 157 
     | 
    
         
            +
                    raise ArgumentError.new(":#{type} is not a valid child type") unless CHILD_TYPES.include?(type)
         
     | 
| 
      
 158 
     | 
    
         
            +
                    context = WorkerContext.new(worker, type, restart)
         
     | 
| 
      
 159 
     | 
    
         
            +
                    @workers << context
         
     | 
| 
      
 160 
     | 
    
         
            +
                    @count.add(context)
         
     | 
| 
      
 161 
     | 
    
         
            +
                    worker.run if @running
         
     | 
| 
      
 162 
     | 
    
         
            +
                    context.object_id
         
     | 
| 
      
 163 
     | 
    
         
            +
                  }
         
     | 
| 
      
 164 
     | 
    
         
            +
                end
         
     | 
| 
      
 165 
     | 
    
         
            +
                alias_method :add_child, :add_worker
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                def add_workers(workers, opts = {})
         
     | 
| 
      
 168 
     | 
    
         
            +
                  workers.collect do |worker|
         
     | 
| 
      
 169 
     | 
    
         
            +
                    add_worker(worker, opts)
         
     | 
| 
      
 170 
     | 
    
         
            +
                  end
         
     | 
| 
      
 171 
     | 
    
         
            +
                end
         
     | 
| 
      
 172 
     | 
    
         
            +
                alias_method :add_children, :add_workers
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                def remove_worker(worker_id)
         
     | 
| 
      
 175 
     | 
    
         
            +
                  @mutex.synchronize do
         
     | 
| 
      
 176 
     | 
    
         
            +
                    index, context = find_worker(worker_id)
         
     | 
| 
      
 177 
     | 
    
         
            +
                    break(nil) if context.nil?
         
     | 
| 
      
 178 
     | 
    
         
            +
                    break(false) if context.alive?
         
     | 
| 
      
 179 
     | 
    
         
            +
                    @workers.delete_at(index)
         
     | 
| 
      
 180 
     | 
    
         
            +
                    context.worker
         
     | 
| 
      
 181 
     | 
    
         
            +
                  end
         
     | 
| 
      
 182 
     | 
    
         
            +
                end
         
     | 
| 
      
 183 
     | 
    
         
            +
                alias_method :remove_child, :remove_worker
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
                def stop_worker(worker_id)
         
     | 
| 
      
 186 
     | 
    
         
            +
                  @mutex.synchronize do
         
     | 
| 
      
 187 
     | 
    
         
            +
                    return true unless @running
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
                    index, context = find_worker(worker_id)
         
     | 
| 
      
 190 
     | 
    
         
            +
                    break(nil) if index.nil?
         
     | 
| 
      
 191 
     | 
    
         
            +
                    context.terminated = true
         
     | 
| 
      
 192 
     | 
    
         
            +
                    terminate_worker(context)
         
     | 
| 
      
 193 
     | 
    
         
            +
                    @workers.delete_at(index) if @workers[index].restart == :temporary
         
     | 
| 
      
 194 
     | 
    
         
            +
                    true
         
     | 
| 
      
 195 
     | 
    
         
            +
                  end
         
     | 
| 
      
 196 
     | 
    
         
            +
                end
         
     | 
| 
      
 197 
     | 
    
         
            +
                alias_method :stop_child, :stop_worker
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
                def start_worker(worker_id)
         
     | 
| 
      
 200 
     | 
    
         
            +
                  @mutex.synchronize do
         
     | 
| 
      
 201 
     | 
    
         
            +
                    return false unless @running
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
                    index, context = find_worker(worker_id)
         
     | 
| 
      
 204 
     | 
    
         
            +
                    break(nil) if context.nil?
         
     | 
| 
      
 205 
     | 
    
         
            +
                    context.terminated = false
         
     | 
| 
      
 206 
     | 
    
         
            +
                    run_worker(context) unless context.alive?
         
     | 
| 
      
 207 
     | 
    
         
            +
                    true
         
     | 
| 
      
 208 
     | 
    
         
            +
                  end
         
     | 
| 
      
 209 
     | 
    
         
            +
                end
         
     | 
| 
      
 210 
     | 
    
         
            +
                alias_method :start_child, :start_worker
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
      
 212 
     | 
    
         
            +
                def restart_worker(worker_id)
         
     | 
| 
      
 213 
     | 
    
         
            +
                  @mutex.synchronize do
         
     | 
| 
      
 214 
     | 
    
         
            +
                    return false unless @running
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
      
 216 
     | 
    
         
            +
                    index, context = find_worker(worker_id)
         
     | 
| 
      
 217 
     | 
    
         
            +
                    break(nil) if context.nil?
         
     | 
| 
      
 218 
     | 
    
         
            +
                    break(false) if context.restart == :temporary
         
     | 
| 
      
 219 
     | 
    
         
            +
                    context.terminated = false
         
     | 
| 
      
 220 
     | 
    
         
            +
                    terminate_worker(context)
         
     | 
| 
      
 221 
     | 
    
         
            +
                    run_worker(context)
         
     | 
| 
      
 222 
     | 
    
         
            +
                    true
         
     | 
| 
      
 223 
     | 
    
         
            +
                  end
         
     | 
| 
      
 224 
     | 
    
         
            +
                end
         
     | 
| 
      
 225 
     | 
    
         
            +
                alias_method :restart_child, :restart_worker
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
                private
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                def behaves_as_worker?(obj)
         
     | 
| 
      
 230 
     | 
    
         
            +
                  WORKER_API.each do |method, arity|
         
     | 
| 
      
 231 
     | 
    
         
            +
                    break(false) unless obj.respond_to?(method) && obj.method(method).arity == arity
         
     | 
| 
      
 232 
     | 
    
         
            +
                    true
         
     | 
| 
      
 233 
     | 
    
         
            +
                  end
         
     | 
| 
      
 234 
     | 
    
         
            +
                end
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                def monitor
         
     | 
| 
      
 237 
     | 
    
         
            +
                  @workers.each{|context| run_worker(context)}
         
     | 
| 
      
 238 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 239 
     | 
    
         
            +
                    sleep(@monitor_interval)
         
     | 
| 
      
 240 
     | 
    
         
            +
                    break unless running?
         
     | 
| 
      
 241 
     | 
    
         
            +
                    @mutex.synchronize do
         
     | 
| 
      
 242 
     | 
    
         
            +
                      prune_workers
         
     | 
| 
      
 243 
     | 
    
         
            +
                      self.send(@restart_strategy)
         
     | 
| 
      
 244 
     | 
    
         
            +
                    end
         
     | 
| 
      
 245 
     | 
    
         
            +
                    break unless running?
         
     | 
| 
      
 246 
     | 
    
         
            +
                  end
         
     | 
| 
      
 247 
     | 
    
         
            +
                rescue MaxRestartFrequencyError => ex
         
     | 
| 
      
 248 
     | 
    
         
            +
                  stop
         
     | 
| 
      
 249 
     | 
    
         
            +
                end
         
     | 
| 
      
 250 
     | 
    
         
            +
             
     | 
| 
      
 251 
     | 
    
         
            +
                def run_worker(context)
         
     | 
| 
      
 252 
     | 
    
         
            +
                  context.thread = Thread.new do
         
     | 
| 
      
 253 
     | 
    
         
            +
                    Thread.current.abort_on_exception = false
         
     | 
| 
      
 254 
     | 
    
         
            +
                    context.worker.run
         
     | 
| 
      
 255 
     | 
    
         
            +
                  end
         
     | 
| 
      
 256 
     | 
    
         
            +
                  context
         
     | 
| 
      
 257 
     | 
    
         
            +
                end
         
     | 
| 
      
 258 
     | 
    
         
            +
             
     | 
| 
      
 259 
     | 
    
         
            +
                def terminate_worker(context)
         
     | 
| 
      
 260 
     | 
    
         
            +
                  if context.alive?
         
     | 
| 
      
 261 
     | 
    
         
            +
                    context.worker.stop
         
     | 
| 
      
 262 
     | 
    
         
            +
                    Thread.pass
         
     | 
| 
      
 263 
     | 
    
         
            +
                  end
         
     | 
| 
      
 264 
     | 
    
         
            +
                rescue Exception => ex
         
     | 
| 
      
 265 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 266 
     | 
    
         
            +
                    Thread.kill(context.thread)
         
     | 
| 
      
 267 
     | 
    
         
            +
                  rescue
         
     | 
| 
      
 268 
     | 
    
         
            +
                    # suppress
         
     | 
| 
      
 269 
     | 
    
         
            +
                  end
         
     | 
| 
      
 270 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 271 
     | 
    
         
            +
                  context.thread = nil
         
     | 
| 
      
 272 
     | 
    
         
            +
                end
         
     | 
| 
      
 273 
     | 
    
         
            +
             
     | 
| 
      
 274 
     | 
    
         
            +
                def prune_workers
         
     | 
| 
      
 275 
     | 
    
         
            +
                  @workers.delete_if{|w| w.restart == :temporary && ! w.alive? }
         
     | 
| 
      
 276 
     | 
    
         
            +
                end
         
     | 
| 
      
 277 
     | 
    
         
            +
             
     | 
| 
      
 278 
     | 
    
         
            +
                def find_worker(worker_id)
         
     | 
| 
      
 279 
     | 
    
         
            +
                  index = @workers.find_index{|worker| worker.object_id == worker_id}
         
     | 
| 
      
 280 
     | 
    
         
            +
                  if index.nil?
         
     | 
| 
      
 281 
     | 
    
         
            +
                    [nil, nil]
         
     | 
| 
      
 282 
     | 
    
         
            +
                  else
         
     | 
| 
      
 283 
     | 
    
         
            +
                    [index, @workers[index]]
         
     | 
| 
      
 284 
     | 
    
         
            +
                  end
         
     | 
| 
      
 285 
     | 
    
         
            +
                end
         
     | 
| 
      
 286 
     | 
    
         
            +
             
     | 
| 
      
 287 
     | 
    
         
            +
                def exceeded_max_restart_frequency?
         
     | 
| 
      
 288 
     | 
    
         
            +
                  @restart_times.unshift(Time.now.to_i)
         
     | 
| 
      
 289 
     | 
    
         
            +
                  diff = (@restart_times.first - @restart_times.last).abs
         
     | 
| 
      
 290 
     | 
    
         
            +
                  if @restart_times.length >= @max_restart && diff <= @max_time
         
     | 
| 
      
 291 
     | 
    
         
            +
                    return true
         
     | 
| 
      
 292 
     | 
    
         
            +
                  elsif diff >= @max_time
         
     | 
| 
      
 293 
     | 
    
         
            +
                    @restart_times.pop
         
     | 
| 
      
 294 
     | 
    
         
            +
                  end
         
     | 
| 
      
 295 
     | 
    
         
            +
                  false
         
     | 
| 
      
 296 
     | 
    
         
            +
                end
         
     | 
| 
      
 297 
     | 
    
         
            +
             
     | 
| 
      
 298 
     | 
    
         
            +
                #----------------------------------------------------------------
         
     | 
| 
      
 299 
     | 
    
         
            +
                # restart strategies
         
     | 
| 
      
 300 
     | 
    
         
            +
             
     | 
| 
      
 301 
     | 
    
         
            +
                def one_for_one
         
     | 
| 
      
 302 
     | 
    
         
            +
                  @workers.each do |context|
         
     | 
| 
      
 303 
     | 
    
         
            +
                    if context.needs_restart?
         
     | 
| 
      
 304 
     | 
    
         
            +
                      raise MaxRestartFrequencyError if exceeded_max_restart_frequency?
         
     | 
| 
      
 305 
     | 
    
         
            +
                      run_worker(context)
         
     | 
| 
      
 306 
     | 
    
         
            +
                    end
         
     | 
| 
      
 307 
     | 
    
         
            +
                  end
         
     | 
| 
      
 308 
     | 
    
         
            +
                end
         
     | 
| 
      
 309 
     | 
    
         
            +
             
     | 
| 
      
 310 
     | 
    
         
            +
                def one_for_all
         
     | 
| 
      
 311 
     | 
    
         
            +
                  restart = false
         
     | 
| 
      
 312 
     | 
    
         
            +
             
     | 
| 
      
 313 
     | 
    
         
            +
                  restart = @workers.each do |context|
         
     | 
| 
      
 314 
     | 
    
         
            +
                    if context.needs_restart?
         
     | 
| 
      
 315 
     | 
    
         
            +
                      raise MaxRestartFrequencyError if exceeded_max_restart_frequency?
         
     | 
| 
      
 316 
     | 
    
         
            +
                      break(true)
         
     | 
| 
      
 317 
     | 
    
         
            +
                    end
         
     | 
| 
      
 318 
     | 
    
         
            +
                  end
         
     | 
| 
      
 319 
     | 
    
         
            +
             
     | 
| 
      
 320 
     | 
    
         
            +
                  if restart
         
     | 
| 
      
 321 
     | 
    
         
            +
                    @workers.each do |context|
         
     | 
| 
      
 322 
     | 
    
         
            +
                      terminate_worker(context)
         
     | 
| 
      
 323 
     | 
    
         
            +
                    end
         
     | 
| 
      
 324 
     | 
    
         
            +
                    @workers.each{|context| run_worker(context)}
         
     | 
| 
      
 325 
     | 
    
         
            +
                  end
         
     | 
| 
      
 326 
     | 
    
         
            +
                end
         
     | 
| 
      
 327 
     | 
    
         
            +
             
     | 
| 
      
 328 
     | 
    
         
            +
                def rest_for_one
         
     | 
| 
      
 329 
     | 
    
         
            +
                  restart = false
         
     | 
| 
      
 330 
     | 
    
         
            +
             
     | 
| 
      
 331 
     | 
    
         
            +
                  @workers.each do |context|
         
     | 
| 
      
 332 
     | 
    
         
            +
                    if restart
         
     | 
| 
      
 333 
     | 
    
         
            +
                      terminate_worker(context)
         
     | 
| 
      
 334 
     | 
    
         
            +
                    elsif context.needs_restart?
         
     | 
| 
      
 335 
     | 
    
         
            +
                      raise MaxRestartFrequencyError if exceeded_max_restart_frequency?
         
     | 
| 
      
 336 
     | 
    
         
            +
                      restart = true
         
     | 
| 
      
 337 
     | 
    
         
            +
                    end
         
     | 
| 
      
 338 
     | 
    
         
            +
                  end
         
     | 
| 
      
 339 
     | 
    
         
            +
             
     | 
| 
      
 340 
     | 
    
         
            +
                  one_for_one if restart
         
     | 
| 
      
 341 
     | 
    
         
            +
                end
         
     | 
| 
      
 342 
     | 
    
         
            +
              end
         
     | 
| 
      
 343 
     | 
    
         
            +
            end
         
     |