class ForkMe
  def self.prepare_child
    # Replace the parent signal handler with one that just ignores the signal.
    # This method causes some children to raise SignalException in Ruby 2.1:
    Signal.trap('USR1', 'IGNORE')

    # This method does not cause any such exception:
    # Signal.trap('USR1') {}

    # Both methods work in Ruby 1.8.7.
  end

  attr_reader :children

  def initialize
    @children = []
  end

  def run!
    # Spawn 5 children.
    5.times do
      child = Process.fork

      unless child.nil?
        @children << child
        next
      end

      self.class.prepare_child

      puts "[Child] Started with PID #{$$}."

      begin
        sleep 10
      rescue Exception => e
        puts "[Child] [#{$$}] Caught #{e.class}: #{e.message}"
      end

      exit 0
    end

    # If we add a sleep here, IGNORE works fine. Seems that there is a window
    # where IGNORE is specified, but has not yet taken effect, resulting in
    # the SignalException raise.
    # sleep 1

    # List children by sending USR1 to my PROCESS GROUP, as pkill -f would do.
    Process.kill('USR1', -(Process.getpgid($$)))

    # Clean up any children who are done.
  end
end

f = ForkMe.new

# Define a signal handler that lists children.
Signal.trap('USR1') do
  puts 'Caught USR1, here are my children:'
  f.children.each do |child_pid|
    puts "- #{child_pid}"
  end
end

f.run!
