~openerp-groupes/openobject-server/6.0-fix-setup-windows

« back to all changes in this revision

Viewing changes to bin/tools/threadinglocal.py

  • Committer: pinky
  • Date: 2006-12-07 13:41:40 UTC
  • Revision ID: pinky-3f10ee12cea3c4c75cef44ab04ad33ef47432907
New trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#Copyright (c) 2004-2005, CherryPy Team (team@cherrypy.org)
 
2
#All rights reserved.
 
3
#
 
4
#Redistribution and use in source and binary forms, with or without
 
5
#modification, are permitted provided that the following conditions are met:
 
6
#
 
7
#    * Redistributions of source code must retain the above copyright notice, 
 
8
#      this list of conditions and the following disclaimer.
 
9
#    * Redistributions in binary form must reproduce the above copyright notice,
 
10
#      this list of conditions and the following disclaimer in the documentation
 
11
#      and/or other materials provided with the distribution.
 
12
#    * Neither the name of the CherryPy Team nor the names of its contributors 
 
13
#      may be used to endorse or promote products derived from this software 
 
14
#      without specific prior written permission.
 
15
#
 
16
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 
17
#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 
18
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 
19
#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
 
20
#FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 
21
#DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 
22
#SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 
23
#CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
 
24
#OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 
25
#OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
26
 
 
27
# This is a backport of Python-2.4's threading.local() implementation
 
28
 
 
29
"""Thread-local objects
 
30
 
 
31
(Note that this module provides a Python version of thread
 
32
 threading.local class.  Depending on the version of Python you're
 
33
 using, there may be a faster one available.  You should always import
 
34
 the local class from threading.)
 
35
 
 
36
Thread-local objects support the management of thread-local data.
 
37
If you have data that you want to be local to a thread, simply create
 
38
a thread-local object and use its attributes:
 
39
 
 
40
  >>> mydata = local()
 
41
  >>> mydata.number = 42
 
42
  >>> mydata.number
 
43
  42
 
44
 
 
45
You can also access the local-object's dictionary:
 
46
 
 
47
  >>> mydata.__dict__
 
48
  {'number': 42}
 
49
  >>> mydata.__dict__.setdefault('widgets', [])
 
50
  []
 
51
  >>> mydata.widgets
 
52
  []
 
53
 
 
54
What's important about thread-local objects is that their data are
 
55
local to a thread. If we access the data in a different thread:
 
56
 
 
57
  >>> log = []
 
58
  >>> def f():
 
59
  ...     items = mydata.__dict__.items()
 
60
  ...     items.sort()
 
61
  ...     log.append(items)
 
62
  ...     mydata.number = 11
 
63
  ...     log.append(mydata.number)
 
64
 
 
65
  >>> import threading
 
66
  >>> thread = threading.Thread(target=f)
 
67
  >>> thread.start()
 
68
  >>> thread.join()
 
69
  >>> log
 
70
  [[], 11]
 
71
 
 
72
we get different data.  Furthermore, changes made in the other thread
 
73
don't affect data seen in this thread:
 
74
 
 
75
  >>> mydata.number
 
76
  42
 
77
 
 
78
Of course, values you get from a local object, including a __dict__
 
79
attribute, are for whatever thread was current at the time the
 
80
attribute was read.  For that reason, you generally don't want to save
 
81
these values across threads, as they apply only to the thread they
 
82
came from.
 
83
 
 
84
You can create custom local objects by subclassing the local class:
 
85
 
 
86
  >>> class MyLocal(local):
 
87
  ...     number = 2
 
88
  ...     initialized = False
 
89
  ...     def __init__(self, **kw):
 
90
  ...         if self.initialized:
 
91
  ...             raise SystemError('__init__ called too many times')
 
92
  ...         self.initialized = True
 
93
  ...         self.__dict__.update(kw)
 
94
  ...     def squared(self):
 
95
  ...         return self.number ** 2
 
96
 
 
97
This can be useful to support default values, methods and
 
98
initialization.  Note that if you define an __init__ method, it will be
 
99
called each time the local object is used in a separate thread.  This
 
100
is necessary to initialize each thread's dictionary.
 
101
 
 
102
Now if we create a local object:
 
103
 
 
104
  >>> mydata = MyLocal(color='red')
 
105
 
 
106
Now we have a default number:
 
107
 
 
108
  >>> mydata.number
 
109
  2
 
110
 
 
111
an initial color:
 
112
 
 
113
  >>> mydata.color
 
114
  'red'
 
115
  >>> del mydata.color
 
116
 
 
117
And a method that operates on the data:
 
118
 
 
119
  >>> mydata.squared()
 
120
  4
 
121
 
 
122
As before, we can access the data in a separate thread:
 
123
 
 
124
  >>> log = []
 
125
  >>> thread = threading.Thread(target=f)
 
126
  >>> thread.start()
 
127
  >>> thread.join()
 
128
  >>> log
 
129
  [[('color', 'red'), ('initialized', True)], 11]
 
130
 
 
131
without affecting this thread's data:
 
132
 
 
133
  >>> mydata.number
 
134
  2
 
135
  >>> mydata.color
 
136
  Traceback (most recent call last):
 
137
  ...
 
138
  AttributeError: 'MyLocal' object has no attribute 'color'
 
139
 
 
140
Note that subclasses can define slots, but they are not thread
 
141
local. They are shared across threads:
 
142
 
 
143
  >>> class MyLocal(local):
 
144
  ...     __slots__ = 'number'
 
145
 
 
146
  >>> mydata = MyLocal()
 
147
  >>> mydata.number = 42
 
148
  >>> mydata.color = 'red'
 
149
 
 
150
So, the separate thread:
 
151
 
 
152
  >>> thread = threading.Thread(target=f)
 
153
  >>> thread.start()
 
154
  >>> thread.join()
 
155
 
 
156
affects what we see:
 
157
 
 
158
  >>> mydata.number
 
159
  11
 
160
 
 
161
>>> del mydata
 
162
"""
 
