system_timer 1.0 → 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,67 @@
1
+ === 1.2.4 / 2011-06-11
2
+
3
+ - Renamed the gem for seemless bundler integration:
4
+ SystemTimer -> system_timer
5
+ Per https://github.com/ph7/system-timer/issues/16
6
+ (Suggested by David Heinemeier Hansson)
7
+
8
+ === 1.2.3 / 2011-03-19
9
+
10
+ - Fix for RubyGems 1.6
11
+ (Contributed by James Tucker <http://blog.ra66i.org>)
12
+
13
+ === 1.2.2 / 2011-01-25
14
+
15
+ * Explicit required_ruby_version = '~> 1.8.7' in gem spec.
16
+ (Contributed by Jesse Storimer <http://jstorimer.com>)
17
+
18
+ === 1.2.1 / 2010-11-15
19
+
20
+ * Better Rubinious support (Contributed by
21
+ Evan Phoenix <http://blog.fallingsnow.net/>)
22
+
23
+ === 1.2 / 2010-02-25
24
+
25
+ * Changed from using Mutex to Monitor. Mutex causes thread join
26
+ errors when Ruby is compiled with -disable-pthreads
27
+ (Contributed by Dmytro Shteflyuk <http://kpumuk.info/>)
28
+
29
+ * Timeouts can now be specified as a float and be a fraction of a second.
30
+ e.g. `SystemTimer.timeout(0.5)`
31
+ (Based on a contribution by Dmytro Shteflyuk <http://kpumuk.info/>)
32
+
33
+ * Added support for custom timeout exception. Useful to avoid interference
34
+ with other libraries using `Timeout::Error` (e.g. `Net::HTTP`)
35
+ (Contributed by runix <http://github.com/runix>)
36
+
37
+ === 1.1.3/ 2009-11-29
38
+
39
+ * Preparing GemCutter migration
40
+
41
+ === 1.1.2 + 1.1.3/ 2009-29-11
42
+
43
+ * Preparing GemCutter migration
44
+
45
+ === 1.1.1 / 2009-03-10
46
+
47
+ * Fixing set_itimerval_with_minimum_1s_interval method signature
48
+ which was incorrect and resulted in a segfault on 64 bits
49
+ platform (int versus VALUE). Thanks to Mike Perham for
50
+ investigating the problem and sending the patch!
51
+
52
+ === 1.1.0 / 2008-11-05
53
+
54
+ * New implementation supporting concurrent timers, i.e. :
55
+
56
+ (1..10).each do
57
+ Thread.new do
58
+ SystemTimer.timeout_after(5) do
59
+ sleep 60
60
+ puts "hi there!"
61
+ end
62
+ end
63
+ end
64
+
1
65
  === 1.0.0 / 2008-02-27
2
66
 
3
67
  * Initial Release
data/README CHANGED
@@ -1,42 +1,118 @@
1
- == Synopsis
1
+ Synopsis
2
+ ========
2
3
 
3
- System Timer, a timer based on underlying SIGALRM system timers, is a
4
+ System Timer, a timer based on underlying `SIGALRM` system timers, is a
4
5
  solution to Ruby processes which hang beyond the time limit when accessing
5
- external resources. This is useful when timeout.rb, which relies on green
6
- threads, does not work consistently.
6
+ external resources. This is useful when `timeout.rb`, which, on M.R.I 1.8,
7
+ relies on green threads, does not work consistently.
7
8
 
8
- == Usage
9
+ More background on:
9
10
 
10
- require 'systemtimer'
11
+ * [http://ph7spot.com/musings/system-timer](http://ph7spot.com/musings/system-timer)
12
+ * [http://davidvollbracht.com/2008/6/2/30-days-of-teach-day-1-systemtimer](http://davidvollbracht.com/2008/6/2/30-days-of-teach-day-1-systemtimer)
11
13
 
12
- SystemTimer.timeout_after(5) do
14
+ Usage
15
+ =====
13
16
 
14
- # Something that should be interrupted if it takes too much time...
15
- # ... even if blocked on a system call!
17
+ require 'system_timer'
16
18
 
17
- end
19
+ SystemTimer.timeout_after(5) do
18
20
 
19
- == Requirements
21
+ # Something that should be interrupted if it takes too much time...
22
+ # ... even if blocked on a system call!
23
+
24
+ end
25
+
26
+ Timeouts as Floats
27
+ ------------------
28
+
29
+ You can use a floating point number when specifying the timeout in
30
+ seconds but SystemTimer will not allow you to go below 200ms, e.g.
31
+
32
+ SystemTimer.timeout_after(0.5) do
33
+ # timeout after 500ms
34
+ end
35
+
36
+ SystemTimer.timeout_after(0.01) do
37
+ # timeout after (uncompressable) 200ms even if 10ms is requested
38
+ end
39
+
40
+ Note that SystemTimer is going through too many layers to be
41
+ able to reliably guarantee a sub-second timeout on all platforms,
42
+ so your mileage may vary when specifying timeouts under one second.
43
+
44
+ Custom Timeout Exceptions
45
+ -------------------------
46
+
47
+ You can also use a custom timeout exception to be raised on timeouts (to
48
+ avoid interference with other libraries using `Timeout::Error` -- e.g. `Net::HTTP`)
49
+
50
+ require 'system_timer'
51
+
52
+ begin
53
+
54
+ SystemTimer.timeout_after(5, MyCustomTimeoutException) do
55
+
56
+ # Something that should be interrupted if it takes too much time...
57
+ # ... even if blocked on a system call!
58
+
59
+ end
60
+
61
+ rescue MyCustomTimeoutException => e
62
+ # Recovering strategy
63
+ end
64
+
65
+
66
+ Requirements
67
+ ============
20
68
 
21
69
  SystemTimer only works on UNIX platforms (Mac OS X, Linux, Solaris, BSD, ...).
22
- You can install the gem on Microsoft Windows, but you will only get
70
+ You can install the gem on Microsoft Windows, but you will only get
23
71
  a convenience shell wrapping a simple call to timeout.rb under the cover.
24
72
 
25
- == Install
26
-
27
- sudo gem install systemtimer
73
+ Install
74
+ =======
28
75
 
76
+ sudo gem install SystemTimer
29
77
 
30
- == Authors
78
+ Authors
79
+ =======
31
80
 
32
81
  * David Vollbracht <http://davidvollbracht.com>
33
- * Philippe Hanrigou <http:/ph7spot.com>
34
-
35
- == Copyright
82
+ * Philippe Hanrigou <http://ph7spot.com>
83
+
84
+ Contributors
85
+ ============
86
+
87
+ * Dmytro Shteflyuk <http://kpumuk.info/> :
88
+ - Changed from using Mutex to Monitor. Evidently Mutex causes thread
89
+ join errors when Ruby is compiled with -disable-pthreads
90
+ <https://github.com/kpumuk/system-micro-timer/commit/fe28f4dcf7d4126e53b7c642c5ec35fe8bc1e081>
91
+ - First tentative to support float timeouts
92
+ <https://github.com/kpumuk/system-micro-timer/commit/57fff73849aad7c94f8b9234352b7288d1314d21>
93
+
94
+ * runix <http://github.com/runix> :
95
+ - Added support for custom timeout exception. Useful to avoid interference
96
+ with other libraries using `Timeout::Error` (e.g. `Net::HTTP`)
97
+ <https://github.com/runix/system-timer/commit/d33acb3acc53d5105c68b25c3a2126fa682f12c0>
98
+ <https://github.com/runix/system-timer/commit/d8ca3452e462ea909d8e11a6091e7c30dfa3a1a8>
99
+
100
+ * Jesse Storimer <http://jstorimer.com>
101
+ - Explicit required_ruby_version = '~> 1.8.7' in gem spec.
102
+ <https://github.com/jstorimer/system-timer/commit/ec08b4d2173ffd635065a1680c8f8b4fbf6691fd>
103
+
104
+ * James Tucker <http://blog.ra66i.org>
105
+ - Fix for RubyGems 1.6, which will not require "thread"
106
+ <https://github.com/raggi/system-timer/commit/f6dd9535e3f1141f319fe7919b8347dd0e40560c>
107
+ <https://github.com/raggi/system-timer/commit/b13ff12bc7392b1aa2fe7911e305a3e8f215efd2>
108
+
109
+ Copyright
110
+ =========
36
111
 
37
- Copyright:: (C) 2008 David Vollbracht & Philippe Hanrigou
112
+ Copyright:: (C) 2008-2010 David Vollbracht & Philippe Hanrigou
38
113
 
39
- == Description
114
+ Description
115
+ ===========
40
116
 
41
117
  While deploying Rails application in production our team discovered
42
118
  that some web service call would not timeout way beyond their defined
@@ -52,18 +128,21 @@ David Vollbracht and Philippe Hanrigou pair programmed an alternative
52
128
  implementation based on system timers (the +SIGALRM+ POSIX signal):
53
129
  This design guarantees proper timeout behavior even when crossing-boundaries and accessing
54
130
  system/external resources. Special care has been taken to interfere as little as
55
- possible with other processes that might also rely on +SIGALRM+,
131
+ possible with other processes that might also rely on +SIGALRM+,
56
132
  in particular MySQL.
57
133
 
58
134
  This implementation is not intended to be drop-in replacement to
59
- timeout.rb, just a way to wrap sensitive call to system resources.
135
+ timeout.rb, just a way to wrap sensitive call to system resources.
60
136
 
137
+ You can find more details on SystemTimer and how to use it
138
+ at http://ph7spot.com/articles/system_timer
61
139
 
62
- == License
140
+ License
141
+ =======
63
142
 
64
143
  (The Ruby License)
65
144
 
66
- Copyright:: (C) 2008 David Vollbracht & Philippe Hanrigou
145
+ Copyright:: (C) 2008-2010 David Vollbracht & Philippe Hanrigou
67
146
 
68
147
  SystemTimer is copyrighted free software by David Vollbracht and Philippe Hanrigou.
69
148
  You can redistribute it and/or modify it under either the terms of the GPL
@@ -76,33 +155,33 @@ You can redistribute it and/or modify it under either the terms of the GPL
76
155
  2. You may modify your copy of the software in any way, provided that
77
156
  you do at least ONE of the following:
78
157
 
79
- a) place your modifications in the Public Domain or otherwise
80
- make them Freely Available, such as by posting said
81
- modifications to Usenet or an equivalent medium, or by allowing
82
- the author to include your modifications in the software.
158
+ a) place your modifications in the Public Domain or otherwise
159
+ make them Freely Available, such as by posting said
160
+ modifications to Usenet or an equivalent medium, or by allowing
161
+ the author to include your modifications in the software.
83
162
 
84
- b) use the modified software only within your corporation or
85
- organization.
163
+ b) use the modified software only within your corporation or
164
+ organization.
86
165
 
87
- c) rename any non-standard executables so the names do not conflict
88
- with standard executables, which must also be provided.
166
+ c) rename any non-standard executables so the names do not conflict
167
+ with standard executables, which must also be provided.
89
168
 
90
- d) make other distribution arrangements with the author.
169
+ d) make other distribution arrangements with the author.
91
170
 
