class Pione::Command::Spawner

Spawner is a utility class for calling pione commands as different process. We assume both caller and callee commands have front server.

Attributes

child_front[R]
pid[R]
thread[R]

Public Class Methods

new(model, name) click to toggle source
# File lib/pione/command/spawner.rb, line 10
def initialize(model, name)
  @model = model # caller's model
  @name = name   # callee command name
  @argv = []     # callee command arguments
end

Public Instance Methods

option(*argv) click to toggle source

Add arguments as command arguments.

# File lib/pione/command/spawner.rb, line 62
def option(*argv)
  @argv += argv.map {|val| val.to_s}
end
option_from(table, key, option_name, converter=nil) click to toggle source

Add the option name and the value as command arguments. If the value doesn’t exist in the table or the value is ‘nil`, no options are added. This is useful for the case a caller’s command option passes callee’s.

@param [Hash] table

value table

@param [Symbol] key

key of the value table

@param [String] option_name

option name

@return [void]

# File lib/pione/command/spawner.rb, line 85
def option_from(table, key, option_name, converter=nil)
  if table[key]
    val = table[key]
    val = converter.call(val) if converter
    option(option_name, val)
  end
end
option_if(cond, *args) click to toggle source

Add arguments if the condition is true.

# File lib/pione/command/spawner.rb, line 67
def option_if(cond, *args)
  if cond
    option(*args)
  end
end
spawn() click to toggle source

Spawn the command process.

# File lib/pione/command/spawner.rb, line 17
def spawn
  Log::Debug.system('process "%{name}" is spawned with arguments %{argv}' % {name: @name, argv: @argv})

  # create a new process and watch it
  @pid = Process.spawn(@name, *@argv)

  # keep to watch child process
  @thread = Process.detach(@pid)

  # fail to spawn if the monitor thread is nil
  unless @thread
    return self
  end

  # find child front while child process is alive
  Timeout.timeout(10) do
    while @thread.alive? do
      # find front and save its uri and pid
      if child_front = find_child_front(@pid)
        @child_front = child_front

        return self
      else
        sleep 0.1
      end
    end

    # error if the process has failed
    unless not(@thread.alive?) and @thread.value.success?
      raise SpawnError.child_process_is_dead(@model[:scenario_name], @name, @argv)
    end

    return self
  end
rescue Timeout::Error
  raise SpawnError.new(@model[:scenario_name], @name, @argv, "timed out")
rescue Object => e
  if e.kind_of?(SpawnError)
    raise
  else
    raise SpawnError.new(@model[:scenario_name], @name, @argv, e.message)
  end
end
when_terminated(&b) click to toggle source

Register the block that is executed when the spawned process is terminated.

# File lib/pione/command/spawner.rb, line 94
def when_terminated(&b)
  Thread.new do
    @thread.join
    b.call
  end
end

Private Instance Methods

find_child_front(pid) click to toggle source

Find child front by PID. Spawned child process sets the front server’s URI to children table of my front, so we get it from my front and create the reference.

# File lib/pione/command/spawner.rb, line 106
def find_child_front(pid)
  if child_front_uri = @model[:front].child_front_uri(pid)
    return DRbObject.new_with_uri(child_front_uri.to_s).tap do |front|
      timeout(1) {front.ping} # test connection
    end
  end
rescue Timeout::Error
  return nil
end