aboutsummaryrefslogtreecommitdiff
path: root/lib/mauve/mauve_thread.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mauve/mauve_thread.rb')
-rw-r--r--lib/mauve/mauve_thread.rb189
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