~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
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
17
"""Signal handling for the smart server code."""
18
6379.6.3 by Jelmer Vernooij
Use absolute_import.
19
from __future__ import absolute_import
20
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
21
import signal
22
import weakref
23
24
from bzrlib import trace
25
26
27
# 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.
28
_on_sighup = None
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
29
# TODO: Using a dict means that the order of calls is unordered. We could use a
30
#       list and then do something like LIFO ordering. A dict was chosen so
31
#       that you could have a key to easily remove your entry. However, you
32
#       could just use the callable itself as the indexed part, and even in
33
#       large cases, we shouldn't have more than 100 or so callbacks
34
#       registered.
35
def _sighup_handler(signal_number, interrupted_frame):
6133.4.46 by John Arbash Meinel
Test that signals.install_sighup_handler does what we want.
36
    """This is the actual function that is registered for handling SIGHUP.
37
38
    It will call out to all the registered functions, letting them know that a
39
    graceful termination has been requested.
40
    """
6133.4.45 by John Arbash Meinel
Change the code a bit.
41
    if _on_sighup is None:
42
        return
6133.4.47 by John Arbash Meinel
Testing that 'bzr serve' actually installs SIGHUP and responds to it showed some problems.
43
    trace.mutter('Caught SIGHUP, sending graceful shutdown requests.')
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
44
    for ref in _on_sighup.valuerefs():
45
        try:
46
            cb = ref()
47
            if cb is not None:
48
                cb()
49
        except KeyboardInterrupt:
50
            raise
51
        except Exception:
52
            trace.mutter('Error occurred while running SIGHUP handlers:')
53
            trace.log_exception_quietly()
54
55
56
def install_sighup_handler():
57
    """Setup a handler for the SIGHUP signal."""
6133.4.48 by John Arbash Meinel
Some win32 specific tweaks.
58
    if getattr(signal, "SIGHUP", None) is None:
59
        # If we can't install SIGHUP, there is no reason (yet) to do graceful
60
        # shutdown.
6133.4.60 by John Arbash Meinel
serve_bzr wasn't properly cleaning up the new _on_sighup dict, etc.
61
        old_signal = None
62
    else:
63
        old_signal = signal.signal(signal.SIGHUP, _sighup_handler)
64
    old_dict = _setup_on_hangup_dict()
65
    return old_signal, old_dict
6133.4.45 by John Arbash Meinel
Change the code a bit.
66
67
68
def _setup_on_hangup_dict():
69
    """Create something for _on_sighup.
70
71
    This is done when we install the sighup handler, and for tests that want to
72
    test the functionality. If this hasn'nt been called, then
73
    register_on_hangup is a no-op. As is unregister_on_hangup.
74
    """
75
    global _on_sighup
76
    old = _on_sighup
77
    _on_sighup = weakref.WeakValueDictionary()
78
    return old
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
79
80
6133.4.60 by John Arbash Meinel
serve_bzr wasn't properly cleaning up the new _on_sighup dict, etc.
81
def restore_sighup_handler(orig):
82
    """Pass in the returned value from install_sighup_handler to reset."""
83
    global _on_sighup
84
    old_signal, old_dict = orig
85
    if old_signal is not None:
86
        signal.signal(signal.SIGHUP, old_signal)
87
    _on_sighup = old_dict
88
89
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
90
# TODO: Should these be single-use callables? Meaning that once we've triggered
91
#       SIGHUP and called them, they should auto-remove themselves? I don't
92
#       think so. Callers need to clean up during shutdown anyway, so that we
93
#       don't end up with lots of garbage in the _on_sighup dict. On the other
94
#       hand, we made _on_sighup a WeakValueDictionary in case cleanups didn't
95
#       get fired properly. Maybe we just assume we don't have to do it?
96
def register_on_hangup(identifier, a_callable):
97
    """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.
98
    if _on_sighup is None:
99
        return
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
100
    _on_sighup[identifier] = a_callable
101
102
103
def unregister_on_hangup(identifier):
104
    """Remove a callback from being called during sighup."""
6133.4.45 by John Arbash Meinel
Change the code a bit.
105
    if _on_sighup is None:
106
        return
6133.4.44 by John Arbash Meinel
Move the code into bzrlib.smart.signals.
107
    try:
108
        del _on_sighup[identifier]
109
    except KeyboardInterrupt:
110
        raise
111
    except Exception:
112
        # This usually runs as a tear-down step. So we don't want to propagate
113
        # most exceptions.
114
        trace.mutter('Error occurred during unregister_on_hangup:')
115
        trace.log_exception_quietly()
116