92
171
  3. You may distribute the software in object code or executable
93
172
  form, provided that you do at least ONE of the following:
94
173
 
95
- a) distribute the executables and library files of the software,
96
- together with instructions (in the manual page or equivalent)
97
- on where to get the original distribution.
174
+ a) distribute the executables and library files of the software,
175
+ together with instructions (in the manual page or equivalent)
176
+ on where to get the original distribution.
98
177
 
99
- b) accompany the distribution with the machine-readable source of
100
- the software.
178
+ b) accompany the distribution with the machine-readable source of
179
+ the software.
101
180
 
102
- c) give non-standard executables non-standard names, with
103
- instructions on where to get the original software distribution.
181
+ c) give non-standard executables non-standard names, with
182
+ instructions on where to get the original software distribution.
104
183
 
105
- d) make other distribution arrangements with the author.
184
+ d) make other distribution arrangements with the author.
106
185
 
107
186
  4. You may modify and include the part of the software into any other
108
187
  software (possibly commercial). But some files in the distribution
@@ -112,9 +191,9 @@ You can redistribute it and/or modify it under either the terms of the GPL
112
191
  files under the ./missing directory. See each file for the copying
113
192
  condition.
114
193
 
115
- 5. The scripts and library files supplied as input to or produced as
194
+ 5. The scripts and library files supplied as input to or produced as
116
195
  output from the software do not automatically fall under the
