Author: Armin Rigo <[email protected]>
Branch: static-callback-embedding
Changeset: r2511:99cb43d80de0
Date: 2016-01-01 14:49 +0100
http://bitbucket.org/cffi/cffi/changeset/99cb43d80de0/

Log:    Fix the multithreaded initialization.

diff --git a/cffi/_embedding.h b/cffi/_embedding.h
--- a/cffi/_embedding.h
+++ b/cffi/_embedding.h
@@ -118,9 +118,14 @@
        present .dll/.so is set up as a CPython C extension module.
     */
     int result;
+    PyGILState_STATE state;
     PyObject *pycode=NULL, *m=NULL, *global_dict, *x;
 
-    PyEval_AcquireLock();      /* acquire the GIL */
+    /* Acquire the GIL.  We have no threadstate here.  If Python is 
+       already initialized, it is possible that there is already one
+       existing for this thread, but it is not made current now.
+    */
+    PyEval_AcquireLock();
 
     /* XXX use initsigs=0, which "skips initialization registration of
        signal handlers, which might be useful when Python is
@@ -132,6 +137,20 @@
     */
     Py_InitializeEx(0);
 
+    /* The Py_InitializeEx() sometimes made a threadstate for us, but
+       not always.  Indeed Py_InitializeEx() could be called and do
+       nothing.  So do we have a threadstate, or not?  We don't know,
+       but we can replace it with NULL in all cases.
+    */
+    (void)PyThreadState_Swap(NULL);
+
+    /* Now we can release the GIL and re-acquire immediately using the
+       logic of PyGILState(), which handles making or installing the
+       correct threadstate.
+    */
+    PyEval_ReleaseLock();
+    state = PyGILState_Ensure();
+
     /* Call the initxxx() function from the present module.  It will
        create and initialize us as a CPython extension module, instead
        of letting the startup Python code do it---it might reimport
@@ -175,7 +194,7 @@
  done:
     Py_XDECREF(pycode);
     Py_XDECREF(m);
-    PyEval_ReleaseLock();      /* release the GIL */
+    PyGILState_Release(state);
     return result;
 
  error:;
diff --git a/testing/embedding/add2.py b/testing/embedding/add2.py
--- a/testing/embedding/add2.py
+++ b/testing/embedding/add2.py
@@ -6,12 +6,13 @@
     extern "Python" int add2(int, int, int);
 """, dllexport=True)
 
-ffi.embedding_init_code("""
-    print("preparing ADD2")
+ffi.embedding_init_code(r"""
+    import sys
+    sys.stdout.write("prepADD2\n")
 
     @ffi.def_extern()
     def add2(x, y, z):
-        print "adding", x, "and", y, "and", z
+        sys.stdout.write("adding %d and %d and %d\n" % (x, y, z))
         return x + y + z
 """)
 
diff --git a/testing/embedding/test_basic.py b/testing/embedding/test_basic.py
--- a/testing/embedding/test_basic.py
+++ b/testing/embedding/test_basic.py
@@ -67,6 +67,6 @@
         output = self.execute('add2-test')
         assert output == ("preparing...\n"
                           "adding 40 and 2\n"
-                          "preparing ADD2\n"
+                          "prepADD2\n"
                           "adding 100 and -5 and -20\n"
                           "got: 42 75\n")
diff --git a/testing/embedding/test_thread.py b/testing/embedding/test_thread.py
--- a/testing/embedding/test_thread.py
+++ b/testing/embedding/test_thread.py
@@ -5,24 +5,44 @@
     def test_first_calls_in_parallel(self):
         self.prepare_module('add1')
         self.compile('thread1-test', ['_add1_cffi'], ['-pthread'])
-        for i in range(5):
+        for i in range(50):
             output = self.execute('thread1-test')
             assert output == ("starting\n"
                               "preparing...\n" +
                               "adding 40 and 2\n" * 10 +
                               "done\n")
 
+    def _take_out(self, text, content):
+        assert content in text
+        i = text.index(content)
+        return text[:i] + text[i+len(content):]
+
     def test_init_different_modules_in_different_threads(self):
         self.prepare_module('add1')
         self.prepare_module('add2')
         self.compile('thread2-test', ['_add1_cffi', '_add2_cffi'], 
['-pthread'])
         output = self.execute('thread2-test')
-        assert output == XXX
+        output = self._take_out(output, "preparing")
+        output = self._take_out(output, ".")
+        output = self._take_out(output, ".")
+        # at least the 3rd dot should be after everything from ADD2
+        assert output == ("starting\n"
+                          "prepADD2\n"
+                          "adding 1000 and 200 and 30\n"
+                          ".\n"
+                          "adding 40 and 2\n"
+                          "done\n")
 
-    def test_next_issue(self):
+    def test_alt_issue(self):
         self.prepare_module('add1')
         self.prepare_module('add2')
         self.compile('thread2-test', ['_add1_cffi', '_add2_cffi'],
                      ['-pthread', '-DT2TEST_AGAIN_ADD1'])
         output = self.execute('thread2-test')
-        assert output == XXX
+        output = self._take_out(output, "adding 40 and 2\n")
+        assert output == ("starting\n"
+                          "preparing...\n"
+                          "adding -1 and -1\n"
+                          "prepADD2\n"
+                          "adding 1000 and 200 and 30\n"
+                          "done\n")
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to