aboutsummaryrefslogtreecommitdiffstats
path: root/python/gevent/libev/watcher.py
blob: 8beaa1f2596875bab7233d132ef35a8298c7a6fe (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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# pylint: disable=too-many-lines, protected-access, redefined-outer-name, not-callable
# pylint: disable=no-member
from __future__ import absolute_import, print_function
import sys

from gevent.libev import _corecffi # pylint:disable=no-name-in-module,import-error

ffi = _corecffi.ffi # pylint:disable=no-member
libev = _corecffi.lib # pylint:disable=no-member

if hasattr(libev, 'vfd_open'):
    # Must be on windows
    assert sys.platform.startswith("win"), "vfd functions only needed on windows"
    vfd_open = libev.vfd_open
    vfd_free = libev.vfd_free
    vfd_get = libev.vfd_get
else:
    vfd_open = vfd_free = vfd_get = lambda fd: fd

#####
## NOTE on Windows:
# The C implementation does several things specially for Windows;
# a possibly incomplete list is:
#
# - the loop runs a periodic signal checker;
# - the io watcher constructor is different and it has a destructor;
# - the child watcher is not defined
#
# The CFFI implementation does none of these things, and so
# is possibly NOT FUNCTIONALLY CORRECT on Win32
#####
_NOARGS = ()
_events = [(libev.EV_READ, 'READ'),
           (libev.EV_WRITE, 'WRITE'),
           (libev.EV__IOFDSET, '_IOFDSET'),
           (libev.EV_PERIODIC, 'PERIODIC'),
           (libev.EV_SIGNAL, 'SIGNAL'),
           (libev.EV_CHILD, 'CHILD'),
           (libev.EV_STAT, 'STAT'),
           (libev.EV_IDLE, 'IDLE'),
           (libev.EV_PREPARE, 'PREPARE'),
           (libev.EV_CHECK, 'CHECK'),
           (libev.EV_EMBED, 'EMBED'),
           (libev.EV_FORK, 'FORK'),
           (libev.EV_CLEANUP, 'CLEANUP'),
           (libev.EV_ASYNC, 'ASYNC'),
           (libev.EV_CUSTOM, 'CUSTOM'),
           (libev.EV_ERROR, 'ERROR')]

from gevent._ffi import watcher as _base

def _events_to_str(events):
    return _base.events_to_str(events, _events)



class watcher(_base.watcher):
    _FFI = ffi
    _LIB = libev
    _watcher_prefix = 'ev'

    # Flags is a bitfield with the following meaning:
    # 0000 -> default, referenced (when active)
    # 0010 -> ev_unref has been called
    # 0100 -> not referenced; independent of 0010
    _flags = 0

    def __init__(self, _loop, ref=True, priority=None, args=_base._NOARGS):
        if ref:
            self._flags = 0
        else:
            self._flags = 4

        super(watcher, self).__init__(_loop, ref=ref, priority=priority, args=args)

    def _watcher_ffi_set_priority(self, priority):
        libev.ev_set_priority(self._watcher, priority)

    def _watcher_ffi_init(self, args):
        self._watcher_init(self._watcher,
                           self._watcher_callback,
                           *args)

    def _watcher_ffi_start(self):
        self._watcher_start(self.loop._ptr, self._watcher)

    def _watcher_ffi_ref(self):
        if self._flags & 2: # we've told libev we're not referenced
            self.loop.ref()
            self._flags &= ~2

    def _watcher_ffi_unref(self):
        if self._flags & 6 == 4:
            # We're not referenced, but we haven't told libev that
            self.loop.unref()
            self._flags |= 2 # now we've told libev

    def _get_ref(self):
        return False if self._flags & 4 else True

    def _set_ref(self, value):
        if value:
            if not self._flags & 4:
                return  # ref is already True
            if self._flags & 2:  # ev_unref was called, undo
                self.loop.ref()
            self._flags &= ~6  # do not want unref, no outstanding unref
        else:
            if self._flags & 4:
                return  # ref is already False
            self._flags |= 4 # we're not referenced
            if not self._flags & 2 and libev.ev_is_active(self._watcher):
                # we haven't told libev we're not referenced, but it thinks we're
                # active so we need to undo that
                self.loop.unref()
                self._flags |= 2 # libev knows we're not referenced

    ref = property(_get_ref, _set_ref)


    def _get_priority(self):
        return libev.ev_priority(self._watcher)

    @_base.not_while_active
    def _set_priority(self, priority):
        libev.ev_set_priority(self._watcher, priority)

    priority = property(_get_priority, _set_priority)

    def feed(self, revents, callback, *args):
        self.callback = callback
        self.args = args or _NOARGS
        if self._flags & 6 == 4:
            self.loop.unref()
            self._flags |= 2
        libev.ev_feed_event(self.loop._ptr, self._watcher, revents)
        if not self._flags & 1:
            # Py_INCREF(<PyObjectPtr>self)
            self._flags |= 1

    @property
    def pending(self):
        return True if self._watcher and libev.ev_is_pending(self._watcher) else False


class io(_base.IoMixin, watcher):

    EVENT_MASK = libev.EV__IOFDSET | libev.EV_READ | libev.EV_WRITE

    def _get_fd(self):
        return vfd_get(self._watcher.fd)

    @_base.not_while_active
    def _set_fd(self, fd):
        vfd = vfd_open(fd)
        vfd_free(self._watcher.fd)
        self._watcher_init(self._watcher, self._watcher_callback, vfd, self._watcher.events)

    fd = property(_get_fd, _set_fd)

    def _get_events(self):
        return self._watcher.events

    @_base.not_while_active
    def _set_events(self, events):
        self._watcher_init(self._watcher, self._watcher_callback, self._watcher.fd, events)

    events = property(_get_events, _set_events)

    @property
    def events_str(self):
        return _events_to_str(self._watcher.events)

    def _format(self):
        return ' fd=%s events=%s' % (self.fd, self.events_str)


class timer(_base.TimerMixin, watcher):

    @property
    def at(self):
        return self._watcher.at

    def again(self, callback, *args, **kw):
        # Exactly the same as start(), just with a different initializer
        # function
        self._watcher_start = libev.ev_timer_again
        try:
            self.start(callback, *args, **kw)
        finally:
            del self._watcher_start


class signal(_base.SignalMixin, watcher):
    pass

class idle(_base.IdleMixin, watcher):
    pass

class prepare(_base.PrepareMixin, watcher):
    pass

class check(_base.CheckMixin, watcher):
    pass

class fork(_base.ForkMixin, watcher):
    pass


class async_(_base.AsyncMixin, watcher):

    def send(self):
        libev.ev_async_send(self.loop._ptr, self._watcher)

    @property
    def pending(self):
        return True if libev.ev_async_pending(self._watcher) else False

# Provide BWC for those that have async
locals()['async'] = async_

class _ClosedWatcher(object):
    __slots__ = ('pid', 'rpid', 'rstatus')

    def __init__(self, other):
        self.pid = other.pid
        self.rpid = other.rpid
        self.rstatus = other.rstatus

    def __bool__(self):
        return False
    __nonzero__ = __bool__

class child(_base.ChildMixin, watcher):
    _watcher_type = 'child'

    def close(self):
        # Capture the properties we defer to our _watcher, because
        # we're about to discard it.
        closed_watcher = _ClosedWatcher(self._watcher)
        super(child, self).close()
        self._watcher = closed_watcher

    @property
    def pid(self):
        return self._watcher.pid

    @property
    def rpid(self):
        return self._watcher.rpid

    @rpid.setter
    def rpid(self, value):
        self._watcher.rpid = value

    @property
    def rstatus(self):
        return self._watcher.rstatus

    @rstatus.setter
    def rstatus(self, value):
        self._watcher.rstatus = value


class stat(_base.StatMixin, watcher):
    _watcher_type = 'stat'

    @property
    def attr(self):
        if not self._watcher.attr.st_nlink:
            return
        return self._watcher.attr

    @property
    def prev(self):
        if not self._watcher.prev.st_nlink:
            return
        return self._watcher.prev

    @property
    def interval(self):
        return self._watcher.interval