summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2014-11-23 10:53:37 +0100
committerVictor Stinner <victor.stinner@gmail.com>2014-11-23 10:53:37 +0100
commitbcbd9d96c29f43e9c8637b81ba951dcc68d6693f (patch)
tree7ab469a9ffbe611df5f5101a3ae11221a81b3fe6
parent75f79b477e2da91d805dc138ae5f8f6d9e507da5 (diff)
downloadaioeventlet-bcbd9d96c29f43e9c8637b81ba951dcc68d6693f.tar.gz
Fix to run an event loop in a thread different than the main thread in debug
mode: disable eventlet "debug_blocking", it is implemented with the SIGALRM signal, but signal handlers can only be set in the main thread. Add a test: run an event loop in a thread different than the main thread.
-rw-r--r--aiogreen.py11
-rw-r--r--doc/changelog.rst3
-rw-r--r--doc/status.rst2
-rw-r--r--doc/using.rst16
-rw-r--r--tests/test_thread.py47
5 files changed, 76 insertions, 3 deletions
diff --git a/aiogreen.py b/aiogreen.py
index 79d005f..dead9a4 100644
--- a/aiogreen.py
+++ b/aiogreen.py
@@ -229,8 +229,15 @@ class EventLoop(asyncio.SelectorEventLoop):
self._hub.debug_exceptions = debug
# Detect blocking eventlet functions. The feature is implemented with
- # signal.alarm() which is is not available on Windows.
- self._hub.debug_blocking = debug and (sys.platform != 'win32')
+ # signal.alarm() which is is not available on Windows. Signal handlers
+ # can only be set from the main loop. So detecting blocking functions
+ # cannot be used on Windows nor from a thread different than the main
+ # thread.
+ self._hub.debug_blocking = (
+ debug
+ and (sys.platform != 'win32')
+ and isinstance(threading.current_thread(), threading._MainThread))
+
if (self._hub.debug_blocking
and hasattr(self, 'slow_callback_duration')):
self._hub.debug_blocking_resolution = self.slow_callback_duration
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 7c98919..f932f61 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -12,6 +12,9 @@ Version 0.3 (development version)
greenthread of the aiogreen event loop.
* Fix eventlet detection of blocking tasks: cancel the alarm when the aiogreen
event loop stops.
+* Fix to run an event loop in a thread different than the main thread in debug
+ mode: disable eventlet "debug_blocking", it is implemented with the SIGALRM
+ signal, but signal handlers can only be set in the main thread.
2014-10-21: version 0.2
-----------------------
diff --git a/doc/status.rst b/doc/status.rst
index 545e974..c1fc995 100644
--- a/doc/status.rst
+++ b/doc/status.rst
@@ -12,7 +12,7 @@ To do
- signals
- subprocesses
-* run an event loop in a thread different than the main thread
+* experiment running an event loop in a thread different than the main thread
* tox.ini: test Python 3.3 with monkey-patching, see eventlet bug:
https://github.com/eventlet/eventlet/pull/168
diff --git a/doc/using.rst b/doc/using.rst
index 0956085..d36a34a 100644
--- a/doc/using.rst
+++ b/doc/using.rst
@@ -79,6 +79,22 @@ Hello World::
<https://docs.python.org/dev/library/asyncio.html>`_.
+Threads
+-------
+
+Running an event loop in a thread different than the main thread is currently
+experimental.
+
+An eventlet Event object is not thread-safe, it must only be used in the
+same thread. Use threading.Event to signal events between threads,
+and threading.Queue to pass data between threads.
+
+Use ``threading = eventlet.patcher.original('threading')`` to get the original
+threading instead of ``import threading``.
+
+It is not possible to run two aiogreen event loops in the same thread.
+
+
Debug mode
----------
diff --git a/tests/test_thread.py b/tests/test_thread.py
index 2c9caae..c41eeaa 100644
--- a/tests/test_thread.py
+++ b/tests/test_thread.py
@@ -55,6 +55,53 @@ class ThreadTests(tests.TestCase):
self.loop.run_until_complete(fut)
self.assertIsInstance(result['loop'], AssertionError)
+ def test_run_in_thread(self):
+ class LoopThread(threading.Thread):
+ def __init__(self, event):
+ super(LoopThread, self).__init__()
+ self.loop = None
+ self.event = event
+
+ def run(self):
+ self.loop = asyncio.new_event_loop()
+ try:
+ self.loop.set_debug(True)
+ asyncio.set_event_loop(self.loop)
+
+ self.event.set()
+ self.loop.run_forever()
+ finally:
+ self.loop.close()
+ asyncio.set_event_loop(None)
+
+ result = []
+
+ # start an event loop in a thread
+ event = threading.Event()
+ thread = LoopThread(event)
+ thread.start()
+ event.wait()
+ loop = thread.loop
+
+ def func(loop):
+ result.append(threading.current_thread().ident)
+ loop.stop()
+
+ # FIXME: call_soon() must raise an exception if if the main thread
+ # has no event loop, bugs.python.org/issue22926
+ #self.loop.close()
+ #asyncio.set_event_loop(None)
+ # call_soon() must fail when called from the wrong thread
+ self.assertRaises(RuntimeError, loop.call_soon, func, loop)
+
+ # call func() in a different thread using the event loop
+ tid = thread.ident
+ loop.call_soon_threadsafe(func, loop)
+
+ # stop the event loop
+ thread.join()
+ self.assertEqual(result, [tid])
+
if __name__ == '__main__':
import unittest