|
13 | 13 | template = None |
14 | 14 |
|
15 | 15 | def gettempdir(): |
| 16 | + """Function to calculate the directory to use.""" |
| 17 | + global tempdir |
| 18 | + if tempdir is not None: |
| 19 | + return tempdir |
| 20 | + |
| 21 | + # _gettempdir_inner deduces whether a candidate temp dir is usable by |
| 22 | + # trying to create a file in it, and write to it. If that succeeds, |
| 23 | + # great, it closes the file and unlinks it. There's a race, though: |
| 24 | + # the *name* of the test file it tries is the same across all threads |
| 25 | + # under most OSes (Linux is an exception), and letting multiple threads |
| 26 | + # all try to open, write to, close, and unlink a single file can cause |
| 27 | + # a variety of bogus errors (e.g., you cannot unlink a file under |
| 28 | + # Windows if anyone has it open, and two threads cannot create the |
| 29 | + # same file in O_EXCL mode under Unix). The simplest cure is to serialize |
| 30 | + # calls to _gettempdir_inner. This isn't a real expense, because the |
| 31 | + # first thread to succeed sets the global tempdir, and all subsequent |
| 32 | + # calls to gettempdir() reuse that without trying _gettempdir_inner. |
| 33 | + _tempdir_lock.acquire() |
| 34 | + try: |
| 35 | + return _gettempdir_inner() |
| 36 | + finally: |
| 37 | + _tempdir_lock.release() |
| 38 | + |
| 39 | +def _gettempdir_inner(): |
16 | 40 | """Function to calculate the directory to use.""" |
17 | 41 | global tempdir |
18 | 42 | if tempdir is not None: |
@@ -179,8 +203,8 @@ def TemporaryFile(mode='w+b', bufsize=-1, suffix=""): |
179 | 203 | # multiple threads will never see the same integer). The integer will |
180 | 204 | # usually be a Python int, but if _counter.get_next() is called often |
181 | 205 | # enough, it will become a Python long. |
182 | | -# Note that the only name that survives this next block of code |
183 | | -# is "_counter". |
| 206 | +# Note that the only names that survive this next block of code |
| 207 | +# are "_counter" and "_tempdir_lock". |
184 | 208 |
|
185 | 209 | class _ThreadSafeCounter: |
186 | 210 | def __init__(self, mutex, initialvalue=0): |
@@ -209,10 +233,12 @@ def acquire(self): |
209 | 233 | release = acquire |
210 | 234 |
|
211 | 235 | _counter = _ThreadSafeCounter(_DummyMutex()) |
| 236 | + _tempdir_lock = _DummyMutes() |
212 | 237 | del _DummyMutex |
213 | 238 |
|
214 | 239 | else: |
215 | 240 | _counter = _ThreadSafeCounter(thread.allocate_lock()) |
| 241 | + _tempdir_lock = thread.allocate_lock() |
216 | 242 | del thread |
217 | 243 |
|
218 | 244 | del _ThreadSafeCounter |
0 commit comments