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