]> granicus.if.org Git - python/commitdiff
Fix test_popen2 on Windows, recently broken by changes to the dict(!)
authorTim Peters <tim.peters@gmail.com>
Fri, 1 Sep 2000 06:51:24 +0000 (06:51 +0000)
committerTim Peters <tim.peters@gmail.com>
Fri, 1 Sep 2000 06:51:24 +0000 (06:51 +0000)
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>.

Modules/posixmodule.c

index d5290f6e84d31eff864943062a29a7bab8fae705..42bde9758ad19f6174cf0e5963ca929afc1ab896 100644 (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