Module: SystemTimer
- Defined in:
- lib/system_timer.rb,
lib/system_timer_stub.rb,
ext/system_timer/system_timer_native.c
Overview
Timer based on underlying SIGALRM system timers, is a solution to Ruby processes which hang beyond the time limit when accessing external resources. This is useful when timeout.rb, which relies on green threads, does not work consistently.
Usage
require 'systemtimer'
SystemTimer.timeout_after(5) do
# Something that should be interrupted if it takes too much time...
# ... even if blocked on a system call!
end
Class Method Summary collapse
- .cleanup_timer(seconds) ⇒ Object
- .debug_enabled? ⇒ Boolean
- .disable_debug ⇒ Object
- .enable_debug ⇒ Object
- .install_timer(seconds) ⇒ Object
-
.timeout ⇒ Object
Executes the method’s block.
-
.timeout_after(seconds) ⇒ Object
Executes the method’s block.
Class Method Details
.cleanup_timer(seconds) ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'ext/system_timer/system_timer_native.c', line 83
static VALUE cleanup_timer(VALUE self, VALUE seconds)
{
/*
* Block SIG_ALRM for safe processing of SIG_ALRM configuration.
*/
if (0 != sigprocmask(SIG_BLOCK, &sigalarm_mask, NULL)) {
log_error("cleanup_timer: Could not block SIG_ALRM", errno);
}
clear_pending_sigalrm_for_ruby_threads();
log_debug("cleanup_timer: Blocked SIG_ALRM");
/*
* Install Ruby Level SIG_ALRM handler
*/
restore_original_ruby_sigalrm_handler(self);
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");
} else {
log_error("cleanup_timer: Could not restore previous handler for SIG_ALRM", DISPLAY_ERRNO);
}
original_signal_handler.sa_handler = NULL;
restore_original_timer_interval();
restore_original_sigalrm_mask_when_blocked();
}
|
.debug_enabled? ⇒ Boolean
151 152 153 |
# File 'ext/system_timer/system_timer_native.c', line 151 static VALUE debug_enabled_p(VALUE self) { return debug_enabled ? Qtrue : Qfalse; } |
.disable_debug ⇒ Object
160 161 162 163 |
# File 'ext/system_timer/system_timer_native.c', line 160 static VALUE disable_debug(VALUE self) { debug_enabled = 0; return Qnil; } |
.enable_debug ⇒ Object
155 156 157 158 |
# File 'ext/system_timer/system_timer_native.c', line 155 static VALUE enable_debug(VALUE self) { debug_enabled = 1; return Qnil; } |
.install_timer(seconds) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'ext/system_timer/system_timer_native.c', line 26
static VALUE install_timer(VALUE self, VALUE seconds)
{
struct itimerval timer_interval;
/*
* Block SIG_ALRM for safe processing of SIG_ALRM configuration and save mask.
*/
if (0 != sigprocmask(SIG_BLOCK, &sigalarm_mask, &original_mask)) {
log_error("install_timer: Could not block SIG_ALRM", DISPLAY_ERRNO);
return Qnil;
}
clear_pending_sigalrm_for_ruby_threads();
log_debug("install_timer: Succesfully blocked SIG_ALRM at O.S. level");
/*
* Save previous signal handler.
*/
original_signal_handler.sa_handler = NULL;
if (0 != sigaction(SIGALRM, NULL, &original_signal_handler)) {
log_error("install_timer: Could not save existing handler for SIG_ALRM", DISPLAY_ERRNO);
restore_original_sigalrm_mask_when_blocked();
return Qnil;
}
log_debug("install_timer: Succesfully saved existing SIG_ALRM handler");
/*
* Install Ruby Level SIG_ALRM handler
*/
install_ruby_sigalrm_handler(self);
/*
* Set new real time interval timer and save the original if any.
*/
set_itimerval(&original_timer_interval, 0);
set_itimerval(&timer_interval, NUM2INT(seconds));
if (0 != setitimer(ITIMER_REAL, &timer_interval, &original_timer_interval)) {
log_error("install_timer: Could not install our own timer, timeout will not work", DISPLAY_ERRNO);
restore_original_ruby_sigalrm_handler(self);
restore_original_sigalrm_mask_when_blocked();
return Qnil;
}
log_debug("install_timer: Successfully installed timer");
/*
* Unblock SIG_ALRM
*/
if (0 != sigprocmask(SIG_UNBLOCK, &sigalarm_mask, NULL)) {
log_error("install_timer: Could not unblock SIG_ALRM, timeout will not work", DISPLAY_ERRNO);
restore_original_timer_interval();
restore_original_ruby_sigalrm_handler(self);
restore_original_sigalrm_mask_when_blocked();
}
log_debug("install_timer: Succesfully unblocked SIG_ALRM.");
return Qnil;
}
|
.timeout ⇒ Object
Executes the method’s block. If the block execution terminates before seconds
seconds has passed, it returns true. If not, it terminates the execution and raises a Timeout::Error
. Backward compatibility with timeout.rb
34 35 36 37 38 39 |
# File 'lib/system_timer.rb', line 34 def timeout_after(seconds) install_timer(seconds) return yield ensure cleanup_timer end |
.timeout_after(seconds) ⇒ Object
Executes the method’s block. If the block execution terminates before seconds
seconds has passed, it returns true. If not, it terminates the execution and raises a Timeout::Error
.
26 27 28 29 30 31 |
# File 'lib/system_timer.rb', line 26 def timeout_after(seconds) install_timer(seconds) return yield ensure cleanup_timer end |