117
- copyright of the software, but belong to whomever generated them,
196
+ copyright of the software, but belong to whomever generated them,
118
197
  and may be sold commercially, and may be aggregated with this
119
198
  software.
120
199
 
File without changes
@@ -1,113 +1,180 @@
1
+ /*
2
+ * SystemTimer native implementation relying on ITIMER_REAL
3
+ *
4
+ * Copyright 2008 David Vollbracht & Philippe Hanrigou
5
+ */
6
+
1
7
  #include "ruby.h"
2
8
  #include "rubysig.h"
3
9
  #include <signal.h>
4
10
  #include <errno.h>
11
+ #include <stdarg.h>
5
12
 
6
13
  #define DISPLAY_ERRNO 1
7
14
  #define DO_NOT_DISPLAY_ERRNO 0
15
+ #define MICRO_SECONDS 1000000.0
16
+ #define MINIMUM_TIMER_INTERVAL_IN_SECONDS 0.2
8
17
 
9
18
  VALUE rb_cSystemTimer;
19
+
20
+ // Ignore most of this for Rubinius
21
+ #ifndef RUBINIUS
22
+
10
23
  sigset_t original_mask;
11
24
  sigset_t sigalarm_mask;
12
25
  struct sigaction original_signal_handler;
13
26
  struct itimerval original_timer_interval;
