[Backgroundrb-devel] How to add some process control...

David Lemstra david at lemstra.ca
Wed Jun 28 12:18:37 EDT 2006


Hi,
First off, thanks Ezra for showing the way with BackgrounDrb. It's a
real godsend.

I wanted to add a bit of job control to the system so that I could
cancel DRB processes and so on. I thought I'd share how I did it in case
anyone should find it useful or have feedback.

I wanted to be able to put checkpoints into the drb job so that I could
let the job do whatever needs to be done (cleanup etc) and then
terminate it gracefully, so that complicates things a little bit. The
code is pretty simple, but I'm just learning DRB, so it took me a while
to get it right.

In MiddleMan::new_worker(), the job is first created, then started. This
is because the instance functions I added to BackgrounDrb::Rails don't
seem to be available when kicked off from BackgrounDRb::Rails.initialize()

def new_worker(opts={})
  @mutex.synchronize {
    job_key = opts[:job_key] || gen_key
    unless self[job_key]
      self[job_key] =
instantiate_worker(opts[:class]).new(job_key,opts[:args])
      self[job_key].start_process
      return job_key
    else
      raise ::BackgrounDRbDuplicateKeyError
    end
  }
end

In MiddleMan::delete_worker(), I've added steps where, if the job is
still alive, activate the kill signal to the job, and wait for it to
clean itself up and activate the :safe_to_kill signal.
Then kill it.

def delete_worker(key)
  @mutex.synchronize {
    if @jobs[key]
      if (@jobs[key].thread.alive?)
        @jobs[key].thread[:kill] = true;
        @jobs[key].thread[:safe_to_kill].wait(@mutex)
        @jobs[key].thread.kill
      end
      @jobs.delete(key)
    end
    @timestamps.delete(key) if @timestamps.has_key?(key)
  }
end

Inside BackgrounDrb:Rails:
you can use terminate() to establish the checkpoints/killpoints inside
your job code, or check_terminate/terminate? to check, run cleanup code,
and then raise the :safe_to_kill signal.

def initialize(job_key, args={})
  @logger = BACKGROUNDRB_LOGGER
  @thread = nil;
  @job_key = job_key
  @args = args
end

def start_process
  begin
    @thread = Thread.new do
      Thread.current[:safe_to_kill] = ConditionVariable.new
      Thread.current[:kill] = false
      do_work(@args)
    end
  rescue Exception => e
    @logger.error e.inspect
  end
end

def check_terminate
  raise "Somehow this worker doesn't have a registered thread" if
thread.nil?
  return @thread[:kill]
end

def terminate?
  terminate if check_terminate
end

def terminate
  if check_terminate
    Thread.critical = true
    @logger.info "Canceling job #{@job_key}"
    @thread[:safe_to_kill].signal
    @thread.stop
  end
  return
end

Hope someone finds it useful,
David Lemstra


More information about the Backgroundrb-devel mailing list