| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* interpreters module */ | 
					
						
							|  |  |  | /* low-level access to interpreter primitives */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "Python.h"
 | 
					
						
							|  |  |  | #include "frameobject.h"
 | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  | #include "interpreteridobject.h"
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static char * | 
					
						
							|  |  |  | _copy_raw_string(PyObject *strobj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const char *str = PyUnicode_AsUTF8(strobj); | 
					
						
							|  |  |  |     if (str == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     char *copied = PyMem_Malloc(strlen(str)+1); | 
					
						
							| 
									
										
										
										
											2018-08-22 21:27:32 +03:00
										 |  |  |     if (copied == NULL) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     strcpy(copied, str); | 
					
						
							|  |  |  |     return copied; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | static PyInterpreterState * | 
					
						
							|  |  |  | _get_current(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-08-03 15:33:52 +02:00
										 |  |  |     // _PyInterpreterState_Get() aborts if lookup fails, so don't need
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     // to check the result for NULL.
 | 
					
						
							| 
									
										
										
										
											2018-08-03 15:33:52 +02:00
										 |  |  |     return _PyInterpreterState_Get(); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | /* data-sharing-specific code ***********************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | struct _sharednsitem { | 
					
						
							|  |  |  |     char *name; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     _PyCrossInterpreterData data; | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  | static void _sharednsitem_clear(struct _sharednsitem *);  // forward
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static int | 
					
						
							|  |  |  | _sharednsitem_init(struct _sharednsitem *item, PyObject *key, PyObject *value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     item->name = _copy_raw_string(key); | 
					
						
							|  |  |  |     if (item->name == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (_PyObject_GetCrossInterpreterData(value, &item->data) != 0) { | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |         _sharednsitem_clear(item); | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _sharednsitem_clear(struct _sharednsitem *item) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (item->name != NULL) { | 
					
						
							|  |  |  |         PyMem_Free(item->name); | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |         item->name = NULL; | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     _PyCrossInterpreterData_Release(&item->data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _sharednsitem_apply(struct _sharednsitem *item, PyObject *ns) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *name = PyUnicode_FromString(item->name); | 
					
						
							|  |  |  |     if (name == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *value = _PyCrossInterpreterData_NewObject(&item->data); | 
					
						
							|  |  |  |     if (value == NULL) { | 
					
						
							|  |  |  |         Py_DECREF(name); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int res = PyDict_SetItem(ns, name, value); | 
					
						
							|  |  |  |     Py_DECREF(name); | 
					
						
							|  |  |  |     Py_DECREF(value); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct _sharedns { | 
					
						
							|  |  |  |     Py_ssize_t len; | 
					
						
							|  |  |  |     struct _sharednsitem* items; | 
					
						
							|  |  |  | } _sharedns; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _sharedns * | 
					
						
							|  |  |  | _sharedns_new(Py_ssize_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _sharedns *shared = PyMem_NEW(_sharedns, 1); | 
					
						
							|  |  |  |     if (shared == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     shared->len = len; | 
					
						
							|  |  |  |     shared->items = PyMem_NEW(struct _sharednsitem, len); | 
					
						
							|  |  |  |     if (shared->items == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         PyMem_Free(shared); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return shared; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static void | 
					
						
							|  |  |  | _sharedns_free(_sharedns *shared) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     for (Py_ssize_t i=0; i < shared->len; i++) { | 
					
						
							|  |  |  |         _sharednsitem_clear(&shared->items[i]); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     PyMem_Free(shared->items); | 
					
						
							|  |  |  |     PyMem_Free(shared); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static _sharedns * | 
					
						
							|  |  |  | _get_shared_ns(PyObject *shareable) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     if (shareable == NULL || shareable == Py_None) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_ssize_t len = PyDict_Size(shareable); | 
					
						
							|  |  |  |     if (len == 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     _sharedns *shared = _sharedns_new(len); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (shared == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_ssize_t pos = 0; | 
					
						
							|  |  |  |     for (Py_ssize_t i=0; i < len; i++) { | 
					
						
							|  |  |  |         PyObject *key, *value; | 
					
						
							|  |  |  |         if (PyDict_Next(shareable, &pos, &key, &value) == 0) { | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         if (_sharednsitem_init(&shared->items[i], key, value) != 0) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (PyErr_Occurred()) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         _sharedns_free(shared); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return shared; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | _sharedns_apply(_sharedns *shared, PyObject *ns) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     for (Py_ssize_t i=0; i < shared->len; i++) { | 
					
						
							|  |  |  |         if (_sharednsitem_apply(&shared->items[i], ns) != 0) { | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Ultimately we'd like to preserve enough information about the
 | 
					
						
							|  |  |  | // exception and traceback that we could re-constitute (or at least
 | 
					
						
							|  |  |  | // simulate, a la traceback.TracebackException), and even chain, a copy
 | 
					
						
							|  |  |  | // of the exception in the calling interpreter.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct _sharedexception { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     char *name; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     char *msg; | 
					
						
							|  |  |  | } _sharedexception; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _sharedexception * | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | _sharedexception_new(void) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     _sharedexception *err = PyMem_NEW(_sharedexception, 1); | 
					
						
							|  |  |  |     if (err == NULL) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         PyErr_NoMemory(); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     err->name = NULL; | 
					
						
							|  |  |  |     err->msg = NULL; | 
					
						
							|  |  |  |     return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _sharedexception_clear(_sharedexception *exc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (exc->name != NULL) { | 
					
						
							|  |  |  |         PyMem_Free(exc->name); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     if (exc->msg != NULL) { | 
					
						
							|  |  |  |         PyMem_Free(exc->msg); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static void | 
					
						
							|  |  |  | _sharedexception_free(_sharedexception *exc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _sharedexception_clear(exc); | 
					
						
							|  |  |  |     PyMem_Free(exc); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static _sharedexception * | 
					
						
							|  |  |  | _sharedexception_bind(PyObject *exctype, PyObject *exc, PyObject *tb) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     assert(exctype != NULL); | 
					
						
							|  |  |  |     char *failure = NULL; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     _sharedexception *err = _sharedexception_new(); | 
					
						
							|  |  |  |     if (err == NULL) { | 
					
						
							|  |  |  |         goto finally; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     PyObject *name = PyUnicode_FromFormat("%S", exctype); | 
					
						
							|  |  |  |     if (name == NULL) { | 
					
						
							|  |  |  |         failure = "unable to format exception type name"; | 
					
						
							|  |  |  |         goto finally; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     err->name = _copy_raw_string(name); | 
					
						
							|  |  |  |     Py_DECREF(name); | 
					
						
							|  |  |  |     if (err->name == NULL) { | 
					
						
							|  |  |  |         if (PyErr_ExceptionMatches(PyExc_MemoryError)) { | 
					
						
							|  |  |  |             failure = "out of memory copying exception type name"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         failure = "unable to encode and copy exception type name"; | 
					
						
							|  |  |  |         goto finally; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     if (exc != NULL) { | 
					
						
							|  |  |  |         PyObject *msg = PyUnicode_FromFormat("%S", exc); | 
					
						
							|  |  |  |         if (msg == NULL) { | 
					
						
							|  |  |  |             failure = "unable to format exception message"; | 
					
						
							|  |  |  |             goto finally; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         err->msg = _copy_raw_string(msg); | 
					
						
							|  |  |  |         Py_DECREF(msg); | 
					
						
							|  |  |  |         if (err->msg == NULL) { | 
					
						
							|  |  |  |             if (PyErr_ExceptionMatches(PyExc_MemoryError)) { | 
					
						
							|  |  |  |                 failure = "out of memory copying exception message"; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             failure = "unable to encode and copy exception message"; | 
					
						
							|  |  |  |             goto finally; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | finally: | 
					
						
							|  |  |  |     if (failure != NULL) { | 
					
						
							|  |  |  |         PyErr_Clear(); | 
					
						
							|  |  |  |         if (err->name != NULL) { | 
					
						
							|  |  |  |             PyMem_Free(err->name); | 
					
						
							|  |  |  |             err->name = NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         err->msg = failure; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return err; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | _sharedexception_apply(_sharedexception *exc, PyObject *wrapperclass) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     if (exc->name != NULL) { | 
					
						
							|  |  |  |         if (exc->msg != NULL) { | 
					
						
							|  |  |  |             PyErr_Format(wrapperclass, "%s: %s",  exc->name, exc->msg); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             PyErr_SetString(wrapperclass, exc->name); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (exc->msg != NULL) { | 
					
						
							|  |  |  |         PyErr_SetString(wrapperclass, exc->msg); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         PyErr_SetNone(wrapperclass); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* channel-specific code ****************************************************/ | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  | #define CHANNEL_SEND 1
 | 
					
						
							|  |  |  | #define CHANNEL_BOTH 0
 | 
					
						
							|  |  |  | #define CHANNEL_RECV -1
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | static PyObject *ChannelError; | 
					
						
							|  |  |  | static PyObject *ChannelNotFoundError; | 
					
						
							|  |  |  | static PyObject *ChannelClosedError; | 
					
						
							|  |  |  | static PyObject *ChannelEmptyError; | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  | static PyObject *ChannelNotEmptyError; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | channel_exceptions_init(PyObject *ns) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // XXX Move the exceptions into per-module memory?
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // A channel-related operation failed.
 | 
					
						
							|  |  |  |     ChannelError = PyErr_NewException("_xxsubinterpreters.ChannelError", | 
					
						
							|  |  |  |                                       PyExc_RuntimeError, NULL); | 
					
						
							|  |  |  |     if (ChannelError == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (PyDict_SetItemString(ns, "ChannelError", ChannelError) != 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // An operation tried to use a channel that doesn't exist.
 | 
					
						
							|  |  |  |     ChannelNotFoundError = PyErr_NewException( | 
					
						
							|  |  |  |             "_xxsubinterpreters.ChannelNotFoundError", ChannelError, NULL); | 
					
						
							|  |  |  |     if (ChannelNotFoundError == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (PyDict_SetItemString(ns, "ChannelNotFoundError", ChannelNotFoundError) != 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // An operation tried to use a closed channel.
 | 
					
						
							|  |  |  |     ChannelClosedError = PyErr_NewException( | 
					
						
							|  |  |  |             "_xxsubinterpreters.ChannelClosedError", ChannelError, NULL); | 
					
						
							|  |  |  |     if (ChannelClosedError == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (PyDict_SetItemString(ns, "ChannelClosedError", ChannelClosedError) != 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // An operation tried to pop from an empty channel.
 | 
					
						
							|  |  |  |     ChannelEmptyError = PyErr_NewException( | 
					
						
							|  |  |  |             "_xxsubinterpreters.ChannelEmptyError", ChannelError, NULL); | 
					
						
							|  |  |  |     if (ChannelEmptyError == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (PyDict_SetItemString(ns, "ChannelEmptyError", ChannelEmptyError) != 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     // An operation tried to close a non-empty channel.
 | 
					
						
							|  |  |  |     ChannelNotEmptyError = PyErr_NewException( | 
					
						
							|  |  |  |             "_xxsubinterpreters.ChannelNotEmptyError", ChannelError, NULL); | 
					
						
							|  |  |  |     if (ChannelNotEmptyError == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (PyDict_SetItemString(ns, "ChannelNotEmptyError", ChannelNotEmptyError) != 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | /* the channel queue */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct _channelitem; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct _channelitem { | 
					
						
							|  |  |  |     _PyCrossInterpreterData *data; | 
					
						
							|  |  |  |     struct _channelitem *next; | 
					
						
							|  |  |  | } _channelitem; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _channelitem * | 
					
						
							|  |  |  | _channelitem_new(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelitem *item = PyMem_NEW(_channelitem, 1); | 
					
						
							|  |  |  |     if (item == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     item->data = NULL; | 
					
						
							|  |  |  |     item->next = NULL; | 
					
						
							|  |  |  |     return item; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _channelitem_clear(_channelitem *item) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (item->data != NULL) { | 
					
						
							|  |  |  |         _PyCrossInterpreterData_Release(item->data); | 
					
						
							|  |  |  |         PyMem_Free(item->data); | 
					
						
							|  |  |  |         item->data = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     item->next = NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _channelitem_free(_channelitem *item) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelitem_clear(item); | 
					
						
							|  |  |  |     PyMem_Free(item); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _channelitem_free_all(_channelitem *item) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     while (item != NULL) { | 
					
						
							|  |  |  |         _channelitem *last = item; | 
					
						
							|  |  |  |         item = item->next; | 
					
						
							|  |  |  |         _channelitem_free(last); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _PyCrossInterpreterData * | 
					
						
							|  |  |  | _channelitem_popped(_channelitem *item) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyCrossInterpreterData *data = item->data; | 
					
						
							|  |  |  |     item->data = NULL; | 
					
						
							|  |  |  |     _channelitem_free(item); | 
					
						
							|  |  |  |     return data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct _channelqueue { | 
					
						
							|  |  |  |     int64_t count; | 
					
						
							|  |  |  |     _channelitem *first; | 
					
						
							|  |  |  |     _channelitem *last; | 
					
						
							|  |  |  | } _channelqueue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _channelqueue * | 
					
						
							|  |  |  | _channelqueue_new(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelqueue *queue = PyMem_NEW(_channelqueue, 1); | 
					
						
							|  |  |  |     if (queue == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     queue->count = 0; | 
					
						
							|  |  |  |     queue->first = NULL; | 
					
						
							|  |  |  |     queue->last = NULL; | 
					
						
							|  |  |  |     return queue; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _channelqueue_clear(_channelqueue *queue) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelitem_free_all(queue->first); | 
					
						
							|  |  |  |     queue->count = 0; | 
					
						
							|  |  |  |     queue->first = NULL; | 
					
						
							|  |  |  |     queue->last = NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _channelqueue_free(_channelqueue *queue) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelqueue_clear(queue); | 
					
						
							|  |  |  |     PyMem_Free(queue); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _channelqueue_put(_channelqueue *queue, _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelitem *item = _channelitem_new(); | 
					
						
							|  |  |  |     if (item == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     item->data = data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     queue->count += 1; | 
					
						
							|  |  |  |     if (queue->first == NULL) { | 
					
						
							|  |  |  |         queue->first = item; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         queue->last->next = item; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     queue->last = item; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _PyCrossInterpreterData * | 
					
						
							|  |  |  | _channelqueue_get(_channelqueue *queue) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelitem *item = queue->first; | 
					
						
							|  |  |  |     if (item == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     queue->first = item->next; | 
					
						
							|  |  |  |     if (queue->last == item) { | 
					
						
							|  |  |  |         queue->last = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     queue->count -= 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return _channelitem_popped(item); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* channel-interpreter associations */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | struct _channelend; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct _channelend { | 
					
						
							|  |  |  |     struct _channelend *next; | 
					
						
							|  |  |  |     int64_t interp; | 
					
						
							|  |  |  |     int open; | 
					
						
							|  |  |  | } _channelend; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _channelend * | 
					
						
							|  |  |  | _channelend_new(int64_t interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelend *end = PyMem_NEW(_channelend, 1); | 
					
						
							|  |  |  |     if (end == NULL) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         PyErr_NoMemory(); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     end->next = NULL; | 
					
						
							|  |  |  |     end->interp = interp; | 
					
						
							|  |  |  |     end->open = 1; | 
					
						
							|  |  |  |     return end; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | _channelend_free(_channelend *end) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyMem_Free(end); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _channelend_free_all(_channelend *end) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     while (end != NULL) { | 
					
						
							|  |  |  |         _channelend *last = end; | 
					
						
							|  |  |  |         end = end->next; | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         _channelend_free(last); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _channelend * | 
					
						
							|  |  |  | _channelend_find(_channelend *first, int64_t interp, _channelend **pprev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelend *prev = NULL; | 
					
						
							|  |  |  |     _channelend *end = first; | 
					
						
							|  |  |  |     while (end != NULL) { | 
					
						
							|  |  |  |         if (end->interp == interp) { | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         prev = end; | 
					
						
							|  |  |  |         end = end->next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (pprev != NULL) { | 
					
						
							|  |  |  |         *pprev = prev; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return end; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | typedef struct _channelassociations { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     // Note that the list entries are never removed for interpreter
 | 
					
						
							|  |  |  |     // for which the channel is closed.  This should be a problem in
 | 
					
						
							|  |  |  |     // practice.  Also, a channel isn't automatically closed when an
 | 
					
						
							|  |  |  |     // interpreter is destroyed.
 | 
					
						
							|  |  |  |     int64_t numsendopen; | 
					
						
							|  |  |  |     int64_t numrecvopen; | 
					
						
							|  |  |  |     _channelend *send; | 
					
						
							|  |  |  |     _channelend *recv; | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | } _channelends; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static _channelends * | 
					
						
							|  |  |  | _channelends_new(void) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     _channelends *ends = PyMem_NEW(_channelends, 1); | 
					
						
							|  |  |  |     if (ends== NULL) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     ends->numsendopen = 0; | 
					
						
							|  |  |  |     ends->numrecvopen = 0; | 
					
						
							|  |  |  |     ends->send = NULL; | 
					
						
							|  |  |  |     ends->recv = NULL; | 
					
						
							|  |  |  |     return ends; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static void | 
					
						
							|  |  |  | _channelends_clear(_channelends *ends) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelend_free_all(ends->send); | 
					
						
							|  |  |  |     ends->send = NULL; | 
					
						
							|  |  |  |     ends->numsendopen = 0; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     _channelend_free_all(ends->recv); | 
					
						
							|  |  |  |     ends->recv = NULL; | 
					
						
							|  |  |  |     ends->numrecvopen = 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static void | 
					
						
							|  |  |  | _channelends_free(_channelends *ends) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelends_clear(ends); | 
					
						
							|  |  |  |     PyMem_Free(ends); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _channelend * | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | _channelends_add(_channelends *ends, _channelend *prev, int64_t interp, | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |                  int send) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelend *end = _channelend_new(interp); | 
					
						
							|  |  |  |     if (end == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (prev == NULL) { | 
					
						
							|  |  |  |         if (send) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |             ends->send = end; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |             ends->recv = end; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         prev->next = end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (send) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         ends->numsendopen += 1; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         ends->numrecvopen += 1; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     return end; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static int | 
					
						
							|  |  |  | _channelends_associate(_channelends *ends, int64_t interp, int send) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     _channelend *prev; | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     _channelend *end = _channelend_find(send ? ends->send : ends->recv, | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |                                         interp, &prev); | 
					
						
							|  |  |  |     if (end != NULL) { | 
					
						
							|  |  |  |         if (!end->open) { | 
					
						
							|  |  |  |             PyErr_SetString(ChannelClosedError, "channel already closed"); | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |             return -1; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |         // already associated
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (_channelends_add(ends, prev, interp, send) == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _channelends_is_open(_channelends *ends) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (ends->numsendopen != 0 || ends->numrecvopen != 0) { | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (ends->send == NULL && ends->recv == NULL) { | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | _channelends_close_end(_channelends *ends, _channelend *end, int send) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     end->open = 0; | 
					
						
							|  |  |  |     if (send) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         ends->numsendopen -= 1; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         ends->numrecvopen -= 1; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | _channelends_close_interpreter(_channelends *ends, int64_t interp, int which) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     _channelend *prev; | 
					
						
							|  |  |  |     _channelend *end; | 
					
						
							|  |  |  |     if (which >= 0) {  // send/both
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         end = _channelend_find(ends->send, interp, &prev); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         if (end == NULL) { | 
					
						
							|  |  |  |             // never associated so add it
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |             end = _channelends_add(ends, prev, interp, 1); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |             if (end == NULL) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |                 return -1; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         _channelends_close_end(ends, end, 1); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     if (which <= 0) {  // recv/both
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         end = _channelend_find(ends->recv, interp, &prev); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         if (end == NULL) { | 
					
						
							|  |  |  |             // never associated so add it
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |             end = _channelends_add(ends, prev, interp, 0); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |             if (end == NULL) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |                 return -1; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         _channelends_close_end(ends, end, 0); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static void | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  | _channelends_close_all(_channelends *ends, int which, int force) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     // XXX Handle the ends.
 | 
					
						
							|  |  |  |     // XXX Handle force is True.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     // Ensure all the "send"-associated interpreters are closed.
 | 
					
						
							|  |  |  |     _channelend *end; | 
					
						
							|  |  |  |     for (end = ends->send; end != NULL; end = end->next) { | 
					
						
							|  |  |  |         _channelends_close_end(ends, end, 1); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     // Ensure all the "recv"-associated interpreters are closed.
 | 
					
						
							|  |  |  |     for (end = ends->recv; end != NULL; end = end->next) { | 
					
						
							|  |  |  |         _channelends_close_end(ends, end, 0); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | /* channels */ | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | struct _channel; | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  | struct _channel_closing; | 
					
						
							|  |  |  | static void _channel_clear_closing(struct _channel *); | 
					
						
							|  |  |  | static void _channel_finish_closing(struct _channel *); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | typedef struct _channel { | 
					
						
							|  |  |  |     PyThread_type_lock mutex; | 
					
						
							|  |  |  |     _channelqueue *queue; | 
					
						
							|  |  |  |     _channelends *ends; | 
					
						
							|  |  |  |     int open; | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     struct _channel_closing *closing; | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | } _PyChannelState; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static _PyChannelState * | 
					
						
							|  |  |  | _channel_new(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyChannelState *chan = PyMem_NEW(_PyChannelState, 1); | 
					
						
							|  |  |  |     if (chan == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     chan->mutex = PyThread_allocate_lock(); | 
					
						
							|  |  |  |     if (chan->mutex == NULL) { | 
					
						
							|  |  |  |         PyMem_Free(chan); | 
					
						
							|  |  |  |         PyErr_SetString(ChannelError, | 
					
						
							|  |  |  |                         "can't initialize mutex for new channel"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     chan->queue = _channelqueue_new(); | 
					
						
							|  |  |  |     if (chan->queue == NULL) { | 
					
						
							|  |  |  |         PyMem_Free(chan); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     chan->ends = _channelends_new(); | 
					
						
							|  |  |  |     if (chan->ends == NULL) { | 
					
						
							|  |  |  |         _channelqueue_free(chan->queue); | 
					
						
							|  |  |  |         PyMem_Free(chan); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     chan->open = 1; | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     chan->closing = NULL; | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     return chan; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static void | 
					
						
							|  |  |  | _channel_free(_PyChannelState *chan) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     _channel_clear_closing(chan); | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     PyThread_acquire_lock(chan->mutex, WAIT_LOCK); | 
					
						
							|  |  |  |     _channelqueue_free(chan->queue); | 
					
						
							|  |  |  |     _channelends_free(chan->ends); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyThread_release_lock(chan->mutex); | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     PyThread_free_lock(chan->mutex); | 
					
						
							|  |  |  |     PyMem_Free(chan); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _channel_add(_PyChannelState *chan, int64_t interp, | 
					
						
							|  |  |  |              _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int res = -1; | 
					
						
							|  |  |  |     PyThread_acquire_lock(chan->mutex, WAIT_LOCK); | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!chan->open) { | 
					
						
							|  |  |  |         PyErr_SetString(ChannelClosedError, "channel closed"); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     if (_channelends_associate(chan->ends, interp, 1) != 0) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     if (_channelqueue_put(chan->queue, data) != 0) { | 
					
						
							|  |  |  |         goto done; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = 0; | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  |     PyThread_release_lock(chan->mutex); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _PyCrossInterpreterData * | 
					
						
							|  |  |  | _channel_next(_PyChannelState *chan, int64_t interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyCrossInterpreterData *data = NULL; | 
					
						
							|  |  |  |     PyThread_acquire_lock(chan->mutex, WAIT_LOCK); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     if (!chan->open) { | 
					
						
							|  |  |  |         PyErr_SetString(ChannelClosedError, "channel closed"); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     if (_channelends_associate(chan->ends, interp, 0) != 0) { | 
					
						
							|  |  |  |         goto done; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     data = _channelqueue_get(chan->queue); | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     if (data == NULL && !PyErr_Occurred() && chan->closing != NULL) { | 
					
						
							|  |  |  |         chan->open = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | done: | 
					
						
							|  |  |  |     PyThread_release_lock(chan->mutex); | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     if (chan->queue->count == 0) { | 
					
						
							|  |  |  |         _channel_finish_closing(chan); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     return data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  | _channel_close_interpreter(_PyChannelState *chan, int64_t interp, int end) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     PyThread_acquire_lock(chan->mutex, WAIT_LOCK); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int res = -1; | 
					
						
							|  |  |  |     if (!chan->open) { | 
					
						
							|  |  |  |         PyErr_SetString(ChannelClosedError, "channel already closed"); | 
					
						
							|  |  |  |         goto done; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     if (_channelends_close_interpreter(chan->ends, interp, end) != 0) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     chan->open = _channelends_is_open(chan->ends); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = 0; | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  |     PyThread_release_lock(chan->mutex); | 
					
						
							|  |  |  |     return res; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  | _channel_close_all(_PyChannelState *chan, int end, int force) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     int res = -1; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyThread_acquire_lock(chan->mutex, WAIT_LOCK); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     if (!chan->open) { | 
					
						
							|  |  |  |         PyErr_SetString(ChannelClosedError, "channel already closed"); | 
					
						
							|  |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     if (!force && chan->queue->count > 0) { | 
					
						
							|  |  |  |         PyErr_SetString(ChannelNotEmptyError, | 
					
						
							|  |  |  |                         "may not be closed if not empty (try force=True)"); | 
					
						
							|  |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     chan->open = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // We *could* also just leave these in place, since we've marked
 | 
					
						
							|  |  |  |     // the channel as closed already.
 | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     _channelends_close_all(chan->ends, end, force); | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     res = 0; | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  |     PyThread_release_lock(chan->mutex); | 
					
						
							|  |  |  |     return res; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | /* the set of channels */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | struct _channelref; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct _channelref { | 
					
						
							|  |  |  |     int64_t id; | 
					
						
							|  |  |  |     _PyChannelState *chan; | 
					
						
							|  |  |  |     struct _channelref *next; | 
					
						
							|  |  |  |     Py_ssize_t objcount; | 
					
						
							|  |  |  | } _channelref; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _channelref * | 
					
						
							|  |  |  | _channelref_new(int64_t id, _PyChannelState *chan) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelref *ref = PyMem_NEW(_channelref, 1); | 
					
						
							|  |  |  |     if (ref == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ref->id = id; | 
					
						
							|  |  |  |     ref->chan = chan; | 
					
						
							|  |  |  |     ref->next = NULL; | 
					
						
							|  |  |  |     ref->objcount = 0; | 
					
						
							|  |  |  |     return ref; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | //static void
 | 
					
						
							|  |  |  | //_channelref_clear(_channelref *ref)
 | 
					
						
							|  |  |  | //{
 | 
					
						
							|  |  |  | //    ref->id = -1;
 | 
					
						
							|  |  |  | //    ref->chan = NULL;
 | 
					
						
							|  |  |  | //    ref->next = NULL;
 | 
					
						
							|  |  |  | //    ref->objcount = 0;
 | 
					
						
							|  |  |  | //}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _channelref_free(_channelref *ref) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     if (ref->chan != NULL) { | 
					
						
							|  |  |  |         _channel_clear_closing(ref->chan); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     //_channelref_clear(ref);
 | 
					
						
							|  |  |  |     PyMem_Free(ref); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | static _channelref * | 
					
						
							|  |  |  | _channelref_find(_channelref *first, int64_t id, _channelref **pprev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _channelref *prev = NULL; | 
					
						
							|  |  |  |     _channelref *ref = first; | 
					
						
							|  |  |  |     while (ref != NULL) { | 
					
						
							|  |  |  |         if (ref->id == id) { | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         prev = ref; | 
					
						
							|  |  |  |         ref = ref->next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (pprev != NULL) { | 
					
						
							|  |  |  |         *pprev = prev; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ref; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct _channels { | 
					
						
							|  |  |  |     PyThread_type_lock mutex; | 
					
						
							|  |  |  |     _channelref *head; | 
					
						
							|  |  |  |     int64_t numopen; | 
					
						
							|  |  |  |     int64_t next_id; | 
					
						
							|  |  |  | } _channels; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _channels_init(_channels *channels) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (channels->mutex == NULL) { | 
					
						
							|  |  |  |         channels->mutex = PyThread_allocate_lock(); | 
					
						
							|  |  |  |         if (channels->mutex == NULL) { | 
					
						
							|  |  |  |             PyErr_SetString(ChannelError, | 
					
						
							|  |  |  |                             "can't initialize mutex for channel management"); | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     channels->head = NULL; | 
					
						
							|  |  |  |     channels->numopen = 0; | 
					
						
							|  |  |  |     channels->next_id = 0; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int64_t | 
					
						
							|  |  |  | _channels_next_id(_channels *channels)  // needs lock
 | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int64_t id = channels->next_id; | 
					
						
							|  |  |  |     if (id < 0) { | 
					
						
							|  |  |  |         /* overflow */ | 
					
						
							|  |  |  |         PyErr_SetString(ChannelError, | 
					
						
							|  |  |  |                         "failed to get a channel ID"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     channels->next_id += 1; | 
					
						
							|  |  |  |     return id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _PyChannelState * | 
					
						
							|  |  |  | _channels_lookup(_channels *channels, int64_t id, PyThread_type_lock *pmutex) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyChannelState *chan = NULL; | 
					
						
							|  |  |  |     PyThread_acquire_lock(channels->mutex, WAIT_LOCK); | 
					
						
							|  |  |  |     if (pmutex != NULL) { | 
					
						
							|  |  |  |         *pmutex = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _channelref *ref = _channelref_find(channels->head, id, NULL); | 
					
						
							|  |  |  |     if (ref == NULL) { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |         PyErr_Format(ChannelNotFoundError, "channel %" PRId64 " not found", id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (ref->chan == NULL || !ref->chan->open) { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |         PyErr_Format(ChannelClosedError, "channel %" PRId64 " closed", id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (pmutex != NULL) { | 
					
						
							|  |  |  |         // The mutex will be closed by the caller.
 | 
					
						
							|  |  |  |         *pmutex = channels->mutex; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     chan = ref->chan; | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  |     if (pmutex == NULL || *pmutex == NULL) { | 
					
						
							|  |  |  |         PyThread_release_lock(channels->mutex); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return chan; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int64_t | 
					
						
							|  |  |  | _channels_add(_channels *channels, _PyChannelState *chan) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int64_t cid = -1; | 
					
						
							|  |  |  |     PyThread_acquire_lock(channels->mutex, WAIT_LOCK); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Create a new ref.
 | 
					
						
							|  |  |  |     int64_t id = _channels_next_id(channels); | 
					
						
							|  |  |  |     if (id < 0) { | 
					
						
							|  |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _channelref *ref = _channelref_new(id, chan); | 
					
						
							|  |  |  |     if (ref == NULL) { | 
					
						
							|  |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Add it to the list.
 | 
					
						
							|  |  |  |     // We assume that the channel is a new one (not already in the list).
 | 
					
						
							|  |  |  |     ref->next = channels->head; | 
					
						
							|  |  |  |     channels->head = ref; | 
					
						
							|  |  |  |     channels->numopen += 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cid = id; | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  |     PyThread_release_lock(channels->mutex); | 
					
						
							|  |  |  |     return cid; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  | /* forward */ | 
					
						
							|  |  |  | static int _channel_set_closing(struct _channelref *, PyThread_type_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  | _channels_close(_channels *channels, int64_t cid, _PyChannelState **pchan, | 
					
						
							|  |  |  |                 int end, int force) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     int res = -1; | 
					
						
							|  |  |  |     PyThread_acquire_lock(channels->mutex, WAIT_LOCK); | 
					
						
							|  |  |  |     if (pchan != NULL) { | 
					
						
							|  |  |  |         *pchan = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _channelref *ref = _channelref_find(channels->head, cid, NULL); | 
					
						
							|  |  |  |     if (ref == NULL) { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |         PyErr_Format(ChannelNotFoundError, "channel %" PRId64 " not found", cid); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (ref->chan == NULL) { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |         PyErr_Format(ChannelClosedError, "channel %" PRId64 " closed", cid); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     else if (!force && end == CHANNEL_SEND && ref->chan->closing != NULL) { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |         PyErr_Format(ChannelClosedError, "channel %" PRId64 " closed", cid); | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     else { | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |         if (_channel_close_all(ref->chan, end, force) != 0) { | 
					
						
							|  |  |  |             if (end == CHANNEL_SEND && | 
					
						
							|  |  |  |                     PyErr_ExceptionMatches(ChannelNotEmptyError)) { | 
					
						
							|  |  |  |                 if (ref->chan->closing != NULL) { | 
					
						
							| 
									
										
										
										
											2018-06-01 16:26:01 -06:00
										 |  |  |                     PyErr_Format(ChannelClosedError, | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |                                  "channel %" PRId64 " closed", cid); | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |                     goto done; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 // Mark the channel as closing and return.  The channel
 | 
					
						
							|  |  |  |                 // will be cleaned up in _channel_next().
 | 
					
						
							|  |  |  |                 PyErr_Clear(); | 
					
						
							|  |  |  |                 if (_channel_set_closing(ref, channels->mutex) != 0) { | 
					
						
							|  |  |  |                     goto done; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (pchan != NULL) { | 
					
						
							|  |  |  |                     *pchan = ref->chan; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 res = 0; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |             goto done; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (pchan != NULL) { | 
					
						
							|  |  |  |             *pchan = ref->chan; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |         else  { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |             _channel_free(ref->chan); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         ref->chan = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = 0; | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  |     PyThread_release_lock(channels->mutex); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _channels_remove_ref(_channels *channels, _channelref *ref, _channelref *prev, | 
					
						
							|  |  |  |                      _PyChannelState **pchan) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (ref == channels->head) { | 
					
						
							|  |  |  |         channels->head = ref->next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         prev->next = ref->next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     channels->numopen -= 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (pchan != NULL) { | 
					
						
							|  |  |  |         *pchan = ref->chan; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     _channelref_free(ref); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _channels_remove(_channels *channels, int64_t id, _PyChannelState **pchan) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int res = -1; | 
					
						
							|  |  |  |     PyThread_acquire_lock(channels->mutex, WAIT_LOCK); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (pchan != NULL) { | 
					
						
							|  |  |  |         *pchan = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _channelref *prev = NULL; | 
					
						
							|  |  |  |     _channelref *ref = _channelref_find(channels->head, id, &prev); | 
					
						
							|  |  |  |     if (ref == NULL) { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |         PyErr_Format(ChannelNotFoundError, "channel %" PRId64 " not found", id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _channels_remove_ref(channels, ref, prev, pchan); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = 0; | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  |     PyThread_release_lock(channels->mutex); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _channels_add_id_object(_channels *channels, int64_t id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int res = -1; | 
					
						
							|  |  |  |     PyThread_acquire_lock(channels->mutex, WAIT_LOCK); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _channelref *ref = _channelref_find(channels->head, id, NULL); | 
					
						
							|  |  |  |     if (ref == NULL) { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |         PyErr_Format(ChannelNotFoundError, "channel %" PRId64 " not found", id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ref->objcount += 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = 0; | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  |     PyThread_release_lock(channels->mutex); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _channels_drop_id_object(_channels *channels, int64_t id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyThread_acquire_lock(channels->mutex, WAIT_LOCK); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _channelref *prev = NULL; | 
					
						
							|  |  |  |     _channelref *ref = _channelref_find(channels->head, id, &prev); | 
					
						
							|  |  |  |     if (ref == NULL) { | 
					
						
							|  |  |  |         // Already destroyed.
 | 
					
						
							|  |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ref->objcount -= 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Destroy if no longer used.
 | 
					
						
							|  |  |  |     if (ref->objcount == 0) { | 
					
						
							|  |  |  |         _PyChannelState *chan = NULL; | 
					
						
							|  |  |  |         _channels_remove_ref(channels, ref, prev, &chan); | 
					
						
							|  |  |  |         if (chan != NULL) { | 
					
						
							|  |  |  |             _channel_free(chan); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  |     PyThread_release_lock(channels->mutex); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-06 23:28:35 -07:00
										 |  |  | static int64_t * | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | _channels_list_all(_channels *channels, int64_t *count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int64_t *cids = NULL; | 
					
						
							|  |  |  |     PyThread_acquire_lock(channels->mutex, WAIT_LOCK); | 
					
						
							|  |  |  |     int64_t numopen = channels->numopen; | 
					
						
							|  |  |  |     if (numopen >= PY_SSIZE_T_MAX) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_RuntimeError, "too many channels open"); | 
					
						
							|  |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int64_t *ids = PyMem_NEW(int64_t, (Py_ssize_t)(channels->numopen)); | 
					
						
							|  |  |  |     if (ids == NULL) { | 
					
						
							|  |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _channelref *ref = channels->head; | 
					
						
							|  |  |  |     for (int64_t i=0; ref != NULL; ref = ref->next, i++) { | 
					
						
							|  |  |  |         ids[i] = ref->id; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *count = channels->numopen; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     cids = ids; | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  |     PyThread_release_lock(channels->mutex); | 
					
						
							|  |  |  |     return cids; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  | /* support for closing non-empty channels */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct _channel_closing { | 
					
						
							|  |  |  |     struct _channelref *ref; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _channel_set_closing(struct _channelref *ref, PyThread_type_lock mutex) { | 
					
						
							|  |  |  |     struct _channel *chan = ref->chan; | 
					
						
							|  |  |  |     if (chan == NULL) { | 
					
						
							|  |  |  |         // already closed
 | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int res = -1; | 
					
						
							|  |  |  |     PyThread_acquire_lock(chan->mutex, WAIT_LOCK); | 
					
						
							|  |  |  |     if (chan->closing != NULL) { | 
					
						
							|  |  |  |         PyErr_SetString(ChannelClosedError, "channel closed"); | 
					
						
							|  |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     chan->closing = PyMem_NEW(struct _channel_closing, 1); | 
					
						
							|  |  |  |     if (chan->closing == NULL) { | 
					
						
							|  |  |  |         goto done; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     chan->closing->ref = ref; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res = 0; | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  |     PyThread_release_lock(chan->mutex); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _channel_clear_closing(struct _channel *chan) { | 
					
						
							|  |  |  |     PyThread_acquire_lock(chan->mutex, WAIT_LOCK); | 
					
						
							|  |  |  |     if (chan->closing != NULL) { | 
					
						
							|  |  |  |         PyMem_Free(chan->closing); | 
					
						
							|  |  |  |         chan->closing = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyThread_release_lock(chan->mutex); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _channel_finish_closing(struct _channel *chan) { | 
					
						
							|  |  |  |     struct _channel_closing *closing = chan->closing; | 
					
						
							|  |  |  |     if (closing == NULL) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _channelref *ref = closing->ref; | 
					
						
							|  |  |  |     _channel_clear_closing(chan); | 
					
						
							|  |  |  |     // Do the things that would have been done in _channels_close().
 | 
					
						
							|  |  |  |     ref->chan = NULL; | 
					
						
							|  |  |  |     _channel_free(chan); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | /* "high"-level channel-related functions */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int64_t | 
					
						
							|  |  |  | _channel_create(_channels *channels) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyChannelState *chan = _channel_new(); | 
					
						
							|  |  |  |     if (chan == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int64_t id = _channels_add(channels, chan); | 
					
						
							|  |  |  |     if (id < 0) { | 
					
						
							|  |  |  |         _channel_free(chan); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _channel_destroy(_channels *channels, int64_t id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyChannelState *chan = NULL; | 
					
						
							|  |  |  |     if (_channels_remove(channels, id, &chan) != 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (chan != NULL) { | 
					
						
							|  |  |  |         _channel_free(chan); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _channel_send(_channels *channels, int64_t id, PyObject *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyInterpreterState *interp = _get_current(); | 
					
						
							|  |  |  |     if (interp == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Look up the channel.
 | 
					
						
							|  |  |  |     PyThread_type_lock mutex = NULL; | 
					
						
							|  |  |  |     _PyChannelState *chan = _channels_lookup(channels, id, &mutex); | 
					
						
							|  |  |  |     if (chan == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Past this point we are responsible for releasing the mutex.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     if (chan->closing != NULL) { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |         PyErr_Format(ChannelClosedError, "channel %" PRId64 " closed", id); | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |         PyThread_release_lock(mutex); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     // Convert the object to cross-interpreter data.
 | 
					
						
							|  |  |  |     _PyCrossInterpreterData *data = PyMem_NEW(_PyCrossInterpreterData, 1); | 
					
						
							|  |  |  |     if (data == NULL) { | 
					
						
							|  |  |  |         PyThread_release_lock(mutex); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (_PyObject_GetCrossInterpreterData(obj, data) != 0) { | 
					
						
							|  |  |  |         PyThread_release_lock(mutex); | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |         PyMem_Free(data); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Add the data to the channel.
 | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     int res = _channel_add(chan, PyInterpreterState_GetID(interp), data); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyThread_release_lock(mutex); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							|  |  |  |         _PyCrossInterpreterData_Release(data); | 
					
						
							|  |  |  |         PyMem_Free(data); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | _channel_recv(_channels *channels, int64_t id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyInterpreterState *interp = _get_current(); | 
					
						
							|  |  |  |     if (interp == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Look up the channel.
 | 
					
						
							|  |  |  |     PyThread_type_lock mutex = NULL; | 
					
						
							|  |  |  |     _PyChannelState *chan = _channels_lookup(channels, id, &mutex); | 
					
						
							|  |  |  |     if (chan == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Past this point we are responsible for releasing the mutex.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Pop off the next item from the channel.
 | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     _PyCrossInterpreterData *data = _channel_next(chan, PyInterpreterState_GetID(interp)); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyThread_release_lock(mutex); | 
					
						
							|  |  |  |     if (data == NULL) { | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |         if (!PyErr_Occurred()) { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |             PyErr_Format(ChannelEmptyError, "channel %" PRId64 " is empty", id); | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Convert the data back to an object.
 | 
					
						
							|  |  |  |     PyObject *obj = _PyCrossInterpreterData_NewObject(data); | 
					
						
							|  |  |  |     if (obj == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _PyCrossInterpreterData_Release(data); | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     PyMem_Free(data); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return obj; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _channel_drop(_channels *channels, int64_t id, int send, int recv) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyInterpreterState *interp = _get_current(); | 
					
						
							|  |  |  |     if (interp == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Look up the channel.
 | 
					
						
							|  |  |  |     PyThread_type_lock mutex = NULL; | 
					
						
							|  |  |  |     _PyChannelState *chan = _channels_lookup(channels, id, &mutex); | 
					
						
							|  |  |  |     if (chan == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Past this point we are responsible for releasing the mutex.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Close one or both of the two ends.
 | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     int res = _channel_close_interpreter(chan, PyInterpreterState_GetID(interp), send-recv); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyThread_release_lock(mutex); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  | _channel_close(_channels *channels, int64_t id, int end, int force) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     return _channels_close(channels, id, NULL, end, force); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ChannelID class */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyTypeObject ChannelIDtype; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct channelid { | 
					
						
							|  |  |  |     PyObject_HEAD | 
					
						
							|  |  |  |     int64_t id; | 
					
						
							|  |  |  |     int end; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     int resolve; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     _channels *channels; | 
					
						
							|  |  |  | } channelid; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static channelid * | 
					
						
							|  |  |  | newchannelid(PyTypeObject *cls, int64_t cid, int end, _channels *channels, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |              int force, int resolve) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     channelid *self = PyObject_New(channelid, cls); | 
					
						
							|  |  |  |     if (self == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     self->id = cid; | 
					
						
							|  |  |  |     self->end = end; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     self->resolve = resolve; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     self->channels = channels; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (_channels_add_id_object(channels, cid) != 0) { | 
					
						
							|  |  |  |         if (force && PyErr_ExceptionMatches(ChannelNotFoundError)) { | 
					
						
							|  |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             Py_DECREF((PyObject *)self); | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return self; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _channels * _global_channels(void); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | channelid_new(PyTypeObject *cls, PyObject *args, PyObject *kwds) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     static char *kwlist[] = {"id", "send", "recv", "force", "_resolve", NULL}; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyObject *id; | 
					
						
							|  |  |  |     int send = -1; | 
					
						
							|  |  |  |     int recv = -1; | 
					
						
							|  |  |  |     int force = 0; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     int resolve = 0; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (!PyArg_ParseTupleAndKeywords(args, kwds, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |                                      "O|$pppp:ChannelID.__new__", kwlist, | 
					
						
							|  |  |  |                                      &id, &send, &recv, &force, &resolve)) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Coerce and check the ID.
 | 
					
						
							|  |  |  |     int64_t cid; | 
					
						
							|  |  |  |     if (PyObject_TypeCheck(id, &ChannelIDtype)) { | 
					
						
							|  |  |  |         cid = ((channelid *)id)->id; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |         cid = _Py_CoerceID(id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         if (cid < 0) { | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle "send" and "recv".
 | 
					
						
							|  |  |  |     if (send == 0 && recv == 0) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_ValueError, | 
					
						
							|  |  |  |                         "'send' and 'recv' cannot both be False"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     int end = 0; | 
					
						
							|  |  |  |     if (send == 1) { | 
					
						
							|  |  |  |         if (recv == 0 || recv == -1) { | 
					
						
							|  |  |  |             end = CHANNEL_SEND; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (recv == 1) { | 
					
						
							|  |  |  |         end = CHANNEL_RECV; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     return (PyObject *)newchannelid(cls, cid, end, _global_channels(), | 
					
						
							|  |  |  |                                     force, resolve); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | channelid_dealloc(PyObject *v) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int64_t cid = ((channelid *)v)->id; | 
					
						
							|  |  |  |     _channels *channels = ((channelid *)v)->channels; | 
					
						
							|  |  |  |     Py_TYPE(v)->tp_free(v); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _channels_drop_id_object(channels, cid); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | channelid_repr(PyObject *self) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyTypeObject *type = Py_TYPE(self); | 
					
						
							|  |  |  |     const char *name = _PyType_Name(type); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     channelid *cid = (channelid *)self; | 
					
						
							|  |  |  |     const char *fmt; | 
					
						
							|  |  |  |     if (cid->end == CHANNEL_SEND) { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |         fmt = "%s(%" PRId64 ", send=True)"; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     else if (cid->end == CHANNEL_RECV) { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |         fmt = "%s(%" PRId64 ", recv=True)"; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |         fmt = "%s(%" PRId64 ")"; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     return PyUnicode_FromFormat(fmt, name, cid->id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | static PyObject * | 
					
						
							|  |  |  | channelid_str(PyObject *self) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     channelid *cid = (channelid *)self; | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |     return PyUnicode_FromFormat("%" PRId64 "", cid->id); | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-06 23:28:35 -07:00
										 |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | channelid_int(PyObject *self) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     channelid *cid = (channelid *)self; | 
					
						
							|  |  |  |     return PyLong_FromLongLong(cid->id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyNumberMethods channelid_as_number = { | 
					
						
							|  |  |  |      0,                        /* nb_add */ | 
					
						
							|  |  |  |      0,                        /* nb_subtract */ | 
					
						
							|  |  |  |      0,                        /* nb_multiply */ | 
					
						
							|  |  |  |      0,                        /* nb_remainder */ | 
					
						
							|  |  |  |      0,                        /* nb_divmod */ | 
					
						
							|  |  |  |      0,                        /* nb_power */ | 
					
						
							|  |  |  |      0,                        /* nb_negative */ | 
					
						
							|  |  |  |      0,                        /* nb_positive */ | 
					
						
							|  |  |  |      0,                        /* nb_absolute */ | 
					
						
							|  |  |  |      0,                        /* nb_bool */ | 
					
						
							|  |  |  |      0,                        /* nb_invert */ | 
					
						
							|  |  |  |      0,                        /* nb_lshift */ | 
					
						
							|  |  |  |      0,                        /* nb_rshift */ | 
					
						
							|  |  |  |      0,                        /* nb_and */ | 
					
						
							|  |  |  |      0,                        /* nb_xor */ | 
					
						
							|  |  |  |      0,                        /* nb_or */ | 
					
						
							|  |  |  |      (unaryfunc)channelid_int, /* nb_int */ | 
					
						
							|  |  |  |      0,                        /* nb_reserved */ | 
					
						
							|  |  |  |      0,                        /* nb_float */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |      0,                        /* nb_inplace_add */ | 
					
						
							|  |  |  |      0,                        /* nb_inplace_subtract */ | 
					
						
							|  |  |  |      0,                        /* nb_inplace_multiply */ | 
					
						
							|  |  |  |      0,                        /* nb_inplace_remainder */ | 
					
						
							|  |  |  |      0,                        /* nb_inplace_power */ | 
					
						
							|  |  |  |      0,                        /* nb_inplace_lshift */ | 
					
						
							|  |  |  |      0,                        /* nb_inplace_rshift */ | 
					
						
							|  |  |  |      0,                        /* nb_inplace_and */ | 
					
						
							|  |  |  |      0,                        /* nb_inplace_xor */ | 
					
						
							|  |  |  |      0,                        /* nb_inplace_or */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |      0,                        /* nb_floor_divide */ | 
					
						
							|  |  |  |      0,                        /* nb_true_divide */ | 
					
						
							|  |  |  |      0,                        /* nb_inplace_floor_divide */ | 
					
						
							|  |  |  |      0,                        /* nb_inplace_true_divide */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |      (unaryfunc)channelid_int, /* nb_index */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static Py_hash_t | 
					
						
							|  |  |  | channelid_hash(PyObject *self) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     channelid *cid = (channelid *)self; | 
					
						
							|  |  |  |     PyObject *id = PyLong_FromLongLong(cid->id); | 
					
						
							|  |  |  |     if (id == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     Py_hash_t hash = PyObject_Hash(id); | 
					
						
							|  |  |  |     Py_DECREF(id); | 
					
						
							|  |  |  |     return hash; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | channelid_richcompare(PyObject *self, PyObject *other, int op) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (op != Py_EQ && op != Py_NE) { | 
					
						
							|  |  |  |         Py_RETURN_NOTIMPLEMENTED; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!PyObject_TypeCheck(self, &ChannelIDtype)) { | 
					
						
							|  |  |  |         Py_RETURN_NOTIMPLEMENTED; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     channelid *cid = (channelid *)self; | 
					
						
							|  |  |  |     int equal; | 
					
						
							|  |  |  |     if (PyObject_TypeCheck(other, &ChannelIDtype)) { | 
					
						
							|  |  |  |         channelid *othercid = (channelid *)other; | 
					
						
							|  |  |  |         if (cid->end != othercid->end) { | 
					
						
							|  |  |  |             equal = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             equal = (cid->id == othercid->id); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         other = PyNumber_Long(other); | 
					
						
							|  |  |  |         if (other == NULL) { | 
					
						
							|  |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |             Py_RETURN_NOTIMPLEMENTED; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         int64_t othercid = PyLong_AsLongLong(other); | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         Py_DECREF(other); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         if (othercid == -1 && PyErr_Occurred() != NULL) { | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         if (othercid < 0) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |             equal = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             equal = (cid->id == othercid); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((op == Py_EQ && equal) || (op == Py_NE && !equal)) { | 
					
						
							|  |  |  |         Py_RETURN_TRUE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_RETURN_FALSE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  | static PyObject * | 
					
						
							|  |  |  | _channel_from_cid(PyObject *cid, int end) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *highlevel = PyImport_ImportModule("interpreters"); | 
					
						
							|  |  |  |     if (highlevel == NULL) { | 
					
						
							|  |  |  |         PyErr_Clear(); | 
					
						
							|  |  |  |         highlevel = PyImport_ImportModule("test.support.interpreters"); | 
					
						
							|  |  |  |         if (highlevel == NULL) { | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const char *clsname = (end == CHANNEL_RECV) ? "RecvChannel" : | 
					
						
							|  |  |  |                                                   "SendChannel"; | 
					
						
							|  |  |  |     PyObject *cls = PyObject_GetAttrString(highlevel, clsname); | 
					
						
							|  |  |  |     Py_DECREF(highlevel); | 
					
						
							|  |  |  |     if (cls == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *chan = PyObject_CallFunctionObjArgs(cls, cid, NULL); | 
					
						
							|  |  |  |     Py_DECREF(cls); | 
					
						
							|  |  |  |     if (chan == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return chan; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | struct _channelid_xid { | 
					
						
							|  |  |  |     int64_t id; | 
					
						
							|  |  |  |     int end; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     int resolve; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | _channelid_from_xid(_PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct _channelid_xid *xid = (struct _channelid_xid *)data->data; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     // Note that we do not preserve the "resolve" flag.
 | 
					
						
							|  |  |  |     PyObject *cid = (PyObject *)newchannelid(&ChannelIDtype, xid->id, xid->end, | 
					
						
							|  |  |  |                                              _global_channels(), 0, 0); | 
					
						
							|  |  |  |     if (xid->end == 0) { | 
					
						
							|  |  |  |         return cid; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!xid->resolve) { | 
					
						
							|  |  |  |         return cid; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Try returning a high-level channel end but fall back to the ID. */ | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |     PyObject *chan = _channel_from_cid(cid, xid->end); | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     if (chan == NULL) { | 
					
						
							| 
									
										
										
										
											2018-06-13 08:02:39 -06:00
										 |  |  |         PyErr_Clear(); | 
					
						
							|  |  |  |         return cid; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     } | 
					
						
							|  |  |  |     Py_DECREF(cid); | 
					
						
							|  |  |  |     return chan; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _channelid_shared(PyObject *obj, _PyCrossInterpreterData *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct _channelid_xid *xid = PyMem_NEW(struct _channelid_xid, 1); | 
					
						
							|  |  |  |     if (xid == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     xid->id = ((channelid *)obj)->id; | 
					
						
							|  |  |  |     xid->end = ((channelid *)obj)->end; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     xid->resolve = ((channelid *)obj)->resolve; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     data->data = xid; | 
					
						
							| 
									
										
										
										
											2018-06-01 18:45:20 -06:00
										 |  |  |     Py_INCREF(obj); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     data->obj = obj; | 
					
						
							|  |  |  |     data->new_object = _channelid_from_xid; | 
					
						
							|  |  |  |     data->free = PyMem_Free; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | channelid_end(PyObject *self, void *end) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int force = 1; | 
					
						
							|  |  |  |     channelid *cid = (channelid *)self; | 
					
						
							|  |  |  |     if (end != NULL) { | 
					
						
							|  |  |  |         return (PyObject *)newchannelid(Py_TYPE(self), cid->id, *(int *)end, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |                                         cid->channels, force, cid->resolve); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (cid->end == CHANNEL_SEND) { | 
					
						
							|  |  |  |         return PyUnicode_InternFromString("send"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (cid->end == CHANNEL_RECV) { | 
					
						
							|  |  |  |         return PyUnicode_InternFromString("recv"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return PyUnicode_InternFromString("both"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int _channelid_end_send = CHANNEL_SEND; | 
					
						
							|  |  |  | static int _channelid_end_recv = CHANNEL_RECV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyGetSetDef channelid_getsets[] = { | 
					
						
							|  |  |  |     {"end", (getter)channelid_end, NULL, | 
					
						
							|  |  |  |      PyDoc_STR("'send', 'recv', or 'both'")}, | 
					
						
							|  |  |  |     {"send", (getter)channelid_end, NULL, | 
					
						
							|  |  |  |      PyDoc_STR("the 'send' end of the channel"), &_channelid_end_send}, | 
					
						
							|  |  |  |     {"recv", (getter)channelid_end, NULL, | 
					
						
							|  |  |  |      PyDoc_STR("the 'recv' end of the channel"), &_channelid_end_recv}, | 
					
						
							|  |  |  |     {NULL} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(channelid_doc, | 
					
						
							|  |  |  | "A channel ID identifies a channel and may be used as an int."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyTypeObject ChannelIDtype = { | 
					
						
							|  |  |  |     PyVarObject_HEAD_INIT(&PyType_Type, 0) | 
					
						
							|  |  |  |     "_xxsubinterpreters.ChannelID", /* tp_name */ | 
					
						
							| 
									
										
										
										
											2018-09-10 18:46:08 +02:00
										 |  |  |     sizeof(channelid),              /* tp_basicsize */ | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     0,                              /* tp_itemsize */ | 
					
						
							|  |  |  |     (destructor)channelid_dealloc,  /* tp_dealloc */ | 
					
						
							|  |  |  |     0,                              /* tp_print */ | 
					
						
							|  |  |  |     0,                              /* tp_getattr */ | 
					
						
							|  |  |  |     0,                              /* tp_setattr */ | 
					
						
							|  |  |  |     0,                              /* tp_as_async */ | 
					
						
							|  |  |  |     (reprfunc)channelid_repr,       /* tp_repr */ | 
					
						
							|  |  |  |     &channelid_as_number,           /* tp_as_number */ | 
					
						
							|  |  |  |     0,                              /* tp_as_sequence */ | 
					
						
							|  |  |  |     0,                              /* tp_as_mapping */ | 
					
						
							|  |  |  |     channelid_hash,                 /* tp_hash */ | 
					
						
							|  |  |  |     0,                              /* tp_call */ | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     (reprfunc)channelid_str,        /* tp_str */ | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     0,                              /* tp_getattro */ | 
					
						
							|  |  |  |     0,                              /* tp_setattro */ | 
					
						
							|  |  |  |     0,                              /* tp_as_buffer */ | 
					
						
							|  |  |  |     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | | 
					
						
							|  |  |  |         Py_TPFLAGS_LONG_SUBCLASS,   /* tp_flags */ | 
					
						
							|  |  |  |     channelid_doc,                  /* tp_doc */ | 
					
						
							|  |  |  |     0,                              /* tp_traverse */ | 
					
						
							|  |  |  |     0,                              /* tp_clear */ | 
					
						
							|  |  |  |     channelid_richcompare,          /* tp_richcompare */ | 
					
						
							|  |  |  |     0,                              /* tp_weaklistoffset */ | 
					
						
							|  |  |  |     0,                              /* tp_iter */ | 
					
						
							|  |  |  |     0,                              /* tp_iternext */ | 
					
						
							|  |  |  |     0,                              /* tp_methods */ | 
					
						
							|  |  |  |     0,                              /* tp_members */ | 
					
						
							|  |  |  |     channelid_getsets,              /* tp_getset */ | 
					
						
							|  |  |  |     0,                              /* tp_base */ | 
					
						
							|  |  |  |     0,                              /* tp_dict */ | 
					
						
							|  |  |  |     0,                              /* tp_descr_get */ | 
					
						
							|  |  |  |     0,                              /* tp_descr_set */ | 
					
						
							|  |  |  |     0,                              /* tp_dictoffset */ | 
					
						
							|  |  |  |     0,                              /* tp_init */ | 
					
						
							|  |  |  |     0,                              /* tp_alloc */ | 
					
						
							|  |  |  |     // Note that we do not set tp_new to channelid_new.  Instead we
 | 
					
						
							|  |  |  |     // set it to NULL, meaning it cannot be instantiated from Python
 | 
					
						
							|  |  |  |     // code.  We do this because there is a strong relationship between
 | 
					
						
							|  |  |  |     // channel IDs and the channel lifecycle, so this limitation avoids
 | 
					
						
							|  |  |  |     // related complications.
 | 
					
						
							|  |  |  |     NULL,                           /* tp_new */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* interpreter-specific code ************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * RunFailedError = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | interp_exceptions_init(PyObject *ns) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // XXX Move the exceptions into per-module memory?
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (RunFailedError == NULL) { | 
					
						
							|  |  |  |         // An uncaught exception came out of interp_run_string().
 | 
					
						
							|  |  |  |         RunFailedError = PyErr_NewException("_xxsubinterpreters.RunFailedError", | 
					
						
							|  |  |  |                                             PyExc_RuntimeError, NULL); | 
					
						
							|  |  |  |         if (RunFailedError == NULL) { | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (PyDict_SetItemString(ns, "RunFailedError", RunFailedError) != 0) { | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _is_running(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); | 
					
						
							|  |  |  |     if (PyThreadState_Next(tstate) != NULL) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_RuntimeError, | 
					
						
							|  |  |  |                         "interpreter has more than one thread"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyFrameObject *frame = tstate->frame; | 
					
						
							|  |  |  |     if (frame == NULL) { | 
					
						
							|  |  |  |         if (PyErr_Occurred() != NULL) { | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return (int)(frame->f_executing); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _ensure_not_running(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int is_running = _is_running(interp); | 
					
						
							|  |  |  |     if (is_running < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (is_running) { | 
					
						
							|  |  |  |         PyErr_Format(PyExc_RuntimeError, "interpreter already running"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _run_script(PyInterpreterState *interp, const char *codestr, | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |             _sharedns *shared, _sharedexception **exc) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     PyObject *exctype = NULL; | 
					
						
							|  |  |  |     PyObject *excval = NULL; | 
					
						
							|  |  |  |     PyObject *tb = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     PyObject *main_mod = _PyInterpreterState_GetMainModule(interp); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (main_mod == NULL) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *ns = PyModule_GetDict(main_mod);  // borrowed
 | 
					
						
							|  |  |  |     Py_DECREF(main_mod); | 
					
						
							|  |  |  |     if (ns == NULL) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_INCREF(ns); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Apply the cross-interpreter data.
 | 
					
						
							|  |  |  |     if (shared != NULL) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         if (_sharedns_apply(shared, ns) != 0) { | 
					
						
							|  |  |  |             Py_DECREF(ns); | 
					
						
							|  |  |  |             goto error; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Run the string (see PyRun_SimpleStringFlags).
 | 
					
						
							|  |  |  |     PyObject *result = PyRun_StringFlags(codestr, Py_file_input, ns, ns, NULL); | 
					
						
							|  |  |  |     Py_DECREF(ns); | 
					
						
							|  |  |  |     if (result == NULL) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         Py_DECREF(result);  // We throw away the result.
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     *exc = NULL; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     PyErr_Fetch(&exctype, &excval, &tb); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _sharedexception *sharedexc = _sharedexception_bind(exctype, excval, tb); | 
					
						
							|  |  |  |     Py_XDECREF(exctype); | 
					
						
							|  |  |  |     Py_XDECREF(excval); | 
					
						
							|  |  |  |     Py_XDECREF(tb); | 
					
						
							|  |  |  |     if (sharedexc == NULL) { | 
					
						
							|  |  |  |         fprintf(stderr, "RunFailedError: script raised an uncaught exception"); | 
					
						
							|  |  |  |         PyErr_Clear(); | 
					
						
							|  |  |  |         sharedexc = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         assert(!PyErr_Occurred()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *exc = sharedexc; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _run_script_in_interpreter(PyInterpreterState *interp, const char *codestr, | 
					
						
							|  |  |  |                            PyObject *shareables) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (_ensure_not_running(interp) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     _sharedns *shared = _get_shared_ns(shareables); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (shared == NULL && PyErr_Occurred()) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Switch to interpreter.
 | 
					
						
							| 
									
										
										
										
											2018-02-20 16:30:17 -07:00
										 |  |  |     PyThreadState *save_tstate = NULL; | 
					
						
							| 
									
										
										
										
											2018-08-03 15:33:52 +02:00
										 |  |  |     if (interp != _PyInterpreterState_Get()) { | 
					
						
							| 
									
										
										
										
											2018-02-20 16:30:17 -07:00
										 |  |  |         // XXX Using the "head" thread isn't strictly correct.
 | 
					
						
							|  |  |  |         PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); | 
					
						
							|  |  |  |         // XXX Possible GILState issues?
 | 
					
						
							|  |  |  |         save_tstate = PyThreadState_Swap(tstate); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Run the script.
 | 
					
						
							|  |  |  |     _sharedexception *exc = NULL; | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     int result = _run_script(interp, codestr, shared, &exc); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Switch back.
 | 
					
						
							|  |  |  |     if (save_tstate != NULL) { | 
					
						
							|  |  |  |         PyThreadState_Swap(save_tstate); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Propagate any exception out to the caller.
 | 
					
						
							|  |  |  |     if (exc != NULL) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         _sharedexception_apply(exc, RunFailedError); | 
					
						
							|  |  |  |         _sharedexception_free(exc); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     else if (result != 0) { | 
					
						
							|  |  |  |         // We were unable to allocate a shared exception.
 | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (shared != NULL) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         _sharedns_free(shared); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* module level code ********************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* globals is the process-global state for the module.  It holds all
 | 
					
						
							|  |  |  |    the data that we need to share between interpreters, so it cannot | 
					
						
							|  |  |  |    hold PyObject values. */ | 
					
						
							|  |  |  | static struct globals { | 
					
						
							|  |  |  |     _channels channels; | 
					
						
							|  |  |  | } _globals = {{0}}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _init_globals(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (_channels_init(&_globals.channels) != 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static _channels * | 
					
						
							|  |  |  | _global_channels(void) { | 
					
						
							|  |  |  |     return &_globals.channels; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | interp_create(PyObject *self, PyObject *args) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!PyArg_UnpackTuple(args, "create", 0, 0)) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Create and initialize the new interpreter.
 | 
					
						
							| 
									
										
										
										
											2018-02-20 16:30:17 -07:00
										 |  |  |     PyThreadState *save_tstate = PyThreadState_Swap(NULL); | 
					
						
							|  |  |  |     // XXX Possible GILState issues?
 | 
					
						
							|  |  |  |     PyThreadState *tstate = Py_NewInterpreter(); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyThreadState_Swap(save_tstate); | 
					
						
							|  |  |  |     if (tstate == NULL) { | 
					
						
							|  |  |  |         /* Since no new thread state was created, there is no exception to
 | 
					
						
							|  |  |  |            propagate; raise a fresh one after swapping in the old thread | 
					
						
							|  |  |  |            state. */ | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_RuntimeError, "interpreter creation failed"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     PyObject *idobj = _PyInterpreterState_GetIDObject(tstate->interp); | 
					
						
							|  |  |  |     if (idobj == NULL) { | 
					
						
							|  |  |  |         // XXX Possible GILState issues?
 | 
					
						
							|  |  |  |         save_tstate = PyThreadState_Swap(tstate); | 
					
						
							|  |  |  |         Py_EndInterpreter(tstate); | 
					
						
							|  |  |  |         PyThreadState_Swap(save_tstate); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _PyInterpreterState_RequireIDRef(tstate->interp, 1); | 
					
						
							|  |  |  |     return idobj; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(create_doc, | 
					
						
							|  |  |  | "create() -> ID\n\
 | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  | Create a new interpreter and return a unique generated ID."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | interp_destroy(PyObject *self, PyObject *args, PyObject *kwds) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     static char *kwlist[] = {"id", NULL}; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyObject *id; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     // XXX Use "L" for id?
 | 
					
						
							|  |  |  |     if (!PyArg_ParseTupleAndKeywords(args, kwds, | 
					
						
							|  |  |  |                                      "O:destroy", kwlist, &id)) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!PyLong_Check(id)) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_TypeError, "ID must be an int"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Look up the interpreter.
 | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     PyInterpreterState *interp = _PyInterpreterID_LookUp(id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (interp == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Ensure we don't try to destroy the current interpreter.
 | 
					
						
							|  |  |  |     PyInterpreterState *current = _get_current(); | 
					
						
							|  |  |  |     if (current == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (interp == current) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_RuntimeError, | 
					
						
							|  |  |  |                         "cannot destroy the current interpreter"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Ensure the interpreter isn't running.
 | 
					
						
							|  |  |  |     /* XXX We *could* support destroying a running interpreter but
 | 
					
						
							|  |  |  |        aren't going to worry about it for now. */ | 
					
						
							|  |  |  |     if (_ensure_not_running(interp) < 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Destroy the interpreter.
 | 
					
						
							|  |  |  |     //PyInterpreterState_Delete(interp);
 | 
					
						
							| 
									
										
										
										
											2018-02-20 16:30:17 -07:00
										 |  |  |     PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); | 
					
						
							|  |  |  |     // XXX Possible GILState issues?
 | 
					
						
							|  |  |  |     PyThreadState *save_tstate = PyThreadState_Swap(tstate); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     Py_EndInterpreter(tstate); | 
					
						
							|  |  |  |     PyThreadState_Swap(save_tstate); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Py_RETURN_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(destroy_doc, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | "destroy(id)\n\
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | \n\ | 
					
						
							|  |  |  | Destroy the identified interpreter.\n\ | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  | Attempting to destroy the current interpreter results in a RuntimeError.\n\ | 
					
						
							|  |  |  | So does an unrecognized ID."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-04-30 00:29:33 +05:30
										 |  |  | interp_list_all(PyObject *self, PyObject *Py_UNUSED(ignored)) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     PyObject *ids, *id; | 
					
						
							|  |  |  |     PyInterpreterState *interp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ids = PyList_New(0); | 
					
						
							|  |  |  |     if (ids == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     interp = PyInterpreterState_Head(); | 
					
						
							|  |  |  |     while (interp != NULL) { | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |         id = _PyInterpreterState_GetIDObject(interp); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         if (id == NULL) { | 
					
						
							|  |  |  |             Py_DECREF(ids); | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // insert at front of list
 | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         int res = PyList_Insert(ids, 0, id); | 
					
						
							|  |  |  |         Py_DECREF(id); | 
					
						
							|  |  |  |         if (res < 0) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |             Py_DECREF(ids); | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         interp = PyInterpreterState_Next(interp); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ids; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(list_all_doc, | 
					
						
							|  |  |  | "list_all() -> [ID]\n\
 | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  | Return a list containing the ID of every existing interpreter."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-04-30 00:29:33 +05:30
										 |  |  | interp_get_current(PyObject *self, PyObject *Py_UNUSED(ignored)) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     PyInterpreterState *interp =_get_current(); | 
					
						
							|  |  |  |     if (interp == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     return _PyInterpreterState_GetIDObject(interp); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(get_current_doc, | 
					
						
							|  |  |  | "get_current() -> ID\n\
 | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  | Return the ID of current interpreter."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-04-30 00:29:33 +05:30
										 |  |  | interp_get_main(PyObject *self, PyObject *Py_UNUSED(ignored)) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     // Currently, 0 is always the main interpreter.
 | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     PY_INT64_T id = 0; | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     return _PyInterpreterID_New(id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(get_main_doc, | 
					
						
							|  |  |  | "get_main() -> ID\n\
 | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  | Return the ID of main interpreter."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | interp_run_string(PyObject *self, PyObject *args, PyObject *kwds) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     static char *kwlist[] = {"id", "script", "shared", NULL}; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyObject *id, *code; | 
					
						
							|  |  |  |     PyObject *shared = NULL; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     if (!PyArg_ParseTupleAndKeywords(args, kwds, | 
					
						
							|  |  |  |                                      "OU|O:run_string", kwlist, | 
					
						
							|  |  |  |                                      &id, &code, &shared)) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!PyLong_Check(id)) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_TypeError, "first arg (ID) must be an int"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Look up the interpreter.
 | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     PyInterpreterState *interp = _PyInterpreterID_LookUp(id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (interp == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Extract code.
 | 
					
						
							|  |  |  |     Py_ssize_t size; | 
					
						
							|  |  |  |     const char *codestr = PyUnicode_AsUTF8AndSize(code, &size); | 
					
						
							|  |  |  |     if (codestr == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (strlen(codestr) != (size_t)size) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_ValueError, | 
					
						
							|  |  |  |                         "source code string cannot contain null bytes"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Run the code in the interpreter.
 | 
					
						
							|  |  |  |     if (_run_script_in_interpreter(interp, codestr, shared) != 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_RETURN_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(run_string_doc, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | "run_string(id, script, shared)\n\
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | \n\ | 
					
						
							|  |  |  | Execute the provided string in the identified interpreter.\n\ | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  | See PyRun_SimpleStrings."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | object_is_shareable(PyObject *self, PyObject *args, PyObject *kwds) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     static char *kwlist[] = {"obj", NULL}; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyObject *obj; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     if (!PyArg_ParseTupleAndKeywords(args, kwds, | 
					
						
							|  |  |  |                                      "O:is_shareable", kwlist, &obj)) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (_PyObject_CheckCrossInterpreterData(obj) == 0) { | 
					
						
							|  |  |  |         Py_RETURN_TRUE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyErr_Clear(); | 
					
						
							|  |  |  |     Py_RETURN_FALSE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(is_shareable_doc, | 
					
						
							|  |  |  | "is_shareable(obj) -> bool\n\
 | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  | Return True if the object's data may be shared between interpreters and\n\ | 
					
						
							|  |  |  | False otherwise."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | interp_is_running(PyObject *self, PyObject *args, PyObject *kwds) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     static char *kwlist[] = {"id", NULL}; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyObject *id; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     if (!PyArg_ParseTupleAndKeywords(args, kwds, | 
					
						
							|  |  |  |                                      "O:is_running", kwlist, &id)) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!PyLong_Check(id)) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_TypeError, "ID must be an int"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     PyInterpreterState *interp = _PyInterpreterID_LookUp(id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (interp == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int is_running = _is_running(interp); | 
					
						
							|  |  |  |     if (is_running < 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (is_running) { | 
					
						
							|  |  |  |         Py_RETURN_TRUE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_RETURN_FALSE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(is_running_doc, | 
					
						
							|  |  |  | "is_running(id) -> bool\n\
 | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  | Return whether or not the identified interpreter is running."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-04-30 00:29:33 +05:30
										 |  |  | channel_create(PyObject *self, PyObject *Py_UNUSED(ignored)) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     int64_t cid = _channel_create(&_globals.channels); | 
					
						
							|  |  |  |     if (cid < 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, cid, 0, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |                                             &_globals.channels, 0, 0); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (id == NULL) { | 
					
						
							|  |  |  |         if (_channel_destroy(&_globals.channels, cid) != 0) { | 
					
						
							|  |  |  |             // XXX issue a warning?
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(((channelid *)id)->channels != NULL); | 
					
						
							|  |  |  |     return id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(channel_create_doc, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | "channel_create() -> cid\n\
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | \n\ | 
					
						
							|  |  |  | Create a new cross-interpreter channel and return a unique generated ID."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | channel_destroy(PyObject *self, PyObject *args, PyObject *kwds) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     static char *kwlist[] = {"cid", NULL}; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyObject *id; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     if (!PyArg_ParseTupleAndKeywords(args, kwds, | 
					
						
							|  |  |  |                                      "O:channel_destroy", kwlist, &id)) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     int64_t cid = _Py_CoerceID(id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (cid < 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (_channel_destroy(&_globals.channels, cid) != 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_RETURN_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(channel_destroy_doc, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | "channel_destroy(cid)\n\
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | \n\ | 
					
						
							|  |  |  | Close and finalize the channel.  Afterward attempts to use the channel\n\ | 
					
						
							|  |  |  | will behave as though it never existed."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-04-30 00:29:33 +05:30
										 |  |  | channel_list_all(PyObject *self, PyObject *Py_UNUSED(ignored)) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     int64_t count = 0; | 
					
						
							|  |  |  |     int64_t *cids = _channels_list_all(&_globals.channels, &count); | 
					
						
							|  |  |  |     if (cids == NULL) { | 
					
						
							|  |  |  |         if (count == 0) { | 
					
						
							|  |  |  |             return PyList_New(0); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *ids = PyList_New((Py_ssize_t)count); | 
					
						
							|  |  |  |     if (ids == NULL) { | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |         goto finally; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  |     int64_t *cur = cids; | 
					
						
							|  |  |  |     for (int64_t i=0; i < count; cur++, i++) { | 
					
						
							|  |  |  |         PyObject *id = (PyObject *)newchannelid(&ChannelIDtype, *cur, 0, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |                                                 &_globals.channels, 0, 0); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         if (id == NULL) { | 
					
						
							|  |  |  |             Py_DECREF(ids); | 
					
						
							|  |  |  |             ids = NULL; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         PyList_SET_ITEM(ids, i, id); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-02-02 21:49:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | finally: | 
					
						
							|  |  |  |     PyMem_Free(cids); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     return ids; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(channel_list_all_doc, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | "channel_list_all() -> [cid]\n\
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | \n\ | 
					
						
							|  |  |  | Return the list of all IDs for active channels."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | channel_send(PyObject *self, PyObject *args, PyObject *kwds) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     static char *kwlist[] = {"cid", "obj", NULL}; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyObject *id; | 
					
						
							|  |  |  |     PyObject *obj; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     if (!PyArg_ParseTupleAndKeywords(args, kwds, | 
					
						
							|  |  |  |                                      "OO:channel_send", kwlist, &id, &obj)) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     int64_t cid = _Py_CoerceID(id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (cid < 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (_channel_send(&_globals.channels, cid, obj) != 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_RETURN_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(channel_send_doc, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | "channel_send(cid, obj)\n\
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | \n\ | 
					
						
							|  |  |  | Add the object's data to the channel's queue."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | channel_recv(PyObject *self, PyObject *args, PyObject *kwds) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     static char *kwlist[] = {"cid", NULL}; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyObject *id; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     if (!PyArg_ParseTupleAndKeywords(args, kwds, | 
					
						
							|  |  |  |                                      "O:channel_recv", kwlist, &id)) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     int64_t cid = _Py_CoerceID(id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (cid < 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return _channel_recv(&_globals.channels, cid); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(channel_recv_doc, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | "channel_recv(cid) -> obj\n\
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | \n\ | 
					
						
							|  |  |  | Return a new object from the data at the from of the channel's queue."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | channel_close(PyObject *self, PyObject *args, PyObject *kwds) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     static char *kwlist[] = {"cid", "send", "recv", "force", NULL}; | 
					
						
							|  |  |  |     PyObject *id; | 
					
						
							|  |  |  |     int send = 0; | 
					
						
							|  |  |  |     int recv = 0; | 
					
						
							|  |  |  |     int force = 0; | 
					
						
							|  |  |  |     if (!PyArg_ParseTupleAndKeywords(args, kwds, | 
					
						
							|  |  |  |                                      "O|$ppp:channel_close", kwlist, | 
					
						
							|  |  |  |                                      &id, &send, &recv, &force)) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     int64_t cid = _Py_CoerceID(id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (cid < 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-17 10:27:09 -04:00
										 |  |  |     if (_channel_close(&_globals.channels, cid, send-recv, force) != 0) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_RETURN_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(channel_close_doc, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | "channel_close(cid, *, send=None, recv=None, force=False)\n\
 | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  | Close the channel for all interpreters.\n\ | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | \n\ | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | If the channel is empty then the keyword args are ignored and both\n\ | 
					
						
							|  |  |  | ends are immediately closed.  Otherwise, if 'force' is True then\n\ | 
					
						
							|  |  |  | all queued items are released and both ends are immediately\n\ | 
					
						
							|  |  |  | closed.\n\ | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  | If the channel is not empty *and* 'force' is False then following\n\ | 
					
						
							|  |  |  | happens:\n\ | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  |  * recv is True (regardless of send):\n\ | 
					
						
							|  |  |  |    - raise ChannelNotEmptyError\n\ | 
					
						
							|  |  |  |  * recv is None and send is None:\n\ | 
					
						
							|  |  |  |    - raise ChannelNotEmptyError\n\ | 
					
						
							|  |  |  |  * send is True and recv is not True:\n\ | 
					
						
							|  |  |  |    - fully close the 'send' end\n\ | 
					
						
							|  |  |  |    - close the 'recv' end to interpreters not already receiving\n\ | 
					
						
							|  |  |  |    - fully close it once empty\n\ | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  | Closing an already closed channel results in a ChannelClosedError.\n\ | 
					
						
							|  |  |  | \n\ | 
					
						
							|  |  |  | Once the channel's ID has no more ref counts in any interpreter\n\ | 
					
						
							|  |  |  | the channel will be destroyed."); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | channel_release(PyObject *self, PyObject *args, PyObject *kwds) | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     // Note that only the current interpreter is affected.
 | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     static char *kwlist[] = {"cid", "send", "recv", "force", NULL}; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     PyObject *id; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     int send = 0; | 
					
						
							|  |  |  |     int recv = 0; | 
					
						
							|  |  |  |     int force = 0; | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (!PyArg_ParseTupleAndKeywords(args, kwds, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |                                      "O|$ppp:channel_release", kwlist, | 
					
						
							|  |  |  |                                      &id, &send, &recv, &force)) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     int64_t cid = _Py_CoerceID(id); | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (cid < 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |     if (send == 0 && recv == 0) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         send = 1; | 
					
						
							|  |  |  |         recv = 1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // XXX Handle force is True.
 | 
					
						
							|  |  |  |     // XXX Fix implicit release.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |     if (_channel_drop(&_globals.channels, cid, send, recv) != 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_RETURN_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  | PyDoc_STRVAR(channel_release_doc, | 
					
						
							|  |  |  | "channel_release(cid, *, send=None, recv=None, force=True)\n\
 | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | \n\ | 
					
						
							|  |  |  | Close the channel for the current interpreter.  'send' and 'recv'\n\ | 
					
						
							|  |  |  | (bool) may be used to indicate the ends to close.  By default both\n\ | 
					
						
							|  |  |  | ends are closed.  Closing an already closed end is a noop."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | channel__channel_id(PyObject *self, PyObject *args, PyObject *kwds) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return channelid_new(&ChannelIDtype, args, kwds); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyMethodDef module_functions[] = { | 
					
						
							|  |  |  |     {"create",                    (PyCFunction)interp_create, | 
					
						
							|  |  |  |      METH_VARARGS, create_doc}, | 
					
						
							| 
									
										
										
										
											2018-11-27 13:27:31 +02:00
										 |  |  |     {"destroy",                   (PyCFunction)(void(*)(void))interp_destroy, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |      METH_VARARGS | METH_KEYWORDS, destroy_doc}, | 
					
						
							| 
									
										
										
										
											2018-04-30 00:29:33 +05:30
										 |  |  |     {"list_all",                  interp_list_all, | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |      METH_NOARGS, list_all_doc}, | 
					
						
							| 
									
										
										
										
											2018-04-30 00:29:33 +05:30
										 |  |  |     {"get_current",               interp_get_current, | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |      METH_NOARGS, get_current_doc}, | 
					
						
							| 
									
										
										
										
											2018-04-30 00:29:33 +05:30
										 |  |  |     {"get_main",                  interp_get_main, | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |      METH_NOARGS, get_main_doc}, | 
					
						
							| 
									
										
										
										
											2018-11-27 13:27:31 +02:00
										 |  |  |     {"is_running",                (PyCFunction)(void(*)(void))interp_is_running, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |      METH_VARARGS | METH_KEYWORDS, is_running_doc}, | 
					
						
							| 
									
										
										
										
											2018-11-27 13:27:31 +02:00
										 |  |  |     {"run_string",                (PyCFunction)(void(*)(void))interp_run_string, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |      METH_VARARGS | METH_KEYWORDS, run_string_doc}, | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-27 13:27:31 +02:00
										 |  |  |     {"is_shareable",              (PyCFunction)(void(*)(void))object_is_shareable, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |      METH_VARARGS | METH_KEYWORDS, is_shareable_doc}, | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-30 00:29:33 +05:30
										 |  |  |     {"channel_create",            channel_create, | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |      METH_NOARGS, channel_create_doc}, | 
					
						
							| 
									
										
										
										
											2018-11-27 13:27:31 +02:00
										 |  |  |     {"channel_destroy",           (PyCFunction)(void(*)(void))channel_destroy, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |      METH_VARARGS | METH_KEYWORDS, channel_destroy_doc}, | 
					
						
							| 
									
										
										
										
											2018-04-30 00:29:33 +05:30
										 |  |  |     {"channel_list_all",          channel_list_all, | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |      METH_NOARGS, channel_list_all_doc}, | 
					
						
							| 
									
										
										
										
											2018-11-27 13:27:31 +02:00
										 |  |  |     {"channel_send",              (PyCFunction)(void(*)(void))channel_send, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |      METH_VARARGS | METH_KEYWORDS, channel_send_doc}, | 
					
						
							| 
									
										
										
										
											2018-11-27 13:27:31 +02:00
										 |  |  |     {"channel_recv",              (PyCFunction)(void(*)(void))channel_recv, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |      METH_VARARGS | METH_KEYWORDS, channel_recv_doc}, | 
					
						
							| 
									
										
										
										
											2018-11-27 13:27:31 +02:00
										 |  |  |     {"channel_close",             (PyCFunction)(void(*)(void))channel_close, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |      METH_VARARGS | METH_KEYWORDS, channel_close_doc}, | 
					
						
							| 
									
										
										
										
											2018-11-27 13:27:31 +02:00
										 |  |  |     {"channel_release",           (PyCFunction)(void(*)(void))channel_release, | 
					
						
							| 
									
										
										
										
											2018-05-16 15:04:57 -04:00
										 |  |  |      METH_VARARGS | METH_KEYWORDS, channel_release_doc}, | 
					
						
							| 
									
										
										
										
											2018-11-27 13:27:31 +02:00
										 |  |  |     {"_channel_id",               (PyCFunction)(void(*)(void))channel__channel_id, | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |      METH_VARARGS | METH_KEYWORDS, NULL}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     {NULL,                        NULL}           /* sentinel */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* initialization function */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyDoc_STRVAR(module_doc, | 
					
						
							|  |  |  | "This module provides primitive operations to manage Python interpreters.\n\
 | 
					
						
							|  |  |  | The 'interpreters' module provides a more convenient interface."); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct PyModuleDef interpretersmodule = { | 
					
						
							|  |  |  |     PyModuleDef_HEAD_INIT, | 
					
						
							|  |  |  |     "_xxsubinterpreters",  /* m_name */ | 
					
						
							|  |  |  |     module_doc,            /* m_doc */ | 
					
						
							|  |  |  |     -1,                    /* m_size */ | 
					
						
							|  |  |  |     module_functions,      /* m_methods */ | 
					
						
							|  |  |  |     NULL,                  /* m_slots */ | 
					
						
							|  |  |  |     NULL,                  /* m_traverse */ | 
					
						
							|  |  |  |     NULL,                  /* m_clear */ | 
					
						
							|  |  |  |     NULL                   /* m_free */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyMODINIT_FUNC | 
					
						
							|  |  |  | PyInit__xxsubinterpreters(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (_init_globals() != 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Initialize types */ | 
					
						
							|  |  |  |     ChannelIDtype.tp_base = &PyLong_Type; | 
					
						
							|  |  |  |     if (PyType_Ready(&ChannelIDtype) != 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Create the module */ | 
					
						
							|  |  |  |     PyObject *module = PyModule_Create(&interpretersmodule); | 
					
						
							|  |  |  |     if (module == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Add exception types */ | 
					
						
							|  |  |  |     PyObject *ns = PyModule_GetDict(module);  // borrowed
 | 
					
						
							|  |  |  |     if (interp_exceptions_init(ns) != 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (channel_exceptions_init(ns) != 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Add other types */ | 
					
						
							|  |  |  |     Py_INCREF(&ChannelIDtype); | 
					
						
							|  |  |  |     if (PyDict_SetItemString(ns, "ChannelID", (PyObject *)&ChannelIDtype) != 0) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     Py_INCREF(&_PyInterpreterID_Type); | 
					
						
							|  |  |  |     if (PyDict_SetItemString(ns, "InterpreterID", (PyObject *)&_PyInterpreterID_Type) != 0) { | 
					
						
							| 
									
										
										
										
											2018-02-16 18:53:40 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 16:50:31 -07:00
										 |  |  |     if (_PyCrossInterpreterData_RegisterClass(&ChannelIDtype, _channelid_shared)) { | 
					
						
							| 
									
										
										
										
											2018-01-29 18:23:44 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return module; | 
					
						
							|  |  |  | } |