27
+ static int debug_enabled = 0;
14
28
 
15
29
  static void clear_pending_sigalrm_for_ruby_threads();
16
- static void log_debug(char*);
17
- static void log_error(char*, int);
18
30
  static void install_ruby_sigalrm_handler(VALUE);
19
31
  static void restore_original_ruby_sigalrm_handler(VALUE);
20
32
  static void restore_original_sigalrm_mask_when_blocked();
21
33
  static void restore_original_timer_interval();
22
- static void set_itimerval(struct itimerval *, int);
34
+ static void set_itimerval_with_minimum_1s_interval(struct itimerval *, VALUE);
35
+ static void set_itimerval(struct itimerval *, double);
36
+ static void restore_sigalrm_mask(sigset_t *previous_mask);
37
+ static void log_debug(char*, ...);
38
+ static void log_error(char*, int);
23
39
 
24
- static int debug_enabled = 0;
25
40
 
26
- static VALUE install_timer(VALUE self, VALUE seconds)
41
+ static VALUE install_first_timer_and_save_original_configuration(VALUE self, VALUE seconds)
27
42
  {
28
- struct itimerval timer_interval;
29
-
30
- /*
31
- * Block SIG_ALRM for safe processing of SIG_ALRM configuration and save mask.
32
- */
33
- if (0 != sigprocmask(SIG_BLOCK, &sigalarm_mask, &original_mask)) {
34
- log_error("install_timer: Could not block SIG_ALRM", DISPLAY_ERRNO);
35
- return Qnil;
36
- }
37
- clear_pending_sigalrm_for_ruby_threads();
38
- log_debug("install_timer: Succesfully blocked SIG_ALRM at O.S. level");
43
+ struct itimerval timer_interval;
44
+
45
+ if (debug_enabled) {
46
+ log_debug("[install_first_timer] %.2lfs\n", NUM2DBL(seconds));
47
+ }
48
+
49
+ /*
50
+ * Block SIG_ALRM for safe processing of SIG_ALRM configuration and save mask.
51
+ */
52
+ if (0 != sigprocmask(SIG_BLOCK, &sigalarm_mask, &original_mask)) {
53
+ log_error("[install_first_timer] Could not block SIG_ALRM\n", DISPLAY_ERRNO);
54
+ return Qnil;
55
+ }
56
+ clear_pending_sigalrm_for_ruby_threads();
57
+ log_debug("[install_first_timer] Successfully blocked SIG_ALRM at O.S. level\n");
39
58
 
40
- /*
41
- * Save previous signal handler.
42
- */
43
- original_signal_handler.sa_handler = NULL;
44
- if (0 != sigaction(SIGALRM, NULL, &original_signal_handler)) {
45
- log_error("install_timer: Could not save existing handler for SIG_ALRM", DISPLAY_ERRNO);
46
- restore_original_sigalrm_mask_when_blocked();
47
- return Qnil;
48
- }
49
- log_debug("install_timer: Succesfully saved existing SIG_ALRM handler");
50
-
51
- /*
52
- * Install Ruby Level SIG_ALRM handler
53
- */
54
- install_ruby_sigalrm_handler(self);
55
-
56
- /*
57
- * Set new real time interval timer and save the original if any.
58
- */
59
- set_itimerval(&original_timer_interval, 0);
60
- set_itimerval(&timer_interval, NUM2INT(seconds));
61
- if (0 != setitimer(ITIMER_REAL, &timer_interval, &original_timer_interval)) {
62
- log_error("install_timer: Could not install our own timer, timeout will not work", DISPLAY_ERRNO);
63
- restore_original_ruby_sigalrm_handler(self);
64
- restore_original_sigalrm_mask_when_blocked();
65
- return Qnil;
66
- }
67
- log_debug("install_timer: Successfully installed timer");
68
-
69
- /*
70
- * Unblock SIG_ALRM
71
- */
72
- if (0 != sigprocmask(SIG_UNBLOCK, &sigalarm_mask, NULL)) {
73
- log_error("install_timer: Could not unblock SIG_ALRM, timeout will not work", DISPLAY_ERRNO);
74
- restore_original_timer_interval();
75
- restore_original_ruby_sigalrm_handler(self);
76
- restore_original_sigalrm_mask_when_blocked();
77
- }
78
- log_debug("install_timer: Succesfully unblocked SIG_ALRM.");
79
-
80
- return Qnil;
81
- }
82
-
83
- static VALUE cleanup_timer(VALUE self, VALUE seconds)
59
+ /*
60
+ * Save previous signal handler.
61
+ */
62
+ log_debug("[install_first_timer] Saving original system handler\n");
63
+ original_signal_handler.sa_handler = NULL;
64
+ if (0 != sigaction(SIGALRM, NULL, &original_signal_handler)) {
65
+ log_error("[install_first_timer] Could not save existing handler for SIG_ALRM\n", DISPLAY_ERRNO);
66
+ restore_original_sigalrm_mask_when_blocked();
67
+ return Qnil;
68
+ }
69
+ log_debug("[install_first_timer] Successfully saved existing SIG_ALRM handler\n");
70
+
71
+ /*
72
+ * Install Ruby Level SIG_ALRM handler
73
+ */
74
+ install_ruby_sigalrm_handler(self);
75
+
76
+ /*
77
+ * Save original real time interval timer and aet new real time interval timer.
78
+ */
79
+ set_itimerval(&original_timer_interval, 0.0);
80
+ set_itimerval_with_minimum_1s_interval(&timer_interval, seconds);
81
+ if (0 != setitimer(ITIMER_REAL, &timer_interval, &original_timer_interval)) {
82
+ log_error("[install_first_timer] Could not install our own timer, timeout will not work", DISPLAY_ERRNO);
83
+ restore_original_ruby_sigalrm_handler(self);
84
+ restore_original_sigalrm_mask_when_blocked();
85
+ return Qnil;
86
+ }
87
+ if (debug_enabled) {
88
+ log_debug("[install_first_timer] Successfully installed timer (%ds)\n",
89
+ timer_interval.it_value.tv_sec);
90
+ }
91
+
92
+ /*
93
+ * Unblock SIG_ALRM
94
+ */
95
+ if (0 != sigprocmask(SIG_UNBLOCK, &sigalarm_mask, NULL)) {
96
+ log_error("[install_first_timer] Could not unblock SIG_ALRM, timeout will not work", DISPLAY_ERRNO);
97
+ restore_original_timer_interval();
98
+ restore_original_ruby_sigalrm_handler(self);
99
+ restore_original_sigalrm_mask_when_blocked();
100
+ }
101
+ log_debug("[install_first_timer] Successfully unblocked SIG_ALRM.\n");
102
+
103
+ return Qnil;
104
+ }
105
+
106
+ static VALUE install_next_timer(VALUE self, VALUE seconds)
84
107
  {
85
- /*
86
- * Block SIG_ALRM for safe processing of SIG_ALRM configuration.
87
- */
88
- if (0 != sigprocmask(SIG_BLOCK, &sigalarm_mask, NULL)) {
89
- log_error("cleanup_timer: Could not block SIG_ALRM", errno);
90
- }
91
- clear_pending_sigalrm_for_ruby_threads();
92
- log_debug("cleanup_timer: Blocked SIG_ALRM");
93
-
94
- /*
95
- * Install Ruby Level SIG_ALRM handler
96
- */
97
- restore_original_ruby_sigalrm_handler(self);
108
+ struct itimerval timer_interval;
109
+ sigset_t previous_sigalarm_mask;
110
+
111
+ if (debug_enabled) {
112
+ log_debug("[install_next_timer] %.2lfs\n", NUM2DBL(seconds));
113
+ }
114
+
115
+ /*
116
+ * Block SIG_ALRM for safe processing of SIG_ALRM configuration and save mask.
117
+ */
118
+ if (0 != sigprocmask(SIG_BLOCK, &sigalarm_mask, &previous_sigalarm_mask)) {
119
+ log_error("[install_next_timer] Could not block SIG_ALRM\n", DISPLAY_ERRNO);
120
+ return Qnil;
121
+ }
122
+ clear_pending_sigalrm_for_ruby_threads();
123
+ log_debug("[install_next_timer] Successfully blocked SIG_ALRM at O.S. level\n");
98
124
 
125
+ /*
126
+ * Set new real time interval timer.
127
+ */
128
+ set_itimerval_with_minimum_1s_interval(&timer_interval, seconds);
129
+ if (0 != setitimer(ITIMER_REAL, &timer_interval, NULL)) {
130
+ log_error("[install_next_timer] Could not install our own timer, timeout will not work", DISPLAY_ERRNO);
131
+ restore_sigalrm_mask(&previous_sigalarm_mask);
132
+ return Qnil;
133
+ }
134
+ if (debug_enabled) {
135
+ log_debug("[install_next_timer] Successfully installed timer (%ds + %dus)\n",
136
+ timer_interval.it_value.tv_sec, timer_interval.it_value.tv_usec);
137
+ }
138
+
139
+ /*
140
+ * Unblock SIG_ALRM
141
+ */
142
+ if (0 != sigprocmask(SIG_UNBLOCK, &sigalarm_mask, NULL)) {
143
+ log_error("[install_next_timer] Could not unblock SIG_ALRM, timeout will not work", DISPLAY_ERRNO);
144
+ restore_sigalrm_mask(&previous_sigalarm_mask);
145
+ }
146
+ log_debug("[install_next_timer] Successfully unblocked SIG_ALRM.\n");
147
+
148
+ return Qnil;
149
+ }
150
+
151
+ static VALUE restore_original_configuration(VALUE self)
152
+ {
153
+ /*
154
+ * Block SIG_ALRM for safe processing of SIG_ALRM configuration.
155
+ */
156
+ if (0 != sigprocmask(SIG_BLOCK, &sigalarm_mask, NULL)) {
157
+ log_error("restore_original_configuration: Could not block SIG_ALRM", errno);
158
+ }
159
+ clear_pending_sigalrm_for_ruby_threads();
160
+ log_debug("[restore_original_configuration] Blocked SIG_ALRM\n");
161
+
162
+ /*
163
+ * Install Ruby Level SIG_ALRM handler
164
+ */
165
+ restore_original_ruby_sigalrm_handler(self);
99
166
 
100
- if (original_signal_handler.sa_handler == NULL) {
101
- log_error("cleanup_timer: Previous SIG_ALRM handler not initialized!", DO_NOT_DISPLAY_ERRNO);
102
- } else if (0 == sigaction(SIGALRM, &original_signal_handler, NULL)) {
103
- log_debug("cleanup_timer: Succesfully restored previous handler for SIG_ALRM");
104
- } else {
105
- log_error("cleanup_timer: Could not restore previous handler for SIG_ALRM", DISPLAY_ERRNO);
106
- }
107
- original_signal_handler.sa_handler = NULL;
167
+ if (original_signal_handler.sa_handler == NULL) {
168
+ log_error("[restore_original_configuration] Previous SIG_ALRM handler not initialized!", DO_NOT_DISPLAY_ERRNO);
169
+ } else if (0 == sigaction(SIGALRM, &original_signal_handler, NULL)) {
170
+ log_debug("[restore_original_configuration] Successfully restored previous handler for SIG_ALRM\n");
171
+ } else {
172
+ log_error("[restore_original_configuration] Could not restore previous handler for SIG_ALRM", DISPLAY_ERRNO);
173
+ }
174
+ original_signal_handler.sa_handler = NULL;
108
175
 
109
- restore_original_timer_interval();
110
- restore_original_sigalrm_mask_when_blocked();
176
+ restore_original_timer_interval();
177
+ restore_original_sigalrm_mask_when_blocked();
111
178
  }
