How to create a resetable “process” or “thread” in Ruby?

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

Answers


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/


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.


Need Your Help

MouseDown in WinForm ListBox Kills SelectedIndexChanged

winforms listbox selectedindexchanged onmousedown

I'm writing some code to detect toggling of selections in a WindForms ListBox with MultiSelect turned on. Since SelectedIndexChanged only lets me see what is selected after the click, I was lookin...

Devise Redirects to specific page based on User Role on Login

ruby-on-rails ruby redirect devise

I have three possible permissions for a User to be in my Rails app, they are User.is_admin, User.is_school, and User.is_security. Based on the nature of my app I need to have a separate home screen...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.