[Python-bugs-list] [ python-Bugs-683658 ] PyErr_Warn may cause import deadlock

SourceForge.net noreply@sourceforge.net
Sun, 29 Jun 2003 11:53:22 -0700


Bugs item #683658, was opened at 2003-02-10 00:26
Message generated for change (Comment added) made by jhylton
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=683658&group_id=5470

Category: Python Interpreter Core
Group: Python 2.3
Status: Open
Resolution: Remind
Priority: 7
Submitted By: Mark Hammond (mhammond)
Assigned to: Guido van Rossum (gvanrossum)
Summary: PyErr_Warn may cause import deadlock

Initial Comment:
PyErr_Warn() does an implicit import.  Thus, if
PyErr_Warn() is called on a thread while the main
thread holds the import lock, and the main thread then
subsequently waits for the child thread, Python will
deadlock.  The builtin 'apply' now calls PyErr_Warn(),
so simply calling 'apply' on another thread may cause
the deadlock.

Attaching a sample case.  Executing 'python -c "import
hang_apply"' will cause Python to deadlock.  Commenting
out the call to "apply" in that file will prevent the
deadlock (even though the apply() in question is
effectively a no-op)

The example is a little contrived, but is extracted
from real code of mine that saw this hang.  The code
path is:

* Main thread acquires import lock to import the module.
* Module import spawns a new thread.  This new thread
calls apply()
* builtin_apply() calls PyErr_Warn
* PyErr_Warn does an import of the warnings module, and
attempts to acquire the import lock.
* Main thread still waiting for child thread to
complete, but still holds import lock.

Note that removing the call to PyErr_Warn() in
builtin_apply also solves this particular hang -
however, it seems like this is a potential time bomb. 
A potential solution would be to prevent PyErr_Warn
from doing an import - this would mean importing
'warnings' at startup, and keeping a static reference
in errors.c.  Other thoughts welcome.


----------------------------------------------------------------------

>Comment By: Jeremy Hylton (jhylton)
Date: 2003-06-29 18:53

Message:
Logged In: YES 
user_id=31392

Did this get resolved for 2.3 or delayed to 2.4?


----------------------------------------------------------------------

Comment By: Just van Rossum (jvr)
Date: 2003-02-25 20:30

Message:
Logged In: YES 
user_id=92689

Well, since you said the issue needs to be revisited anyway, I'll just leave it at moving the warnings.py import after initsite().

What I don't like is that warnings.py is imported preemptively to begin with, not just with -S. Whether that's resolvable I don't know, but if it is, it would be better than not importing warnings.py with -S.

----------------------------------------------------------------------

Comment By: Guido van Rossum (gvanrossum)
Date: 2003-02-25 20:20

Message:
Logged In: YES 
user_id=6380

OK, implement that.

----------------------------------------------------------------------

Comment By: Just van Rossum (jvr)
Date: 2003-02-25 18:58

Message:
Logged In: YES 
user_id=92689

Only if PyErr_Warn() would still also attempt to do the import if PyModule_WarningsModule is NULL. But that means that deadlocks can still occur when -S is used. But I _do_ like it that no file system imports are done by Python itself with -S. And I actually don't like it at all that warnings.py always gets imported, even if it's never needed.

----------------------------------------------------------------------

Comment By: Guido van Rossum (gvanrossum)
Date: 2003-02-25 18:31

Message:
Logged In: YES 
user_id=6380

OK, as a stopgap measure that sounds like a good idea.

(I wonder if -S should also suppress loading warnings? Or is
that too drastic?)


----------------------------------------------------------------------

Comment By: Just van Rossum (jvr)
Date: 2003-02-25 18:27

Message:
Logged In: YES 
user_id=92689

Additional info regarding the current situation (with the patch in place):
if warnings.py can't be imported before site.py is run, _all_ warnings issued from C will be printed to stderr, even if warnings.py _can_ be imported later.

I think it's fair to assume that any warning issued during the import of site.py signifies that something is pretty broken, so having them dumped to stderr isn't all that bad.

If you and Mark agree, I'll move the import of warnings.py to after initsite().

----------------------------------------------------------------------

Comment By: Guido van Rossum (gvanrossum)
Date: 2003-02-25 17:44

Message:
Logged In: YES 
user_id=6380

Good point. But loading site.py first means that warnings
issued as a result of importing site.py are not handled
properly (they will be dumped to stderr, which is better
than nothing). Well, I have to revisit this anyway; I'll
make sure to take this issue into account as well.

----------------------------------------------------------------------