112
179
 
113
180
  /*
@@ -119,61 +186,70 @@ static VALUE cleanup_timer(VALUE self, VALUE seconds)
119
186
  *
120
187
  */
121
188
  static void restore_original_timer_interval() {
122
- if (0 != setitimer (ITIMER_REAL, &original_timer_interval, NULL)) {
123
- log_error("install_timer: Could not restore original timer", DISPLAY_ERRNO);
124
- }
125
- log_debug("install_timer: Successfully restored timer");
189
+ if (0 != setitimer(ITIMER_REAL, &original_timer_interval, NULL)) {
190
+ log_error("[restore_original_configuration] Could not restore original timer", DISPLAY_ERRNO);
191
+ }
192
+ log_debug("[restore_original_configuration] Successfully restored original timer\n");
193
+ }
194
+
195
+ static void restore_sigalrm_mask(sigset_t *previous_mask)
196
+ {
197
+ if (!sigismember(previous_mask, SIGALRM)) {
198
+ sigprocmask(SIG_UNBLOCK, &sigalarm_mask, NULL);
199
+ log_debug("[restore_sigalrm_mask] Unblocked SIG_ALRM\n");
200
+ } else {
201
+ log_debug("[restore_sigalrm_mask] No Need to unblock SIG_ALRM\n");
202
+ }
126
203
  }
