Threads spawning more threads

Stylish 0 Tallied Votes 123 Views Share

I needed a way to spawn multiple threads, have them monitored, and restarted if an error occured. I came up with the following.

This snippet is from a larger program, so if there is a problem please let me know. This is for command line and tested on Linux (well, the larger program).

<%

#!/usr/bin/env ruby
## kill some warnings about writable directories
$VERBOSE=nil

# we want to parse input, right?
require 'optparse'

## global options (which can and will be changed)
@@options = {
		:runtime => "01:00:00",
		:total_time => 0,
		:threadlist => [],
	}


def parse_input()
  ARGV.options do |opts|
    opts.banner = "Usage: #{$0} [options] <thread names>"
    opts.separator "Threadeds spawning threads, oh my!"
    opts.separator ""
    opts.separator "Options:"

    opts.on("-h", "--help", "show this message") {
      puts opts
      exit
    }

    opts.on("-v", "--version", "show version details") {
      version = `grep -m 1 "#(@)" #{$0} | awk '{printf("version: %s (%s)",$3,$2)}'`
      puts version
      exit
    }  

    opts.on("--duration=HH:MM:SS", String, "specify how long to run test (default: #{@@options[:runtime]})") { |@@options[:runtime]| }

    opts.parse!
  end
  
  ## validate time
  hours, mins, secs = @@options[:runtime].to_s.split(":")
  if hours.nil? or mins.nil? or secs.nil?
    puts "ERROR -- Invalid time format!"
    exit 1
  end  

  @@options[:total_time] = (hours.to_i * 60 * 60) + (mins.to_i * 60) + secs.to_i

  ## thread list, anyone?
  if ARGV.length < 1
  	puts "ERROR! No thread names specified!"
	exit 1
  else
  	@@options[:threadlist] = ARGV
  end	
end

@threads = []
@start_time = Time.now

## trap ctrl-c
trap("INT") { threadDestroyAll() }

def newPass(threadname)
  ## create a new thread and add it to @threads
  @threads << Thread.new(threadname) { |myThread|
	## trap that CTRL-C
	trap("INT") { threadDestroyAll() }
  	## name thread to that of name given. easier management
  	Thread.current[:name] = "#{myThread}"
  	
  	## Stuff for thread to do goes here.
  }	
end

## this is triggered via CTRL-C trap above
# kill all threads and exit
def threadDestroyAll()
  puts "***************************"
  puts "** Killing all processes **"
  puts "***************************"
  @threads.each { |thread| thread.kill }
  sleep 2
  Thread.main.kill
end

## needed a way to watch over all the baby threads
# this will cycle through all threads, gathering a list of
#+ those that are still running (and their names, which are that of the client)
#+ any missing clients are then restarted.
def threadOverlord(threads)
  threadList = activeThreads = deadThreads = [] 
  threadList = @@options[:threadlist]
  threads.each { |thread| activeThreads << thread[:name] if thread.alive? and keepGoing() }
  ## restart baby threads that have died?
  # if time limit has been reached, that is a no
  if keepGoing()
    deadThreads = threadList - activeThreads
    deadThreads.each { |threadname| newPass(threadname) }
  end  
  activeThreads = deadThreads = threadList = nil
end  

## parent thread
# this is just to trigger threadOverlord every X seconds
#+ and check on the health of client threads
def statusThread()
  statusT = Thread.new { 
    Thread.current[:name] = "Overlord"
    loop do 
      threadOverlord(@threads) 
      sleep 15 
      break if !keepGoing() 
    end
   }
  statusT.join
end

## returns true if there is still time remaining
def keepGoing()
  return true if Time.now.to_i - @start_time.to_i < @@options[:total_time].to_i
  false
end  

parse_input()
statusThread

%>
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.