Comments and Feedback on Reliable Ruby timeouts with SystemTimer: Do not trust timeout.rb too blindly...
(9 comments)
08 Aug 2008 Hongli Lai said...
@Elderclei Reami: It’s not really that the Ruby implementation is broken. It’s just the nature of userspace threads. I also kinda blame it on operating systems for not providing non-blocking/asynchronous APIs for everything. Current operating system APIs kinda force you to use kernel threads. And that’s a pity, because userspace threads are much more lightweight than kernel threads, and when used properly they can bring many performance benefits.
26 Aug 2008 Elderclei Reami said...
I get your point Hongli, and green threads are something really useful, as shown by Erlang.
What I was trying to mention is that there is duplicated timeout code spread everywhere, even though they seem to be the same (copy & paste?). By the way, we are using SystemTimer in production and it’s working quite well – thank you, Philippe.
Cheers,
PS: Thanks for your amazing packages (REE and Phusion)
31 Aug 2008 Mikel Lindsaar from http://lindsaar.net/ said...
THANK YOU FOR THIS!
I have been fighting with a timeout problem where a certain process was doing a call to another DB on the other side of the planet and it kept timing out, but timeout.rb would never die and the process would just sit there and lock.
Of course, the cron job would not fire again because an existing process was running. Was about to resort to system level monitoring apps to try and fix it. This came as a timely save.
Thanks again.
Mikel
Philippe Hanrigou replied...
Hi Mikel,
Thanks for the kind words, this is the kind of feedback that make my day! ;-) It is always awsome to learn that your work end up being useful to the community!
09 Sep 2008 Ahsan said...
Thanks for a wonderful gem, I am wondering if the exception is guaranteed within the given time? because when i tried
require ‘open-uri’ SystemTimer.timeout_after(1) do open(‘http://www.abcd.comz’) end
The exception is raised, but after 15-20 seconds instead of 1 second. I am running ubuntu 6.10 and ruby 1.8.6
11 Sep 2008 Gorenje said...
Thread.new do
SystemTimer.timeout(30) do
doing_something_that_takes_more_than_30s
end
end
This works fine, nothing happens and the do something is killed. However when I do something that takes less than 30 seconds and completes, I get:
cleanup_timer: Previous SIG_ALRM handler not initialized!:
bash> irb irb(main):001:0> require 'system_timer' => true irb(main):002:0> (1..4).each do irb(main):003:1* Thread.new do irb(main):004:2* SystemTimer.timeout(5) do irb(main):005:3* puts "hi there!" irb(main):006:3> end irb(main):007:2> end irb(main):008:1> end hi there!hi there!hi there! hi there! cleanup_timer: Previous SIG_ALRM handler not initialized!: => 1..4 irb(main):009:0> cleanup_timer: Previous SIG_ALRM handler not initialized!: cleanup_timer: Previous SIG_ALRM handler not initialized!: Alarm clock bash>Worse still, my irb gets shot in the head (Alarm clock) and I’m back in bash.
Also using this in production causes all threads to exit immediately with the “SIG_ALRM” error, i.e. the code block doesn’t actually start execution.
BTW I did this all on a MacBook, ie BSD system—> it should work with BSD right?
Philippe Hanrigou replied...
Hi Gorenje,
Thanks you for reporting this. As explained in the article, SystemTimer was designed for a Rails application, so we did not bother implementing support for concurrent timers. Ruby on Rails was single-threaded at the time and we mostly wanted to solve our client's production problem.
Thanks to your post I started looking at what it would take to implement support for concurrent timers. The good news is that it can be done with a minimal impact of complexity. I finally found the time to work on it and I have just released SystemTimer 1.1 which provide full support for concurrent timers, including the scenario that you are describing.
Install it with
and let me
know if the new release works for you.
sudo gem install SystemTimer
17 Sep 2008 Ahsan from http://tech.bytefull.com said...
I get the same error as Gerenje on a RedHat Enterprise 5.1 with the same code.
Philippe Hanrigou replied...
Hi Ahsan,
Thanks you for reporting this. As explained in my response to Gerenje, SystemTimer did not support concurrent timers at the time.
The good news is that SystemTimer 1.1 release fixes the problem and
provide full support for concurrent timers.
Install it with
and let me
know if the new release works for you.
sudo gem install SystemTimer
14 Oct 2008 Jason Clouse said...
I have the same problem as Gerenje and Ahsan, but I’m on SuSE Linux Enterprise Server 10, 64-bit.
Philippe Hanrigou replied...
Hi Jason,
Thanks you for reporting this. Please upgrade to SystemTimer 1.1
which fixes the problem and
provide full support for concurrent timers.
Install it with
and let me
know if the new release works for you.
sudo gem install SystemTimer
23 Oct 2008 Chris Thompson from http://www.nexopia.com/ said...
I have a fix for the reported issues. I have sent a patch to the author, but if you need it immediately, please contact me by email. First initial last name at nexopia dot com.
Philippe Hanrigou replied...
Hi Chris,
Thanks a lot for looking into this and submitting your patch, appreciate it.
The root of the problem is that originally SystemTimer was not designed to support concurrent timers. So the patch is unfortunately treating the symptom more than the cause.
The good news though is that your post finally motivated me to evolve
SystemTimer to a more generic solution, beyond its original
Ruby on Rails lineage (where there is typically no re-entrant code).
Full concurrent timer support is now implemented as part of SystemTimer
1.1. You can install it with
and let me know if the new release works for you.
sudo gem install SystemTimer
Thanks again, I have included your patch at the end of this post for reference:
Index: ext/system_timer/system_timer_native.c
===================================================================
--- ext/system_timer/system_timer_native.c (revision 18927)
+++ ext/system_timer/system_timer_native.c (working copy)
@@ -35,7 +35,7 @@
return Qnil;
}
clear_pending_sigalrm_for_ruby_threads();
- log_debug("install_timer: Succesfully blocked SIG_ALRM at O.S. level");
+ log_debug("install_timer: Successfully blocked SIG_ALRM at O.S. level");
/*
* Save previous signal handler.
@@ -46,7 +46,7 @@
restore_original_sigalrm_mask_when_blocked();
return Qnil;
}
- log_debug("install_timer: Succesfully saved existing SIG_ALRM handler");
+ log_debug("install_timer: Successfully saved existing SIG_ALRM handler");
/*
* Install Ruby Level SIG_ALRM handler
@@ -75,7 +75,7 @@
restore_original_ruby_sigalrm_handler(self);
restore_original_sigalrm_mask_when_blocked();
}
- log_debug("install_timer: Succesfully unblocked SIG_ALRM.");
+ log_debug("install_timer: Successfully unblocked SIG_ALRM.");
return Qnil;
}
@@ -98,9 +98,12 @@
if (original_signal_handler.sa_handler == NULL) {
- log_error("cleanup_timer: Previous SIG_ALRM handler not initialized!", DO_NOT_DISPLAY_ERRNO);
- } else if (0 == sigaction(SIGALRM, &original_signal_handler, NULL)) {
- log_debug("cleanup_timer: Succesfully restored previous handler for SIG_ALRM");
+ log_debug("cleanup_timer: No previous handler for SIG_ALRM, resetting to default");
+ original_signal_handler.sa_handler = SIG_DFL;
+ set_itimerval(&original_timer_interval, 0);
+ }
+ if (0 == sigaction(SIGALRM, &original_signal_handler, NULL)) {
+ log_debug("cleanup_timer: Successfully restored previous handler for SIG_ALRM");
} else {
log_error("cleanup_timer: Could not restore previous handler for SIG_ALRM", DISPLAY_ERRNO);
}
@@ -187,7 +190,7 @@
static void clear_pending_sigalrm_for_ruby_threads()
{
CHECK_INTS;
- log_debug("Succesfully triggered all pending signals at Green Thread level");
+ log_debug("Successfully triggered all pending signals at Green Thread level");
}
static void init_sigalarm_mask()
Index: ChangeLog
===================================================================
--- ChangeLog (revision 18927)
+++ ChangeLog (working copy)
@@ -2,3 +2,13 @@
* Initial Release
+=== 1.0.1 / 2008-10-23
+
+* Fix issue where signal handler was not restored if we had not previously
+ installed a handler.
+
+=== 1.0.2 / 2008-10-24
+
+* Fix spelling mistakes
+* Fix restoring timeout interval if we had not previously installed a
+ handler.
Index: SystemTimer.gemspec
===================================================================
--- SystemTimer.gemspec (revision 18927)
+++ SystemTimer.gemspec (working copy)
@@ -1,19 +1,19 @@
Gem::Specification.new do |s|
s.name = %q{SystemTimer}
- s.version = "1.0"
+ s.version = "1.0.2"
s.specification_version = 2 if s.respond_to? :specification_version=
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.autorequire = %q{system_timer}
- s.date = %q{2008-05-28}
+ s.date = %q{2008-10-24}
s.extensions = ["ext/system_timer/extconf.rb"]
s.extra_rdoc_files = ["README"]
s.files = ["COPYING", "LICENSE", "ChangeLog", "ext/system_timer/system_timer_native.c", "ext/system_timer/extconf.rb", "lib/system_timer.rb", "lib/system_timer_stub.rb", "test/all_tests.rb", "test/system_timer_test.rb", "README"]
s.has_rdoc = true
s.rdoc_options = ["--title", "SystemTimer", "--main", "README", "--line-numbers"]
s.require_paths = ["lib"]
- s.rubygems_version = %q{1.0.1}
+ s.rubygems_version = %q{1.0.2}
s.summary = %q{Set a Timeout based on signals, which are more reliable than Timeout. Timeout is based on green threads.}
s.test_files = ["test/all_tests.rb"]
end
Post a new comment:
Enter your comment
Preview your comment
Textile Syntax Essentials
- a phrase
- a phrase
- _a phrase_
- a phrase
- *a phrase*
- a phrase
- "Google":http://google.com
- @some code@
some code- * A first item
* A second item
* A third -
- A first item
- A second item
- A third
For a complete overview, take a look at Hobix.com Textile Reference.

17 Jun 2008 Elderclei Reami from http://www.uol.com.br said...
Hi guys,
These are really great news, but it made me think about how many features are really broken in Ruby implementation.
I am experiencing problems with net/imap.rb, and realized that there’s yet another “homicidal thread”-based timeout implementation in lib/ruby/1.8/monitor.rb:
I guess it would it be possible to monkey-patch this and solve the problem safely using SystemTimer, but I am not sure. I’ll have a try anyway.
Thanks for the great work!
Cheers. Elderclei Reami