127
204
 
128
205
  static void restore_original_sigalrm_mask_when_blocked()
129
206
  {
130
- if (!sigismember(&original_mask, SIGALRM)) {
131
- sigprocmask(SIG_UNBLOCK, &sigalarm_mask, NULL);
132
- log_debug("cleanup_timer: Unblocked SIG_ALRM");
133
- } else {
134
- log_debug("cleanup_timer: No Need to unblock SIG_ALRM");
135
- }
207
+ restore_sigalrm_mask(&original_mask);
136
208
  }
137
209
 
138
210
  static void install_ruby_sigalrm_handler(VALUE self) {
139
- rb_thread_critical = 1;
140
- rb_funcall(self, rb_intern("install_ruby_sigalrm_handler"), 0);
141
- rb_thread_critical = 0;
211
+ rb_thread_critical = 1;
212
+ rb_funcall(self, rb_intern("install_ruby_sigalrm_handler"), 0);
213
+ rb_thread_critical = 0;
142
214
  }
143
215
 
144
216
  static void restore_original_ruby_sigalrm_handler(VALUE self) {
145
- rb_thread_critical = 1;
146
- rb_funcall(self, rb_intern("restore_original_ruby_sigalrm_handler"), 0);
147
- rb_thread_critical = 0;
217
+ rb_thread_critical = 1;
218
+ rb_funcall(self, rb_intern("restore_original_ruby_sigalrm_handler"), 0);
219
+ rb_thread_critical = 0;
148
220
  }
