Example #1
0
    def _io_callback(self, events):
        if events < 0:
            # actually a status error code
            _dbg("Callback error on", self._fd,
                 ffi.string(libuv.uv_err_name(events)),
                 ffi.string(libuv.uv_strerror(events)))
            # XXX: We've seen one half of a FileObjectPosix pair
            # (the read side of a pipe) report errno 11 'bad file descriptor'
            # after the write side was closed and its watcher removed. But
            # we still need to attempt to read from it to clear out what's in
            # its buffers--if we return with the watcher inactive before proceeding to wake up
            # the reader, we get a LoopExit. So we can't return here and arguably shouldn't print it
            # either. The negative events mask will match the watcher's mask.
            # See test__fileobject.py:Test.test_newlines for an example.

            # On Windows (at least with PyPy), we can get ENOTSOCK (socket operation on non-socket)
            # if a socket gets closed. If we don't pass the events on, we hang.
            # See test__makefile_ref.TestSSL for examples.
            # return

        for watcher in self._multiplex_watchers:
            if not watcher.callback:
                # Stopped
                continue
            assert watcher._watcher_ref is self, (self, watcher._watcher_ref)

            send_event = (events & watcher.events) or events < 0
            if send_event:
                if not watcher.pass_events:
                    watcher.callback(*watcher.args)
                else:
                    watcher.callback(events, *watcher.args)
Example #2
0
 def python_handle_error(self, handle, _revents):
     _dbg("Handling error for handle", handle)
     if not handle:
         return
     try:
         watcher = self.from_handle(handle)
         exc_info = watcher._exc_info
         del watcher._exc_info
         # In the past, we passed the ``watcher`` itself as the context,
         # which typically meant that the Hub would just print
         # the exception. This is a problem because sometimes we can't
         # detect signals until late in ``python_callback``; specifically,
         # test_selectors.py:DefaultSelectorTest.test_select_interrupt_exc
         # installs a SIGALRM handler that raises an exception. That exception can happen
         # before we enter ``python_callback`` or at any point within it because of the way
         # libuv swallows signals. By passing None, we get the exception prapagated into
         # the main greenlet (which is probably *also* not what we always want, but
         # I see no way to distinguish the cases).
         watcher.loop.handle_error(None, *exc_info)
     finally:
         # XXX Since we're here on an error condition, and we
         # made sure that the watcher object was put in loop._keepaliveset,
         # what about not stopping the watcher? Looks like a possible
         # memory leak?
         # XXX: This used to do "if revents & (libev.EV_READ | libev.EV_WRITE)"
         # before stopping. Why?
         try:
             watcher.stop()
         except: # pylint:disable=bare-except
             watcher.loop.handle_error(watcher, *sys.exc_info())
         return # pylint:disable=lost-exception
Example #3
0
 def from_handle(self, handle):  # pylint:disable=method-hidden
     _dbg("Getting from handle", handle)
     x = self.ffi.from_handle(handle)
     _dbg("Got from handle", handle, x)
     return x
Example #4
0
 def callback(_):
     active_watchers.pop(ffi_watcher)
     _dbg("Python weakref callback closing", debug)
     typ._watcher_ffi_close(ffi_watcher)
Example #5
0
 def _io_start(self):
     _dbg("IO start on behalf of multiplex", self, "fd", self._fd, "events",
          self._events)
     self.start(self._io_callback, pass_events=True)
Example #6
0
 def _watcher_ffi_start(self):
     _dbg("Starting watcher", self, "with events", self._events)
     self._watcher_start(self._watcher, self._events,
                         self._watcher_callback)
Example #7
0
 def _watcher_ffi_unref(self):
     _dbg("Unreffing", self)
     libuv.uv_unref(self._watcher)
Example #8
0
 def _watcher_ffi_ref(self):
     _dbg("Reffing", self)
     libuv.uv_ref(self._watcher)
Example #9
0
 def _watcher_ffi_stop(self):
     _dbg("Stopping", self, self._watcher_stop)
     self._watcher_stop(self._watcher)
     _dbg("Stopped", self)
Example #10
0
 def _watcher_ffi_start(self):
     _dbg("Starting", self)
     self._watcher_start(self._watcher, self._watcher_callback)
     _dbg("\tStarted", self)
Example #11
0
 def _watcher_ffi_set_init_ref(self, ref):
     _dbg("Creating", type(self), "with ref", ref)
     self.ref = ref
