Comments and Feedback on Reliable Ruby timeouts with SystemTimer: Do not trust timeout.rb too blindly...
(13 comments)
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
sudo gem install SystemTimer
and let me know if the new release works for you.
11 Sep 2008 Gorenje said...
Hm, I've just installed the gem and found an interesting (if irritating) feature:
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!:
Example:
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!:
cleanuptimer: Previous SIGALRM 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 sudo gem install SystemTimer and let me know if the new release works for you.
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:
def create_timer(timeout)
if timeout
waiter = Thread.current
return Thread.start {
Thread.pass
sleep(timeout)
Thread.critical = true
waiter.raise(Timeout.new)
}
else
return nil
end
end
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
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)
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
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!
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 sudo gem install SystemTimer and let me know if the new release works for you.
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
19 Dec 2008 Arnon Moscona from http://www.fansnap.com said...
Thanks for the excellent gem! Good work.
As an aside, the JRuby interpreter does not use green threads, and instead delegates all threading to the underlying JVM (implementation varies by platform, but is never "green"; but pretty robust). This should solve much of the problem, but not all of it, since I have seen Java threads hang on some system calls as well (especially network calls under Linux). I have not had a chance to test it with a recent JVM and in particular did not test the JRuby implementation. Since JRuby performance is now on a par with the rest of the leading Ruby interpreters, it is now a viable candidate for use as a primary deployment platform.
I wonder whether anyone tested these issues with a recent JRuby interpreter
Thanks ahead or any responses
30 Jan 2010 Sylvain Viart said...
Hi,
SystemTimer-1.1.3 fail to install on ubuntu with Ruby 1.9.1. I try to look at some fix. Already reported here : http://isitruby19.com/systemtimer
sudo gem install SystemTimer
Building native extensions. This could take a while...
ERROR: Error installing SystemTimer:
ERROR: Failed to build gem native extension.
/usr/bin/ruby1.9.1 extconf.rb
creating Makefile
make
cc -I. -I/usr/include/ruby-1.9.1/x86_64-linux -I/usr/include/ruby-1.9.1/ruby/backward -I/usr/include/ruby-1.9.1 -I. -fPIC -fno-strict-aliasing -g -g -O2 -O2 -g -Wall -Wno-parentheses -fPIC -o system_timer_native.o -c system_timer_native.c
In file included from system_timer_native.c:8:
/usr/include/ruby-1.9.1/ruby/backward/rubysig.h:14:2: warning: #warning rubysig.h is obsolete
system_timer_native.c: In function ‘restore_original_configuration’:
system_timer_native.c:166: warning: no return statement in function returning non-void
system_timer_native.c: In function ‘install_ruby_sigalrm_handler’:
system_timer_native.c:199: error: ‘rb_thread_critical’ undeclared (first use in this function)
system_timer_native.c:199: error: (Each undeclared identifier is reported only once
system_timer_native.c:199: error: for each function it appears in.)
system_timer_native.c: In function ‘restore_original_ruby_sigalrm_handler’:
system_timer_native.c:205: error: ‘rb_thread_critical’ undeclared (first use in this function)
make: *** [system_timer_native.o] Error 1
Gem files will remain installed in /var/lib/gems/1.9.1/gems/SystemTimer-1.1.3 for inspection.
Results logged to /var/lib/gems/1.9.1/gems/SystemTimer-1.1.3/ext/system_timer/gem_make.out
Philippe Hanrigou replied...
SystemTimer was designed to work around MRI 1.8 and its green thread model (as explained in this article ;-).
You should not need it for MRI 1.9 which is based on native threads (in theory there is still some potential for staying stuck on the GIL – Global Interpreter Lock – but I never witnessed it happen so far).
Do you have a (reproducible) use case where timeouts are a problem with MRI 1.9?
Thanks in advance,
- Philippe
03 Apr 2010 Marcelo de Moraes Serpa from www.fullofcaffeine.com said...
Hi Phillippe,
Great work on the gem! I could use some help though -- we are trying to wrap a Ruby/LDAP search with SystemTimer, to timeout after 5.seconds. It does timeouts, but doesn't long after the 5 seconds have passed:
require 'ldap' require 'system_timer'
arr = SystemTimer::timeout_after(5.seconds) do
connection.search2(self.base_dn,1,"(& (userPassword=#{password}) (mail=#{email}))",nil,false,0,0)
end
If the LDAP query string is wrong for some reason, the LDAP server connection hangs (that's the server fault, of course, and that's why I wanted to use SystemTimer) for more than 1 minute. Then, it raises the Timeout exception.
I'm using SystemTimer v. 1.2.
Any ideas why it is failing?
Cheers,
Marcelo.
10 May 2010 jisub,lee said...
Hi Phillippe,
I tried to install in windows with ruby 1.86 , error occured below
gem install systemtimer
gem install system_timer
And got error:
'make' is not recognized as an internal or external command operable program or batch file.
I need this for my dev env. is there is anyway to install to windows?
thank you , jisub,lee from south korea
Philippe Hanrigou replied...
Hi,
SystemTimer relies on Unix/POSIX signal semantics and does not work on Windows.
Windows is seldom a good production platform for Ruby applications though, so this is rarely a problem ;-)
Cheers,
- Philippe
Post a new comment:
Enter your comment
Preview your comment
Markdown Syntax Essentials
- a sentence
- a sentence
- _emphasize something_
- emphasize something
- **emphasize even more**
- emphasize even more
- `a variable`
a variable- [A link to Google](http://google.com)
- A link to Google
- a 4-space-indented code block
(can span multiple lines) a 4 space indented code block (can span multiple lines)- * A first item
* A second item
* A third -
- A first item
- A second item
- A third
For a complete overview, take a look at Markdown Syntax Documentation.

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
sudo gem install SystemTimerand let me know if the new release works for you.