149
221
 
150
222
 
151
223
  static VALUE debug_enabled_p(VALUE self) {
152
- return debug_enabled ? Qtrue : Qfalse;
224
+ return debug_enabled ? Qtrue : Qfalse;
153
225
  }
154
226
 
155
227
  static VALUE enable_debug(VALUE self) {
156
- debug_enabled = 1;
157
- return Qnil;
228
+ debug_enabled = 1;
229
+ return Qnil;
158
230
  }
159
231
 
160
232
  static VALUE disable_debug(VALUE self) {
161
- debug_enabled = 0;
162
- return Qnil;
233
+ debug_enabled = 0;
234
+ return Qnil;
163
235
  }
164
236
 
165
- static void log_debug(char* message)
237
+ static void log_debug(char* message, ...)
166
238
  {
167
- if (0 != debug_enabled) {
168
- printf("%s\n", message);
169
- }
170
- return;
239
+ va_list argp;
240
+
241
+ if (0 != debug_enabled) {
242
+ va_start(argp, message);
243
+ vfprintf(stdout, message, argp);
244
+ va_end(argp);
245
+ }
246
+ return;
171
247
  }
172
248
 
173
249
  static void log_error(char* message, int display_errno)
174
250
  {
175
- fprintf(stderr, "%s: %s\n", message, display_errno ? strerror(errno) : "");
176
- return;
251
+ fprintf(stderr, "%s: %s\n", message, display_errno ? strerror(errno) : "");
252
+ return;
177
253
  }
178
254
 
