views:

464

answers:

2

What is active_record doing to the signal processes under windows (I don't see this with the same versions on the mac) that causes it to behave so strangely? For instance:

require 'rubygems'
trap("INT"){puts "interrupted"}
puts __LINE__
sleep 5
require 'active_record'
trap("INT"){puts "interrupted again"}
puts __LINE__
sleep 5

When I run the above code (ruby 1.8.6, gem 1.3.1, activerecord 2.2.2,) I can hit ^C as many times as I like during the first sleep, but the first interrupt after the require of activerecord causes the script to terminate. In the above case, the trap still executes, it only fails to allow the program to continue. Usually.

Removing the second call to trap does not have any effect upon the behaviors.

The real annoyance is that in some conditions, the trap fails to execute at all. Considering that the whole point of doing this is to get my code to clean up after itself (remove its footprint in the database so the next guy sees a sane state,) this is a real problem. For instance:

require 'rubygems'
require 'active_record'
trap("INT"){puts "interrupted"}
puts __LINE__
gets

Pressing ^C after seeing the puts will not execute the trap at all.

I only see this problem after requiring active_record. Is there a workaround? I'd be curious to know if this is a bug or if there is an explanation of some sort. As I said, I have no issue with this on the mac - repeated ^Cs result in multiple executions of the trap proc.

thanks...

+1  A: 

Considering that the whole point of doing this is to get my code to clean up after itself (remove its footprint in the database ...

Have you considered just using a database transaction? It seems like it would be a much easier way to solve the problem.

Orion Edwards
The problem is that the script runs for 10-60 minutes and does a number of reads/writes - it can't be transactionalized.
A: 

I saw a different pattern when trying to duplicate this problem:

puts "start"
trap("INT") { puts "interrupted" }
sleep 5
puts "end"

On Ubuntu (Ruby 1.8.6) this produces

start
interrupted
interrupted
(etc)
interrupted
end

So "interrupted" prints each time Crtl-C is pressed, until the 5 seconds are up. Under Windows (also Ruby 1.8.6), this produces:

start
interrupted
end

i.e. it prints "interrupted" once and then exits.

So it appears that while handling SIGINT Ruby exits the sleep routine and continues on to the next statement. My guess (hand-waving) is that this is somehow due to Ruby using green threads instead of native threads on Windows. Any experts please chime in here.

You could emulate the Unix-y behavior by restarting sleep in the handler:

puts "start"
trap("INT") do 
  puts "interrupted"
  sleep 5
end
sleep 5
puts "end"

Unfortunately this resets the timer each time SIGINT is trapped, so it needs some hacking:

$interval = 5
def go_to_sleep(secs)
  $started = Time.now
  sleep secs
end
trap("INT") do 
  puts "interrupted"
  time_to_sleep = [0,$interval - (Time.now - $started)].max
  if time_to_sleep > 0
    sleep time_to_sleep
  end
end
puts "one"
go_to_sleep($interval)
puts "two"
go_to_sleep($interval)
puts "three"
go_to_sleep($interval)
zetetic