SystemTimer 1.0 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +13 -0
- data/README +7 -0
- data/ext/system_timer/extconf.rb +0 -0
- data/ext/system_timer/system_timer_native.c +204 -127
- data/lib/system_timer.rb +65 -30
- data/lib/system_timer/concurrent_timer_pool.rb +83 -0
- data/lib/system_timer/thread_timer.rb +21 -0
- data/lib/system_timer_stub.rb +5 -0
- data/test/all_tests.rb +3 -1
- data/test/system_timer/concurrent_timer_pool_unit_test.rb +274 -0
- data/test/system_timer/thread_timer_test.rb +20 -0
- data/test/system_timer_functional_test.rb +260 -0
- data/test/system_timer_unit_test.rb +96 -0
- data/test/test_helper.rb +10 -0
- metadata +10 -4
- data/test/system_timer_test.rb +0 -121
@@ -0,0 +1,83 @@
|
|
1
|
+
# Copyright 2008 David Vollbracht & Philippe Hanrigou
|
2
|
+
|
3
|
+
module SystemTimer
|
4
|
+
|
5
|
+
class ConcurrentTimerPool
|
6
|
+
|
7
|
+
def registered_timers
|
8
|
+
@timers ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def register_timer(trigger_time, thread)
|
12
|
+
new_timer = ThreadTimer.new(trigger_time, thread)
|
13
|
+
registered_timers << new_timer
|
14
|
+
new_timer
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_timer(interval_in_seconds)
|
18
|
+
new_timer = register_timer(Time.now.to_i + interval_in_seconds, Thread.current)
|
19
|
+
log_registered_timers if SystemTimer.debug_enabled?
|
20
|
+
new_timer
|
21
|
+
end
|
22
|
+
|
23
|
+
def cancel(registered_timer)
|
24
|
+
registered_timers.delete registered_timer
|
25
|
+
end
|
26
|
+
|
27
|
+
def first_timer?
|
28
|
+
registered_timers.size == 1
|
29
|
+
end
|
30
|
+
|
31
|
+
def next_timer
|
32
|
+
registered_timers.sort {|x,y| x.trigger_time <=> y.trigger_time}.first
|
33
|
+
end
|
34
|
+
|
35
|
+
def next_trigger_time
|
36
|
+
timer = next_timer
|
37
|
+
timer.trigger_time unless timer.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
def next_trigger_interval_in_seconds
|
41
|
+
timer = next_timer
|
42
|
+
[0, (timer.trigger_time - Time.now.to_i)].max unless timer.nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
def next_expired_timer(now_in_seconds_since_epoch)
|
46
|
+
candidate_timer = next_timer
|
47
|
+
return nil if candidate_timer.nil? ||
|
48
|
+
candidate_timer.trigger_time > now_in_seconds_since_epoch
|
49
|
+
candidate_timer
|
50
|
+
end
|
51
|
+
|
52
|
+
def trigger_next_expired_timer_at(now_in_seconds_since_epoch)
|
53
|
+
timer = next_expired_timer(now_in_seconds_since_epoch)
|
54
|
+
return if timer.nil?
|
55
|
+
|
56
|
+
cancel timer
|
57
|
+
log_timeout_received(timer) if SystemTimer.debug_enabled?
|
58
|
+
timer.thread.raise Timeout::Error.new("time's up!")
|
59
|
+
end
|
60
|
+
|
61
|
+
def trigger_next_expired_timer
|
62
|
+
trigger_next_expired_timer_at Time.now.to_i
|
63
|
+
end
|
64
|
+
|
65
|
+
def log_timeout_received(thread_timer) #:nodoc:
|
66
|
+
puts <<-EOS
|
67
|
+
==== Triger Timer ==== #{thread_timer}
|
68
|
+
Main thread : #{Thread.main}
|
69
|
+
Timed_thread : #{thread_timer.thread}
|
70
|
+
All Threads : #{Thread.list.inspect}
|
71
|
+
EOS
|
72
|
+
log_registered_timers
|
73
|
+
end
|
74
|
+
|
75
|
+
def log_registered_timers #:nodoc:
|
76
|
+
puts <<-EOS
|
77
|
+
Registered Timers: #{registered_timers.collect do |t| t.to_s end}
|
78
|
+
EOS
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Copyright 2008 David Vollbracht & Philippe Hanrigou
|
2
|
+
|
3
|
+
module SystemTimer
|
4
|
+
|
5
|
+
# Timer saving associated thread. This is needed because we trigger timers
|
6
|
+
# from a Ruby signal handler and Ruby signals are always delivered to
|
7
|
+
# main thread.
|
8
|
+
class ThreadTimer
|
9
|
+
attr_reader :trigger_time, :thread
|
10
|
+
|
11
|
+
def initialize(trigger_time, thread)
|
12
|
+
@trigger_time = trigger_time
|
13
|
+
@thread = thread
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
"<ThreadTimer :time => #{trigger_time}, :thread => #{thread}>"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/system_timer_stub.rb
CHANGED
data/test/all_tests.rb
CHANGED
@@ -0,0 +1,274 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
unit_tests do
|
4
|
+
|
5
|
+
test "registered_timers is empty when there is no registered timers" do
|
6
|
+
assert_equal [], SystemTimer::ConcurrentTimerPool.new.registered_timers
|
7
|
+
end
|
8
|
+
|
9
|
+
test "a new timer is added to the registered timer list when you register a timer" do
|
10
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
11
|
+
pool.register_timer :a_trigger_time, :a_thread
|
12
|
+
assert_equal [[:a_trigger_time, :a_thread]],
|
13
|
+
pool.registered_timers.collect {|t| [t.trigger_time, t.thread] }
|
14
|
+
end
|
15
|
+
|
16
|
+
test "register_timer returns the timer that was just added to the pool" do
|
17
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
18
|
+
timer = pool.register_timer :a_trigger_time, :a_thread
|
19
|
+
assert_equal [:a_trigger_time, :a_thread], [timer.trigger_time, timer.thread]
|
20
|
+
end
|
21
|
+
|
22
|
+
test "add_timer is a shortcut method to register a timer given its interval" do
|
23
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
24
|
+
Thread.stubs(:current).returns(:the_current_thread)
|
25
|
+
now = Time.now
|
26
|
+
Time.stubs(:now).returns(now)
|
27
|
+
|
28
|
+
pool.expects(:register_timer).with(now.to_i + 15, :the_current_thread)
|
29
|
+
pool.add_timer 15
|
30
|
+
end
|
31
|
+
|
32
|
+
test "cancel removes a timer from the registered timer list" do
|
33
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
34
|
+
registered_timer = pool.register_timer :a_trigger_time, :a_thread
|
35
|
+
pool.cancel registered_timer
|
36
|
+
assert_equal [], pool.registered_timers
|
37
|
+
end
|
38
|
+
|
39
|
+
test "cancel does not complain when timer is cancelled " +
|
40
|
+
"(useful for ensure blocks)" do
|
41
|
+
|
42
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
43
|
+
a_timer = pool.add_timer 123
|
44
|
+
another_timer = pool.add_timer 456
|
45
|
+
pool.cancel(another_timer)
|
46
|
+
pool.cancel(another_timer)
|
47
|
+
assert_equal [a_timer], pool.registered_timers
|
48
|
+
end
|
49
|
+
|
50
|
+
test "first_timer? returns false when there is no timer" do
|
51
|
+
assert_equal false, SystemTimer::ConcurrentTimerPool.new.first_timer?
|
52
|
+
end
|
53
|
+
|
54
|
+
test "first_timer? returns true when there is a single timer" do
|
55
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
56
|
+
pool.add_timer 7
|
57
|
+
assert_equal true, pool.first_timer?
|
58
|
+
end
|
59
|
+
|
60
|
+
test "first_timer? returns false when there is more than one timer" do
|
61
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
62
|
+
pool.add_timer 7
|
63
|
+
pool.add_timer 3
|
64
|
+
assert_equal false, pool.first_timer?
|
65
|
+
end
|
66
|
+
|
67
|
+
test "first_timer? returns false when there is a single timer left" do
|
68
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
69
|
+
first_timer = pool.add_timer 7
|
70
|
+
pool.add_timer 3
|
71
|
+
pool.cancel first_timer
|
72
|
+
assert_equal true, pool.first_timer?
|
73
|
+
end
|
74
|
+
|
75
|
+
test "next expired timer return nil when there is no registered timer" do
|
76
|
+
assert_nil SystemTimer::ConcurrentTimerPool.new.next_expired_timer(24)
|
77
|
+
end
|
78
|
+
|
79
|
+
test "next_timer returns nil when there is no registered timer" do
|
80
|
+
assert_nil SystemTimer::ConcurrentTimerPool.new.next_timer
|
81
|
+
end
|
82
|
+
|
83
|
+
test "next_timer returns the registered timer when " +
|
84
|
+
"there is only one registered timer" do
|
85
|
+
|
86
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
87
|
+
the_timer = pool.register_timer 24, stub_everything
|
88
|
+
assert_equal the_timer, pool.next_timer
|
89
|
+
end
|
90
|
+
|
91
|
+
test "next_timer returns the trigger time of the first timer to" +
|
92
|
+
"expire when there is more than one registered timer" do
|
93
|
+
|
94
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
95
|
+
late_timer = pool.register_timer 64, stub_everything
|
96
|
+
early_timer = pool.register_timer 24, stub_everything
|
97
|
+
assert_equal early_timer, pool.next_timer
|
98
|
+
end
|
99
|
+
|
100
|
+
test "next_trigger_time returns nil when next_timer is nil" do
|
101
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
102
|
+
pool.expects(:next_timer).returns(nil)
|
103
|
+
assert_nil pool.next_trigger_time
|
104
|
+
end
|
105
|
+
|
106
|
+
test "next_trigger_time returns trigger time of next timer when " +
|
107
|
+
"next timer is not nil" do
|
108
|
+
|
109
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
110
|
+
the_timer = SystemTimer::ThreadTimer.new 24, stub_everything
|
111
|
+
pool.expects(:next_timer).returns(the_timer)
|
112
|
+
assert_equal 24, pool.next_trigger_time
|
113
|
+
end
|
114
|
+
|
115
|
+
test "next_trigger_interval_in_seconds returns nil when next_timer is nil" do
|
116
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
117
|
+
pool.expects(:next_timer).returns(nil)
|
118
|
+
assert_nil pool.next_trigger_interval_in_seconds
|
119
|
+
end
|
120
|
+
|
121
|
+
test "next_trigger_interval_in_seconds returns the interval between now and " +
|
122
|
+
"next_timer timer time when next timer is in the future" do
|
123
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
124
|
+
now = Time.now
|
125
|
+
Time.stubs(:now).returns(now)
|
126
|
+
next_timer = SystemTimer::ThreadTimer.new((now.to_i + 7), stub_everything)
|
127
|
+
pool.expects(:next_timer).returns(next_timer)
|
128
|
+
assert_equal 7, pool.next_trigger_interval_in_seconds
|
129
|
+
end
|
130
|
+
|
131
|
+
test "next_trigger_interval_in_seconds returns 0 when next timer is now" do
|
132
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
133
|
+
now = Time.now
|
134
|
+
Time.stubs(:now).returns(now)
|
135
|
+
next_timer = SystemTimer::ThreadTimer.new now.to_i, stub_everything
|
136
|
+
pool.expects(:next_timer).returns(next_timer)
|
137
|
+
assert_equal 0, pool.next_trigger_interval_in_seconds
|
138
|
+
end
|
139
|
+
|
140
|
+
test "next_trigger_interval_in_seconds returns 0 when next timer is in the past" do
|
141
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
142
|
+
now = Time.now
|
143
|
+
Time.stubs(:now).returns(now)
|
144
|
+
next_timer = SystemTimer::ThreadTimer.new((now.to_i - 3), stub_everything)
|
145
|
+
pool.expects(:next_timer).returns(next_timer)
|
146
|
+
assert_equal 0, pool.next_trigger_interval_in_seconds
|
147
|
+
end
|
148
|
+
|
149
|
+
test "next_expired_timer returns the timer that was trigerred" +
|
150
|
+
"when a timer has expired" do
|
151
|
+
|
152
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
153
|
+
the_timer = pool.register_timer 24, :a_thread
|
154
|
+
assert_equal the_timer, pool.next_expired_timer(24)
|
155
|
+
end
|
156
|
+
|
157
|
+
test "next_expired_timer returns nil when no timer has expired yet" do
|
158
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
159
|
+
pool.register_timer 24, :a_thread
|
160
|
+
assert_nil pool.next_expired_timer(23)
|
161
|
+
end
|
162
|
+
|
163
|
+
test "next_expired_timer returns the timer that first expired " +
|
164
|
+
"when there is more than one expired timer" do
|
165
|
+
|
166
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
167
|
+
last_to_expire = pool.register_timer 64, :a_thread
|
168
|
+
first_to_expire = pool.register_timer 24, :a_thread
|
169
|
+
assert_equal first_to_expire, pool.next_expired_timer(100)
|
170
|
+
end
|
171
|
+
|
172
|
+
test "trigger_next_expired_timer_at does not raise when there is no registered timer" do
|
173
|
+
SystemTimer::ConcurrentTimerPool.new.trigger_next_expired_timer_at 1234
|
174
|
+
end
|
175
|
+
|
176
|
+
test "trigger_next_expired_timer_at raises a TimeoutError in the context of " +
|
177
|
+
"its thread when there is a registered timer that has expired" do
|
178
|
+
|
179
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
180
|
+
the_thread = mock('thread')
|
181
|
+
|
182
|
+
Timeout::Error.expects(:new).with("time's up!").returns(:the_exception)
|
183
|
+
the_thread.expects(:raise).with(:the_exception)
|
184
|
+
pool.register_timer 24, the_thread
|
185
|
+
pool.trigger_next_expired_timer_at 24
|
186
|
+
end
|
187
|
+
|
188
|
+
test "trigger_next_expired_timer_at does not raise when registered timer has not expired" do
|
189
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
190
|
+
pool.register_timer 24, stub_everything
|
191
|
+
pool.trigger_next_expired_timer_at(10)
|
192
|
+
end
|
193
|
+
|
194
|
+
test "trigger_next_expired_timer_at triggers the first registered timer that expired" do
|
195
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
196
|
+
first_to_expire = pool.register_timer 24, stub_everything
|
197
|
+
second_to_expire = pool.register_timer 64, stub_everything
|
198
|
+
pool.trigger_next_expired_timer_at(100)
|
199
|
+
assert_equal [second_to_expire], pool.registered_timers
|
200
|
+
end
|
201
|
+
|
202
|
+
test "trigger_next_expired_timer_at triggers the first registered timer that " +
|
203
|
+
"expired whatever the timer insertion order is" do
|
204
|
+
|
205
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
206
|
+
second_to_expire = pool.register_timer 64, stub_everything
|
207
|
+
first_to_expire = pool.register_timer 24, stub_everything
|
208
|
+
pool.trigger_next_expired_timer_at(100)
|
209
|
+
assert_equal [second_to_expire], pool.registered_timers
|
210
|
+
end
|
211
|
+
|
212
|
+
test "trigger_next_expired_timer_at remove the expired timer from the pool" do
|
213
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
214
|
+
pool.register_timer 24, stub_everything
|
215
|
+
pool.trigger_next_expired_timer_at 24
|
216
|
+
end
|
217
|
+
|
218
|
+
test "trigger_next_expired_timer_at logs timeout a registered timer has expired" +
|
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
|
+
|
224
|
+
pool.expects(:log_timeout_received).with(the_timer)
|
225
|
+
pool.trigger_next_expired_timer_at 24
|
226
|
+
end
|
227
|
+
|
228
|
+
test "trigger_next_expired_timer_at does not logs timeoout when SystemTimer " +
|
229
|
+
"debug mode is disabled " do
|
230
|
+
|
231
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
232
|
+
the_timer = pool.register_timer 24, stub_everything
|
233
|
+
SystemTimer.stubs(:debug_enabled?).returns(false)
|
234
|
+
|
235
|
+
pool.expects(:log_timeout_received).never
|
236
|
+
pool.trigger_next_expired_timer_at 24
|
237
|
+
end
|
238
|
+
|
239
|
+
test "trigger_next_expired_timer_at does not logs timeout no registered timer " +
|
240
|
+
"has expired and SystemTimer debug mode is enabled " do
|
241
|
+
|
242
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
243
|
+
the_timer = pool.register_timer 24, stub_everything
|
244
|
+
SystemTimer.stubs(:debug_enabled?).returns(true)
|
245
|
+
|
246
|
+
pool.expects(:log_timeout_received).never
|
247
|
+
pool.trigger_next_expired_timer_at 23
|
248
|
+
end
|
249
|
+
|
250
|
+
test "trigger_next_expired_timer is a shorcut method calling " +
|
251
|
+
"trigger_next_expired_timer_at with current epoch time" do
|
252
|
+
|
253
|
+
now = Time.now
|
254
|
+
pool = SystemTimer::ConcurrentTimerPool.new
|
255
|
+
Time.stubs(:now).returns(now)
|
256
|
+
|
257
|
+
pool.expects(:trigger_next_expired_timer_at).with(now.to_i)
|
258
|
+
pool.trigger_next_expired_timer
|
259
|
+
end
|
260
|
+
|
261
|
+
test "log_timeout_received does not raise" do
|
262
|
+
original_stdout = $stdout
|
263
|
+
begin
|
264
|
+
stdout = StringIO.new
|
265
|
+
$stdout = stdout
|
266
|
+
|
267
|
+
SystemTimer::ConcurrentTimerPool.new.log_timeout_received(SystemTimer::ThreadTimer.new(:a_time, :a_thread))
|
268
|
+
assert_match %r{==== Triger Timer ====}, stdout.string
|
269
|
+
ensure
|
270
|
+
$stdout = original_stdout
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
unit_tests do
|
4
|
+
|
5
|
+
test "trigger_time returns the time given in the constructor" do
|
6
|
+
timer = SystemTimer::ThreadTimer.new(:a_tigger_time, nil)
|
7
|
+
assert_equal :a_tigger_time, timer.trigger_time
|
8
|
+
end
|
9
|
+
|
10
|
+
test "thread returns the thread given in the constructor" do
|
11
|
+
timer = SystemTimer::ThreadTimer.new(nil, :a_thread)
|
12
|
+
assert_equal :a_thread, timer.thread
|
13
|
+
end
|
14
|
+
|
15
|
+
test "to_s retruns a human friendly description of the timer" do
|
16
|
+
assert_match /<ThreadTimer :time => 24, :thread => #<Thread(.*)>>/,
|
17
|
+
SystemTimer::ThreadTimer.new(24, Thread.current).to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
functional_tests do
|
4
|
+
|
5
|
+
DEFAULT_ERROR_MARGIN = 2
|
6
|
+
|
7
|
+
test "original_ruby_sigalrm_handler is nil after reset" do
|
8
|
+
SystemTimer.send(:install_ruby_sigalrm_handler)
|
9
|
+
SystemTimer.send(:reset_original_ruby_sigalrm_handler)
|
10
|
+
assert_nil SystemTimer.send(:original_ruby_sigalrm_handler)
|
11
|
+
end
|
12
|
+
|
13
|
+
test "original_ruby_sigalrm_handler is set to existing handler after " +
|
14
|
+
"install_ruby_sigalrm_handler when save_previous_handler is true" do
|
15
|
+
SystemTimer.expects(:trap).with('SIGALRM').returns(:an_existing_handler)
|
16
|
+
SystemTimer.send(:install_ruby_sigalrm_handler)
|
17
|
+
assert_equal :an_existing_handler, SystemTimer.send(:original_ruby_sigalrm_handler)
|
18
|
+
end
|
19
|
+
|
20
|
+
test "restore_original_ruby_sigalrm_handler traps sigalrm using original_ruby_sigalrm_handler" do
|
21
|
+
SystemTimer.stubs(:original_ruby_sigalrm_handler).returns(:the_original_handler)
|
22
|
+
SystemTimer.expects(:trap).with('SIGALRM', :the_original_handler)
|
23
|
+
SystemTimer.send :restore_original_ruby_sigalrm_handler
|
24
|
+
end
|
25
|
+
|
26
|
+
test "restore_original_ruby_sigalrm_handler resets original_ruby_sigalrm_handler" do
|
27
|
+
SystemTimer.stubs(:trap)
|
28
|
+
SystemTimer.expects(:reset_original_ruby_sigalrm_handler)
|
29
|
+
SystemTimer.send :restore_original_ruby_sigalrm_handler
|
30
|
+
end
|
31
|
+
|
32
|
+
test "restore_original_ruby_sigalrm_handler reset SIGALRM handler to default when original_ruby_sigalrm_handler is nil" do
|
33
|
+
SystemTimer.stubs(:original_ruby_sigalrm_handler)
|
34
|
+
SystemTimer.expects(:trap).with('SIGALRM', 'DEFAULT')
|
35
|
+
SystemTimer.stubs(:reset_original_ruby_sigalrm_handler)
|
36
|
+
SystemTimer.send :restore_original_ruby_sigalrm_handler
|
37
|
+
end
|
38
|
+
|
39
|
+
test "restore_original_ruby_sigalrm_handler resets original_ruby_sigalrm_handler when trap raises" do
|
40
|
+
SystemTimer.stubs(:trap).returns(:the_original_handler)
|
41
|
+
SystemTimer.send(:install_ruby_sigalrm_handler)
|
42
|
+
SystemTimer.expects(:trap).raises("next time maybe...")
|
43
|
+
SystemTimer.expects(:reset_original_ruby_sigalrm_handler)
|
44
|
+
|
45
|
+
SystemTimer.send(:restore_original_ruby_sigalrm_handler) rescue nil
|
46
|
+
end
|
47
|
+
|
48
|
+
test "timeout_after raises TimeoutError if block takes too long" do
|
49
|
+
assert_raises(Timeout::Error) do
|
50
|
+
SystemTimer.timeout_after(1) {sleep 5}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
test "timeout_after does not raises Timeout Error if block completes in time" do
|
55
|
+
SystemTimer.timeout_after(5) {sleep 1}
|
56
|
+
end
|
57
|
+
|
58
|
+
test "timeout_after returns the value returned by the black" do
|
59
|
+
assert_equal :block_value, SystemTimer.timeout_after(1) {:block_value}
|
60
|
+
end
|
61
|
+
|
62
|
+
test "timeout_after raises TimeoutError in thread that called timeout_after" do
|
63
|
+
raised_thread = nil
|
64
|
+
other_thread = Thread.new do
|
65
|
+
begin
|
66
|
+
SystemTimer.timeout_after(1) {sleep 5}
|
67
|
+
flunk "Should have timed out"
|
68
|
+
rescue Timeout::Error
|
69
|
+
raised_thread = Thread.current
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
other_thread.join
|
74
|
+
assert_equal other_thread, raised_thread
|
75
|
+
end
|
76
|
+
|
77
|
+
test "cancelling a timer that was installed restores previous ruby handler for SIG_ALRM" do
|
78
|
+
begin
|
79
|
+
fake_original_ruby_handler = proc {}
|
80
|
+
initial_ruby_handler = trap "SIGALRM", fake_original_ruby_handler
|
81
|
+
SystemTimer.install_first_timer_and_save_original_configuration 3
|
82
|
+
SystemTimer.restore_original_configuration
|
83
|
+
assert_equal fake_original_ruby_handler, trap("SIGALRM", "IGNORE")
|
84
|
+
ensure # avoid interfering with test infrastructure
|
85
|
+
trap("SIGALRM", initial_ruby_handler) if initial_ruby_handler
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
test "debug_enabled returns true after enabling debug" do
|
90
|
+
begin
|
91
|
+
SystemTimer.disable_debug
|
92
|
+
SystemTimer.enable_debug
|
93
|
+
assert_equal true, SystemTimer.debug_enabled?
|
94
|
+
ensure
|
95
|
+
SystemTimer.disable_debug
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
test "debug_enabled returns false after disable debug" do
|
100
|
+
begin
|
101
|
+
SystemTimer.enable_debug
|
102
|
+
SystemTimer.disable_debug
|
103
|
+
assert_equal false, SystemTimer.debug_enabled?
|
104
|
+
ensure
|
105
|
+
SystemTimer.disable_debug
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
test "timeout offers an API fully compatible with timeout.rb" do
|
110
|
+
assert_raises(Timeout::Error) do
|
111
|
+
SystemTimer.timeout(1) {sleep 5}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Disable this test as it is failing on Ubuntu. The problem is that
|
116
|
+
# for some reason M.R.I 1.8 is trapping the Ruby signals at the
|
117
|
+
# time the system SIGALRM is delivered, hence we do not timeout as
|
118
|
+
# quickly as we should. Needs further investigation. At least the
|
119
|
+
# SIGALRM ensures that the system will schedule M.R.I. native thread.
|
120
|
+
#
|
121
|
+
#
|
122
|
+
# test "while exact timeouts cannot be guaranted the timeout should not exceed the provided timeout by 2 seconds" do
|
123
|
+
# start = Time.now
|
124
|
+
# begin
|
125
|
+
# SystemTimer.timeout_after(2) do
|
126
|
+
# open "http://www.invalid.domain.comz"
|
127
|
+
# end
|
128
|
+
# raise "should never get there"
|
129
|
+
# rescue SocketError => e
|
130
|
+
# rescue Timeout::Error => e
|
131
|
+
# end
|
132
|
+
# elapsed = Time.now - start
|
133
|
+
# assert elapsed < 4, "Got #{elapsed} s, expected 2, at most 4"
|
134
|
+
# end
|
135
|
+
|
136
|
+
|
137
|
+
test "timeout are enforced on system calls" do
|
138
|
+
assert_timeout_within(3) do
|
139
|
+
SystemTimer.timeout(3) do
|
140
|
+
sleep 30
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
test "timeout work when spawning a different thread" do
|
146
|
+
assert_timeout_within(3) do
|
147
|
+
thread = Thread.new do
|
148
|
+
SystemTimer.timeout(3) do
|
149
|
+
sleep 60
|
150
|
+
end
|
151
|
+
end
|
152
|
+
thread.join
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
test "can set multiple serial timers" do
|
157
|
+
10.times do
|
158
|
+
assert_timeout_within(3) do
|
159
|
+
SystemTimer.timeout(3) do
|
160
|
+
sleep 60
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
test "timeout work when setting concurrent timers, the first one " +
|
167
|
+
"expiring before the second one" do
|
168
|
+
|
169
|
+
first_thread = Thread.new do
|
170
|
+
assert_timeout_within(3) do
|
171
|
+
SystemTimer.timeout(3) do
|
172
|
+
sleep 60
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
second_thread = Thread.new do
|
177
|
+
assert_timeout_within(5) do
|
178
|
+
SystemTimer.timeout(5) do
|
179
|
+
sleep 60
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
first_thread.join
|
184
|
+
second_thread.join
|
185
|
+
end
|
186
|
+
|
187
|
+
test "timeout work when setting concurrent timers, the second one " +
|
188
|
+
"expiring before the first one" do
|
189
|
+
|
190
|
+
first_thread = Thread.new do
|
191
|
+
assert_timeout_within(10) do
|
192
|
+
SystemTimer.timeout(10) do
|
193
|
+
sleep 60
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
second_thread = Thread.new do
|
198
|
+
assert_timeout_within(3) do
|
199
|
+
SystemTimer.timeout(3) do
|
200
|
+
sleep 60
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
first_thread.join
|
205
|
+
second_thread.join
|
206
|
+
end
|
207
|
+
|
208
|
+
test "timeout work when setting concurrent timers with the exact" +
|
209
|
+
"same timeout" do
|
210
|
+
|
211
|
+
first_thread = Thread.new do
|
212
|
+
assert_timeout_within(2) do
|
213
|
+
SystemTimer.timeout(2) do
|
214
|
+
sleep 60
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
second_thread = Thread.new do
|
219
|
+
assert_timeout_within(2) do
|
220
|
+
SystemTimer.timeout(2) do
|
221
|
+
sleep 60
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
first_thread.join
|
226
|
+
second_thread.join
|
227
|
+
end
|
228
|
+
|
229
|
+
test "timeout works with random concurrent timers dynamics" do
|
230
|
+
all_threads = []
|
231
|
+
|
232
|
+
10.times do
|
233
|
+
a_timeout = [1, (rand(10)).to_i].max
|
234
|
+
all_threads << Thread.new do
|
235
|
+
assert_timeout_within(a_timeout, 10) do
|
236
|
+
SystemTimer.timeout(a_timeout) do
|
237
|
+
sleep 180
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
all_threads.each {|t| t.join}
|
244
|
+
end
|
245
|
+
|
246
|
+
def assert_timeout_within(expected_timeout_in_seconds,
|
247
|
+
error_margin = DEFAULT_ERROR_MARGIN,
|
248
|
+
&block)
|
249
|
+
start = Time.now
|
250
|
+
yield
|
251
|
+
flunk "Did not timeout as expected!"
|
252
|
+
rescue Timeout::Error
|
253
|
+
elapsed = Time.now - start
|
254
|
+
assert elapsed >= expected_timeout_in_seconds,
|
255
|
+
"Timed out too early, expected #{expected_timeout_in_seconds}, got #{elapsed} s"
|
256
|
+
assert elapsed < (expected_timeout_in_seconds + error_margin),
|
257
|
+
"Timed out after #{elapsed} seconds, expected #{expected_timeout_in_seconds}"
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|