179
255
  /*
@@ -186,32 +262,64 @@ static void log_error(char* message, int display_errno)
186
262
  */
187
263
  static void clear_pending_sigalrm_for_ruby_threads()
188
264
  {
189
- CHECK_INTS;
190
- log_debug("Succesfully triggered all pending signals at Green Thread level");
265
+ CHECK_INTS;
266
+ log_debug("[native] Successfully triggered all pending signals at Green Thread level\n");
191
267
  }
192
268
 
193
269
  static void init_sigalarm_mask()
194
270
  {
195
- sigemptyset(&sigalarm_mask);
196
- sigaddset(&sigalarm_mask, SIGALRM);
197
- return;
271
+ sigemptyset(&sigalarm_mask);
272
+ sigaddset(&sigalarm_mask, SIGALRM);
273
+ return;
198
274
  }
199
275
 
200
- static void set_itimerval(struct itimerval *value, int seconds) {
201
- value->it_interval.tv_usec = 0;
202
- value->it_interval.tv_sec = 0;
203
- value->it_value.tv_usec = 0;
204
- value->it_value.tv_sec = seconds; // (long int)
205
- return;
276
+ static void set_itimerval_with_minimum_1s_interval(struct itimerval *value,
277
+ VALUE seconds) {
278
+
279
+ double sanitized_second_interval;
280
+
281
+ sanitized_second_interval = NUM2DBL(seconds) + MINIMUM_TIMER_INTERVAL_IN_SECONDS;
282
+ if (sanitized_second_interval < MINIMUM_TIMER_INTERVAL_IN_SECONDS ) {
283
+ sanitized_second_interval = MINIMUM_TIMER_INTERVAL_IN_SECONDS;
284
+ }
285
+ set_itimerval(value, sanitized_second_interval);
286
+ }
287
+
288
+ static void set_itimerval(struct itimerval *value, double seconds) {
289
+ if (debug_enabled) {
290
+ log_debug("[set_itimerval] %.3lfs\n", seconds);
291
+ }
292
+ value->it_interval.tv_usec = 0;
293
+ value->it_interval.tv_sec = 0;
294
+ value->it_value.tv_sec = (long int) (seconds);
295
+ value->it_value.tv_usec = (long int) ((seconds - value->it_value.tv_sec) \
296
+ * MICRO_SECONDS);
297
+ if (debug_enabled) {
298
+ log_debug("[set_itimerval] Set to %ds + %dus\n", value->it_value.tv_sec,
299
+ value->it_value.tv_usec);
300
+ }
301
+ return;
206
302
  }
207
303
 
304
+
208
305
  void Init_system_timer_native()
209
306
  {
210
- init_sigalarm_mask();
307
+ init_sigalarm_mask();
308
+ rb_cSystemTimer = rb_define_module("SystemTimer");
309
+ rb_define_singleton_method(rb_cSystemTimer, "install_first_timer_and_save_original_configuration", install_first_timer_and_save_original_configuration, 1);
310
+ rb_define_singleton_method(rb_cSystemTimer, "install_next_timer", install_next_timer, 1);
311
+ rb_define_singleton_method(rb_cSystemTimer, "restore_original_configuration", restore_original_configuration, 0);
312
+ rb_define_singleton_method(rb_cSystemTimer, "debug_enabled?", debug_enabled_p, 0);
313
+ rb_define_singleton_method(rb_cSystemTimer, "enable_debug", enable_debug, 0);
314
+ rb_define_singleton_method(rb_cSystemTimer, "disable_debug", disable_debug, 0);
315
+ }
316
+
317
+ #else
318
+
319
+ // Exists just to make things happy
320
+ void Init_system_timer_native()
321
+ {
211
322
  rb_cSystemTimer = rb_define_module("SystemTimer");
212
- rb_define_singleton_method(rb_cSystemTimer, "install_timer", install_timer, 1);
213
- rb_define_singleton_method(rb_cSystemTimer, "cleanup_timer", cleanup_timer, 0);
214
- rb_define_singleton_method(rb_cSystemTimer, "debug_enabled?", debug_enabled_p, 0);
215
- rb_define_singleton_method(rb_cSystemTimer, "enable_debug", enable_debug, 0);
216
- rb_define_singleton_method(rb_cSystemTimer, "disable_debug", disable_debug, 0);
217
323
  }
324
+
325
+ #endif