views:

44

answers:

2

I've got this script, that uploads some files, connects via ssh and does some stuff on the remote server, kinda like deployment script, and I want to make it run whenever I want to and to be able to reset it in the middle of processing.

def deploy
  # some stuff happens here
end

def do_deploy
   $deploy.kill! if $deploy
   $deploy = Thread.new { deploy }
   $deploy.join  # when I don't have the join here, it stop executing the deploy
                 # like after first when thread switches to something else, and
                 # then never finishes
end

reading = Thread.new do
  while line = gets.chomp!
    case line
    when "q" then
      puts "exiting"
      Thread.exit
      exit
    when "r" then
      do_deploy
    end
  end
end

reading.join

Even though the $deploy.join makes the whole deploy thread execute, it prevents reading any input, so I can't reset it in the middle of execution. And I can't join it at the end of the script.

What I essentially need to do, is to run a task while listening for an input and be able to kill the process and restart it at any given time. Or even better, send it a message that would get processed immediately, like shut down test execution.

I know that killing threads isn't really a neat thing to do, but in this case, I don't think that it's a big issue.

I'd also like to point out, that I'm working on Windows, so I don't have fork() available.

Is there any other way how to solve this without threads?

edit: Just found out, that calling gets blocks all other Threads from execution until any input is given. In this example

t1 = Thread.new { 10.times { |i| puts i } }
t2 = Thread.new { puts gets }
t1.join
t2.join

the t1 doesn't get executed untill I give some input to the gets. It just keeps sitting there forever. How should I read from input without blocking all threads?

edit2: just found out, that this is a Windows related issue

edit3: the problem goes away in JRuby or Ruby 1.9

A: 

Use JRuby. It has real threads and doesn't fall prey to the GIL (Global Interpreter Lock) that the C Ruby interpreter has.

http://www.jruby.org/

Joshua Smith
A: 

When you use Thread.join, the calling thread will block until the specified thread terminates. This is the reason you are unable to process input at this point (do_deploy is no longer being executed).

The deploy thread should be executing as soon as you call Thread.new. If I am reading your code correctly, you want to move your call to .join from inside do_deploy to inside your reading block. Try using the following for your reading block (I don't have Ruby in front of me so this may not be perfect syntax):

reading = Thread.new do
  while line = gets.chomp!
    case line
    when "q" then
      puts "exiting"
      $deploy.kill if $deploy
      break
    when "r" then
      do_deploy
    end
  end
  $deploy.join
end

This should launch (or kill and re-launch) the deploy process when "r" is input and then return to the input processing loop. Input a "q" and the deploy process will be sent the "terminate" signal and the input processing loop will wait for it to terminate before terminating itself.

With Ruby threads, you may run into trouble where it seems like the input processing thread isn't running when the deploy thread is working (busy threads can starve out low-priority threads). If this happens to you, I would recommend adding some explicit calls to Thread.pass in your deploy routine. This will force the thread scheduler to let another thread run. You may also want to experiment using thread priorities and placing your input processing thread at a higher priority than your worker thread.

bta
just found out that the problem is with `gets`, which blocks all other threads, check edit
Darth
gets shouldn't block in 1.9/jruby
rogerdpack
yep, I was using 1.8, 1.9 solved the problem
Darth