Fix test_popen2 on Windows, recently broken by changes to the dict(!)

implementation.  You don't want to know.  I've asked Guido to give this
a critical review (we agreed on the approach, but the implementation
proved more ... interesting ... than anticipated).  This will almost
certainly be the highlight of Mark Hammond's day <wink>.
This commit is contained in:
Tim Peters 2000-09-01 06:51:24 +00:00
parent 5bf94a0b77
commit 736aa32a39
1 changed files with 57 additions and 1 deletions

View File

@ -2685,6 +2685,21 @@ _PyPopen(char *cmdstring, int mode, int n)
* files to be closed in any order - it is always the close() of the
* final handle that will return the exit code.
*/
/* RED_FLAG 31-Aug-2000 Tim
* This is always called (today!) between a pair of
* Py_BEGIN_ALLOW_THREADS/ Py_END_ALLOW_THREADS
* macros. So the thread running this has no valid thread state, as
* far as Python is concerned. However, this calls some Python API
* functions that cannot be called safely without a valid thread
* state, in particular PyDict_GetItem.
* As a temporary hack (although it may last for years ...), we
* *rely* on not having a valid thread state in this function, in
* order to create our own "from scratch".
* This will deadlock if _PyPclose is ever called by a thread
* holding the global lock.
*/
static int _PyPclose(FILE *file)
{
int result;
@ -2692,12 +2707,41 @@ static int _PyPclose(FILE *file)
HANDLE hProcess;
PyObject *procObj, *hProcessObj, *intObj, *fileObj;
long file_count;
#ifdef WITH_THREAD
PyInterpreterState* pInterpreterState;
PyThreadState* pThreadState;
#endif
/* Close the file handle first, to ensure it can't block the
* child from exiting if it's the last handle.
*/
result = fclose(file);
#ifdef WITH_THREAD
/* Bootstrap a valid thread state into existence. */
pInterpreterState = PyInterpreterState_New();
if (!pInterpreterState) {
/* Well, we're hosed now! We don't have a thread
* state, so can't call a nice error routine, or raise
* an exception. Just die.
*/
Py_FatalError("unable to allocate interpreter state "
" when closing popen object.");
return -1; /* unreachable */
}
pThreadState = PyThreadState_New(pInterpreterState);
if (!pThreadState) {
Py_FatalError("unable to allocate thread state "
" when closing popen object.");
return -1; /* unreachable */
}
/* Grab the global lock. Note that this will deadlock if the
* current thread already has the lock! (see RED_FLAG comments
* before this function)
*/
PyEval_RestoreThread(pThreadState);
#endif
if (_PyPopenProcs) {
if ((fileObj = PyLong_FromVoidPtr(file)) != NULL &&
(procObj = PyDict_GetItem(_PyPopenProcs,
@ -2755,6 +2799,18 @@ static int _PyPclose(FILE *file)
Py_XDECREF(fileObj);
} /* if _PyPopenProcs */
#ifdef WITH_THREAD
/* Tear down the thread & interpreter states.
* Note that interpreter state clear & delete functions automatically
* call the thread & clear functions, and * indeed insist on doing
* that themselves. The lock must be held during the clear, but need
* not be held during the delete.
*/
PyInterpreterState_Clear(pInterpreterState);
PyEval_ReleaseThread(pThreadState);
PyInterpreterState_Delete(pInterpreterState);
#endif
return result;
}
#else