Commit af8e9dac authored by Jason Madden's avatar Jason Madden

Simplify libev default loop handling and let it regenerate itself.

parent 9bdd5317
......@@ -64,6 +64,11 @@
extension implementation only) deallocated (garbage collected). See
:issue:`1098`.
- Simplify handling of the libev default loop and the ``destroy()``
method. The default loop, when destroyed, can again be requested and
it will regenerate itself. The default loop is the only one that can
receive child events.
1.3a1 (2018-01-27)
==================
......
......@@ -487,12 +487,11 @@ class AbstractLoop(object):
"""
if self._ptr:
try:
if self._default:
if not self._can_destroy_default_loop():
return False
type(self)._default_loop_destroyed = True
if not self._can_destroy_loop(self._ptr):
return False
self._destroyed_loop(self._ptr)
self._stop_aux_watchers()
finally:
# not ffi.NULL, we don't want something that can be
# passed to C and crash later. This will create nice friendly
......@@ -501,8 +500,11 @@ class AbstractLoop(object):
return True
def _can_destroy_default_loop(self):
return not type(self)._default_loop_destroyed
def _can_destroy_loop(self, ptr):
raise NotImplementedError()
def _destroyed_loop(self, ptr):
raise NotImplementedError()
@property
def ptr(self):
......
......@@ -259,8 +259,6 @@ cdef bint _check_loop(loop loop) except -1:
return 1
cdef bint _default_loop_destroyed = False
cdef public class callback [object PyGeventCallbackObject, type PyGeventCallback_Type]:
cdef public object callback
......@@ -419,8 +417,6 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]:
c_flags |= libev.EVFLAG_FORKCHECK
if default is None:
default = True
if _default_loop_destroyed:
default = False
if default:
self._default = 1
self._ptr = libev.gevent_ev_default_loop(c_flags)
......@@ -436,6 +432,8 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]:
if default or __SYSERR_CALLBACK is None:
set_syserr_cb(self._handle_syserr)
# Mark as not destroyed
libev.ev_set_userdata(self._ptr, self._ptr)
libev.ev_prepare_start(self._ptr, &self._prepare)
libev.ev_unref(self._ptr)
......@@ -489,36 +487,35 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]:
libev.ev_timer_stop(ptr, &self._periodic_signal_checker)
def destroy(self):
global _default_loop_destroyed
cdef libev.ev_loop* ptr = self._ptr
self._ptr = NULL
if ptr:
if self._default and _default_loop_destroyed:
# Whoops! Program error. They destroyed the default loop,
if not libev.ev_userdata(ptr):
# Whoops! Program error. They destroyed the loop,
# using a different loop object. Our _ptr is still
# valid, but the libev loop is gone. Doing anything
# else with it will likely cause a crash.
return
# Mark as destroyed
libev.ev_set_userdata(ptr, NULL)
self._stop_watchers(ptr)
if __SYSERR_CALLBACK == self._handle_syserr:
set_syserr_cb(None)
if self._default:
_default_loop_destroyed = True
libev.ev_loop_destroy(ptr)
def __dealloc__(self):
cdef libev.ev_loop* ptr = self._ptr
self._ptr = NULL
if ptr != NULL:
if self._default and _default_loop_destroyed:
if not libev.ev_userdata(ptr):
# See destroy(). This is a bug in the caller.
return
self._stop_watchers(ptr)
if not self._default:
libev.ev_loop_destroy(ptr)
# Mark as destroyed
libev.ev_set_userdata(ptr, NULL)
@property
def ptr(self):
......
......@@ -223,9 +223,6 @@ class loop(AbstractLoop):
c_flags |= libev.EVFLAG_FORKCHECK
if default is None:
default = True
if loop._default_loop_destroyed:
default = False
if default:
ptr = libev.gevent_ev_default_loop(c_flags)
if not ptr:
......@@ -237,6 +234,8 @@ class loop(AbstractLoop):
if default or globals()["__SYSERR_CALLBACK"] is None:
set_syserr_cb(self._handle_syserr)
# Mark this loop as being used.
libev.ev_set_userdata(ptr, ptr)
return ptr
def _init_and_start_check(self):
......@@ -282,6 +281,14 @@ class loop(AbstractLoop):
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):
# Mark as destroyed.
libev.ev_set_userdata(ptr, ffi.NULL)
@property
def MAXPRI(self):
return libev.EV_MAXPRI
......
......@@ -202,6 +202,8 @@ cdef extern from "libev.h" nogil:
ev_loop* ev_default_loop(unsigned int flags)
ev_loop* ev_loop_new(unsigned int flags)
void* ev_userdata(ev_loop*)
void ev_set_userdata(ev_loop*, void*)
void ev_loop_destroy(ev_loop*)
void ev_loop_fork(ev_loop*)
int ev_is_default_loop(ev_loop*)
......
......@@ -68,8 +68,6 @@ class loop(AbstractLoop):
# know this in general on libev
min_sleep_time = 0.001 # 1ms
DEFAULT_LOOP_REGENERATES = True
error_handler = None
_CHECK_POINTER = 'uv_check_t *'
......@@ -296,14 +294,16 @@ class loop(AbstractLoop):
closed_failed = libuv.uv_loop_close(ptr)
assert closed_failed == 0, closed_failed
def _can_destroy_default_loop(self):
def _can_destroy_loop(self, ptr):
# We're being asked to destroy a loop that's,
# at the time it was constructed, was the default loop.
# If loop objects were constructed more than once,
# it may have already been destroyed, though.
# We track this in the data member.
return self._ptr.data
return ptr.data
def _destroyed_loop(self, ptr):
ptr.data = ffi.NULL
def debug(self):
"""
......
......@@ -19,6 +19,9 @@ class TestCore(unittest.TestCase):
class TestWatchers(unittest.TestCase):
def makeOne(self):
return core.loop()
def test_io(self):
if sys.platform == 'win32':
# libev raises IOError, libuv raises ValueError
......@@ -28,17 +31,17 @@ class TestWatchers(unittest.TestCase):
Error = ValueError
win32 = False
with self.assertRaises(Error):
core.loop().io(-1, 1)
self.makeOne().io(-1, 1)
if hasattr(core, 'TIMER'):
# libev
with self.assertRaises(ValueError):
core.loop().io(1, core.TIMER)
self.makeOne().io(1, core.TIMER)
# Test we can set events and io before it's started
if not win32:
# We can't do this with arbitrary FDs on windows;
# see libev_vfd.h
io = core.loop().io(1, core.READ)
io = self.makeOne().io(1, core.READ)
io.fd = 2
self.assertEqual(io.fd, 2)
io.events = core.WRITE
......@@ -47,15 +50,29 @@ class TestWatchers(unittest.TestCase):
self.assertEqual(core._events_to_str(io.events), 'WRITE|_IOFDSET')
else:
self.assertEqual(core._events_to_str(io.events), 'WRITE')
io.start(lambda: None)
io.close()
def test_timer_constructor(self):
with self.assertRaises(ValueError):
core.loop().timer(1, -1)
self.makeOne().timer(1, -1)
def test_signal_constructor(self):
with self.assertRaises(ValueError):
core.loop().signal(1000)
self.makeOne().signal(1000)
class TestWatchersDefault(TestWatchers):
def makeOne(self):
return core.loop(default=True)
class TestWatchersDefaultDestroyed(TestWatchers):
def makeOne(self):
l = core.loop(default=True)
l.destroy()
del l
return core.loop(default=True)
@skipOnLibuv("Tests for libev-only functions")
class TestLibev(unittest.TestCase):
......
......@@ -40,13 +40,10 @@ class TestDestroyHub(unittest.TestCase):
self.assertIsNot(hub.loop.ptr, initloop.ptr)
self.assertNotEqual(hub.loop.ptr, initloop.ptr)
# Destroy hub including default loop, create new hub with non-default loop.
# Destroy hub including default loop. The default loop regenerates.
hub.destroy(destroy_loop=True)
hub = gevent.get_hub()
if not getattr(hub.loop, 'DEFAULT_LOOP_REGENERATES', False):
self.assertFalse(hub.loop.default)
else:
self.assertTrue(hub.loop.default)
self.assertTrue(hub.loop.default)
hub.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