template = None
def gettempdir():
+ """Function to calculate the directory to use."""
+ global tempdir
+ if tempdir is not None:
+ return tempdir
+
+ # _gettempdir_inner deduces whether a candidate temp dir is usable by
+ # trying to create a file in it, and write to it. If that succeeds,
+ # great, it closes the file and unlinks it. There's a race, though:
+ # the *name* of the test file it tries is the same across all threads
+ # under most OSes (Linux is an exception), and letting multiple threads
+ # all try to open, write to, close, and unlink a single file can cause
+ # a variety of bogus errors (e.g., you cannot unlink a file under
+ # Windows if anyone has it open, and two threads cannot create the
+ # same file in O_EXCL mode under Unix). The simplest cure is to serialize
+ # calls to _gettempdir_inner. This isn't a real expense, because the
+ # first thread to succeed sets the global tempdir, and all subsequent
+ # calls to gettempdir() reuse that without trying _gettempdir_inner.
+ _tempdir_lock.acquire()
+ try:
+ return _gettempdir_inner()
+ finally:
+ _tempdir_lock.release()
+
+def _gettempdir_inner():
"""Function to calculate the directory to use."""
global tempdir
if tempdir is not None:
# multiple threads will never see the same integer). The integer will
# usually be a Python int, but if _counter.get_next() is called often
# enough, it will become a Python long.
-# Note that the only name that survives this next block of code
-# is "_counter".
+# Note that the only names that survive this next block of code
+# are "_counter" and "_tempdir_lock".
class _ThreadSafeCounter:
def __init__(self, mutex, initialvalue=0):
release = acquire
_counter = _ThreadSafeCounter(_DummyMutex())
+ _tempdir_lock = _DummyMutes()
del _DummyMutex
else:
_counter = _ThreadSafeCounter(thread.allocate_lock())
+ _tempdir_lock = thread.allocate_lock()
del thread
del _ThreadSafeCounter