You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildstream.apache.org by no...@apache.org on 2020/12/29 12:45:09 UTC

[buildstream] 11/16: basic user SIG interrupt handling in Stream

This is an automated email from the ASF dual-hosted git repository.

not-in-ldap pushed a commit to branch tpollard/buildsubtemp
in repository https://gitbox.apache.org/repos/asf/buildstream.git

commit 7870bc3bcbc958e07fc9cbdb320418209a489113
Author: Tom Pollard <to...@codethink.co.uk>
AuthorDate: Mon Oct 21 16:24:33 2019 +0100

    basic user SIG interrupt handling in Stream
---
 src/buildstream/_scheduler/scheduler.py |  9 +++++++--
 src/buildstream/_stream.py              | 21 +++++++++++++++++++--
 2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/src/buildstream/_scheduler/scheduler.py b/src/buildstream/_scheduler/scheduler.py
index 854921b..74a688f 100644
--- a/src/buildstream/_scheduler/scheduler.py
+++ b/src/buildstream/_scheduler/scheduler.py
@@ -70,6 +70,7 @@ class NotificationType(FastEnum):
     TASK_GROUPS = "task_groups"
     ELEMENT_TOTALS = "element_totals"
     FINISH = "finish"
+    SIGTSTP = "sigstp"
 
 
 # Notification()
@@ -536,8 +537,10 @@ class Scheduler():
         if self.terminated:
             return
 
-        notification = Notification(NotificationType.INTERRUPT)
-        self._notify_front(notification)
+        if not self._notify_front_queue:
+            # Not running in a subprocess, scheduler process to handle keyboard interrupt
+            notification = Notification(NotificationType.INTERRUPT)
+            self._notify_front(notification)
 
     # _terminate_event():
     #
@@ -629,6 +632,8 @@ class Scheduler():
             self.jobs_unsuspended()
         elif notification.notification_type == NotificationType.RETRY:
             self._failure_retry(notification.job_action, notification.element)
+        elif notification.notification_type == NotificationType.SIGTSTP:
+            self._suspend_event()
         else:
             raise ValueError("Unrecognised notification type received")
 
diff --git a/src/buildstream/_stream.py b/src/buildstream/_stream.py
index 8bae8ec..866cbd7 100644
--- a/src/buildstream/_stream.py
+++ b/src/buildstream/_stream.py
@@ -30,6 +30,7 @@ import shutil
 import tarfile
 import tempfile
 import queue
+import signal
 from contextlib import contextmanager, suppress
 from fnmatch import fnmatch
 
@@ -150,17 +151,17 @@ class Stream():
         # We can now launch another async
         self.loop = asyncio.new_event_loop()
         asyncio.set_event_loop(self.loop)
+        self._connect_signals()
         self._start_listening()
         self.loop.set_exception_handler(self._handle_exception)
         self._watch_casd()
         self.loop.run_forever()
 
-        # TODO connect signal handlers with asyncio
-
         # Scheduler has stopped running, so safe to still have async here
         self._stop_listening()
         self._stop_watching_casd()
         self.loop.close()
+        self._disconnect_signals()
         self.loop = None
         self._subprocess.join()
         self._subprocess = None
@@ -1221,10 +1222,14 @@ class Stream():
         # Send the notification to suspend jobs
         notification = Notification(NotificationType.SUSPEND)
         self._notify_back(notification)
+        # Disconnect signals if stream is handling them
+        self._disconnect_signals()
         yield
         # Unsuspend jobs on context exit
         notification = Notification(NotificationType.UNSUSPEND)
         self._notify_back(notification)
+        # Connect signals if stream is handling them
+        self._connect_signals()
 
     #############################################################
     #                    Private Methods                        #
@@ -1835,6 +1840,18 @@ class Stream():
         self._context._subprocess_exception = exception
         self.loop.stop()
 
+    def _connect_signals(self):
+        if self.loop:
+            self.loop.add_signal_handler(signal.SIGINT, self._interrupt_callback)
+            self.loop.add_signal_handler(signal.SIGTERM, lambda: self._notify_back(Notification(NotificationType.TERMINATE)))
+            self.loop.add_signal_handler(signal.SIGTSTP, lambda: self._notify_back(Notification(NotificationType.SIGTSTP)))
+
+    def _disconnect_signals(self):
+        if self.loop:
+            self.loop.remove_signal_handler(signal.SIGINT)
+            self.loop.remove_signal_handler(signal.SIGTSTP)
+            self.loop.remove_signal_handler(signal.SIGTERM)
+
     def __getstate__(self):
         # The only use-cases for pickling in BuildStream at the time of writing
         # are enabling the 'spawn' method of starting child processes, and