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