Discussion:
[gentoo-portage-dev] [PATCH] AsyncioEventLoop: exit after unhandled exception (bug 658684)
Zac Medico
2018-06-22 15:51:45 UTC
Permalink
Fix portage commands to exit immediately for any unhandled
exceptions that are raised while the asyncio event loop is running
without a tty. If we have a tty then start the debugger,
since in might aid in diagnosis of the problem.

In order to avoid potential interference with API consumers, do not
call set_exception_handler unless portage._internal_caller is True.

Bug: https://bugs.gentoo.org/658684
---
pym/portage/util/_eventloop/asyncio_event_loop.py | 31 +++++++++++++++++++++++
1 file changed, 31 insertions(+)

diff --git a/pym/portage/util/_eventloop/asyncio_event_loop.py b/pym/portage/util/_eventloop/asyncio_event_loop.py
index c07b71103c..ea0e03b23b 100644
--- a/pym/portage/util/_eventloop/asyncio_event_loop.py
+++ b/pym/portage/util/_eventloop/asyncio_event_loop.py
@@ -2,7 +2,9 @@
# Distributed under the terms of the GNU General Public License v2

import os
+import pdb
import signal
+import sys

try:
import asyncio as _real_asyncio
@@ -53,6 +55,35 @@ class AsyncioEventLoop(_AbstractEventLoop):
self.get_debug = loop.get_debug
self._wakeup_fd = -1

+ if portage._internal_caller:
+ loop.set_exception_handler(self._internal_caller_exception_handler)
+
+ @staticmethod
+ def _internal_caller_exception_handler(loop, context):
+ """
+ An exception handler which drops to a pdb shell if std* streams
+ refer to a tty, and otherwise kills the process with SIGTERM.
+
+ In order to avoid potential interference with API consumers, this
+ implementation is only used when portage._internal_caller is True.
+ """
+ loop.default_exception_handler(context)
+ if 'exception' in context:
+ # If we have a tty then start the debugger, since in might
+ # aid in diagnosis of the problem. If there's no tty, then
+ # exit immediately.
+ if all(s.isatty() for s in (sys.stdout, sys.stderr, sys.stdin)):
+ pdb.set_trace()
+ else:
+ # Normally emerge will wait for all coroutines to complete
+ # after SIGTERM has been received. However, an unhandled
+ # exception will prevent the interrupted coroutine from
+ # completing, therefore use the default SIGTERM handler
+ # in order to ensure that emerge exits immediately (though
+ # uncleanly).
+ signal.signal(signal.SIGTERM, signal.SIG_DFL)
+ os.kill(os.getpid(), signal.SIGTERM)
+
def _create_future(self):
"""
Provide AbstractEventLoop.create_future() for python3.4.
--
2.13.6
Loading...