Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Support for fractional part when specifying timeout in seconds

  • Loading branch information...
commit da632ea5b676dfb964bdd7994114390aaf1b99a7 1 parent 662fba9
@ph7 ph7 authored
View
42 ext/system_timer/system_timer_native.c
@@ -12,6 +12,8 @@
#define DISPLAY_ERRNO 1
#define DO_NOT_DISPLAY_ERRNO 0
+#define MICRO_SECONDS 1000000.0
+#define MINIMUM_TIMER_INTERVAL_IN_SECONDS 0.2
VALUE rb_cSystemTimer;
sigset_t original_mask;
@@ -26,7 +28,7 @@ static void restore_original_ruby_sigalrm_handler(VALUE);
static void restore_original_sigalrm_mask_when_blocked();
static void restore_original_timer_interval();
static void set_itimerval_with_minimum_1s_interval(struct itimerval *, VALUE);
-static void set_itimerval(struct itimerval *, int);
+static void set_itimerval(struct itimerval *, double);
static void restore_sigalrm_mask(sigset_t *previous_mask);
static void log_debug(char*, ...);
static void log_error(char*, int);
@@ -37,7 +39,7 @@ static VALUE install_first_timer_and_save_original_configuration(VALUE self, VAL
struct itimerval timer_interval;
if (debug_enabled) {
- log_debug("[install_first_timer] %d s\n", NUM2INT(seconds));
+ log_debug("[install_first_timer] %.2lfs\n", NUM2DBL(seconds));
}
/*
@@ -70,7 +72,7 @@ static VALUE install_first_timer_and_save_original_configuration(VALUE self, VAL
/*
* Save original real time interval timer and aet new real time interval timer.
*/
- set_itimerval(&original_timer_interval, 0);
+ set_itimerval(&original_timer_interval, 0.0);
set_itimerval_with_minimum_1s_interval(&timer_interval, seconds);
if (0 != setitimer(ITIMER_REAL, &timer_interval, &original_timer_interval)) {
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
restore_original_sigalrm_mask_when_blocked();
return Qnil;
}
- log_debug("[install_first_timer] Successfully installed timer (%ds)\n", timer_interval.it_value.tv_sec);
+ if (debug_enabled) {
+ log_debug("[install_first_timer] Successfully installed timer (%ds)\n",
+ timer_interval.it_value.tv_sec);
+ }
/*
* Unblock SIG_ALRM
@@ -100,7 +105,7 @@ static VALUE install_next_timer(VALUE self, VALUE seconds)
sigset_t previous_sigalarm_mask;
if (debug_enabled) {
- log_debug("[install_next_timer] %ds\n", NUM2INT(seconds));
+ log_debug("[install_next_timer] %.2lfs\n", NUM2DBL(seconds));
}
/*
@@ -122,7 +127,10 @@ static VALUE install_next_timer(VALUE self, VALUE seconds)
restore_sigalrm_mask(&previous_sigalarm_mask);
return Qnil;
}
- log_debug("[install_next_timer] Successfully installed timer (%ds)\n", timer_interval.it_value.tv_sec);
+ if (debug_enabled) {
+ log_debug("[install_next_timer] Successfully installed timer (%ds + %dus)\n",
+ timer_interval.it_value.tv_sec, timer_interval.it_value.tv_usec);
+ }
/*
* Unblock SIG_ALRM
@@ -264,20 +272,28 @@ static void init_sigalarm_mask()
static void set_itimerval_with_minimum_1s_interval(struct itimerval *value,
VALUE seconds) {
- int sanitized_second_interval;
+ double sanitized_second_interval;
- sanitized_second_interval = NUM2INT(seconds);
- if (sanitized_second_interval <= 0 ) {
- sanitized_second_interval = 1;
+ sanitized_second_interval = NUM2DBL(seconds) + MINIMUM_TIMER_INTERVAL_IN_SECONDS;
+ if (sanitized_second_interval < MINIMUM_TIMER_INTERVAL_IN_SECONDS ) {
+ sanitized_second_interval = MINIMUM_TIMER_INTERVAL_IN_SECONDS;
}
set_itimerval(value, sanitized_second_interval);
}
-static void set_itimerval(struct itimerval *value, int seconds) {
+static void set_itimerval(struct itimerval *value, double seconds) {
+ if (debug_enabled) {
+ log_debug("[set_itimerval] %.3lfs\n", seconds);
+ }
value->it_interval.tv_usec = 0;
value->it_interval.tv_sec = 0;
- value->it_value.tv_usec = 0;
- value->it_value.tv_sec = seconds; // (long int)
+ value->it_value.tv_sec = (long int) (seconds);
+ value->it_value.tv_usec = (long int) ((seconds - value->it_value.tv_sec) \
+ * MICRO_SECONDS);
+ if (debug_enabled) {
+ log_debug("[set_itimerval] Set to %ds + %dus\n", value->it_value.tv_sec,
+ value->it_value.tv_usec);
+ }
return;
}
View
4 lib/system_timer.rb
@@ -46,7 +46,7 @@ def timeout_after(seconds, exception_class = nil)
@monitor.synchronize do
new_timer = timer_pool.add_timer seconds, exception_class
timer_interval = timer_pool.next_trigger_interval_in_seconds
- debug "==== Install Timer ==== at #{Time.now.to_i}, next interval: #{timer_interval}"
+ debug "==== Install Timer ==== at #{Time.now.to_f}, next interval: #{timer_interval}"
if timer_pool.first_timer?
install_first_timer_and_save_original_configuration timer_interval
else
@@ -56,7 +56,7 @@ def timeout_after(seconds, exception_class = nil)
return yield
ensure
@monitor.synchronize do
- debug "==== Cleanup Timer ==== at #{Time.now.to_i}, #{new_timer} "
+ debug "==== Cleanup Timer ==== at #{Time.now.to_f}, #{new_timer} "
timer_pool.cancel new_timer
timer_pool.log_registered_timers if debug_enabled?
next_interval = timer_pool.next_trigger_interval_in_seconds
View
14 lib/system_timer/concurrent_timer_pool.rb
@@ -15,7 +15,7 @@ def register_timer(trigger_time, thread, exception_class=nil)
end
def add_timer(interval_in_seconds, exception_class=nil)
- new_timer = register_timer(Time.now.to_i + interval_in_seconds, Thread.current, exception_class)
+ new_timer = register_timer(Time.now.to_f + interval_in_seconds, Thread.current, exception_class)
log_registered_timers if SystemTimer.debug_enabled?
new_timer
end
@@ -39,11 +39,15 @@ def next_trigger_time
def next_trigger_interval_in_seconds
timer = next_timer
- [0, (timer.trigger_time - Time.now.to_i)].max unless timer.nil?
+ [0, (timer.trigger_time - Time.now.to_f)].max unless timer.nil?
end
def next_expired_timer(now_in_seconds_since_epoch)
candidate_timer = next_timer
+ if SystemTimer.debug_enabled?
+ puts "Candidate timer at #{now_in_seconds_since_epoch} : " +
+ candidate_timer.inspect
+ end
return nil if candidate_timer.nil? ||
candidate_timer.trigger_time > now_in_seconds_since_epoch
candidate_timer
@@ -51,6 +55,7 @@ def next_expired_timer(now_in_seconds_since_epoch)
def trigger_next_expired_timer_at(now_in_seconds_since_epoch)
timer = next_expired_timer(now_in_seconds_since_epoch)
+ puts "Next expired timer : #{timer.inspect}" if SystemTimer.debug_enabled?
return if timer.nil?
cancel timer
@@ -59,7 +64,8 @@ def trigger_next_expired_timer_at(now_in_seconds_since_epoch)
end
def trigger_next_expired_timer
- trigger_next_expired_timer_at Time.now.to_i
+ puts "Trigger next expired timer" if SystemTimer.debug_enabled?
+ trigger_next_expired_timer_at Time.now.to_f
end
def log_timeout_received(thread_timer) #:nodoc:
@@ -74,7 +80,7 @@ def log_timeout_received(thread_timer) #:nodoc:
def log_registered_timers #:nodoc:
puts <<-EOS
- Registered Timers: #{registered_timers.collect do |t| t.to_s end}
+ Registered Timers: #{registered_timers.map {|t| t.to_s}.join("\n ")}
EOS
end
View
47 test/system_timer/concurrent_timer_pool_unit_test.rb
@@ -25,7 +25,7 @@
now = Time.now
Time.stubs(:now).returns(now)
- pool.expects(:register_timer).with(now.to_i + 15, :the_current_thread, nil)
+ pool.expects(:register_timer).with(now.to_f + 15, :the_current_thread, nil)
pool.add_timer 15
end
@@ -123,7 +123,7 @@
pool = SystemTimer::ConcurrentTimerPool.new
now = Time.now
Time.stubs(:now).returns(now)
- next_timer = SystemTimer::ThreadTimer.new((now.to_i + 7), stub_everything)
+ next_timer = SystemTimer::ThreadTimer.new((now.to_f + 7), stub_everything)
pool.expects(:next_timer).returns(next_timer)
assert_equal 7, pool.next_trigger_interval_in_seconds
end
@@ -132,7 +132,7 @@
pool = SystemTimer::ConcurrentTimerPool.new
now = Time.now
Time.stubs(:now).returns(now)
- next_timer = SystemTimer::ThreadTimer.new now.to_i, stub_everything
+ next_timer = SystemTimer::ThreadTimer.new now.to_f, stub_everything
pool.expects(:next_timer).returns(next_timer)
assert_equal 0, pool.next_trigger_interval_in_seconds
end
@@ -141,7 +141,7 @@
pool = SystemTimer::ConcurrentTimerPool.new
now = Time.now
Time.stubs(:now).returns(now)
- next_timer = SystemTimer::ThreadTimer.new((now.to_i - 3), stub_everything)
+ next_timer = SystemTimer::ThreadTimer.new((now.to_f - 3), stub_everything)
pool.expects(:next_timer).returns(next_timer)
assert_equal 0, pool.next_trigger_interval_in_seconds
end
@@ -217,12 +217,21 @@
test "trigger_next_expired_timer_at logs timeout a registered timer has expired" +
"and SystemTimer debug mode is enabled " do
- pool = SystemTimer::ConcurrentTimerPool.new
- the_timer = pool.register_timer 24, stub_everything
- SystemTimer.stubs(:debug_enabled?).returns(true)
- pool.expects(:log_timeout_received).with(the_timer)
- pool.trigger_next_expired_timer_at 24
+ original_stdout = $stdout
+ begin
+ stdout = StringIO.new
+ $stdout = stdout
+
+ pool = SystemTimer::ConcurrentTimerPool.new
+ the_timer = pool.register_timer 24, stub_everything
+ SystemTimer.stubs(:debug_enabled?).returns(true)
+
+ pool.expects(:log_timeout_received).with(the_timer)
+ pool.trigger_next_expired_timer_at 24
+ ensure
+ $stdout = original_stdout
+ end
end
test "trigger_next_expired_timer_at does not logs timeoout when SystemTimer " +
@@ -238,13 +247,21 @@
test "trigger_next_expired_timer_at does not logs timeout no registered timer " +
"has expired and SystemTimer debug mode is enabled " do
+
+ original_stdout = $stdout
+ begin
+ stdout = StringIO.new
+ $stdout = stdout
- pool = SystemTimer::ConcurrentTimerPool.new
- the_timer = pool.register_timer 24, stub_everything
- SystemTimer.stubs(:debug_enabled?).returns(true)
+ pool = SystemTimer::ConcurrentTimerPool.new
+ the_timer = pool.register_timer 24, stub_everything
+ SystemTimer.stubs(:debug_enabled?).returns(true)
- pool.expects(:log_timeout_received).never
- pool.trigger_next_expired_timer_at 23
+ pool.expects(:log_timeout_received).never
+ pool.trigger_next_expired_timer_at 23
+ ensure
+ $stdout = original_stdout
+ end
end
test "trigger_next_expired_timer is a shorcut method calling " +
@@ -254,7 +271,7 @@
pool = SystemTimer::ConcurrentTimerPool.new
Time.stubs(:now).returns(now)
- pool.expects(:trigger_next_expired_timer_at).with(now.to_i)
+ pool.expects(:trigger_next_expired_timer_at).with(now.to_f)
pool.trigger_next_expired_timer
end
View
39 test/system_timer_functional_test.rb
@@ -51,6 +51,13 @@
end
end
+ test "timeout_after timeout can be a fraction of a second" do
+ assert_raises(Timeout::Error) do
+ SystemTimer.timeout_after(0.2) {sleep 3}
+ end
+ end
+
+
test "timeout_after raises a custom timeout when block takes too long and a custom exception class is provided" do
ACustomException = Class.new(Exception)
assert_raises(ACustomException) do
@@ -161,18 +168,28 @@
end
test "can set multiple serial timers" do
- 10.times do
- assert_timeout_within(3) do
- SystemTimer.timeout(3) do
+ 10.times do |i|
+ print(i) & STDOUT.flush
+ assert_timeout_within(1) do
+ SystemTimer.timeout(1) do
+ sleep 60
+ end
+ end
+ end
+ end
+
+ test "can set multiple serial timers with fractional timeout" do
+ 10.times do |i|
+ print(i) & STDOUT.flush
+ assert_timeout_within(0.5) do
+ SystemTimer.timeout(0.5) do
sleep 60
end
end
end
end
- test "timeout work when setting concurrent timers, the first one " +
- "expiring before the second one" do
-
+ test "timeout work when setting concurrent timers, the first one expiring before the second one" do
first_thread = Thread.new do
assert_timeout_within(3) do
SystemTimer.timeout(3) do
@@ -191,9 +208,8 @@
second_thread.join
end
- test "timeout work when setting concurrent timers, the second one " +
- "expiring before the first one" do
-
+ test "timeout work when setting concurrent timers, the second one expiring before the first one" do
+
first_thread = Thread.new do
assert_timeout_within(10) do
SystemTimer.timeout(10) do
@@ -212,8 +228,7 @@
second_thread.join
end
- test "timeout work when setting concurrent timers with the exact" +
- "same timeout" do
+ test "timeout work when setting concurrent timers with the exact same timeout" do
first_thread = Thread.new do
assert_timeout_within(2) do
@@ -237,7 +252,7 @@
all_threads = []
10.times do
- a_timeout = [1, (rand(10)).to_i].max
+ a_timeout = [1, (rand(10)).to_f].max
all_threads << Thread.new do
assert_timeout_within(a_timeout, 10) do
SystemTimer.timeout(a_timeout) do
View
14 test/system_timer_unit_test.rb
@@ -31,8 +31,8 @@
now = Time.now
Time.stubs(:now).returns(now)
SystemTimer.stubs(:restore_original_configuration)
-
- SystemTimer.expects(:install_first_timer_and_save_original_configuration).with(24)
+ SystemTimer.expects(:install_first_timer_and_save_original_configuration) \
+ .with {|value| value.between?(23.99, 24.01) }
SystemTimer.timeout_after(24) {}
end
@@ -41,11 +41,12 @@
now = Time.now
Time.stubs(:now).returns(now)
- SystemTimer.timer_pool.register_timer now.to_i + 100, :a_thread
+ SystemTimer.timer_pool.register_timer now.to_f + 100, :a_thread
SystemTimer.stubs(:restore_original_configuration)
SystemTimer.stubs(:install_next_timer)
- SystemTimer.expects(:install_next_timer).with(24)
+ SystemTimer.expects(:install_next_timer) \
+ .with {|value| value.between?(23.99, 24.01) }
SystemTimer.timeout_after(24) {}
end
@@ -54,11 +55,12 @@
now = Time.now
Time.stubs(:now).returns(now)
- SystemTimer.timer_pool.register_timer now.to_i + 24, :a_thread
+ SystemTimer.timer_pool.register_timer now.to_f + 24, :a_thread
SystemTimer.stubs(:restore_original_configuration)
SystemTimer.stubs(:install_next_timer)
- SystemTimer.expects(:install_next_timer).with(24)
+ SystemTimer.expects(:install_next_timer) \
+ .with {|value| value.between?(23.99, 24.01) }
SystemTimer.timeout_after(100) {}
end
Please sign in to comment.
Something went wrong with that request. Please try again.