163
 
 
164
# Threading import is at end
 
165
 
 
166
class _localbase(object):
 
167
    __slots__ = '_local__key', '_local__args', '_local__lock'
 
168
 
 
169
    def __new__(cls, *args, **kw):
 
170
        self = object.__new__(cls)
 
171
        key = '_local__key', 'thread.local.' + str(id(self))
 
172
        object.__setattr__(self, '_local__key', key)
 
173
        object.__setattr__(self, '_local__args', (args, kw))
 
174
        object.__setattr__(self, '_local__lock', RLock())
 
175
 
 
176
        if args or kw and (cls.__init__ is object.__init__):
 
177
            raise TypeError("Initialization arguments are not supported")
 
178
 
 
179
        # We need to create the thread dict in anticipation of
 
180
        # __init__ being called, to make sure we don't call it
 
181
        # again ourselves.
 
182
        dict = object.__getattribute__(self, '__dict__')
 
183
        currentThread().__dict__[key] = dict
 
184
 
 
185
        return self
 
186
 
 
187
def _patch(self):
 
188
    key = object.__getattribute__(self, '_local__key')
 
189
    d = currentThread().__dict__.get(key)
 
190
    if d is None:
 
191
        d = {}
 
192
        currentThread().__dict__[key] = d
 
193
        object.__setattr__(self, '__dict__', d)
 
194
 
 
195
        # we have a new instance dict, so call out __init__ if we have
 
196
        # one
 
197
        cls = type(self)
 
198
        if cls.__init__ is not object.__init__:
 
199
            args, kw = object.__getattribute__(self, '_local__args')
 
200
            cls.__init__(self, *args, **kw)
 
201
    else:
 
202
        object.__setattr__(self, '__dict__', d)
 
203
 
 
204
class local(_localbase):
 
205
 
 
206
    def __getattribute__(self, name):
 
207
        lock = object.__getattribute__(self, '_local__lock')
 
208
        lock.acquire()
 
209
        try:
 
210
            _patch(self)
 
211
            return object.__getattribute__(self, name)
 
212
        finally:
 
213
            lock.release()
 
214
 
 
215
    def __setattr__(self, name, value):
 
216
        lock = object.__getattribute__(self, '_local__lock')
 
217
        lock.acquire()
 
218
        try:
 
219
            _patch(self)
 
220
            return object.__setattr__(self, name, value)
 
221
        finally:
 
222
            lock.release()
 
223
 
 
224
    def __delattr__(self, name):
 
225
        lock = object.__getattribute__(self, '_local__lock')
 
226
        lock.acquire()
 
227
        try:
 
228
            _patch(self)
 
229
            return object.__delattr__(self, name)
 
230
        finally:
 
231
            lock.release()
 
232
 
 
233
 
 
234
    def __del__():
 
235
        threading_enumerate = enumerate
 
236
        __getattribute__ = object.__getattribute__
 
237
 
 
238
        def __del__(self):
 
239
            key = __getattribute__(self, '_local__key')
 
240
 
 
241
            try:
 
242
                threads = list(threading_enumerate())
 
243
            except:
 
244
                # if enumerate fails, as it seems to do during
 
245
                # shutdown, we'll skip cleanup under the assumption
 
246
                # that there is nothing to clean up
 
247
                return
 
248
 
 
249
            for thread in threads:
 
250
                try:
 
251
                    __dict__ = thread.__dict__
 
252
                except AttributeError:
 
253
                    # Thread is dying, rest in peace
 
254
                    continue
 
255
 
 
256
                if key in __dict__:
 
257
                    try:
 
258
                        del __dict__[key]
 
259
                    except KeyError:
 
260
                        pass # didn't have anything in this thread
 
261
 
 
262
        return __del__
 
263
    __del__ = __del__()
 
264
 
 
265
from threading import currentThread, enumerate, RLock