Revision b11c9e5c

b/lib/daemon.py
25 25
import select
26 26
import signal
27 27
import errno
28
import time
28 29

  
29 30
from ganeti import utils
30 31

  
31 32

  
33
class Timer(object):
34
  def __init__(self, owner, timer_id, start, interval, repeat):
35
    self.owner = owner
36
    self.timer_id = timer_id
37
    self.start = start
38
    self.interval = interval
39
    self.repeat = repeat
40

  
41

  
32 42
class Mainloop(object):
33 43
  """Generic mainloop for daemons
34 44

  
......
41 51
    self._io_wait_add = []
42 52
    self._io_wait_remove = []
43 53
    self._signal_wait = []
54
    self._timer_id_last = 0
55
    self._timer = {}
56
    self._timer_add = []
57
    self._timer_remove = []
44 58

  
45 59
  def Run(self, handle_sigchld=True, handle_sigterm=True, stop_on_empty=False):
46 60
    """Runs the mainloop.
......
69 83

  
70 84
      try:
71 85
        running = True
86
        timeout = None
87
        timeout_needs_update = True
72 88

  
73 89
        # Start actual main loop
74 90
        while running:
......
92 108
              poller.register(fd, conditions)
93 109
            self._io_wait_add = []
94 110

  
111
          # Add new timers
112
          if self._timer_add:
113
            timeout_needs_update = True
114
            for timer in self._timer_add:
115
              self._timer[timer.timer_id] = timer
116
            del self._timer_add[:]
117

  
118
          # Remove timers
119
          if self._timer_remove:
120
            timeout_needs_update = True
121
            for timer_id in self._timer_remove:
122
              try:
123
                del self._timer[timer_id]
124
              except KeyError:
125
                pass
126
            del self._timer_remove[:]
127

  
95 128
          # Stop if nothing is listening anymore
96
          if stop_on_empty and not self._io_wait:
129
          if stop_on_empty and not (self._io_wait or self._timer):
97 130
            break
98 131

  
132
          # Calculate timeout again if required
133
          if timeout_needs_update:
134
            timeout = self._CalcTimeout(time.time())
135

  
99 136
          # Wait for I/O events
100 137
          try:
101
            io_events = poller.poll()
138
            io_events = poller.poll(timeout)
102 139
          except select.error, err:
103 140
            # EINTR can happen when signals are sent
104 141
            if err.args and err.args[0] in (errno.EINTR,):
......
106 143
            else:
107 144
              raise
108 145

  
146
          after_poll = time.time()
147

  
109 148
          if io_events:
110 149
            # Check for I/O events
111 150
            for (evfd, evcond) in io_events:
......
113 152
              if owner:
114 153
                owner.OnIO(evfd, evcond)
115 154

  
155
          if self._timer:
156
            self._CheckTimers(after_poll)
157

  
116 158
          # Check whether signal was raised
117 159
          if sigchld_handler and sigchld_handler.called:
118 160
            self._CallSignalWaiters(signal.SIGCHLD)
......
130 172
      if sigchld_handler:
131 173
        sigchld_handler.Reset()
132 174

  
175
  def _CalcTimeout(self, now):
176
    if not self._timer:
177
      return None
178

  
179
    timeout = None
180

  
181
    # TODO: Repeating timers
182

  
183
    min_timeout = 0.001
184

  
185
    for timer in self._timer.itervalues():
186
      time_left = (timer.start + timer.interval) - now
187
      if timeout is None or time_left < timeout:
188
        timeout = time_left
189
      if timeout < 0:
190
        timeout = 0
191
        break
192
      elif timeout < min_timeout:
193
        timeout = min_timeout
194
        break
195

  
196
    return timeout * 1000.0
197

  
198
  def _CheckTimers(self, now):
199
    # TODO: Repeating timers
200
    for timer in self._timer.itervalues():
201
      if now < (timer.start + timer.interval):
202
        continue
203

  
204
      timer.owner.OnTimer(timer.timer_id)
205

  
206
      # TODO: Repeating timers should not be removed
207
      self._timer_remove.append(timer.timer_id)
208

  
133 209
  def _CallSignalWaiters(self, signum):
134 210
    """Calls all signal waiters for a certain signal.
135 211

  
......
185 261

  
186 262
    """
187 263
    self._signal_wait.append(owner)
264

  
265
  def AddTimer(self, owner, interval, repeat):
266
    """Add a new timer.
267

  
268
    The receiver must support a "OnTimer(self, timer_id)" function.
269

  
270
    @type owner: instance
271
    @param owner: Receiver
272
    @type interval: int or float
273
    @param interval: Timer interval in seconds
274
    @type repeat: bool
275
    @param repeat: Whether this is a repeating timer or one-off
276

  
277
    """
278
    # TODO: Implement repeating timers
279
    assert not repeat, "Repeating timers are not yet supported"
280

  
281
    # Get new ID
282
    self._timer_id_last += 1
283

  
284
    timer_id = self._timer_id_last
285

  
286
    self._timer_add.append(Timer(owner, timer_id, time.time(),
287
                                 float(interval), repeat))
288

  
289
    return timer_id
290

  
291
  def RemoveTimer(self, timer_id):
292
    """Removes a timer.
293

  
294
    @type timer_id: int
295
    @param timer_id: Timer ID
296

  
297
    """
298
    self._timer_remove.append(timer_id)

Also available in: Unified diff