~bzr-pqm/bzr/bzr.dev

6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
1
# Copyright (C) 2011 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
"""Signal handling for the smart server code."""
18
19
import signal
20
import weakref
21
22
from bzrlib import trace
23
24
25
# I'm pretty sure this has to be global, since signal handling is per-process.
6133.4.45 by John Arbash Meinel
Change the code a bit.
26
_on_sighup = None
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
27
# TODO: Using a dict means that the order of calls is unordered. We could use a
28
#       list and then do something like LIFO ordering. A dict was chosen so
29
#       that you could have a key to easily remove your entry. However, you
30
#       could just use the callable itself as the indexed part, and even in
31
#       large cases, we shouldn't have more than 100 or so callbacks
32
#       registered.
33
def _sighup_handler(signal_number, interrupted_frame):
6133.4.46 by John Arbash Meinel
Test that signals.install_sighup_handler does what we want.
34
    """This is the actual function that is registered for handling SIGHUP.
35
36
    It will call out to all the registered functions, letting them know that a
37
    graceful termination has been requested.
38
    """
6133.4.45 by John Arbash Meinel
Change the code a bit.
39
    if _on_sighup is None:
40
        return
6133.4.47 by John Arbash Meinel
Testing that 'bzr serve' actually installs SIGHUP and responds to it showed some problems.
41
    trace.mutter('Caught SIGHUP, sending graceful shutdown requests.')
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
42
    for ref in _on_sighup.valuerefs():
43
        try:
44
            cb = ref()
45
            if cb is not None:
46
                cb()
47
        except KeyboardInterrupt:
48
            raise
49
        except Exception:
50
            trace.mutter('Error occurred while running SIGHUP handlers:')
51
            trace.log_exception_quietly()
52
53
54
def install_sighup_handler():
55
    """Setup a handler for the SIGHUP signal."""
6133.4.48 by John Arbash Meinel
Some win32 specific tweaks.
56
    if getattr(signal, "SIGHUP", None) is None:
57
        # If we can't install SIGHUP, there is no reason (yet) to do graceful
58
        # shutdown.
6133.4.60 by John Arbash Meinel
serve_bzr wasn't properly cleaning up the new _on_sighup dict, etc.
59
        old_signal = None
60
    else:
61
        old_signal = signal.signal(signal.SIGHUP, _sighup_handler)
62
    old_dict = _setup_on_hangup_dict()
63
    return old_signal, old_dict
6133.4.45 by John Arbash Meinel
Change the code a bit.
64
65
66
def _setup_on_hangup_dict():
67
    """Create something for _on_sighup.
68
69
    This is done when we install the sighup handler, and for tests that want to
70
    test the functionality. If this hasn'nt been called, then
71
    register_on_hangup is a no-op. As is unregister_on_hangup.
72
    """
73
    global _on_sighup
74
    old = _on_sighup
75
    _on_sighup = weakref.WeakValueDictionary()
76
    return old
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
77
78
6133.4.60 by John Arbash Meinel
serve_bzr wasn't properly cleaning up the new _on_sighup dict, etc.
79
def restore_sighup_handler(orig):
80
    """Pass in the returned value from install_sighup_handler to reset."""
81
    global _on_sighup
82
    old_signal, old_dict = orig
83
    if old_signal is not None:
84
        signal.signal(signal.SIGHUP, old_signal)
85
    _on_sighup = old_dict
86
87
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
88
# TODO: Should these be single-use callables? Meaning that once we've triggered
89
#       SIGHUP and called them, they should auto-remove themselves? I don't
90
#       think so. Callers need to clean up during shutdown anyway, so that we
91
#       don't end up with lots of garbage in the _on_sighup dict. On the other
92
#       hand, we made _on_sighup a WeakValueDictionary in case cleanups didn't
93
#       get fired properly. Maybe we just assume we don't have to do it?
94
def register_on_hangup(identifier, a_callable):
95
    """Register for us to call a_callable as part of a graceful shutdown."""
6133.4.45 by John Arbash Meinel
Change the code a bit.
96
    if _on_sighup is None:
97
        return
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
98
    _on_sighup[identifier] = a_callable
99
100
101
def unregister_on_hangup(identifier):
102
    """Remove a callback from being called during sighup."""
6133.4.45 by John Arbash Meinel
Change the code a bit.
103
    if _on_sighup is None:
104
        return
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
105
    try:
106
        del _on_sighup[identifier]
107
    except KeyboardInterrupt:
108
        raise
109
    except Exception:
110
        # This usually runs as a tear-down step. So we don't want to propagate
111
        # most exceptions.
112
        trace.mutter('Error occurred during unregister_on_hangup:')
113
        trace.log_exception_quietly()
114