SF bug #1473760 TempFile can hang on Windows.

Python 2.4 changed ntpath.abspath to do an import
inside the function.  As a result, due to Python's
import lock, anything calling abspath on Windows
(directly, or indirectly like tempfile.TemporaryFile)
hung when it was called from a thread spawned as a
side effect of importing a module.

This is a depressingly frequent problem, and
deserves a more general fix.  I'm settling for
a micro-fix here because this specific one accounts
for a report of Zope Corp's ZEO hanging on Windows,
and it was an odd way to change abspath to begin
with (ntpath needs a different implementation
depending on whether we're actually running on
Windows, and the _obvious_ way to arrange for that
is not to bury a possibly-failing import _inside_
the function).

Note that if/when other micro-fixes of this kind
get made, the new Lib/test/threaded_import_hangers.py
is a convenient place to add tests for them.
This commit is contained in:
Tim Peters 2006-04-21 21:18:10 +00:00
parent 9f7e58afa7
commit 21fbd57d66
4 changed files with 86 additions and 21 deletions

View File

@ -481,18 +481,19 @@ def normpath(path):
# Return an absolute path.
def abspath(path):
"""Return the absolute version of a path"""
try:
from nt import _getfullpathname
except ImportError: # Not running on Windows - mock up something sensible.
global abspath
def _abspath(path):
except ImportError: # not running on Windows - mock up something sensible
def abspath(path):
"""Return the absolute version of a path."""
if not isabs(path):
path = join(os.getcwd(), path)
return normpath(path)
abspath = _abspath
return _abspath(path)
else: # use native Windows method on Windows
def abspath(path):
"""Return the absolute version of a path."""
if path: # Empty path must return current working directory.
try:

View File

@ -6,7 +6,7 @@
# randrange, and then Python hangs.
import thread
from test.test_support import verbose, TestSkipped
from test.test_support import verbose, TestSkipped, TestFailed
critical_section = thread.allocate_lock()
done = thread.allocate_lock()
@ -25,6 +25,23 @@ def task():
if finished:
done.release()
def test_import_hangers():
import sys
if verbose:
print "testing import hangers ...",
from test import threaded_import_hangers
try:
if threaded_import_hangers.errors:
raise TestFailed(threaded_import_hangers.errors)
elif verbose:
print "OK."
finally:
# In case this test is run again, make sure the helper module
# gets loaded from scratch again.
del sys.modules['test.threaded_import_hangers']
# Tricky: When regrtest imports this module, the thread running regrtest
# grabs the import lock and won't let go of it until this module returns.
# All other threads attempting an import hang for the duration. Since
@ -53,5 +70,7 @@ def test_main(): # magic name! see above
print "OK."
done.release()
test_import_hangers()
if __name__ == "__main__":
test_main()

View File

@ -0,0 +1,42 @@
# This is a helper module for test_threaded_import. The test imports this
# module, and this module tries to run various Python library functions in
# their own thread, as a side effect of being imported. If the spawned
# thread doesn't complete in TIMEOUT seconds, an "appeared to hang" message
# is appended to the module-global `errors` list. That list remains empty
# if (and only if) all functions tested complete.
TIMEOUT = 10
import threading
import tempfile
import os.path
errors = []
# This class merely runs a function in its own thread T. The thread importing
# this module holds the import lock, so if the function called by T tries
# to do its own imports it will block waiting for this module's import
# to complete.
class Worker(threading.Thread):
def __init__(self, function, args):
threading.Thread.__init__(self)
self.function = function
self.args = args
def run(self):
self.function(*self.args)
for name, func, args in [
# Bug 147376: TemporaryFile hung on Windows, starting in Python 2.4.
("tempfile.TemporaryFile", tempfile.TemporaryFile, ()),
# The real cause for bug 147376: ntpath.abspath() caused the hang.
("os.path.abspath", os.path.abspath, ('.',)),
]:
t = Worker(func, args)
t.start()
t.join(TIMEOUT)
if t.isAlive():
errors.append("%s appeared to hang" % name)

View File

@ -86,6 +86,9 @@ Extension Modules
Library
-------
- Bug #1473760: ``tempfile.TemporaryFile()`` could hang on Windows, when
called from a thread spawned as a side effect of importing a module.
- New modules: setuptools, easy_install, and pkg_resources, to support
building, installing, and using Python eggs, respectively.