Commit acb6c056 authored by Jason Madden's avatar Jason Madden

Tweak the way libuv stops handles after a callback.

Fixes #1564.
parent 0130e205
This diff is collapsed.
Fix a spurious warning about watchers and resource leaks on libuv on
Windows. Reported by Stéphane Rainville.
......@@ -26,6 +26,6 @@ directory = "docs/changes"
filename = "CHANGES.rst"
package = "gevent"
package_dir = "src"
issue_format = ":issue:`{issue}`"
issue_format = "See :issue:`{issue}`."
title_format = false
template = "docs/_templates/hr-between-versions.rst.tmpl"
......@@ -21,7 +21,7 @@ _version_info = namedtuple('version_info',
#: .. deprecated:: 1.2
#: Use ``pkg_resources.parse_version(__version__)`` (or the equivalent
#: ``packaging.version.Version(__version__)``).
version_info = _version_info(1, 5, 0, 'dev', 0)
version_info = _version_info(20, 0, 0, 'dev', 0) # XXX: Remove me
#: The human-readable PEP 440 version identifier.
#: Use ``pkg_resources.parse_version(__version__)`` or
......
......@@ -310,17 +310,28 @@ class AbstractCallbacks(object):
def assign_standard_callbacks(ffi, lib, callbacks_class, extras=()): # pylint:disable=unused-argument
"""
Given the typical *ffi* and *lib* arguments, and a subclass of :class:`AbstractCallbacks`
in *callbacks_class*, set up the ``def_extern`` Python callbacks from C
into an instance of *callbacks_class*.
:param tuple extras: If given, this is a sequence of ``(name, error_function)``
additional callbacks to register. Each *name* is an attribute of
the *callbacks_class* instance. (Each element cas also be just a *name*.)
:return: The *callbacks_class* instance. This object must be kept alive,
typically at module scope.
"""
# callbacks keeps these cdata objects alive at the python level
callbacks = callbacks_class(ffi)
extras = [extra if len(extra) == 2 else (extra, None) for extra in extras]
extras = tuple([(getattr(callbacks, name), error) for name, error in extras])
for (func, error_func) in ((callbacks.python_callback, None),
(callbacks.python_handle_error, None),
(callbacks.python_stop, None),
(callbacks.python_check_callback,
callbacks.check_callback_onerror),
(callbacks.python_prepare_callback,
callbacks.check_callback_onerror)) + extras:
for (func, error_func) in (
(callbacks.python_callback, None),
(callbacks.python_handle_error, None),
(callbacks.python_stop, None),
(callbacks.python_check_callback, callbacks.check_callback_onerror),
(callbacks.python_prepare_callback, callbacks.check_callback_onerror)
) + extras:
# The name of the callback function matches the 'extern Python' declaration.
error_func = error_func or callbacks.unhandled_onerror
callback = ffi.def_extern(onerror=error_func)(func)
......
......@@ -448,8 +448,8 @@ class loop(AbstractLoop):
if not self._queued_callbacks:
return False
cbs = list(self._queued_callbacks)
self._queued_callbacks = []
cbs = self._queued_callbacks[:]
del self._queued_callbacks[:]
for watcher_ptr, arg in cbs:
handle = watcher_ptr.data
......@@ -458,15 +458,23 @@ class loop(AbstractLoop):
assert not libuv.uv_is_active(watcher_ptr)
continue
val = _callbacks.python_callback(handle, arg)
if val == -1:
if val == -1: # Failure.
_callbacks.python_handle_error(handle, arg)
elif val == 1:
elif val == 1: # Success
if not libuv.uv_is_active(watcher_ptr):
if watcher_ptr.data != handle:
if watcher_ptr.data:
_callbacks.python_stop(None)
else:
_callbacks.python_stop(handle)
# The callback closed the watcher in C. Good.
# It's supposed to also reset the pointer to NULL at
# that same time. If it resets it to something else, we're
# re-using the same watcher object, and that's not correct either.
# Prevoiusly we checked for that case, but we shouldn't need to.
handle_after_callback = watcher_ptr.data
try:
if handle_after_callback:
_callbacks.python_stop(handle_after_callback)
if handle_after_callback != handle:
_callbacks.python_stop(handle)
finally:
watcher_ptr.data = ffi.NULL
return True
......
......@@ -125,7 +125,7 @@ class watcher(_base.watcher):
# Instead, this is arranged as a callback to GC when the
# watcher class dies. Obviously it's important to keep the ffi
# watcher alive.
# We can pass in "subclasses" if uv_handle_t that line up at the C level,
# We can pass in "subclasses" of uv_handle_t that line up at the C level,
# but that don't in CFFI without a cast. But be careful what we use the cast
# for, don't pass it back to C.
ffi_handle_watcher = cls._FFI.cast('uv_handle_t*', ffi_watcher)
......
......@@ -13,10 +13,11 @@
#include "cares_ntop.h"
#include "cares_pton.h"
#if PY_VERSION_HEX < 0x02060000
#define PyUnicode_FromString PyString_FromString
#elif PY_MAJOR_VERSION < 3
#define PyUnicode_FromString PyBytes_FromString
#if PY_MAJOR_VERSION >= 3
#define PY3K
#define GPyNative_FromString PyUnicode_FromString
#else
#define GPyNative_FromString PyString_FromString
#endif
......@@ -51,7 +52,7 @@ gevent_append_addr(PyObject* list, int family, void* src, char* tmpbuf, size_t t
int status = -1;
PyObject* tmp;
if (ares_inet_ntop(family, src, tmpbuf, tmpsize)) {
tmp = PyUnicode_FromString(tmpbuf);
tmp = GPyNative_FromString(tmpbuf);
if (tmp) {
status = PyList_Append(list, tmp);
Py_DECREF(tmp);
......@@ -64,7 +65,7 @@ gevent_append_addr(PyObject* list, int family, void* src, char* tmpbuf, size_t t
static PyObject*
parse_h_name(struct hostent *h)
{
return PyUnicode_FromString(h->h_name);
return GPyNative_FromString(h->h_name);
}
......@@ -81,7 +82,7 @@ parse_h_aliases(struct hostent *h)
for (pch = h->h_aliases; *pch != NULL; pch++) {
if (*pch != h->h_name && strcmp(*pch, h->h_name)) {
int status;
tmp = PyUnicode_FromString(*pch);
tmp = GPyNative_FromString(*pch);
if (tmp == NULL) {
break;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment