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

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

Returns:

  • (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_debugObject



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_debugObject



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;
}

.timeoutObject

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