diff options
Diffstat (limited to 'python/gevent/libev/watcher.py')
-rw-r--r-- | python/gevent/libev/watcher.py | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/python/gevent/libev/watcher.py b/python/gevent/libev/watcher.py new file mode 100644 index 0000000..8beaa1f --- /dev/null +++ b/python/gevent/libev/watcher.py @@ -0,0 +1,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 |