/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/thread.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

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
 
import sys
18
 
import threading
19
 
 
20
 
 
21
 
class CatchingExceptionThread(threading.Thread):
22
 
    """A thread that keeps track of exceptions.
23
 
 
24
 
    If an exception occurs during the thread execution, it's caught and
25
 
    re-raised when the thread is joined().
26
 
    """
27
 
 
28
 
    def __init__(self, *args, **kwargs):
29
 
        # There are cases where the calling thread must wait, yet, if an
30
 
        # exception occurs, the event should be set so the caller is not
31
 
        # blocked. The main example is a calling thread that want to wait for
32
 
        # the called thread to be in a given state before continuing.
33
 
        try:
34
 
            sync_event = kwargs.pop('sync_event')
35
 
        except KeyError:
36
 
            # If the caller didn't pass a specific event, create our own
37
 
            sync_event = threading.Event()
38
 
        super(CatchingExceptionThread, self).__init__(*args, **kwargs)
39
 
        self.set_sync_event(sync_event)
40
 
        self.exception = None
41
 
        self.ignored_exceptions = None # see set_ignored_exceptions
42
 
 
43
 
    # compatibility thunk for python-2.4 and python-2.5...
44
 
    if sys.version_info < (2, 6):
45
 
        name = property(threading.Thread.getName, threading.Thread.setName)
46
 
 
47
 
    def set_sync_event(self, event):
48
 
        """Set the ``sync_event`` event used to synchronize exception catching.
49
 
 
50
 
        When the thread uses an event to synchronize itself with another thread
51
 
        (setting it when the other thread can wake up from a ``wait`` call),
52
 
        the event must be set after catching an exception or the other thread
53
 
        will hang.
54
 
 
55
 
        Some threads require multiple events and should set the relevant one
56
 
        when appropriate.
57
 
 
58
 
        Note that the event should be cleared so the caller can wait() on him
59
 
        and be released when the thread set the event.
60
 
        """
61
 
        self.sync_event = event
62
 
 
63
 
    def set_ignored_exceptions(self, ignored):
64
 
        """Declare which exceptions will be ignored.
65
 
 
66
 
        :param ignored: Can be either:
67
 
           - None: all exceptions will be raised,
68
 
           - an exception class: the instances of this class will be ignored,
69
 
           - a tuple of exception classes: the instances of any class of the
70
 
             list will be ignored,
71
 
           - a callable: that will be passed the exception object
72
 
             and should return True if the exception should be ignored
73
 
        """
74
 
        if ignored is None:
75
 
            self.ignored_exceptions = None
76
 
        elif isinstance(ignored, (Exception, tuple)):
77
 
            self.ignored_exceptions = lambda e: isinstance(e, ignored)
78
 
        else:
79
 
            self.ignored_exceptions = ignored
80
 
 
81
 
    def run(self):
82
 
        """Overrides Thread.run to capture any exception."""
83
 
        self.sync_event.clear()
84
 
        try:
85
 
            try:
86
 
                super(CatchingExceptionThread, self).run()
87
 
            except:
88
 
                self.exception = sys.exc_info()
89
 
        finally:
90
 
            # Make sure the calling thread is released
91
 
            self.sync_event.set()
92
 
 
93
 
 
94
 
    def join(self, timeout=None):
95
 
        """Overrides Thread.join to raise any exception caught.
96
 
 
97
 
        Calling join(timeout=0) will raise the caught exception or return None
98
 
        if the thread is still alive.
99
 
        """
100
 
        super(CatchingExceptionThread, self).join(timeout)
101
 
        if self.exception is not None:
102
 
            exc_class, exc_value, exc_tb = self.exception
103
 
            self.exception = None # The exception should be raised only once
104
 
            if (self.ignored_exceptions is None
105
 
                or not self.ignored_exceptions(exc_value)):
106
 
                # Raise non ignored exceptions
107
 
                raise exc_class, exc_value, exc_tb
108
 
 
109
 
    def pending_exception(self):
110
 
        """Raise the caught exception.
111
 
 
112
 
        This does nothing if no exception occurred.
113
 
        """
114
 
        self.join(timeout=0)