Timer implementation

R

Robert Larsen

Hi all

I am porting a timer class that I wrote in Java many years ago but I cannot see how to implement it in Ruby.

What I want is a single thread, that sleeps forever or until the next timer block should be called or until a new timer block i added.
The thread should be woken when a new timer block is added because it might be the next one to be called.

I have implemented it like this:

#!/usr/bin/ruby

require 'thread'
require 'bsearch'

class Timer
class TimerObject
attr_reader :invoke_time, :block

def initialize(invoke_time, block)
@invoke_time, @block = invoke_time, block
end
end

def initialize
@timer_list = []
@mutex = Mutex.new
@condition = ConditionVariable.new
@running = false
start
end

def stop
running = false
@mutex.synchronize { @condition.signal }
end

def start
@running = true
Thread.new do
@mutex.synchronize do
an_hour = 60 * 60 * 1000
while running
now_in_milliseconds = (Time.now.to_f * 1000).to_i
milliseconds_to_sleep = @timer_list.empty? ? an_hour : @timer_list.first.invoke_time - now_in_milliseconds
@condition.wait(@mutex, now_in_milliseconds) while @timer_list.empty?

now_in_milliseconds = (Time.now.to_f * 1000).to_i
while @time_list.empty? == false and @time_list.first.invoke_time < now_in_milliseconds
@time_list.delete_at(0).block.call
end
end
end
end
end

def postpone (timeout, &block)
now = (Time.now.to_f * 1000).to_i
time = now + timeout

timerobject = TimerObject.new time, block

@mutex.synchronize do
idx = @timer_list.bsearch_lower_boundary { |x| x.invoke_time <=> time }
@timer_list.insert(idx, timerobject) unless idx.nil?
@timer_list << timerobject if idx.nil?
@condition.signal
end
end
end


BUT...the 'start' method does not work since 'ConditionVariable' does not have a 'wait' method that takes a maximum wait time in milliseconds like we have in Java.

Any ideas as to how this can be done ?
Or is there a better way ?

I really don't want a thread per timer.

Best regards,
Robert
 
R

Robert Larsen

Robert said:
@condition.wait(@mutex, now_in_milliseconds) while @timer_list.empty?

Whoops, the above should of course be:
@condition.wait(@mutex, milliseconds_to_sleep)
 
T

Tanaka Akira

Robert Larsen said:
BUT...the 'start' method does not work since 'ConditionVariable' does not have a 'wait' method that takes a maximum wait time in milliseconds like we have in Java.

In ruby 1.9, timeout argument can be implemented as follows.

Index: lib/thread.rb
===================================================================
--- lib/thread.rb (revision 24783)
+++ lib/thread.rb (working copy)
@@ -59,13 +59,16 @@
#
# Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
#
- def wait(mutex)
+ # If +timeout+ is given, this method returns after +timeout+ seconds passed,
+ # even if no other thread doesn't signal.
+ #
+ def wait(mutex, timeout=nil)
begin
# TODO: mutex should not be used
@waiters_mutex.synchronize do
@waiters.push(Thread.current)
end
- mutex.sleep
+ mutex.sleep timeout
end
end
 
S

spox

Hi all

I am porting a timer class that I wrote in Java many years ago but I cannot
see how to implement it in Ruby.

What I want is a single thread, that sleeps forever or until the next timer
block should be called or until a new timer block i added. The thread
should be woken when a new timer block is added because it might be the
next one to be called.

I have implemented it like this:

#!/usr/bin/ruby

require 'thread'
require 'bsearch'

class Timer
class TimerObject
attr_reader :invoke_time, :block

def initialize(invoke_time, block)
@invoke_time, @block = invoke_time, block
end
end

def initialize
@timer_list = []
@mutex = Mutex.new
@condition = ConditionVariable.new
@running = false
start
end

def stop
running = false
@mutex.synchronize { @condition.signal }
end

def start
@running = true
Thread.new do
@mutex.synchronize do
an_hour = 60 * 60 * 1000
while running
now_in_milliseconds = (Time.now.to_f * 1000).to_i
milliseconds_to_sleep = @timer_list.empty? ? an_hour :
@timer_list.first.invoke_time - now_in_milliseconds @condition.wait(@mutex,
now_in_milliseconds) while @timer_list.empty?

now_in_milliseconds = (Time.now.to_f * 1000).to_i
while @time_list.empty? == false and
@time_list.first.invoke_time < now_in_milliseconds
@time_list.delete_at(0).block.call
end
end
end
end
end

def postpone (timeout, &block)
now = (Time.now.to_f * 1000).to_i
time = now + timeout

timerobject = TimerObject.new time, block

@mutex.synchronize do
idx = @timer_list.bsearch_lower_boundary { |x| x.invoke_time
<=> time } @timer_list.insert(idx, timerobject) unless idx.nil?
@timer_list << timerobject if idx.nil?
@condition.signal
end
end
end


BUT...the 'start' method does not work since 'ConditionVariable' does not
have a 'wait' method that takes a maximum wait time in milliseconds like we
have in Java.

Any ideas as to how this can be done ?
Or is there a better way ?

I really don't want a thread per timer.

Best regards,
Robert

Hi Robert. I have a simple timer that I built awhile ago. It uses a single
thread for timed events, and a thread pool for executing blocks. If you are
looking to build your own, the source can probably help give you some ideas.

Project:
http://rubyforge.org/projects/actiontimer

Source:
http://github.com/spox/actiontimer

- spox
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top