~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/signals.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
from __future__ import absolute_import
 
20
 
 
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.
 
28
_on_sighup = None
 
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):
 
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
    """
 
41
    if _on_sighup is None:
 
42
        return
 
43
    trace.mutter('Caught SIGHUP, sending graceful shutdown requests.')
 
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."""
 
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.
 
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
 
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
 
79
 
 
80
 
 
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
 
 
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."""
 
98
    if _on_sighup is None:
 
99
        return
 
100
    _on_sighup[identifier] = a_callable
 
101
 
 
102
 
 
103
def unregister_on_hangup(identifier):
 
104
    """Remove a callback from being called during sighup."""
 
105
    if _on_sighup is None:
 
106
        return
 
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