SystemTimer 1.1.3 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|