summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2014-11-21 01:37:36 +0100
committerVictor Stinner <victor.stinner@gmail.com>2014-11-21 01:37:36 +0100
commit77ca2876b20591dc450e5a3137804182d9fb0fd8 (patch)
treee29f2ae0405473d22be21f11703398e40ba6737e
parent97a2f28ba38ec6231c21bb4f92758c27c39d1ee4 (diff)
downloadaioeventlet-77ca2876b20591dc450e5a3137804182d9fb0fd8.tar.gz
In debug mode, detect calls to call_soon() from greenthreads which are not
threadsafe (would not wake up the event loop).
-rw-r--r--README18
-rw-r--r--aiogreen.py8
-rw-r--r--tests/test_eventlet.py26
3 files changed, 51 insertions, 1 deletions
diff --git a/README b/README
index cbb2ca0..d316cee 100644
--- a/README
+++ b/README
@@ -4,6 +4,22 @@ asyncio event loop scheduling callbacks in eventlet.
* aiogreen at PyPI: https://pypi.python.org/pypi/aiogreen
+Usage
+=====
+
+aiogreen implements the asyncio API, see asyncio documentation:
+https://docs.python.org/dev/library/asyncio.html
+
+To support Python 2, you can use Trollius which uses ``yield`` instead
+of ``yield from`` for coroutines:
+http://trollius.readthedocs.org/
+
+Using the event loop from greenthreads is not safe: calls to the event loop
+must be passed to ``call_soon_threadsafe()``. Example to stop the event loop:
+
+ eventlet.spawn(loop.call_soon_threadsafe, loop.stop)
+
+
Installation
============
@@ -87,6 +103,8 @@ Changes:
in eventlet.
* add_reader() and add_writer() now cancels the previous handle and sets
a new handle
+* In debug mode, detect calls to call_soon() from greenthreads which are not
+ threadsafe (would not wake up the event loop).
2014-11-19: version 0.1
-----------------------
diff --git a/aiogreen.py b/aiogreen.py
index a57a47a..3a46956 100644
--- a/aiogreen.py
+++ b/aiogreen.py
@@ -199,6 +199,14 @@ class EventLoop(asyncio.SelectorEventLoop):
def time(self):
return self._hub.clock()
+ def _assert_is_current_event_loop(self):
+ super(EventLoop, self)._assert_is_current_event_loop()
+ if self._selector._event:
+ # call_soon() must not be called while selector.select() is
+ # running, it does not wake up the event loop
+ raise RuntimeError(
+ "Non-thread-safe operation invoked from a greenthread")
+
class EventLoopPolicy(asyncio.DefaultEventLoopPolicy):
_loop_factory = EventLoop
diff --git a/tests/test_eventlet.py b/tests/test_eventlet.py
index d50c8ee..9e6b699 100644
--- a/tests/test_eventlet.py
+++ b/tests/test_eventlet.py
@@ -2,6 +2,30 @@ import eventlet
import tests
class EventletTests(tests.TestCase):
+ def test_stop(self):
+ def func():
+ eventlet.spawn(self.loop.call_soon_threadsafe, self.loop.stop)
+
+ def schedule_greenthread():
+ eventlet.spawn(func)
+
+ self.loop.call_soon(schedule_greenthread)
+ self.loop.run_forever()
+
+ def test_soon(self):
+ result = []
+
+ def func():
+ result.append("spawn")
+ self.loop.call_soon_threadsafe(self.loop.stop)
+
+ def schedule_greenthread():
+ eventlet.spawn(func)
+
+ self.loop.call_soon(schedule_greenthread)
+ self.loop.run_forever()
+ self.assertEqual(result, ["spawn"])
+
def test_soon_spawn(self):
result = []
@@ -14,7 +38,7 @@ class EventletTests(tests.TestCase):
def schedule_greenthread():
eventlet.spawn(func1)
- eventlet.spawn_after(0.001, func2)
+ eventlet.spawn_after(0.010, func2)
self.loop.call_soon(schedule_greenthread)
self.loop.run_forever()