Skip to content

Commit

Permalink
Merge pull request gevent#1111 from gevent/libev-cffi-cleanup
Browse files Browse the repository at this point in the history
Cleanup the libev destroyed data like the libuv destroyed data.
  • Loading branch information
jamadden authored Feb 20, 2018
2 parents b3eebab + 92d7779 commit a5f7a83
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 60 deletions.
13 changes: 5 additions & 8 deletions src/gevent/_ffi/loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,6 @@ def assign_standard_callbacks(ffi, lib, callbacks_class, extras=()): # pylint:di



_default_loop_destroyed = False


_NOARGS = ()

Expand All @@ -320,9 +318,6 @@ class AbstractLoop(object):
# whether they were the default loop.
_default = None

# A class variable.
_default_loop_destroyed = False

def __init__(self, ffi, lib, watchers, flags=None, default=None):
self._ffi = ffi
self._lib = lib
Expand Down Expand Up @@ -493,21 +488,23 @@ def destroy(self):
try:
if not self._can_destroy_loop(self._ptr):
return False
self._destroyed_loop(self._ptr)
self._stop_aux_watchers()

self._destroy_loop(self._ptr)
finally:
# not ffi.NULL, we don't want something that can be
# passed to C and crash later. This will create nice friendly
# TypeError from CFFI.
self._ptr = None
del self._handle_to_self
del self._callbacks
del self._keepaliveset

return True

def _can_destroy_loop(self, ptr):
raise NotImplementedError()

def _destroyed_loop(self, ptr):
def _destroy_loop(self, ptr):
raise NotImplementedError()

@property
Expand Down
4 changes: 4 additions & 0 deletions src/gevent/libev/_corecffi_cdef.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,7 @@ extern "Python" {
* object.
*/
static void _gevent_generic_callback(struct ev_loop* loop, struct ev_watcher* watcher, int revents);

static void gevent_zero_check(struct ev_check* handle);
static void gevent_zero_timer(struct ev_timer* handle);
static void gevent_zero_prepare(struct ev_prepare* handle);
15 changes: 15 additions & 0 deletions src/gevent/libev/_corecffi_source.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,18 @@ static void _gevent_generic_callback(struct ev_loop* loop,
// closing the watcher?
}
}

static void gevent_zero_timer(struct ev_timer* handle)
{
memset(handle, 0, sizeof(struct ev_timer));
}

static void gevent_zero_check(struct ev_check* handle)
{
memset(handle, 0, sizeof(struct ev_check));
}

static void gevent_zero_prepare(struct ev_prepare* handle)
{
memset(handle, 0, sizeof(struct ev_prepare));
}
20 changes: 14 additions & 6 deletions src/gevent/libev/corecffi.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,29 +265,37 @@ def _stop_aux_watchers(self):
if libev.ev_is_active(self._check):
self.ref()
libev.ev_check_stop(self._ptr, self._check)
if libev.ev_is_active(self._timer0):
libev.ev_timer_stop(self._timer0)

def _setup_for_run_callback(self):
self.ref() # we should go through the loop now

def destroy(self):
if self._ptr:
ptr = self._ptr

should_destroy_loop = super(loop, self).destroy()
super(loop, self).destroy()

if globals()["__SYSERR_CALLBACK"] == self._handle_syserr:
set_syserr_cb(None)

if should_destroy_loop:
libev.ev_loop_destroy(ptr)

def _can_destroy_loop(self, ptr):
# Is it marked as destroyed?
return libev.ev_userdata(ptr)

def _destroyed_loop(self, ptr):
def _destroy_loop(self, ptr):
# Mark as destroyed.
libev.ev_set_userdata(ptr, ffi.NULL)
libev.ev_loop_destroy(ptr)

libev.gevent_zero_prepare(self._prepare)
libev.gevent_zero_check(self._check)
libev.gevent_zero_timer(self._timer0)

del self._prepare
del self._check
del self._timer0


@property
def MAXPRI(self):
Expand Down
82 changes: 36 additions & 46 deletions src/gevent/libuv/loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,51 +266,6 @@ def _setup_for_run_callback(self):
self._start_callback_timer()
libuv.uv_ref(self._timer0)

def destroy(self):
if self._ptr:
ptr = self._ptr
should_destroy = super(loop, self).destroy()
ptr.data = ffi.NULL
assert self._ptr is None
libuv.uv_stop(ptr)
if not should_destroy:
# The default loop has already been destroyed.
# libuv likes to abort() the process in this case.
return

libuv.gevent_close_all_handles(ptr)

closed_failed = libuv.uv_loop_close(ptr)
if closed_failed:
assert closed_failed == libuv.UV_EBUSY
# We already closed all the handles. Run the loop
# once to let them be cut off from the loop.
ran_has_more_callbacks = libuv.uv_run(ptr, libuv.UV_RUN_ONCE)
if ran_has_more_callbacks:
libuv.uv_run(ptr, libuv.UV_RUN_NOWAIT)
closed_failed = libuv.uv_loop_close(ptr)
assert closed_failed == 0, closed_failed

# Destroy the native resources *after* we have closed
# the loop. If we do it before, walking the handles
# attached to the loop is likely to segfault.

libuv.gevent_zero_check(self._check)
libuv.gevent_zero_check(self._timer0)
libuv.gevent_zero_prepare(self._prepare)
libuv.gevent_zero_timer(self._signal_idle)
del self._check
del self._prepare
del self._signal_idle
del self._timer0

libuv.gevent_zero_loop(ptr)

# Destroy any watchers we're still holding on to.
del self._io_watchers
del self._fork_watchers
del self._child_watchers


def _can_destroy_loop(self, ptr):
# We're being asked to destroy a loop that's,
Expand All @@ -320,8 +275,43 @@ def _can_destroy_loop(self, ptr):
# We track this in the data member.
return ptr.data

def _destroyed_loop(self, ptr):
def _destroy_loop(self, ptr):
ptr.data = ffi.NULL
libuv.uv_stop(ptr)

libuv.gevent_close_all_handles(ptr)

closed_failed = libuv.uv_loop_close(ptr)
if closed_failed:
assert closed_failed == libuv.UV_EBUSY
# We already closed all the handles. Run the loop
# once to let them be cut off from the loop.
ran_has_more_callbacks = libuv.uv_run(ptr, libuv.UV_RUN_ONCE)
if ran_has_more_callbacks:
libuv.uv_run(ptr, libuv.UV_RUN_NOWAIT)
closed_failed = libuv.uv_loop_close(ptr)
assert closed_failed == 0, closed_failed

# Destroy the native resources *after* we have closed
# the loop. If we do it before, walking the handles
# attached to the loop is likely to segfault.

libuv.gevent_zero_check(self._check)
libuv.gevent_zero_check(self._timer0)
libuv.gevent_zero_prepare(self._prepare)
libuv.gevent_zero_timer(self._signal_idle)
del self._check
del self._prepare
del self._signal_idle
del self._timer0

libuv.gevent_zero_loop(ptr)

# Destroy any watchers we're still holding on to.
del self._io_watchers
del self._fork_watchers
del self._child_watchers


def debug(self):
"""
Expand Down

0 comments on commit a5f7a83

Please sign in to comment.