Example #12
0
    def python_callback(self, handle, revents):
        """
        Returns an integer having one of three values:

        - -1
          An exception occurred during the callback and you must call
          :func:`_python_handle_error` to deal with it. The Python watcher
          object will have the exception tuple saved in ``_exc_info``.
        - 1
          Everything went according to plan. You should check to see if the libev
          watcher is still active, and call :func:`python_stop` if it is not. This will
          clean up the memory. Finding the watcher still active at the event loop level,
          but not having stopped itself at the gevent level is a buggy scenario and
          shouldn't happen.
        - 2
          Everything went according to plan, but the watcher has already
          been stopped. Its memory may no longer be valid.

        This function should never return 0, as that's the default value that
        Python exceptions will produce.
        """
        #_dbg("Running callback", handle)
        orig_ffi_watcher = None
        orig_loop = None
        try:
            # Even dereferencing the handle needs to be inside the try/except;
            # if we don't return normally (e.g., a signal) then we wind up going
            # to the 'onerror' handler (unhandled_onerror), which
            # is not what we want; that can permanently wedge the loop depending
            # on which callback was executing.
            # XXX: See comments in that function. We may be able to restart and do better?
            if not handle:
                # Hmm, a NULL handle. That's not supposed to happen.
                # We can easily get into a loop if we deref it and allow that
                # to raise.
                _dbg("python_callback got null handle")
                return 1
            the_watcher = self.from_handle(handle)
            orig_ffi_watcher = the_watcher._watcher
            orig_loop = the_watcher.loop
            args = the_watcher.args
            if args is None:
                # Legacy behaviour from corecext: convert None into ()
                # See test__core_watcher.py
                args = _NOARGS
            if args and args[0] == GEVENT_CORE_EVENTS:
                args = (revents, ) + args[1:]
            the_watcher.callback(*args)  # None here means we weren't started
        except:  # pylint:disable=bare-except
            # It's possible for ``the_watcher`` to be undefined (UnboundLocalError)
            # if we threw an exception (signal) on the line that created that variable.
            # This is typically the case with a signal under libuv
            try:
                the_watcher
            except UnboundLocalError:
                the_watcher = self.from_handle(handle)

            # It may not be safe to do anything with `handle` or `orig_ffi_watcher`
            # anymore. If the watcher closed or stopped itself *before* throwing the exception,
            # then the `handle` and `orig_ffi_watcher` may no longer be valid. Attempting to
            # e.g., dereference the handle is likely to crash the process.
            the_watcher._exc_info = sys.exc_info()

            # If it hasn't been stopped, we need to make sure its
            # memory stays valid so we can stop it at the native level if needed.
            # If its loop is gone, it has already been stopped,
            # see https://github.com/gevent/gevent/issues/1295 for a case where
            # that happened, as well as issue #1482
            if (
                    # The last thing it does. Full successful close.
                    the_watcher.loop is None
                    # Only a partial close. We could leak memory and even crash later.
                    or the_watcher._handle is None):
                # Prevent unhandled_onerror from using the invalid handle
                handle = None
                exc_info = the_watcher._exc_info
                del the_watcher._exc_info
                try:
                    if orig_loop is not None:
                        orig_loop.handle_error(the_watcher, *exc_info)
                    else:
                        self.unhandled_onerror(*exc_info)
                except:
                    print("WARNING: gevent: Error when handling error",
                          file=sys.stderr)
                    traceback.print_exc()
                # Signal that we're closed, no need to do more.
                return 2

            # Keep it around so we can close it later.
            the_watcher.loop._keepaliveset.add(the_watcher)
            return -1
        else:
            if (the_watcher.loop is not None
                    and the_watcher in the_watcher.loop._keepaliveset
                    and the_watcher._watcher is orig_ffi_watcher):
                # It didn't stop itself, *and* it didn't stop itself, reset
                # its watcher, and start itself again. libuv's io watchers MAY
                # do that.
                # The normal, expected scenario when we find the watcher still
                # in the keepaliveset is that it is still active at the event loop
                # level, so we don't expect that python_stop gets called.
                #_dbg("The watcher has not stopped itself, possibly still active", the_watcher)
                return 1
            return 2  # it stopped itself
