SystemTimer 1.1.3 → 1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +15 -1
- data/README +103 -39
- data/ext/system_timer/system_timer_native.c +29 -13
- data/lib/system_timer.rb +11 -10
- data/lib/system_timer/concurrent_timer_pool.rb +15 -9
- data/lib/system_timer/thread_timer.rb +4 -3
- data/test/system_timer/concurrent_timer_pool_unit_test.rb +32 -15
- data/test/system_timer/thread_timer_test.rb +1 -1
- data/test/system_timer_functional_test.rb +36 -14
- data/test/system_timer_unit_test.rb +22 -8
- metadata +2 -2
data/ChangeLog
CHANGED
@@ -1,4 +1,18 @@
|
|
1
|
-
=== 1.
|
1
|
+
=== 1.2 / 2010-02-25
|
2
|
+
|
3
|
+
* Changed from using Mutex to Monitor. Mutex causes thread join
|
4
|
+
errors when Ruby is compiled with -disable-pthreads
|
5
|
+
(Contributed by Dmytro Shteflyuk <http://kpumuk.info/>)
|
6
|
+
|
7
|
+
* Timeouts can now be specified as a float and be a fraction of a second.
|
8
|
+
e.g. `SystemTimer.timeout(0.5)`
|
9
|
+
(Based on a contribution by Dmytro Shteflyuk <http://kpumuk.info/>)
|
10
|
+
|
11
|
+
* Added support for custom timeout exception. Useful to avoid interference
|
12
|
+
with other libraries using `Timeout::Error` (e.g. `Net::HTTP`)
|
13
|
+
(Contributed by runix <http://github.com/runix>)
|
14
|
+
|
15
|
+
=== 1.1.3/ 2009-11-29
|
2
16
|
|
3
17
|
* Preparing GemCutter migration
|
4
18
|
|
data/README
CHANGED
@@ -1,47 +1,110 @@
|
|
1
|
-
|
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
|
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
9
|
More background on:
|
9
10
|
|
10
|
-
* http://ph7spot.com/
|
11
|
-
* http://davidvollbracht.com/2008/6/2/30-days-of-teach-day-1-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)
|
12
13
|
|
13
|
-
|
14
|
+
Usage
|
15
|
+
=====
|
14
16
|
|
15
|
-
|
17
|
+
require 'systemtimer'
|
18
|
+
|
19
|
+
SystemTimer.timeout_after(5) do
|
20
|
+
|
21
|
+
# Something that should be interrupted if it takes too much time...
|
22
|
+
# ... even if blocked on a system call!
|
23
|
+
|
24
|
+
end
|
16
25
|
|
17
|
-
|
26
|
+
Timeouts as Floats
|
27
|
+
------------------
|
18
28
|
|
19
|
-
|
20
|
-
|
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.
|
21
31
|
|
22
|
-
|
32
|
+
SystemTimer.timeout_after(0.5) do
|
33
|
+
# timeout after 500ms
|
34
|
+
end
|
23
35
|
|
24
|
-
|
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 'systemtimer'
|
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
|
+
============
|
25
68
|
|
26
69
|
SystemTimer only works on UNIX platforms (Mac OS X, Linux, Solaris, BSD, ...).
|
27
70
|
You can install the gem on Microsoft Windows, but you will only get
|
28
71
|
a convenience shell wrapping a simple call to timeout.rb under the cover.
|
29
72
|
|
30
|
-
|
73
|
+
Install
|
74
|
+
=======
|
31
75
|
|
32
76
|
sudo gem install systemtimer
|
33
77
|
|
34
78
|
|
35
|
-
|
79
|
+
Authors
|
80
|
+
=======
|
36
81
|
|
37
82
|
* David Vollbracht <http://davidvollbracht.com>
|
38
|
-
* Philippe Hanrigou <http
|
39
|
-
|
40
|
-
|
83
|
+
* Philippe Hanrigou <http://ph7spot.com>
|
84
|
+
|
85
|
+
Contributor
|
86
|
+
===========
|
87
|
+
|
88
|
+
* Dmytro Shteflyuk <http://kpumuk.info/> :
|
89
|
+
- Changed from using Mutex to Monitor. Evidently Mutex causes thread
|
90
|
+
join errors when Ruby is compiled with -disable-pthreads
|
91
|
+
<http://github.com/kpumuk/system-micro-timer/commit/fe28f4dcf7d4126e53b7c642c5ec35fe8bc1e081>
|
92
|
+
- First tentative to support float timeouts
|
93
|
+
<http://github.com/kpumuk/system-micro-timer/commit/57fff73849aad7c94f8b9234352b7288d1314d21>
|
94
|
+
|
95
|
+
* runix <http://github.com/runix> :
|
96
|
+
- Added support for custom timeout exception. Useful to avoid interference
|
97
|
+
with other libraries using `Timeout::Error` (e.g. `Net::HTTP`)
|
98
|
+
<http://github.com/runix/system-timer/commit/d33acb3acc53d5105c68b25c3a2126fa682f12c0>
|
99
|
+
<http://github.com/runix/system-timer/commit/d8ca3452e462ea909d8e11a6091e7c30dfa3a1a8>
|
100
|
+
|
101
|
+
Copyright
|
102
|
+
=========
|
41
103
|
|
42
|
-
Copyright:: (C) 2008 David Vollbracht & Philippe Hanrigou
|
104
|
+
Copyright:: (C) 2008-2010 David Vollbracht & Philippe Hanrigou
|
43
105
|
|
44
|
-
|
106
|
+
Description
|
107
|
+
===========
|
45
108
|
|
46
109
|
While deploying Rails application in production our team discovered
|
47
110
|
that some web service call would not timeout way beyond their defined
|
@@ -66,11 +129,12 @@ timeout.rb, just a way to wrap sensitive call to system resources.
|
|
66
129
|
You can find more details on SystemTimer and how to use it
|
67
130
|
at http://ph7spot.com/articles/system_timer
|
68
131
|
|
69
|
-
|
132
|
+
License
|
133
|
+
=======
|
70
134
|
|
71
135
|
(The Ruby License)
|
72
136
|
|
73
|
-
Copyright:: (C) 2008 David Vollbracht & Philippe Hanrigou
|
137
|
+
Copyright:: (C) 2008-2010 David Vollbracht & Philippe Hanrigou
|
74
138
|
|
75
139
|
SystemTimer is copyrighted free software by David Vollbracht and Philippe Hanrigou.
|
76
140
|
You can redistribute it and/or modify it under either the terms of the GPL
|
@@ -83,33 +147,33 @@ You can redistribute it and/or modify it under either the terms of the GPL
|
|
83
147
|
2. You may modify your copy of the software in any way, provided that
|
84
148
|
you do at least ONE of the following:
|
85
149
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
150
|
+
a) place your modifications in the Public Domain or otherwise
|
151
|
+
make them Freely Available, such as by posting said
|
152
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
153
|
+
the author to include your modifications in the software.
|
90
154
|
|
91
|
-
|
92
|
-
|
155
|
+
b) use the modified software only within your corporation or
|
156
|
+
organization.
|
93
157
|
|
94
|
-
|
95
|
-
|
158
|
+
c) rename any non-standard executables so the names do not conflict
|
159
|
+
with standard executables, which must also be provided.
|
96
160
|
|
97
|
-
|
161
|
+
d) make other distribution arrangements with the author.
|
98
162
|
|
99
163
|
3. You may distribute the software in object code or executable
|
100
164
|
form, provided that you do at least ONE of the following:
|
101
165
|
|
102
|
-
|
103
|
-
|
104
|
-
|
166
|
+
a) distribute the executables and library files of the software,
|
167
|
+
together with instructions (in the manual page or equivalent)
|
168
|
+
on where to get the original distribution.
|
105
169
|
|
106
|
-
|
107
|
-
|
170
|
+
b) accompany the distribution with the machine-readable source of
|
171
|
+
the software.
|
108
172
|
|
109
|
-
|
110
|
-
|
173
|
+
c) give non-standard executables non-standard names, with
|
174
|
+
instructions on where to get the original software distribution.
|
111
175
|
|
112
|
-
|
176
|
+
d) make other distribution arrangements with the author.
|
113
177
|
|
114
178
|
4. You may modify and include the part of the software into any other
|
115
179
|
software (possibly commercial). But some files in the distribution
|
@@ -12,6 +12,8 @@
|
|
12
12
|
|
13
13
|
#define DISPLAY_ERRNO 1
|
14
14
|
#define DO_NOT_DISPLAY_ERRNO 0
|
15
|
+
#define MICRO_SECONDS 1000000.0
|
16
|
+
#define MINIMUM_TIMER_INTERVAL_IN_SECONDS 0.2
|
15
17
|
|
16
18
|
VALUE rb_cSystemTimer;
|
17
19
|
sigset_t original_mask;
|
@@ -26,7 +28,7 @@ static void restore_original_ruby_sigalrm_handler(VALUE);
|
|
26
28
|
static void restore_original_sigalrm_mask_when_blocked();
|
27
29
|
static void restore_original_timer_interval();
|
28
30
|
static void set_itimerval_with_minimum_1s_interval(struct itimerval *, VALUE);
|
29
|
-
static void set_itimerval(struct itimerval *,
|
31
|
+
static void set_itimerval(struct itimerval *, double);
|
30
32
|
static void restore_sigalrm_mask(sigset_t *previous_mask);
|
31
33
|
static void log_debug(char*, ...);
|
32
34
|
static void log_error(char*, int);
|
@@ -37,7 +39,7 @@ static VALUE install_first_timer_and_save_original_configuration(VALUE self, VAL
|
|
37
39
|
struct itimerval timer_interval;
|
38
40
|
|
39
41
|
if (debug_enabled) {
|
40
|
-
log_debug("[install_first_timer]
|
42
|
+
log_debug("[install_first_timer] %.2lfs\n", NUM2DBL(seconds));
|
41
43
|
}
|
42
44
|
|
43
45
|
/*
|
@@ -70,7 +72,7 @@ static VALUE install_first_timer_and_save_original_configuration(VALUE self, VAL
|
|
70
72
|
/*
|
71
73
|
* Save original real time interval timer and aet new real time interval timer.
|
72
74
|
*/
|
73
|
-
set_itimerval(&original_timer_interval, 0);
|
75
|
+
set_itimerval(&original_timer_interval, 0.0);
|
74
76
|
set_itimerval_with_minimum_1s_interval(&timer_interval, seconds);
|
75
77
|
if (0 != setitimer(ITIMER_REAL, &timer_interval, &original_timer_interval)) {
|
76
78
|
log_error("[install_first_timer] Could not install our own timer, timeout will not work", DISPLAY_ERRNO);
|
@@ -78,7 +80,10 @@ static VALUE install_first_timer_and_save_original_configuration(VALUE self, VAL
|
|
78
80
|
restore_original_sigalrm_mask_when_blocked();
|
79
81
|
return Qnil;
|
80
82
|
}
|
81
|
-
|
83
|
+
if (debug_enabled) {
|
84
|
+
log_debug("[install_first_timer] Successfully installed timer (%ds)\n",
|
85
|
+
timer_interval.it_value.tv_sec);
|
86
|
+
}
|
82
87
|
|
83
88
|
/*
|
84
89
|
* Unblock SIG_ALRM
|
@@ -100,7 +105,7 @@ static VALUE install_next_timer(VALUE self, VALUE seconds)
|
|
100
105
|
sigset_t previous_sigalarm_mask;
|
101
106
|
|
102
107
|
if (debug_enabled) {
|
103
|
-
log_debug("[install_next_timer]
|
108
|
+
log_debug("[install_next_timer] %.2lfs\n", NUM2DBL(seconds));
|
104
109
|
}
|
105
110
|
|
106
111
|
/*
|
@@ -122,7 +127,10 @@ static VALUE install_next_timer(VALUE self, VALUE seconds)
|
|
122
127
|
restore_sigalrm_mask(&previous_sigalarm_mask);
|
123
128
|
return Qnil;
|
124
129
|
}
|
125
|
-
|
130
|
+
if (debug_enabled) {
|
131
|
+
log_debug("[install_next_timer] Successfully installed timer (%ds + %dus)\n",
|
132
|
+
timer_interval.it_value.tv_sec, timer_interval.it_value.tv_usec);
|
133
|
+
}
|
126
134
|
|
127
135
|
/*
|
128
136
|
* Unblock SIG_ALRM
|
@@ -264,20 +272,28 @@ static void init_sigalarm_mask()
|
|
264
272
|
static void set_itimerval_with_minimum_1s_interval(struct itimerval *value,
|
265
273
|
VALUE seconds) {
|
266
274
|
|
267
|
-
|
275
|
+
double sanitized_second_interval;
|
268
276
|
|
269
|
-
sanitized_second_interval =
|
270
|
-
if (sanitized_second_interval
|
271
|
-
sanitized_second_interval =
|
277
|
+
sanitized_second_interval = NUM2DBL(seconds) + MINIMUM_TIMER_INTERVAL_IN_SECONDS;
|
278
|
+
if (sanitized_second_interval < MINIMUM_TIMER_INTERVAL_IN_SECONDS ) {
|
279
|
+
sanitized_second_interval = MINIMUM_TIMER_INTERVAL_IN_SECONDS;
|
272
280
|
}
|
273
281
|
set_itimerval(value, sanitized_second_interval);
|
274
282
|
}
|
275
283
|
|
276
|
-
static void set_itimerval(struct itimerval *value,
|
284
|
+
static void set_itimerval(struct itimerval *value, double seconds) {
|
285
|
+
if (debug_enabled) {
|
286
|
+
log_debug("[set_itimerval] %.3lfs\n", seconds);
|
287
|
+
}
|
277
288
|
value->it_interval.tv_usec = 0;
|
278
289
|
value->it_interval.tv_sec = 0;
|
279
|
-
value->it_value.
|
280
|
-
value->it_value.
|
290
|
+
value->it_value.tv_sec = (long int) (seconds);
|
291
|
+
value->it_value.tv_usec = (long int) ((seconds - value->it_value.tv_sec) \
|
292
|
+
* MICRO_SECONDS);
|
293
|
+
if (debug_enabled) {
|
294
|
+
log_debug("[set_itimerval] Set to %ds + %dus\n", value->it_value.tv_sec,
|
295
|
+
value->it_value.tv_usec);
|
296
|
+
}
|
281
297
|
return;
|
282
298
|
}
|
283
299
|
|
data/lib/system_timer.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'timeout'
|
5
5
|
require 'forwardable'
|
6
|
+
require 'monitor'
|
6
7
|
require File.dirname(__FILE__) + '/system_timer/thread_timer'
|
7
8
|
require File.dirname(__FILE__) + '/system_timer/concurrent_timer_pool'
|
8
9
|
|
@@ -29,9 +30,9 @@ require File.dirname(__FILE__) + '/system_timer/concurrent_timer_pool'
|
|
29
30
|
#
|
30
31
|
module SystemTimer
|
31
32
|
|
32
|
-
Thread.exclusive do # Avoid race conditions for
|
33
|
+
Thread.exclusive do # Avoid race conditions for monitor and pool creation
|
33
34
|
@timer_pool = ConcurrentTimerPool.new
|
34
|
-
@
|
35
|
+
@monitor = Monitor.new
|
35
36
|
end
|
36
37
|
|
37
38
|
class << self
|
@@ -40,12 +41,12 @@ module SystemTimer
|
|
40
41
|
# Executes the method's block. If the block execution terminates before
|
41
42
|
# +seconds+ seconds has passed, it returns true. If not, it terminates
|
42
43
|
# the execution and raises a +Timeout::Error+.
|
43
|
-
def timeout_after(seconds)
|
44
|
+
def timeout_after(seconds, exception_class = nil)
|
44
45
|
new_timer = nil # just for scope
|
45
|
-
@
|
46
|
-
new_timer = timer_pool.add_timer seconds
|
46
|
+
@monitor.synchronize do
|
47
|
+
new_timer = timer_pool.add_timer seconds, exception_class
|
47
48
|
timer_interval = timer_pool.next_trigger_interval_in_seconds
|
48
|
-
debug "==== Install Timer ==== at #{Time.now.
|
49
|
+
debug "==== Install Timer ==== at #{Time.now.to_f}, next interval: #{timer_interval}"
|
49
50
|
if timer_pool.first_timer?
|
50
51
|
install_first_timer_and_save_original_configuration timer_interval
|
51
52
|
else
|
@@ -54,8 +55,8 @@ module SystemTimer
|
|
54
55
|
end
|
55
56
|
return yield
|
56
57
|
ensure
|
57
|
-
@
|
58
|
-
debug "==== Cleanup Timer ==== at #{Time.now.
|
58
|
+
@monitor.synchronize do
|
59
|
+
debug "==== Cleanup Timer ==== at #{Time.now.to_f}, #{new_timer} "
|
59
60
|
timer_pool.cancel new_timer
|
60
61
|
timer_pool.log_registered_timers if debug_enabled?
|
61
62
|
next_interval = timer_pool.next_trigger_interval_in_seconds
|
@@ -75,7 +76,7 @@ module SystemTimer
|
|
75
76
|
|
76
77
|
def install_ruby_sigalrm_handler #:nodoc:
|
77
78
|
@original_ruby_sigalrm_handler = trap('SIGALRM') do
|
78
|
-
@
|
79
|
+
@monitor.synchronize do
|
79
80
|
# Triggers timers one at a time to ensure more deterministic results
|
80
81
|
timer_pool.trigger_next_expired_timer
|
81
82
|
end
|
@@ -104,4 +105,4 @@ module SystemTimer
|
|
104
105
|
|
105
106
|
end
|
106
107
|
|
107
|
-
require 'system_timer_native'
|
108
|
+
require 'system_timer_native'
|
@@ -8,14 +8,14 @@ module SystemTimer
|
|
8
8
|
@timers ||= []
|
9
9
|
end
|
10
10
|
|
11
|
-
def register_timer(trigger_time, thread)
|
12
|
-
new_timer = ThreadTimer.new(trigger_time, thread)
|
11
|
+
def register_timer(trigger_time, thread, exception_class=nil)
|
12
|
+
new_timer = ThreadTimer.new(trigger_time, thread, exception_class)
|
13
13
|
registered_timers << new_timer
|
14
14
|
new_timer
|
15
15
|
end
|
16
16
|
|
17
|
-
def add_timer(interval_in_seconds)
|
18
|
-
new_timer = register_timer(Time.now.
|
17
|
+
def add_timer(interval_in_seconds, exception_class=nil)
|
18
|
+
new_timer = register_timer(Time.now.to_f + interval_in_seconds, Thread.current, exception_class)
|
19
19
|
log_registered_timers if SystemTimer.debug_enabled?
|
20
20
|
new_timer
|
21
21
|
end
|
@@ -39,11 +39,15 @@ module SystemTimer
|
|
39
39
|
|
40
40
|
def next_trigger_interval_in_seconds
|
41
41
|
timer = next_timer
|
42
|
-
[0, (timer.trigger_time - Time.now.
|
42
|
+
[0, (timer.trigger_time - Time.now.to_f)].max unless timer.nil?
|
43
43
|
end
|
44
44
|
|
45
45
|
def next_expired_timer(now_in_seconds_since_epoch)
|
46
46
|
candidate_timer = next_timer
|
47
|
+
if SystemTimer.debug_enabled?
|
48
|
+
puts "Candidate timer at #{now_in_seconds_since_epoch} : " +
|
49
|
+
candidate_timer.inspect
|
50
|
+
end
|
47
51
|
return nil if candidate_timer.nil? ||
|
48
52
|
candidate_timer.trigger_time > now_in_seconds_since_epoch
|
49
53
|
candidate_timer
|
@@ -51,15 +55,17 @@ module SystemTimer
|
|
51
55
|
|
52
56
|
def trigger_next_expired_timer_at(now_in_seconds_since_epoch)
|
53
57
|
timer = next_expired_timer(now_in_seconds_since_epoch)
|
58
|
+
puts "Next expired timer : #{timer.inspect}" if SystemTimer.debug_enabled?
|
54
59
|
return if timer.nil?
|
55
60
|
|
56
61
|
cancel timer
|
57
62
|
log_timeout_received(timer) if SystemTimer.debug_enabled?
|
58
|
-
timer.thread.raise
|
63
|
+
timer.thread.raise timer.exception_class.new("time's up!")
|
59
64
|
end
|
60
65
|
|
61
66
|
def trigger_next_expired_timer
|
62
|
-
|
67
|
+
puts "Trigger next expired timer" if SystemTimer.debug_enabled?
|
68
|
+
trigger_next_expired_timer_at Time.now.to_f
|
63
69
|
end
|
64
70
|
|
65
71
|
def log_timeout_received(thread_timer) #:nodoc:
|
@@ -74,10 +80,10 @@ module SystemTimer
|
|
74
80
|
|
75
81
|
def log_registered_timers #:nodoc:
|
76
82
|
puts <<-EOS
|
77
|
-
Registered Timers: #{registered_timers.
|
83
|
+
Registered Timers: #{registered_timers.map {|t| t.to_s}.join("\n ")}
|
78
84
|
EOS
|
79
85
|
end
|
80
86
|
|
81
87
|
end
|
82
88
|
|
83
|
-
end
|
89
|
+
end
|
@@ -6,15 +6,16 @@ module SystemTimer
|
|
6
6
|
# from a Ruby signal handler and Ruby signals are always delivered to
|
7
7
|
# main thread.
|
8
8
|
class ThreadTimer
|
9
|
-
attr_reader :trigger_time, :thread
|
9
|
+
attr_reader :trigger_time, :thread, :exception_class
|
10
10
|
|
11
|
-
def initialize(trigger_time, thread)
|
11
|
+
def initialize(trigger_time, thread, exception_class = nil)
|
12
12
|
@trigger_time = trigger_time
|
13
13
|
@thread = thread
|
14
|
+
@exception_class = exception_class || Timeout::Error
|
14
15
|
end
|
15
16
|
|
16
17
|
def to_s
|
17
|
-
"<ThreadTimer :time => #{trigger_time}, :thread => #{thread}>"
|
18
|
+
"<ThreadTimer :time => #{trigger_time}, :thread => #{thread}, :exception_class => #{exception_class}>"
|
18
19
|
end
|
19
20
|
|
20
21
|
end
|
@@ -25,7 +25,7 @@ unit_tests do
|
|
25
25
|
now = Time.now
|
26
26
|
Time.stubs(:now).returns(now)
|
27
27
|
|
28
|
-
pool.expects(:register_timer).with(now.
|
28
|
+
pool.expects(:register_timer).with(now.to_f + 15, :the_current_thread, nil)
|
29
29
|
pool.add_timer 15
|
30
30
|
end
|
31
31
|
|
@@ -123,7 +123,7 @@ unit_tests do
|
|
123
123
|
pool = SystemTimer::ConcurrentTimerPool.new
|
124
124
|
now = Time.now
|
125
125
|
Time.stubs(:now).returns(now)
|
126
|
-
next_timer = SystemTimer::ThreadTimer.new((now.
|
126
|
+
next_timer = SystemTimer::ThreadTimer.new((now.to_f + 7), stub_everything)
|
127
127
|
pool.expects(:next_timer).returns(next_timer)
|
128
128
|
assert_equal 7, pool.next_trigger_interval_in_seconds
|
129
129
|
end
|
@@ -132,7 +132,7 @@ unit_tests do
|
|
132
132
|
pool = SystemTimer::ConcurrentTimerPool.new
|
133
133
|
now = Time.now
|
134
134
|
Time.stubs(:now).returns(now)
|
135
|
-
next_timer = SystemTimer::ThreadTimer.new now.
|
135
|
+
next_timer = SystemTimer::ThreadTimer.new now.to_f, stub_everything
|
136
136
|
pool.expects(:next_timer).returns(next_timer)
|
137
137
|
assert_equal 0, pool.next_trigger_interval_in_seconds
|
138
138
|
end
|
@@ -141,7 +141,7 @@ unit_tests do
|
|
141
141
|
pool = SystemTimer::ConcurrentTimerPool.new
|
142
142
|
now = Time.now
|
143
143
|
Time.stubs(:now).returns(now)
|
144
|
-
next_timer = SystemTimer::ThreadTimer.new((now.
|
144
|
+
next_timer = SystemTimer::ThreadTimer.new((now.to_f - 3), stub_everything)
|
145
145
|
pool.expects(:next_timer).returns(next_timer)
|
146
146
|
assert_equal 0, pool.next_trigger_interval_in_seconds
|
147
147
|
end
|
@@ -217,12 +217,21 @@ unit_tests do
|
|
217
217
|
|
218
218
|
test "trigger_next_expired_timer_at logs timeout a registered timer has expired" +
|
219
219
|
"and SystemTimer debug mode is enabled " do
|
220
|
-
pool = SystemTimer::ConcurrentTimerPool.new
|
221
|
-
the_timer = pool.register_timer 24, stub_everything
|
222
|
-
SystemTimer.stubs(:debug_enabled?).returns(true)
|
223
220
|
|
224
|
-
|
225
|
-
|
221
|
+
original_stdout = $stdout
|
222
|
+
begin
|
223
|
+
stdout = StringIO.new
|
224
|
+
$stdout = stdout
|
225
|
+
|
226
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
227
|
+
the_timer = pool.register_timer 24, stub_everything
|
228
|
+
SystemTimer.stubs(:debug_enabled?).returns(true)
|
229
|
+
|
230
|
+
pool.expects(:log_timeout_received).with(the_timer)
|
231
|
+
pool.trigger_next_expired_timer_at 24
|
232
|
+
ensure
|
233
|
+
$stdout = original_stdout
|
234
|
+
end
|
226
235
|
end
|
227
236
|
|
228
237
|
test "trigger_next_expired_timer_at does not logs timeoout when SystemTimer " +
|
@@ -238,13 +247,21 @@ unit_tests do
|
|
238
247
|
|
239
248
|
test "trigger_next_expired_timer_at does not logs timeout no registered timer " +
|
240
249
|
"has expired and SystemTimer debug mode is enabled " do
|
250
|
+
|
251
|
+
original_stdout = $stdout
|
252
|
+
begin
|
253
|
+
stdout = StringIO.new
|
254
|
+
$stdout = stdout
|
241
255
|
|
242
|
-
|
243
|
-
|
244
|
-
|
256
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
257
|
+
the_timer = pool.register_timer 24, stub_everything
|
258
|
+
SystemTimer.stubs(:debug_enabled?).returns(true)
|
245
259
|
|
246
|
-
|
247
|
-
|
260
|
+
pool.expects(:log_timeout_received).never
|
261
|
+
pool.trigger_next_expired_timer_at 23
|
262
|
+
ensure
|
263
|
+
$stdout = original_stdout
|
264
|
+
end
|
248
265
|
end
|
249
266
|
|
250
267
|
test "trigger_next_expired_timer is a shorcut method calling " +
|
@@ -254,7 +271,7 @@ unit_tests do
|
|
254
271
|
pool = SystemTimer::ConcurrentTimerPool.new
|
255
272
|
Time.stubs(:now).returns(now)
|
256
273
|
|
257
|
-
pool.expects(:trigger_next_expired_timer_at).with(now.
|
274
|
+
pool.expects(:trigger_next_expired_timer_at).with(now.to_f)
|
258
275
|
pool.trigger_next_expired_timer
|
259
276
|
end
|
260
277
|
|
@@ -13,7 +13,7 @@ unit_tests do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
test "to_s retruns a human friendly description of the timer" do
|
16
|
-
assert_match /<ThreadTimer :time => 24, :thread => #<Thread(.*)
|
16
|
+
assert_match /<ThreadTimer :time => 24, :thread => #<Thread(.*)>, :exception_class => Timeout::Error>/,
|
17
17
|
SystemTimer::ThreadTimer.new(24, Thread.current).to_s
|
18
18
|
end
|
19
19
|
|
@@ -50,7 +50,21 @@ functional_tests do
|
|
50
50
|
SystemTimer.timeout_after(1) {sleep 5}
|
51
51
|
end
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
|
+
test "timeout_after timeout can be a fraction of a second" do
|
55
|
+
assert_raises(Timeout::Error) do
|
56
|
+
SystemTimer.timeout_after(0.2) {sleep 3}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
test "timeout_after raises a custom timeout when block takes too long and a custom exception class is provided" do
|
62
|
+
ACustomException = Class.new(Exception)
|
63
|
+
assert_raises(ACustomException) do
|
64
|
+
SystemTimer.timeout_after(1, ACustomException) {sleep 5}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
54
68
|
test "timeout_after does not raises Timeout Error if block completes in time" do
|
55
69
|
SystemTimer.timeout_after(5) {sleep 1}
|
56
70
|
end
|
@@ -154,18 +168,28 @@ functional_tests do
|
|
154
168
|
end
|
155
169
|
|
156
170
|
test "can set multiple serial timers" do
|
157
|
-
10.times do
|
158
|
-
|
159
|
-
|
171
|
+
10.times do |i|
|
172
|
+
print(i) & STDOUT.flush
|
173
|
+
assert_timeout_within(1) do
|
174
|
+
SystemTimer.timeout(1) do
|
175
|
+
sleep 60
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
test "can set multiple serial timers with fractional timeout" do
|
182
|
+
10.times do |i|
|
183
|
+
print(i) & STDOUT.flush
|
184
|
+
assert_timeout_within(0.5) do
|
185
|
+
SystemTimer.timeout(0.5) do
|
160
186
|
sleep 60
|
161
187
|
end
|
162
188
|
end
|
163
189
|
end
|
164
190
|
end
|
165
191
|
|
166
|
-
test "timeout work when setting concurrent timers, the first one "
|
167
|
-
"expiring before the second one" do
|
168
|
-
|
192
|
+
test "timeout work when setting concurrent timers, the first one expiring before the second one" do
|
169
193
|
first_thread = Thread.new do
|
170
194
|
assert_timeout_within(3) do
|
171
195
|
SystemTimer.timeout(3) do
|
@@ -184,9 +208,8 @@ functional_tests do
|
|
184
208
|
second_thread.join
|
185
209
|
end
|
186
210
|
|
187
|
-
test "timeout work when setting concurrent timers, the second one "
|
188
|
-
|
189
|
-
|
211
|
+
test "timeout work when setting concurrent timers, the second one expiring before the first one" do
|
212
|
+
|
190
213
|
first_thread = Thread.new do
|
191
214
|
assert_timeout_within(10) do
|
192
215
|
SystemTimer.timeout(10) do
|
@@ -205,8 +228,7 @@ functional_tests do
|
|
205
228
|
second_thread.join
|
206
229
|
end
|
207
230
|
|
208
|
-
test "timeout work when setting concurrent timers with the exact"
|
209
|
-
"same timeout" do
|
231
|
+
test "timeout work when setting concurrent timers with the exact same timeout" do
|
210
232
|
|
211
233
|
first_thread = Thread.new do
|
212
234
|
assert_timeout_within(2) do
|
@@ -230,7 +252,7 @@ functional_tests do
|
|
230
252
|
all_threads = []
|
231
253
|
|
232
254
|
10.times do
|
233
|
-
a_timeout = [1, (rand(10)).
|
255
|
+
a_timeout = [1, (rand(10)).to_f].max
|
234
256
|
all_threads << Thread.new do
|
235
257
|
assert_timeout_within(a_timeout, 10) do
|
236
258
|
SystemTimer.timeout(a_timeout) do
|
@@ -257,4 +279,4 @@ functional_tests do
|
|
257
279
|
"Timed out after #{elapsed} seconds, expected #{expected_timeout_in_seconds}"
|
258
280
|
end
|
259
281
|
|
260
|
-
end
|
282
|
+
end
|
@@ -9,18 +9,30 @@ unit_tests do
|
|
9
9
|
SystemTimer.stubs(:install_next_timer)
|
10
10
|
SystemTimer.stubs(:restore_original_configuration)
|
11
11
|
|
12
|
-
pool.expects(:add_timer).with(5).returns(stub_everything)
|
12
|
+
pool.expects(:add_timer).with(5, nil).returns(stub_everything)
|
13
13
|
SystemTimer.timeout_after(5) {}
|
14
14
|
end
|
15
15
|
|
16
|
+
test "timeout_after registers a new timer with a custom timeout exception in the timer pool" do
|
17
|
+
MyCustomException = Class.new(Exception)
|
18
|
+
pool = stub_everything
|
19
|
+
Thread.stubs(:current).returns(:the_current_thread)
|
20
|
+
SystemTimer.stubs(:timer_pool).returns(pool)
|
21
|
+
SystemTimer.stubs(:install_next_timer)
|
22
|
+
SystemTimer.stubs(:restore_original_configuration)
|
23
|
+
|
24
|
+
pool.expects(:add_timer).with(5, MyCustomException).returns(stub_everything)
|
25
|
+
SystemTimer.timeout_after(5, MyCustomException) {}
|
26
|
+
end
|
27
|
+
|
16
28
|
test "timeout_after installs a system timer saving the previous " +
|
17
29
|
"configuration when there is only one timer" do
|
18
30
|
|
19
31
|
now = Time.now
|
20
32
|
Time.stubs(:now).returns(now)
|
21
33
|
SystemTimer.stubs(:restore_original_configuration)
|
22
|
-
|
23
|
-
|
34
|
+
SystemTimer.expects(:install_first_timer_and_save_original_configuration) \
|
35
|
+
.with {|value| value.between?(23.99, 24.01) }
|
24
36
|
SystemTimer.timeout_after(24) {}
|
25
37
|
end
|
26
38
|
|
@@ -29,11 +41,12 @@ unit_tests do
|
|
29
41
|
|
30
42
|
now = Time.now
|
31
43
|
Time.stubs(:now).returns(now)
|
32
|
-
SystemTimer.timer_pool.register_timer now.
|
44
|
+
SystemTimer.timer_pool.register_timer now.to_f + 100, :a_thread
|
33
45
|
SystemTimer.stubs(:restore_original_configuration)
|
34
46
|
SystemTimer.stubs(:install_next_timer)
|
35
47
|
|
36
|
-
SystemTimer.expects(:install_next_timer)
|
48
|
+
SystemTimer.expects(:install_next_timer) \
|
49
|
+
.with {|value| value.between?(23.99, 24.01) }
|
37
50
|
SystemTimer.timeout_after(24) {}
|
38
51
|
end
|
39
52
|
|
@@ -42,11 +55,12 @@ unit_tests do
|
|
42
55
|
|
43
56
|
now = Time.now
|
44
57
|
Time.stubs(:now).returns(now)
|
45
|
-
SystemTimer.timer_pool.register_timer now.
|
58
|
+
SystemTimer.timer_pool.register_timer now.to_f + 24, :a_thread
|
46
59
|
SystemTimer.stubs(:restore_original_configuration)
|
47
60
|
SystemTimer.stubs(:install_next_timer)
|
48
61
|
|
49
|
-
SystemTimer.expects(:install_next_timer)
|
62
|
+
SystemTimer.expects(:install_next_timer) \
|
63
|
+
.with {|value| value.between?(23.99, 24.01) }
|
50
64
|
SystemTimer.timeout_after(100) {}
|
51
65
|
end
|
52
66
|
|
@@ -93,4 +107,4 @@ unit_tests do
|
|
93
107
|
end
|
94
108
|
end
|
95
109
|
|
96
|
-
end
|
110
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: SystemTimer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: "1.2"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Philippe Hanrigou
|
@@ -10,7 +10,7 @@ autorequire: system_timer
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date:
|
13
|
+
date: 2010-02-25 00:00:00 -08:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|