Global replace of Nicira Networks.
[sliver-openvswitch.git] / python / ovs / poller.py
1 # Copyright (c) 2010 Nicira, Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import errno
16 import select
17 import ovs.timeval
18 import ovs.vlog
19
20 vlog = ovs.vlog.Vlog("poller")
21
22
23 class Poller(object):
24     """High-level wrapper around the "poll" system call.
25
26     Intended usage is for the program's main loop to go about its business
27     servicing whatever events it needs to.  Then, when it runs out of immediate
28     tasks, it calls each subordinate module or object's "wait" function, which
29     in turn calls one (or more) of the functions Poller.fd_wait(),
30     Poller.immediate_wake(), and Poller.timer_wait() to register to be awakened
31     when the appropriate event occurs.  Then the main loop calls
32     Poller.block(), which blocks until one of the registered events happens."""
33
34     def __init__(self):
35         self.__reset()
36
37     def fd_wait(self, fd, events):
38         """Registers 'fd' as waiting for the specified 'events' (which should
39         be select.POLLIN or select.POLLOUT or their bitwise-OR).  The following
40         call to self.block() will wake up when 'fd' becomes ready for one or
41         more of the requested events.
42
43         The event registration is one-shot: only the following call to
44         self.block() is affected.  The event will need to be re-registered
45         after self.block() is called if it is to persist.
46
47         'fd' may be an integer file descriptor or an object with a fileno()
48         method that returns an integer file descriptor."""
49         self.poll.register(fd, events)
50
51     def __timer_wait(self, msec):
52         if self.timeout < 0 or msec < self.timeout:
53             self.timeout = msec
54
55     def timer_wait(self, msec):
56         """Causes the following call to self.block() to block for no more than
57         'msec' milliseconds.  If 'msec' is nonpositive, the following call to
58         self.block() will not block at all.
59
60         The timer registration is one-shot: only the following call to
61         self.block() is affected.  The timer will need to be re-registered
62         after self.block() is called if it is to persist."""
63         if msec <= 0:
64             self.immediate_wake()
65         else:
66             self.__timer_wait(msec)
67
68     def timer_wait_until(self, msec):
69         """Causes the following call to self.block() to wake up when the
70         current time, as returned by ovs.timeval.msec(), reaches 'msec' or
71         later.  If 'msec' is earlier than the current time, the following call
72         to self.block() will not block at all.
73
74         The timer registration is one-shot: only the following call to
75         self.block() is affected.  The timer will need to be re-registered
76         after self.block() is called if it is to persist."""
77         now = ovs.timeval.msec()
78         if msec <= now:
79             self.immediate_wake()
80         else:
81             self.__timer_wait(msec - now)
82
83     def immediate_wake(self):
84         """Causes the following call to self.block() to wake up immediately,
85         without blocking."""
86         self.timeout = 0
87
88     def block(self):
89         """Blocks until one or more of the events registered with
90         self.fd_wait() occurs, or until the minimum duration registered with
91         self.timer_wait() elapses, or not at all if self.immediate_wake() has
92         been called."""
93         try:
94             try:
95                 events = self.poll.poll(self.timeout)
96                 self.__log_wakeup(events)
97             except select.error, e:
98                 # XXX rate-limit
99                 error, msg = e
100                 if error != errno.EINTR:
101                     vlog.err("poll: %s" % e[1])
102         finally:
103             self.__reset()
104
105     def __log_wakeup(self, events):
106         if not events:
107             vlog.dbg("%d-ms timeout" % self.timeout)
108         else:
109             for fd, revents in events:
110                 if revents != 0:
111                     s = ""
112                     if revents & select.POLLIN:
113                         s += "[POLLIN]"
114                     if revents & select.POLLOUT:
115                         s += "[POLLOUT]"
116                     if revents & select.POLLERR:
117                         s += "[POLLERR]"
118                     if revents & select.POLLHUP:
119                         s += "[POLLHUP]"
120                     if revents & select.POLLNVAL:
121                         s += "[POLLNVAL]"
122                     vlog.dbg("%s on fd %d" % (s, fd))
123
124     def __reset(self):
125         self.poll = select.poll()
126         self.timeout = -1