209 lines
3.7 KiB
Ruby
209 lines
3.7 KiB
Ruby
require 'timeout' # for Timeout::Error
|
|
|
|
# = Timer
|
|
#
|
|
# Provides a strightforward means for controlling time critical execution.
|
|
# Can be used as a "stop watch" timer or as a "time bomb" timer:
|
|
#
|
|
# t = Timer.new(10) { raise Timeout::Error, "timeout!" }
|
|
# t.start
|
|
# : # done within 10sec timeout
|
|
# t.stop
|
|
# t.start
|
|
# :
|
|
# if condition then
|
|
# t.reset #--> restart timer
|
|
# end
|
|
#
|
|
# A class method is also provided for easily timing the exectuion of a block.
|
|
#
|
|
# Timer.time do |timer|
|
|
# timer.total_time.round #=> 0
|
|
#
|
|
# sleep 1
|
|
# timer.total_time.round #=> 1
|
|
#
|
|
# timer.stop
|
|
# timer.total_time.round #=> 1
|
|
#
|
|
# sleep 1
|
|
# timer.total_time.round #=> 1
|
|
#
|
|
# timer.start
|
|
# timer.total_time.round #=> 1
|
|
#
|
|
# sleep 1
|
|
# timer.total_time.round #=> 2
|
|
# end
|
|
#
|
|
# Thanks to Paul Brannan for TimeLimit and Minero Aoki for Timer.
|
|
# These two libraries served as models for building this class.
|
|
|
|
class Timer
|
|
|
|
#
|
|
def initialize(time_limit=nil, &block)
|
|
# standard timer
|
|
@start_time = nil
|
|
@end_time = nil
|
|
@total_time = 0
|
|
@runnning = nil
|
|
# for using time limit
|
|
@time_limit = time_limit
|
|
@on_timeout = block
|
|
@current_thread = nil
|
|
@timer_thread = nil
|
|
end
|
|
|
|
#
|
|
attr_accessor :time_limit
|
|
|
|
#
|
|
def start_time
|
|
@start_time
|
|
end
|
|
|
|
#
|
|
def end_time
|
|
@end_time
|
|
end
|
|
|
|
#
|
|
def on_timeout( &block )
|
|
if block then
|
|
@on_timeout = block
|
|
true
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
# Start the timer.
|
|
def start
|
|
@running = true
|
|
@start_time = Time.now
|
|
|
|
limit if @time_limit
|
|
|
|
self
|
|
|
|
##if block_given? then
|
|
## begin
|
|
## yield( self )
|
|
## ensure
|
|
## stop
|
|
## end
|
|
##else
|
|
## @time_limit
|
|
##end
|
|
end
|
|
|
|
# Establish a time limit on execution.
|
|
def limit( time_limit=nil )
|
|
if @time_limit || time_limit
|
|
@current_thread = Thread.current
|
|
@timer_thread = Thread.fork {
|
|
sleep @time_limit
|
|
if @on_timeout then
|
|
@on_timeout.call @time_limit
|
|
else
|
|
@current_thread.raise Timeout::Error, "#{@time_limit} seconds past"
|
|
end
|
|
}
|
|
end
|
|
end
|
|
|
|
# Kill time limit thread, if any.
|
|
def defuse
|
|
if @timer_thread
|
|
#Thread.kill @timer_thread
|
|
@timer_thread.kill
|
|
@timer_thread = nil
|
|
end
|
|
end
|
|
|
|
# Stops timer and returns total time.
|
|
# If timer was not running returns false.
|
|
def stop
|
|
if @running
|
|
defuse
|
|
# record running time
|
|
@end_time = Time.now
|
|
@running = false
|
|
@total_time += (@end_time - @start_time)
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
# Stops and resets the timer. If the timer was running
|
|
# returns the total time. If not returns 0.
|
|
def reset
|
|
if running?
|
|
r = stop
|
|
else
|
|
r = 0
|
|
end
|
|
@total_time = 0
|
|
return r
|
|
end
|
|
|
|
# Resets the time limit. Same as:
|
|
#
|
|
# t.stop
|
|
# t.start
|
|
#
|
|
def reset_limit
|
|
#stop
|
|
#start
|
|
defuse
|
|
limit
|
|
end
|
|
|
|
# Queries whether the timer is still running.
|
|
def running?
|
|
return @running
|
|
end
|
|
|
|
# Queries whether the timer is still not running.
|
|
def stopped?
|
|
return !@running
|
|
end
|
|
|
|
# Queries total recorded time of timer.
|
|
def total_time
|
|
if running? then
|
|
return @total_time + (Time.now - @start_time)
|
|
else
|
|
return @total_time
|
|
end
|
|
end
|
|
|
|
# Takes a block and returns the total time it took to execute.
|
|
def self.time
|
|
yield( timer = Timer.new.start )
|
|
return timer.total_time
|
|
end
|
|
|
|
# Timer::Dummy - Dummy Timer (i.e. no real time limit)
|
|
#--
|
|
# TODO: Timer::Dummy needs some work.
|
|
#++
|
|
class Dummy < self
|
|
def start
|
|
if block_given? then
|
|
yield
|
|
else
|
|
time_limit()
|
|
end
|
|
end
|
|
|
|
def stop
|
|
false
|
|
end
|
|
end #class Dummy
|
|
|
|
end #class Timer
|
|
|
|
# Copyright (c) 2004 Rubyworks
|