Commit 77aabaa8 authored by Jason Madden's avatar Jason Madden

(hopefully) fix a crash destroying loops in test__core.py; only seen on...

(hopefully) fix a crash destroying loops in test__core.py; only seen on Windows, but with some tweaks I could reproduce it locally.
parent c6637d27
......@@ -236,6 +236,10 @@ class AbstractCallbacks(object):
def python_prepare_callback(self, watcher_ptr):
loop = self._find_loop_from_c_watcher(watcher_ptr)
if loop is None: # pragma: no cover
print("WARNING: gevent: running prepare callbacks from a destroyed handle: ",
watcher_ptr)
return
loop._run_callbacks()
def check_callback_onerror(self, t, v, tb):
......
......@@ -383,3 +383,7 @@ static void _gevent_fs_poll_callback3(void* handle, int status, const uv_stat_t*
static void gevent_uv_walk_callback_close(uv_handle_t* handle, void* arg);
static void gevent_close_all_handles(uv_loop_t* loop);
static void gevent_zero_timer(uv_timer_t* handle);
static void gevent_zero_prepare(uv_prepare_t* handle);
static void gevent_zero_check(uv_check_t* handle);
static void gevent_zero_loop(uv_loop_t* handle);
......@@ -120,3 +120,23 @@ static void gevent_close_all_handles(uv_loop_t* loop)
{
uv_walk(loop, gevent_uv_walk_callback_close, NULL);
}
static void gevent_zero_timer(uv_timer_t* handle)
{
memset(handle, 0, sizeof(uv_timer_t));
}
static void gevent_zero_check(uv_check_t* handle)
{
memset(handle, 0, sizeof(uv_check_t));
}
static void gevent_zero_prepare(uv_prepare_t* handle)
{
memset(handle, 0, sizeof(uv_prepare_t));
}
static void gevent_zero_loop(uv_loop_t* handle)
{
memset(handle, 0, sizeof(uv_loop_t));
}
......@@ -27,7 +27,7 @@ class _Callbacks(AbstractCallbacks):
def _find_loop_from_c_watcher(self, watcher_ptr):
loop_handle = ffi.cast('uv_handle_t*', watcher_ptr).data
return self.from_handle(loop_handle)
return self.from_handle(loop_handle) if loop_handle else None
def python_sigchld_callback(self, watcher_ptr, _signum):
self.from_handle(ffi.cast('uv_handle_t*', watcher_ptr).data)._sigchld_callback()
......@@ -253,6 +253,7 @@ class loop(AbstractLoop):
assert self._signal_idle
libuv.uv_prepare_stop(self._prepare)
libuv.uv_ref(self._prepare) # Why are we doing this?
libuv.uv_check_stop(self._check)
libuv.uv_ref(self._check)
......@@ -277,15 +278,13 @@ class loop(AbstractLoop):
# 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
# Walk the open handlers, close them, then
# run the loop once to clear them out and
# close again.
libuv.gevent_close_all_handles(ptr)
# 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)
......@@ -295,11 +294,18 @@ class loop(AbstractLoop):
# 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.
del self._prepare
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
......
......@@ -88,11 +88,12 @@ class TestWatchersDefault(TestWatchers):
def destroyOne(self, loop):
return
# XXX: This crash may be fixed
@greentest.skipOnLibuvOnPyPyOnWin("This crashes with PyPy 5.10.0, only on Windows. "
"See https://ci.appveyor.com/project/denik/gevent/build/1.0.1380/job/lrlvid6mkjtyrhn5#L1103")
class TestWatchersDefaultDestroyed(TestWatchers):
def makeOne(self):
def _makeOne(self):
# pylint: disable=no-member
l = core.loop(default=True)
l.destroy()
......
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