Browse Source

Merge pull request #8167 from ThomasWaldmann/debounce-sigint-1.4

fix Ctrl-C / SIGINT behaviour for pyinstaller-made binaries, fixes #8155
TW 1 year ago
parent
commit
00fd3708ac
1 changed files with 18 additions and 4 deletions
  1. 18 4
      src/borg/helpers/process.py

+ 18 - 4
src/borg/helpers/process.py

@@ -188,6 +188,8 @@ class SigIntManager:
         self._action_triggered = False
         self._action_done = False
         self.ctx = signal_handler('SIGINT', self.handler)
+        self.debounce_interval = 20000000  # ns
+        self.last = None  # monotonic time when we last processed SIGINT
 
     def __bool__(self):
         # this will be True (and stay True) after the first Ctrl-C/SIGINT
@@ -208,10 +210,22 @@ class SigIntManager:
         self._action_done = True
 
     def handler(self, sig_no, stack):
-        # handle the first ctrl-c / SIGINT.
-        self.__exit__(None, None, None)
-        self._sig_int_triggered = True
-        self._action_triggered = True
+        # Ignore a SIGINT if it comes too quickly after the last one, e.g. because it
+        # was caused by the same Ctrl-C key press and a parent process forwarded it to us.
+        # This can easily happen for the pyinstaller-made binaries because the bootloader
+        # process and the borg process are in same process group (see #8155), but maybe also
+        # under other circumstances.
+        now = time.monotonic_ns()
+        if self.last is None:  # first SIGINT
+            self.last = now
+            self._sig_int_triggered = True
+            self._action_triggered = True
+        elif now - self.last >= self.debounce_interval:  # second SIGINT
+            # restore the original signal handler for the 3rd+ SIGINT -
+            # this implies that this handler here loses control!
+            self.__exit__(None, None, None)
+            # handle 2nd SIGINT like the default handler would do it:
+            raise KeyboardInterrupt  # python docs say this might show up at an arbitrary place.
 
     def __enter__(self):
         self.ctx.__enter__()