Example #13
0
    def python_callback(self, handle, revents):
        """
        Returns an integer having one of three values:

        - -1
          An exception occurred during the callback and you must call
          :func:`_python_handle_error` to deal with it. The Python watcher
          object will have the exception tuple saved in ``_exc_info``.
        - 1
          Everything went according to plan. You should check to see if the libev
          watcher is still active, and call :func:`python_stop` if it is not. This will
          clean up the memory. Finding the watcher still active at the event loop level,
          but not having stopped itself at the gevent level is a buggy scenario and
          shouldn't happen.
        - 2
          Everything went according to plan, but the watcher has already
          been stopped. Its memory may no longer be valid.

        This function should never return 0, as that's the default value that
        Python exceptions will produce.
        """
        orig_ffi_watcher = None
        try:
            # Even dereferencing the handle needs to be inside the try/except;
            # if we don't return normally (e.g., a signal) then we wind up going
            # to the 'onerror' handler (unhandled_onerror), which
            # is not what we want; that can permanently wedge the loop depending
            # on which callback was executing.
            # XXX: See comments in that function. We may be able to restart and do better?
            if not handle:
                # Hmm, a NULL handle. That's not supposed to happen.
                # We can easily get into a loop if we deref it and allow that
                # to raise.
                _dbg("python_callback got null handle")
                return 1
            the_watcher = self.from_handle(handle)
            orig_ffi_watcher = the_watcher._watcher
            args = the_watcher.args
            if args is None:
                # Legacy behaviour from corecext: convert None into ()
                # See test__core_watcher.py
                args = _NOARGS
            if args and args[0] == GEVENT_CORE_EVENTS:
                args = (revents, ) + args[1:]
            the_watcher.callback(*args)
        except:  # pylint:disable=bare-except
            _dbg("Got exception servicing watcher with handle", handle)
            # It's possible for ``the_watcher`` to be undefined (UnboundLocalError)
            # if we threw an exception (signal) on the line that created that variable.
            # This is typically the case with a signal under libuv
            try:
                the_watcher
            except UnboundLocalError:
                the_watcher = self.from_handle(handle)
            the_watcher._exc_info = sys.exc_info()
            # Depending on when the exception happened, the watcher
            # may or may not have been stopped. We need to make sure its
            # memory stays valid so we can stop it at the ev level if needed.
            the_watcher.loop._keepaliveset.add(the_watcher)
            return -1
        else:
            if the_watcher in the_watcher.loop._keepaliveset and the_watcher._watcher is orig_ffi_watcher:
                # It didn't stop itself, *and* it didn't stop itself, reset
                # its watcher, and start itself again. libuv's io watchers MAY
                # do that.
                # The normal, expected scenario when we find the watcher still
                # in the keepaliveset is that it is still active at the event loop
                # level, so we don't expect that python_stop gets called.
                _dbg(
                    "The watcher has not stopped itself, possibly still active",
                    the_watcher)
                return 1
            return 2  # it stopped itself
Example #14
0
    def python_callback(self, handle, revents):
        """
        Returns an integer having one of three values:

        - -1
          An exception occurred during the callback and you must call
          :func:`_python_handle_error` to deal with it. The Python watcher
          object will have the exception tuple saved in ``_exc_info``.
        - 1
          Everything went according to plan. You should check to see if the libev
          watcher is still active, and call :func:`python_stop` if it is not. This will
          clean up the memory. Finding the watcher still active at the event loop level,
          but not having stopped itself at the gevent level is a buggy scenario and
          shouldn't happen.
        - 2
          Everything went according to plan, but the watcher has already
          been stopped. Its memory may no longer be valid.

        This function should never return 0, as that's the default value that
        Python exceptions will produce.
        """
        #print("Running callback", handle)
        orig_ffi_watcher = None
        try:
            # Even dereferencing the handle needs to be inside the try/except;
            # if we don't return normally (e.g., a signal) then we wind up going
            # to the 'onerror' handler (unhandled_onerror), which
            # is not what we want; that can permanently wedge the loop depending
            # on which callback was executing.
            # XXX: See comments in that function. We may be able to restart and do better?
            if not handle:
                # Hmm, a NULL handle. That's not supposed to happen.
                # We can easily get into a loop if we deref it and allow that
                # to raise.
                _dbg("python_callback got null handle")
                return 1
            the_watcher = self.from_handle(handle)
            orig_ffi_watcher = the_watcher._watcher
            args = the_watcher.args
            if args is None:
                # Legacy behaviour from corecext: convert None into ()
                # See test__core_watcher.py
                args = _NOARGS
            if args and args[0] == GEVENT_CORE_EVENTS:
                args = (revents, ) + args[1:]
            #print("Calling function", the_watcher.callback, args)
            the_watcher.callback(*args)
        except: # pylint:disable=bare-except
            _dbg("Got exception servicing watcher with handle", handle, sys.exc_info())
            # It's possible for ``the_watcher`` to be undefined (UnboundLocalError)
            # if we threw an exception (signal) on the line that created that variable.
            # This is typically the case with a signal under libuv
            try:
                the_watcher
            except UnboundLocalError:
                the_watcher = self.from_handle(handle)
            the_watcher._exc_info = sys.exc_info()
            # Depending on when the exception happened, the watcher
            # may or may not have been stopped. We need to make sure its
            # memory stays valid so we can stop it at the ev level if needed.
            # If its loop is gone, it has already been stopped,
            # see https://github.com/gevent/gevent/issues/1295 for a case where
            # that happened
            if the_watcher.loop is not None:
                the_watcher.loop._keepaliveset.add(the_watcher)
            return -1
        else:
            if (the_watcher.loop is not None
                    and the_watcher in the_watcher.loop._keepaliveset
                    and the_watcher._watcher is orig_ffi_watcher):
                # It didn't stop itself, *and* it didn't stop itself, reset
                # its watcher, and start itself again. libuv's io watchers MAY
                # do that.
                # The normal, expected scenario when we find the watcher still
                # in the keepaliveset is that it is still active at the event loop
                # level, so we don't expect that python_stop gets called.
                #_dbg("The watcher has not stopped itself, possibly still active", the_watcher)
                return 1
            return 2 # it stopped itself