[pypy-commit] pypy cpyext-tls-operror: cpyext: add a test for error state race condition

pv pypy.commits at gmail.com
Sun May 6 15:27:25 EDT 2018


Author: Pauli Virtanen <pav at iki.fi>
Branch: cpyext-tls-operror
Changeset: r94472:db2b75a607e4
Date: 2018-03-20 21:17 +0100
http://bitbucket.org/pypy/pypy/changeset/db2b75a607e4/

Log:	cpyext: add a test for error state race condition

diff --git a/pypy/module/cpyext/test/test_pyerrors.py b/pypy/module/cpyext/test/test_pyerrors.py
--- a/pypy/module/cpyext/test/test_pyerrors.py
+++ b/pypy/module/cpyext/test/test_pyerrors.py
@@ -478,3 +478,59 @@
              '''),
             ])
         raises(SystemError, module.oops)
+
+    def test_error_thread_race(self):
+        # Check race condition: thread 0 returns from cpyext with error set,
+        # after thread 1 has set an error but before it returns.
+        module = self.import_extension('foo', [
+            ("emit_error", "METH_VARARGS",
+             '''
+             PyThreadState *save = NULL;
+             PyGILState_STATE gilsave;
+
+             /* NB. synchronization due to GIL */
+             static volatile int flag = 0;
+             int id;
+
+             if (!PyArg_ParseTuple(args, "i", &id))
+                 return NULL;
+
+             /* Proceed in thread 1 first */
+             save = PyEval_SaveThread();
+             while (id == 0 && flag == 0);
+             gilsave = PyGILState_Ensure();
+
+             PyErr_SetString(PyExc_ValueError, "failure");
+
+             /* Proceed in thread 0 first */
+             if (id == 1) flag = 1;
+             PyGILState_Release(gilsave);
+             while (id == 1 && flag == 1);
+             PyEval_RestoreThread(save);
+
+             if (id == 0) flag = 0;
+             return NULL;
+             '''
+             ),
+            ])
+
+        import threading
+
+        failures = []
+
+        def worker(arg):
+            try:
+                module.emit_error(arg)
+                failures.append(True)
+            except ValueError as exc:
+                if str(exc) != "failure":
+                    failures.append(exc)
+
+        threads = [threading.Thread(target=worker, args=(j,))
+                   for j in (0, 1)]
+        for t in threads:
+            t.start()
+        for t in threads:
+            t.join()
+
+        assert not failures


More information about the pypy-commit mailing list