Home | History | Annotate | Download | only in gslib
      1 # -*- coding: utf-8 -*-
      2 # Copyright 2015 Google Inc. All Rights Reserved.
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #     http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 """Signal handling functions."""
     16 
     17 from __future__ import absolute_import
     18 
     19 import signal
     20 from gslib.util import IS_WINDOWS
     21 
     22 
     23 # Maps from signal_num to list of signal handlers to call.
     24 _non_final_signal_handlers = {}
     25 # Maps from signal_num to the final signal handler (if any) that should be
     26 # called for that signal.
     27 _final_signal_handlers = {}
     28 
     29 
     30 def RegisterSignalHandler(signal_num, handler, is_final_handler=False):
     31   """Registers a handler for signal signal_num.
     32 
     33   Unlike calling signal.signal():
     34     - This function can be called from any thread (and will cause the handler to
     35       be run by the main thread when the signal is received).
     36     - Handlers are cumulative: When a given signal is received, all registered
     37       handlers will be executed (with the exception that only the last handler
     38       to register with is_final_handler=True will be called).
     39 
     40   Handlers should make no ordering assumptions, other than that the last handler
     41   to register with is_final_handler=True will be called after all the other
     42   handlers.
     43 
     44   Args:
     45     signal_num: The signal number with which to associate handler.
     46     handler: The handler.
     47     is_final_handler: Bool indicator whether handler should be called last among
     48                       all the handlers for this signal_num. The last handler to
     49                       register this way survives; other handlers registered with
     50                       is_final_handler=True will not be called when the signal
     51                       is received.
     52   Raises:
     53     RuntimeError: if attempt is made to register a signal_num not in
     54         GetCaughtSignals.
     55   """
     56   if signal_num not in GetCaughtSignals():
     57     raise RuntimeError('Attempt to register handler (%s) for signal %d, which '
     58                        'is not in GetCaughtSignals' % (handler, signal_num))
     59   if is_final_handler:
     60     _final_signal_handlers[signal_num] = handler
     61   else:
     62     _non_final_signal_handlers[signal_num].append(handler)
     63 
     64 
     65 def _SignalHandler(signal_num, cur_stack_frame):
     66   """Global signal handler.
     67 
     68   When a signal is caught we execute each registered handler for that signal.
     69 
     70   Args:
     71     signal_num: Signal that was caught.
     72     cur_stack_frame: Unused.
     73   """
     74   if signal_num in _non_final_signal_handlers:
     75     for handler in _non_final_signal_handlers[signal_num]:
     76       handler(signal_num, cur_stack_frame)
     77   if signal_num in _final_signal_handlers:
     78     _final_signal_handlers[signal_num](signal_num, cur_stack_frame)
     79 
     80 
     81 def InitializeSignalHandling():
     82   """Initializes global signal handling.
     83 
     84   Sets up global signal handler for each signal we handle.
     85   """
     86   for signal_num in GetCaughtSignals():
     87     _non_final_signal_handlers[signal_num] = []
     88     # Make main signal handler catch the signal.
     89     signal.signal(signal_num, _SignalHandler)
     90 
     91 
     92 def GetCaughtSignals():
     93   """Returns terminating signals that can be caught on this OS platform."""
     94   signals = [signal.SIGINT, signal.SIGTERM]
     95   if not IS_WINDOWS:
     96     # Windows doesn't have SIGQUIT.
     97     signals.append(signal.SIGQUIT)
     98   return signals
     99 
    100