class ZTK::Background

Background Processing Class

This class can be used to easily run a linear process in a background manner.

The before fork callback is called once in the parent process.

The after fork callback is called twice, once in the parent process and once in the child process.

@example Run a background process which simply sleeps illustrating the callback hooks:

a_callback = Proc.new do |pid|
  puts "Hello from After Callback - PID #{pid}"
end

b_callback = Proc.new do |pid|
  puts "Hello from Before Callback - PID #{pid}"
end

background = ZTK::Background.new
background.config do |config|
  config.before_fork = b_callback
  config.after_fork = a_callback
end

pid = background.process do
  sleep(1)
end
puts pid.inspect

background.wait
puts background.result.inspect

@author Zachary Patten <zpatten AT jovelabs DOT io>

Attributes

pid[RW]

Result Set

result[RW]

Result Set

Public Class Methods

new(configuration={}) click to toggle source

@param [Hash] configuration Configuration options hash.

Calls superclass method ZTK::Base::new
# File lib/ztk/background.rb, line 51
def initialize(configuration={})
  super(configuration)

  @result = nil
  GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true
end

Public Instance Methods

alive?() click to toggle source
# File lib/ztk/background.rb, line 128
def alive?
  (Process.getpgid(@pid).is_a?(Integer) rescue false)
end
dead?() click to toggle source
# File lib/ztk/background.rb, line 132
def dead?
  !alive?
end
process(&block) click to toggle source

Process in background.

@yield Block should execute tasks to be performed in background. @yieldreturn [Object] Block can return any object to be marshalled back to

the parent processes.

@return [Integer] Returns the pid of the child process forked.

# File lib/ztk/background.rb, line 65
def process(&block)
  !block_given? and log_and_raise(BackgroundError, "You must supply a block to the process method!")

  @child_reader, @parent_writer = IO.pipe
  @parent_reader, @child_writer = IO.pipe

  config.before_fork and config.before_fork.call(Process.pid)
  @pid = Process.fork do
    config.after_fork and config.after_fork.call(Process.pid)

    @parent_writer.close
    @parent_reader.close

    STDOUT.reopen("/dev/null", "a")
    STDERR.reopen("/dev/null", "a")
    STDIN.reopen("/dev/null")

    if !(data = block.call).nil?
      config.ui.logger.debug { "write(#{data.inspect})" }
      @child_writer.write(Base64.encode64(Marshal.dump(data)))
    end

    @child_reader.close
    @child_writer.close
    Process.exit!(0)
  end
  config.after_fork and config.after_fork.call(Process.pid)

  @child_reader.close
  @child_writer.close

  @pid
end
wait() click to toggle source

Wait for the background process to finish.

If a process successfully finished, it's return value from the process block is stored into the result set.

It's advisable to use something like the at_exit hook to ensure you don't leave orphaned processes. For example, in the at_exit hook you could call wait to block until the child process finishes up.

@return [Array<pid, status, data>] An array containing the pid,

status and data returned from the process block.  If wait2() fails nil
is returned.
# File lib/ztk/background.rb, line 112
def wait
  config.ui.logger.debug { "wait" }
  pid, status = (Process.wait2(@pid) rescue nil)
  if !pid.nil? && !status.nil?
    data = (Marshal.load(Base64.decode64(@parent_reader.read.to_s)) rescue nil)
    config.ui.logger.debug { "read(#{data.inspect})" }
    !data.nil? and @result = data

    @parent_reader.close
    @parent_writer.close

    return [pid, status, data]
  end
  nil
end