aboutsummaryrefslogtreecommitdiff
path: root/lib/mauve/mauve_thread.rb
blob: 52c2801afb8b64ec26b5ac404d23424560d401b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
require 'thread'
require 'singleton'

module Mauve

  class MauveThread

    attr_reader :state, :poll_every

    def initialize
      @thread = nil
      @state = :stopped
    end

    def logger
      @logger ||= Log4r::Logger.new(self.class.to_s) 
    end

    def poll_every=(i)
      raise ArgumentError.new("poll_every must be numeric") unless i.is_a?(Numeric)
      @poll_every = i
    end

    def run_thread(interval = 0.1)
      #
      # Good to go.
      #
      self.state = :starting

      @poll_every ||= interval

      sleep_loops = (@poll_every.to_f / 0.1).round.to_i
      sleep_loops = 1 if sleep_loops.nil? or sleep_loops < 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

        #
        # Ah-ha! Sleep with a break clause.
        #
        sleep_loops.times do

          break if self.should_stop?

          #
          # This is a rate-limiting step
          #
          Kernel.sleep 0.1
        end
      end

      self.state = :stopped
    end

    def should_stop?
      [:freezing, :stopping].include?(self.state)
    end

    def state=(s)
      raise "Bad state for mauve_thread #{s.inspect}" unless [:stopped, :starting, :started, :freezing, :frozen, :stopping, :killing, :killed].include?(s)
      unless @state == s
        @state = s
        logger.debug(s.to_s.capitalize) 
      end
    end

    def freeze
      self.state = :freezing
      
      20.times { Kernel.sleep 0.1 ; break if @thread.stop? }

      logger.debug("Thread has not frozen!") unless @thread.stop?
    end

    def frozen?
      self.stop? and self.state == :frozen
    end

    def run
      if self.alive? 
        if self.stop?
          @thread.wakeup 
        end
      else
        @logger = nil
        self.state = :starting
        @thread = Thread.new{ self.run_thread { self.main_loop } }
      end
    end

    alias start run
    alias thaw  run

    def alive?
      @thread.is_a?(Thread) and @thread.alive?
    end

    def stop?
      self.alive? and @thread.stop?
    end

    def join(ok_exceptions=[])
      @thread.join if @thread.is_a?(Thread)
    end

#    def raise(ex)
#      @thread.raise(ex)
#    end

    def backtrace
      @thread.backtrace if @thread.is_a?(Thread)
    end

    def restart
      self.stop
      self.start
    end
    
    def stop
      self.state = :stopping

      10.times do 
        break unless self.alive?
        Kernel.sleep 1 if self.alive? 
      end

      #
      # OK I've had enough now.
      #
      self.kill if self.alive?

      self.join 
    end

    alias exit stop

    def kill
      self.state = :killing
      @thread.kill
      self.state = :killed
    end

    def thread
      @thread
    end

  end

end