Comment By: Just van Rossum (jvr)
Date: 2003-02-25 17:20

Message:
Logged In: YES 
user_id=92689

Here's an issue with the patch I just ran into: it seems this patch causes the first Python module to be imported to be warnings.py instead of site.py. This means site.py doesn't have full control of the import mechanism anymore, as all modules loaded by warnings.py will already be loaded. Can't the PyModule_WarningsModule global be set _after_ site.py has run?

Use case: I'm building standalone applications in which the import bootstrapping happens in a custom site.py, assuming it will be the first Python module that will be imported. It sets up sys.path appropriately. Now it turns out that when my site.py gets run (eg.) os.py is already loaded from my system-wide installation, instead of from the zip file embedded in the application. This means I can't reliably test whether my built app really works when there's no Python installation available.

----------------------------------------------------------------------

Comment By: Guido van Rossum (gvanrossum)
Date: 2003-02-19 16:36

Message:
Logged In: YES 
user_id=6380

I'm not happy with this solution, since it breaks for
multiple interpreters. So keeping this open. Will think
about what to do instead after 2.3a2 is released.

----------------------------------------------------------------------

Comment By: Mark Hammond (mhammond)
Date: 2003-02-19 00:42

Message:
Logged In: YES 
user_id=14198

Checking in a patch that avoids the import, thereby avoiding
the hang.  As suggested, the "linecache" import in warnings
had to be moved.

Even though I checked it in, I attach the patch here, and
have assigned it to Guido for review.  If happy, just mark
it as closed.

/cvsroot/python/python/dist/src/Python/errors.c,v  <--  errors.c
new revision: 2.76; previous revision: 2.75
/cvsroot/python/python/dist/src/Python/pythonrun.c,v  <-- 
pythonrun.c
new revision: 2.178; previous revision: 2.177
/cvsroot/python/python/dist/src/Lib/warnings.py,v  <-- 
warnings.py
new revision: 1.19; previous revision: 1.18


----------------------------------------------------------------------

Comment By: Mark Hammond (mhammond)
Date: 2003-02-15 23:17

Message:
Logged In: YES 
user_id=14198

Yes, the linecache import was the problem.  I abandonded
that approach mainly to avoid yet another import at startup.
 Should 'warnings' get new features, all required imports in
the future will also need to be imported at process startup.
 It also struck me as a time bomb.

I will make an alternative patch that moves the imports.


----------------------------------------------------------------------

Comment By: Guido van Rossum (gvanrossum)
Date: 2003-02-15 21:01

Message:
Logged In: YES 
user_id=6380

Oh, and the import of linecache by warnings can be put at
the top of warnings, if that's an issue.

----------------------------------------------------------------------

Comment By: Guido van Rossum (gvanrossum)
Date: 2003-02-15 15:46

Message:
Logged In: YES 
user_id=6380

No time to review everything here, but maybe PyErr_Warn
should not do an import? The import could be done at startup
time (when site.py is also imported) and saved in the
interpreter state.

----------------------------------------------------------------------

Comment By: Mark Hammond (mhammond)
Date: 2003-02-15 05:02

Message:
Logged In: YES 
user_id=14198

The best I can come up with here is exposing the import lock
to the C API, with a "wait" flag.

This will allow a C function to reliably determine if an
"import" work block, or acquire the lock if not.  It can
then complete its import before releasing the lock.  If the
import would block, then it can take whatever action is
necessary - in the case of PyErr_Warn, it dumps the warning
to stdout.

Attaching a patch.  It exposes (to the core, but not in
headers) two functions:

extern int PyImport_LockImport(int wait);
extern void PyImport_UnlockImport(void);

PyErr_Warn then uses these.

If we do go this route, it makes sense to make these
functions truly public (ie, add them to the headers), and
cleanup import.c appropriately.  I didn't do this in the
interests of keeping the patch small so it can be easily
digested.

Comments?  Other ideas?

----------------------------------------------------------------------

Comment By: Mark Hammond (mhammond)
Date: 2003-02-14 05:16

Message:
Logged In: YES 
user_id=14198

This simple strategy doesn't work - avoiding the import of
'warnings' works fine, until 'warnings' imports 'linecache'!

I'll look at how we can simply throw the warning away if a
deadlock would occur.

----------------------------------------------------------------------

Comment By: Neal Norwitz (nnorwitz)
Date: 2003-02-11 23:23

Message:
Logged In: YES 
user_id=33168

I can't think of any other/better solution.  Any idea if
there are there other ticking bombs like this?

----------------------------------------------------------------------

You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=683658&group_id=5470