Timer implementation

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

Robert Larsen wrote:

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

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

In article <4aa8deb2$0$25181$d40e179e@nntp02.dk.telia.net>,
  Robert Larsen <robert@komogvind.dk> writes:

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

--
Tanaka Akira

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:

- spox

···

On Thursday 10 September 2009 04:15:08 am Robert Larsen wrote:

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