~ubuntu-branches/ubuntu/vivid/sahara/vivid-proposed

« back to all changes in this revision

Viewing changes to sahara/openstack/common/loopingcall.py

  • Committer: Package Import Robot
  • Author(s): Thomas Goirand
  • Date: 2014-09-24 16:34:46 UTC
  • Revision ID: package-import@ubuntu.com-20140924163446-8gu3zscu5e3n9lr2
Tags: upstream-2014.2~b3
ImportĀ upstreamĀ versionĀ 2014.2~b3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2010 United States Government as represented by the
 
2
# Administrator of the National Aeronautics and Space Administration.
 
3
# Copyright 2011 Justin Santa Barbara
 
4
# All Rights Reserved.
 
5
#
 
6
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
7
#    not use this file except in compliance with the License. You may obtain
 
8
#    a copy of the License at
 
9
#
 
10
#         http://www.apache.org/licenses/LICENSE-2.0
 
11
#
 
12
#    Unless required by applicable law or agreed to in writing, software
 
13
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
14
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
15
#    License for the specific language governing permissions and limitations
 
16
#    under the License.
 
17
 
 
18
import sys
 
19
import time
 
20
 
 
21
from eventlet import event
 
22
from eventlet import greenthread
 
23
 
 
24
from sahara.openstack.common.gettextutils import _LE, _LW
 
25
from sahara.openstack.common import log as logging
 
26
 
 
27
LOG = logging.getLogger(__name__)
 
28
 
 
29
# NOTE(zyluo): This lambda function was declared to avoid mocking collisions
 
30
#              with time.time() called in the standard logging module
 
31
#              during unittests.
 
32
_ts = lambda: time.time()
 
33
 
 
34
 
 
35
class LoopingCallDone(Exception):
 
36
    """Exception to break out and stop a LoopingCallBase.
 
37
 
 
38
    The poll-function passed to LoopingCallBase can raise this exception to
 
39
    break out of the loop normally. This is somewhat analogous to
 
40
    StopIteration.
 
41
 
 
42
    An optional return-value can be included as the argument to the exception;
 
43
    this return-value will be returned by LoopingCallBase.wait()
 
44
 
 
45
    """
 
46
 
 
47
    def __init__(self, retvalue=True):
 
48
        """:param retvalue: Value that LoopingCallBase.wait() should return."""
 
49
        self.retvalue = retvalue
 
50
 
 
51
 
 
52
class LoopingCallBase(object):
 
53
    def __init__(self, f=None, *args, **kw):
 
54
        self.args = args
 
55
        self.kw = kw
 
56
        self.f = f
 
57
        self._running = False
 
58
        self.done = None
 
59
 
 
60
    def stop(self):
 
61
        self._running = False
 
62
 
 
63
    def wait(self):
 
64
        return self.done.wait()
 
65
 
 
66
 
 
67
class FixedIntervalLoopingCall(LoopingCallBase):
 
68
    """A fixed interval looping call."""
 
69
 
 
70
    def start(self, interval, initial_delay=None):
 
71
        self._running = True
 
72
        done = event.Event()
 
73
 
 
74
        def _inner():
 
75
            if initial_delay:
 
76
                greenthread.sleep(initial_delay)
 
77
 
 
78
            try:
 
79
                while self._running:
 
80
                    start = _ts()
 
81
                    self.f(*self.args, **self.kw)
 
82
                    end = _ts()
 
83
                    if not self._running:
 
84
                        break
 
85
                    delay = end - start - interval
 
86
                    if delay > 0:
 
87
                        LOG.warn(_LW('task %(func_name)s run outlasted '
 
88
                                     'interval by %(delay).2f sec'),
 
89
                                 {'func_name': repr(self.f), 'delay': delay})
 
90
                    greenthread.sleep(-delay if delay < 0 else 0)
 
91
            except LoopingCallDone as e:
 
92
                self.stop()
 
93
                done.send(e.retvalue)
 
94
            except Exception:
 
95
                LOG.exception(_LE('in fixed duration looping call'))
 
96
                done.send_exception(*sys.exc_info())
 
97
                return
 
98
            else:
 
99
                done.send(True)
 
100
 
 
101
        self.done = done
 
102
 
 
103
        greenthread.spawn_n(_inner)
 
104
        return self.done
 
105
 
 
106
 
 
107
class DynamicLoopingCall(LoopingCallBase):
 
108
    """A looping call which sleeps until the next known event.
 
109
 
 
110
    The function called should return how long to sleep for before being
 
111
    called again.
 
112
    """
 
113
 
 
114
    def start(self, initial_delay=None, periodic_interval_max=None):
 
115
        self._running = True
 
116
        done = event.Event()
 
117
 
 
118
        def _inner():
 
119
            if initial_delay:
 
120
                greenthread.sleep(initial_delay)
 
121
 
 
122
            try:
 
123
                while self._running:
 
124
                    idle = self.f(*self.args, **self.kw)
 
125
                    if not self._running:
 
126
                        break
 
127
 
 
128
                    if periodic_interval_max is not None:
 
129
                        idle = min(idle, periodic_interval_max)
 
130
                    LOG.debug('Dynamic looping call %(func_name)s sleeping '
 
131
                              'for %(idle).02f seconds',
 
132
                              {'func_name': repr(self.f), 'idle': idle})
 
133
                    greenthread.sleep(idle)
 
134
            except LoopingCallDone as e:
 
135
                self.stop()
 
136
                done.send(e.retvalue)
 
137
            except Exception:
 
138
                LOG.exception(_LE('in dynamic looping call'))
 
139
                done.send_exception(*sys.exc_info())
 
140
                return
 
141
            else:
 
142
                done.send(True)
 
143
 
 
144
        self.done = done
 
145
 
 
146
        greenthread.spawn(_inner)
 
147
        return self.done