summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/tempfile.py30
1 files changed, 28 insertions, 2 deletions
diff --git a/Lib/tempfile.py b/Lib/tempfile.py
index 417b749..51e43b0 100644
--- a/Lib/tempfile.py
+++ b/Lib/tempfile.py
@@ -17,6 +17,30 @@ def gettempdir():
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:
+ return tempdir
try:
pwd = os.getcwd()
except (AttributeError, os.error):
@@ -179,8 +203,8 @@ def TemporaryFile(mode='w+b', bufsize=-1, suffix=""):
# 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):
@@ -209,10 +233,12 @@ except ImportError:
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