diff options
Diffstat (limited to 'lib/mauve/mauve_thread.rb')
-rw-r--r-- | lib/mauve/mauve_thread.rb | 189 |
1 files changed, 135 insertions, 54 deletions
diff --git a/lib/mauve/mauve_thread.rb b/lib/mauve/mauve_thread.rb index 2191d58..42f41a2 100644 --- a/lib/mauve/mauve_thread.rb +++ b/lib/mauve/mauve_thread.rb @@ -3,18 +3,39 @@ require 'singleton' module Mauve + # + # This is a class to wrap our threads that have processing loops. + # + # The thread is kept in a wrapper to allow it to be frozen and thawed at + # convenient times. + # class MauveThread + # + # The sleep interval between runs of the main loop. Defaults to 5 seconds. + # attr_reader :poll_every + # + # Set the thread up + # def initialize @thread = nil end + # @return [Log4r::Logger] def logger @logger ||= Log4r::Logger.new(self.class.to_s) end + # Set the sleep interval between runs of the main loop. This can be + # anything greater than or equal to zero. If a number less than zero gets + # entered, it will be increased to zero. + # + # @param [Numeric] i The number of seconds to sleep + # @raise [ArgumentError] If +i+ is not numeric + # @return [Numeric] + # def poll_every=(i) raise ArgumentError.new("poll_every must be numeric") unless i.is_a?(Numeric) # @@ -28,60 +49,20 @@ module Mauve @poll_every = i end - def run_thread(interval = 5.0) - # - # Good to go. - # - @thread = Thread.current - self.state = :starting - - @poll_every ||= interval - # - # Make sure we get a number. - # - @poll_every = 5 unless @poll_every.is_a?(Numeric) - - rate_limit = 0.1 - - while self.state != :stopping do - - self.state = :started if self.state == :starting - - # - # Schtop! - # - if self.state == :freezing - self.state = :frozen - Thread.stop - self.state = :started - end - - yield_start = Time.now.to_f - - yield - - # - # Ah-ha! Sleep with a break clause. Make sure we poll every @poll_every seconds. - # - ((@poll_every.to_f - Time.now.to_f + yield_start.to_f)/rate_limit). - round.to_i.times do - - break if self.should_stop? - - # - # This is a rate-limiting step - # - Kernel.sleep rate_limit - end - end - - self.state = :stopped - end - + # This determines if a thread should stop + # + # @return [Boolean] def should_stop? [:freezing, :stopping].include?(self.state) end + # This is the current state of the thread. It can be one of + # [:stopped, :starting, :started, :freezing, :frozen, :stopping, :killing, :killed] + # + # If the thread is not alive it will be +:stopped+. + # + # @return [Symbol] One of [:stopped, :starting, :started, :freezing, + # :frozen, :stopping, :killing, :killed] def state if self.alive? @thread.key?(:state) ? @thread[:state] : :unknown @@ -90,9 +71,18 @@ module Mauve end end + # This sets the state of a thread. It also records the last time the + # thread changed status. + # + # @param [Symbol] s One of [:stopped, :starting, :started, :freezing, + # :frozen, :stopping, :killing, :killed] + # @raise [ArgumentError] if +s+ is not a valid symbol or the thread is not + # ready + # @return [Symbol] the current thread state. + # def state=(s) - raise "Bad state for mauve_thread #{s.inspect}" unless [:stopped, :starting, :started, :freezing, :frozen, :stopping, :killing, :killed].include?(s) - raise "Thread not ready yet." unless @thread.is_a?(Thread) + raise ArgumentError, "Bad state for mauve_thread #{s.inspect}" unless [:stopped, :starting, :started, :freezing, :frozen, :stopping, :killing, :killed].include?(s) + raise ArgumentError, "Thread not ready yet." unless @thread.is_a?(Thread) unless @thread[:state] == s @thread[:state] = s @@ -103,6 +93,9 @@ module Mauve @thread[:state] end + # This returns the time of the last state change, or nil if the thread is dead. + # + # @return [Time or Nilclass] def last_state_change if self.alive? and @thread.key?(:last_state_change) return @thread[:last_state_change] @@ -111,6 +104,8 @@ module Mauve end end + # This asks the thread to freeze at the next opportunity. + # def freeze self.state = :freezing @@ -119,10 +114,16 @@ module Mauve logger.warn("Thread has not frozen!") unless @thread.stop? end + # This returns true if the thread has frozen successfully. + # + # @return [Boolean] def frozen? self.stop? and self.state == :frozen end + # This starts the thread. It wakes it up if it is alive, or starts it from + # fresh if it is dead. + # def run if self.alive? # Wake up if we're stopped. @@ -132,7 +133,7 @@ module Mauve else @logger = nil Thread.new do - self.run_thread { self.main_loop } + run_thread { main_loop } end end end @@ -140,15 +141,23 @@ module Mauve alias start run alias thaw run + # This checks to see if the thread is alive + # + # @return [Boolean] def alive? @thread.is_a?(Thread) and @thread.alive? end + # This checks to see if the thread is stopped + # + # @return [Boolean] def stop? self.alive? and @thread.stop? end - def join(ok_exceptions=[]) + # This joins the thread + # + def join @thread.join if @thread.is_a?(Thread) end @@ -156,15 +165,24 @@ module Mauve # @thread.raise(ex) # end + # This returns the thread's backtrace + # + # @return [Array or Nilclass] def backtrace @thread.backtrace if @thread.is_a?(Thread) end + # This restarts the thread + # + # def restart self.stop self.start end + # This stops the thread + # + # def stop self.state = :stopping @@ -183,16 +201,79 @@ module Mauve alias exit stop + # This kills the thread -- faster than #stop + # def kill self.state = :killing @thread.kill self.state = :killed end + # This returns the thread itself. + # + # @return [Thread] def thread @thread end + + private + + # This is the main run loop for the thread. In here are all the calls + # allowing use to freeze and thaw the thread neatly. + # + # This thread will run untill the thread state is changed to :stopping. + # + def run_thread(interval = 5.0) + # + # Good to go. + # + @thread = Thread.current + self.state = :starting + + @poll_every ||= interval + # + # Make sure we get a number. + # + @poll_every = 5 unless @poll_every.is_a?(Numeric) + + rate_limit = 0.1 + + while self.state != :stopping do + + self.state = :started if self.state == :starting + + # + # Schtop! + # + if self.state == :freezing + self.state = :frozen + Thread.stop + self.state = :started + end + + yield_start = Time.now.to_f + + yield + + # + # Ah-ha! Sleep with a break clause. Make sure we poll every @poll_every seconds. + # + ((@poll_every.to_f - Time.now.to_f + yield_start.to_f)/rate_limit). + round.to_i.times do + + break if self.should_stop? + + # + # This is a rate-limiting step + # + Kernel.sleep rate_limit + end + end + + self.state = :stopped + end + end end |