1 /* This is a set of functions used to test C++ exceptions are not
2  * broken during greenlet switches
3  */
4 
5 #include "../greenlet.h"
6 
7 struct exception_t {
8     int depth;
exception_texception_t9     exception_t(int depth) : depth(depth) {}
10 };
11 
12 /* Functions are called via pointers to prevent inlining */
13 static void (*p_test_exception_throw)(int depth);
14 static PyObject* (*p_test_exception_switch_recurse)(int depth, int left);
15 
16 static void
test_exception_throw(int depth)17 test_exception_throw(int depth)
18 {
19     throw exception_t(depth);
20 }
21 
22 static PyObject*
test_exception_switch_recurse(int depth,int left)23 test_exception_switch_recurse(int depth, int left)
24 {
25     if (left > 0) {
26         return p_test_exception_switch_recurse(depth, left - 1);
27     }
28 
29     PyObject* result = NULL;
30     PyGreenlet* self = PyGreenlet_GetCurrent();
31     if (self == NULL)
32         return NULL;
33 
34     try {
35         PyGreenlet_Switch(self->parent, NULL, NULL);
36         p_test_exception_throw(depth);
37         PyErr_SetString(PyExc_RuntimeError,
38                         "throwing C++ exception didn't work");
39     }
40     catch (exception_t& e) {
41         if (e.depth != depth)
42             PyErr_SetString(PyExc_AssertionError, "depth mismatch");
43         else
44             result = PyLong_FromLong(depth);
45     }
46     catch (...) {
47         PyErr_SetString(PyExc_RuntimeError, "unexpected C++ exception");
48     }
49 
50     Py_DECREF(self);
51     return result;
52 }
53 
54 /* test_exception_switch(int depth)
55  * - recurses depth times
56  * - switches to parent inside try/catch block
57  * - throws an exception that (expected to be caught in the same function)
58  * - verifies depth matches (exceptions shouldn't be caught in other greenlets)
59  */
60 static PyObject*
test_exception_switch(PyObject * self,PyObject * args)61 test_exception_switch(PyObject* self, PyObject* args)
62 {
63     int depth;
64     if (!PyArg_ParseTuple(args, "i", &depth))
65         return NULL;
66     return p_test_exception_switch_recurse(depth, depth);
67 }
68 
69 static PyMethodDef test_methods[] = {
70     {"test_exception_switch",
71      (PyCFunction)&test_exception_switch,
72      METH_VARARGS,
73      "Switches to parent twice, to test exception handling and greenlet "
74      "switching."},
75     {NULL, NULL, 0, NULL}};
76 
77 #if PY_MAJOR_VERSION >= 3
78 #    define INITERROR return NULL
79 
80 static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT,
81                                        "greenlet.tests._test_extension_cpp",
82                                        NULL,
83                                        0,
84                                        test_methods,
85                                        NULL,
86                                        NULL,
87                                        NULL,
88                                        NULL};
89 
90 PyMODINIT_FUNC
PyInit__test_extension_cpp(void)91 PyInit__test_extension_cpp(void)
92 #else
93 #    define INITERROR return
94 PyMODINIT_FUNC
95 init_test_extension_cpp(void)
96 #endif
97 {
98     PyObject* module = NULL;
99 
100 #if PY_MAJOR_VERSION >= 3
101     module = PyModule_Create(&moduledef);
102 #else
103     module = Py_InitModule("greenlet.tests._test_extension_cpp", test_methods);
104 #endif
105 
106     if (module == NULL) {
107         INITERROR;
108     }
109 
110     PyGreenlet_Import();
111     if (_PyGreenlet_API == NULL) {
112         INITERROR;
113     }
114 
115     p_test_exception_throw = test_exception_throw;
116     p_test_exception_switch_recurse = test_exception_switch_recurse;
117 
118 #if PY_MAJOR_VERSION >= 3
119     return module;
120 #endif
121 }
122