For the most part, these changes make is substantially easier to backport subinterpreter-related code to 3.12, especially the related modules (e.g. _xxsubinterpreters). The main motivation is to support releasing a PyPI package with the 3.13 capabilities compiled for 3.12.
A lot of the changes here involve either hiding details behind macros/functions or splitting up some files.
# error "this header requires Py_BUILD_CORE define"
#endif
+
+// We hide some of the newer PyCodeObject fields behind macros.
+// This helps with backporting certain changes to 3.12.
+#define _PyCode_HAS_EXECUTORS(CODE) \
+ (CODE->co_executors != NULL)
+#define _PyCode_HAS_INSTRUMENTATION(CODE) \
+ (CODE->_co_instrumentation_version > 0)
+
+
#define CODE_MAX_WATCHERS 8
/* PEP 659
PyAPI_FUNC(_PyCrossInterpreterData *) _PyCrossInterpreterData_New(void);
PyAPI_FUNC(void) _PyCrossInterpreterData_Free(_PyCrossInterpreterData *data);
+#define _PyCrossInterpreterData_DATA(DATA) ((DATA)->data)
+#define _PyCrossInterpreterData_OBJ(DATA) ((DATA)->obj)
+#define _PyCrossInterpreterData_INTERPID(DATA) ((DATA)->interpid)
+// Users should not need getters for "new_object" or "free".
+
/* defining cross-interpreter data */
PyAPI_FUNC(void) _PyCrossInterpreterData_Clear(
PyInterpreterState *, _PyCrossInterpreterData *);
+// Normally the Init* functions are sufficient. The only time
+// additional initialization might be needed is to set the "free" func,
+// though that should be infrequent.
+#define _PyCrossInterpreterData_SET_FREE(DATA, FUNC) \
+ do { \
+ (DATA)->free = (FUNC); \
+ } while (0)
+// Additionally, some shareable types are essentially light wrappers
+// around other shareable types. The crossinterpdatafunc of the wrapper
+// can often be implemented by calling the wrapped object's
+// crossinterpdatafunc and then changing the "new_object" function.
+// We have _PyCrossInterpreterData_SET_NEW_OBJECT() here for that,
+// but might be better to have a function like
+// _PyCrossInterpreterData_AdaptToWrapper() instead.
+#define _PyCrossInterpreterData_SET_NEW_OBJECT(DATA, FUNC) \
+ do { \
+ (DATA)->new_object = (FUNC); \
+ } while (0)
+
/* using cross-interpreter data */
extern PyStatus _PyXI_InitTypes(PyInterpreterState *interp);
extern void _PyXI_FiniTypes(PyInterpreterState *interp);
+#define _PyInterpreterState_GetXIState(interp) (&(interp)->xi)
+
/***************************/
/* short-term data sharing */
#include "pycore_brc.h" // struct _brc_thread_state
+static inline void
+_PyThreadState_SetWhence(PyThreadState *tstate, int whence)
+{
+ tstate->_whence = whence;
+}
+
+
// Every PyThreadState is actually allocated as a _PyThreadStateImpl. The
// PyThreadState fields are exposed as part of the C API, although most fields
// are intended to be private. The _PyThreadStateImpl fields not exposed.
Modules/signalmodule.o: $(srcdir)/Modules/signalmodule.c $(srcdir)/Modules/posixmodule.h
+Modules/_xxsubinterpretersmodule.o: $(srcdir)/Modules/_xxsubinterpretersmodule.c $(srcdir)/Modules/_interpreters_common.h
+
+Modules/_xxinterpqueuesmodule.o: $(srcdir)/Modules/_xxinterpqueuesmodule.c $(srcdir)/Modules/_interpreters_common.h
+
+Modules/_xxinterpchannelsmodule.o: $(srcdir)/Modules/_xxinterpchannelsmodule.c $(srcdir)/Modules/_interpreters_common.h
+
+Python/crossinterp.o: $(srcdir)/Python/crossinterp.c $(srcdir)/Python/crossinterp_data_lookup.h $(srcdir)/Python/crossinterp_exceptions.h
+
Python/dynload_shlib.o: $(srcdir)/Python/dynload_shlib.c Makefile
$(CC) -c $(PY_CORE_CFLAGS) \
-DSOABI='"$(SOABI)"' \
--- /dev/null
+
+#define _RESOLVE_MODINIT_FUNC_NAME(NAME) \
+ PyInit_ ## NAME
+#define RESOLVE_MODINIT_FUNC_NAME(NAME) \
+ _RESOLVE_MODINIT_FUNC_NAME(NAME)
+
+
+static int
+ensure_xid_class(PyTypeObject *cls, crossinterpdatafunc getdata)
+{
+ //assert(cls->tp_flags & Py_TPFLAGS_HEAPTYPE);
+ return _PyCrossInterpreterData_RegisterClass(cls, getdata);
+}
#include <sched.h> // sched_yield()
#endif
+#include "_interpreters_common.h"
+
/*
This module has the following process-global state:
API.. The module does not create any objects that are shared globally.
*/
-#define MODULE_NAME "_xxinterpchannels"
+#define MODULE_NAME _xxinterpchannels
+#define MODULE_NAME_STR Py_STRINGIFY(MODULE_NAME)
+#define MODINIT_FUNC_NAME RESOLVE_MODINIT_FUNC_NAME(MODULE_NAME)
#define GLOBAL_MALLOC(TYPE) \
register_xid_class(PyTypeObject *cls, crossinterpdatafunc shared,
struct xid_class_registry *classes)
{
- int res = _PyCrossInterpreterData_RegisterClass(cls, shared);
+ int res = ensure_xid_class(cls, shared);
if (res == 0) {
assert(classes->count < MAX_XID_CLASSES);
// The class has refs elsewhere, so we need to incref here.
static PyObject *
_get_current_module(void)
{
- PyObject *name = PyUnicode_FromString(MODULE_NAME);
+ PyObject *name = PyUnicode_FromString(MODULE_NAME_STR);
if (name == NULL) {
return NULL;
}
}
#define ADD_NEW_EXCEPTION(MOD, NAME, BASE) \
- add_new_exception(MOD, MODULE_NAME "." Py_STRINGIFY(NAME), BASE)
+ add_new_exception(MOD, MODULE_NAME_STR "." Py_STRINGIFY(NAME), BASE)
static PyTypeObject *
add_new_type(PyObject *mod, PyType_Spec *spec, crossinterpdatafunc shared,
if (mod == NULL) {
// XXX import it?
PyErr_SetString(PyExc_RuntimeError,
- MODULE_NAME " module not imported yet");
+ MODULE_NAME_STR " module not imported yet");
return NULL;
}
module_state *state = get_module_state(mod);
while (next != NULL) {
_channelitem *item = next;
next = item->next;
- if (item->data->interpid == interpid) {
+ if (_PyCrossInterpreterData_INTERPID(item->data) == interpid) {
if (prev == NULL) {
queue->first = item->next;
}
};
static PyStructSequence_Desc channel_info_desc = {
- .name = MODULE_NAME ".ChannelInfo",
+ .name = MODULE_NAME_STR ".ChannelInfo",
.doc = channel_info_doc,
.fields = channel_info_fields,
.n_in_sequence = 8,
static PyObject *
_channelid_from_xid(_PyCrossInterpreterData *data)
{
- struct _channelid_xid *xid = (struct _channelid_xid *)data->data;
+ struct _channelid_xid *xid = \
+ (struct _channelid_xid *)_PyCrossInterpreterData_DATA(data);
// It might not be imported yet, so we can't use _get_current_module().
- PyObject *mod = PyImport_ImportModule(MODULE_NAME);
+ PyObject *mod = PyImport_ImportModule(MODULE_NAME_STR);
if (mod == NULL) {
return NULL;
}
{
return -1;
}
- struct _channelid_xid *xid = (struct _channelid_xid *)data->data;
+ struct _channelid_xid *xid = \
+ (struct _channelid_xid *)_PyCrossInterpreterData_DATA(data);
xid->cid = ((channelid *)obj)->cid;
xid->end = ((channelid *)obj)->end;
xid->resolve = ((channelid *)obj)->resolve;
};
static PyType_Spec channelid_typespec = {
- .name = MODULE_NAME ".ChannelID",
+ .name = MODULE_NAME_STR ".ChannelID",
.basicsize = sizeof(channelid),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE),
if (res < 0) {
return -1;
}
- data->new_object = _channelend_from_xid;
+ _PyCrossInterpreterData_SET_NEW_OBJECT(data, _channelend_from_xid);
return 0;
}
static struct PyModuleDef moduledef = {
.m_base = PyModuleDef_HEAD_INIT,
- .m_name = MODULE_NAME,
+ .m_name = MODULE_NAME_STR,
.m_doc = module_doc,
.m_size = sizeof(module_state),
.m_methods = module_functions,
};
PyMODINIT_FUNC
-PyInit__xxinterpchannels(void)
+MODINIT_FUNC_NAME(void)
{
return PyModuleDef_Init(&moduledef);
}
#include "Python.h"
#include "pycore_crossinterp.h" // struct _xid
+#include "_interpreters_common.h"
-#define MODULE_NAME "_xxinterpqueues"
+
+#define MODULE_NAME _xxinterpqueues
+#define MODULE_NAME_STR Py_STRINGIFY(MODULE_NAME)
+#define MODINIT_FUNC_NAME RESOLVE_MODINIT_FUNC_NAME(MODULE_NAME)
#define GLOBAL_MALLOC(TYPE) \
static PyObject *
_get_current_module(void)
{
- PyObject *name = PyUnicode_FromString(MODULE_NAME);
+ PyObject *name = PyUnicode_FromString(MODULE_NAME_STR);
if (name == NULL) {
return NULL;
}
while (next != NULL) {
_queueitem *item = next;
next = item->next;
- if (item->data->interpid == interpid) {
+ if (_PyCrossInterpreterData_INTERPID(item->data) == interpid) {
if (prev == NULL) {
queue->items.first = item->next;
}
}
state->queue_type = (PyTypeObject *)Py_NewRef(queue_type);
- if (_PyCrossInterpreterData_RegisterClass(queue_type, _queueobj_shared) < 0) {
+ if (ensure_xid_class(queue_type, _queueobj_shared) < 0) {
return -1;
}
static PyObject *
_queueobj_from_xid(_PyCrossInterpreterData *data)
{
- int64_t qid = *(int64_t *)data->data;
+ int64_t qid = *(int64_t *)_PyCrossInterpreterData_DATA(data);
PyObject *qidobj = PyLong_FromLongLong(qid);
if (qidobj == NULL) {
return NULL;
if (mod == NULL) {
// XXX import it?
PyErr_SetString(PyExc_RuntimeError,
- MODULE_NAME " module not imported yet");
+ MODULE_NAME_STR " module not imported yet");
return NULL;
}
_PyCrossInterpreterData_Init(data, tstate->interp, raw, NULL,
_queueobj_from_xid);
Py_DECREF(qidobj);
- data->free = _queueid_xid_free;
+ _PyCrossInterpreterData_SET_FREE(data, _queueid_xid_free);
return 0;
}
static struct PyModuleDef moduledef = {
.m_base = PyModuleDef_HEAD_INIT,
- .m_name = MODULE_NAME,
+ .m_name = MODULE_NAME_STR,
.m_doc = module_doc,
.m_size = sizeof(module_state),
.m_methods = module_functions,
};
PyMODINIT_FUNC
-PyInit__xxinterpqueues(void)
+MODINIT_FUNC_NAME(void)
{
return PyModuleDef_Init(&moduledef);
}
#include "interpreteridobject.h"
#include "marshal.h" // PyMarshal_ReadObjectFromString()
+#include "_interpreters_common.h"
-#define MODULE_NAME "_xxsubinterpreters"
+
+#define MODULE_NAME _xxsubinterpreters
+#define MODULE_NAME_STR Py_STRINGIFY(MODULE_NAME)
+#define MODINIT_FUNC_NAME RESOLVE_MODINIT_FUNC_NAME(MODULE_NAME)
static PyInterpreterState *
static PyObject *
_get_current_module(void)
{
- PyObject *name = PyUnicode_FromString(MODULE_NAME);
+ PyObject *name = PyUnicode_FromString(MODULE_NAME_STR);
if (name == NULL) {
return NULL;
}
static PyObject *
xibufferview_from_xid(PyTypeObject *cls, _PyCrossInterpreterData *data)
{
- assert(data->data != NULL);
- assert(data->obj == NULL);
- assert(data->interpid >= 0);
+ assert(_PyCrossInterpreterData_DATA(data) != NULL);
+ assert(_PyCrossInterpreterData_OBJ(data) == NULL);
+ assert(_PyCrossInterpreterData_INTERPID(data) >= 0);
XIBufferViewObject *self = PyObject_Malloc(sizeof(XIBufferViewObject));
if (self == NULL) {
return NULL;
}
PyObject_Init((PyObject *)self, cls);
- self->view = (Py_buffer *)data->data;
- self->interpid = data->interpid;
+ self->view = (Py_buffer *)_PyCrossInterpreterData_DATA(data);
+ self->interpid = _PyCrossInterpreterData_INTERPID(data);
return (PyObject *)self;
}
};
static PyType_Spec XIBufferViewType_spec = {
- .name = MODULE_NAME ".CrossInterpreterBufferView",
+ .name = MODULE_NAME_STR ".CrossInterpreterBufferView",
.basicsize = sizeof(XIBufferViewObject),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE),
*p_state = cls;
// Register XID for the builtin memoryview type.
- if (_PyCrossInterpreterData_RegisterClass(
- &PyMemoryView_Type, _memoryview_shared) < 0) {
+ if (ensure_xid_class(&PyMemoryView_Type, _memoryview_shared) < 0) {
return -1;
}
// We don't ever bother un-registering memoryview.
if (mod == NULL) {
// XXX import it?
PyErr_SetString(PyExc_RuntimeError,
- MODULE_NAME " module not imported yet");
+ MODULE_NAME_STR " module not imported yet");
return NULL;
}
module_state *state = get_module_state(mod);
}
// We trust that no code objects under co_consts have unbound cell vars.
- if (code->co_executors != NULL
- || code->_co_instrumentation_version > 0)
- {
+ if (_PyCode_HAS_EXECUTORS(code) || _PyCode_HAS_INSTRUMENTATION(code)) {
return "only basic functions are supported";
}
if (code->_co_monitoring != NULL) {
// Destroy the interpreter.
PyThreadState *tstate = PyThreadState_New(interp);
- tstate->_whence = _PyThreadState_WHENCE_INTERP;
+ _PyThreadState_SetWhence(tstate, _PyThreadState_WHENCE_INTERP);
// XXX Possible GILState issues?
PyThreadState *save_tstate = PyThreadState_Swap(tstate);
Py_EndInterpreter(tstate);
interp_set___main___attrs(PyObject *self, PyObject *args)
{
PyObject *id, *updates;
- if (!PyArg_ParseTuple(args, "OO:" MODULE_NAME ".set___main___attrs",
+ if (!PyArg_ParseTuple(args, "OO:" MODULE_NAME_STR ".set___main___attrs",
&id, &updates))
{
return NULL;
PyObject *id, *code;
PyObject *shared = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "OO|O:" MODULE_NAME ".exec", kwlist,
+ "OO|O:" MODULE_NAME_STR ".exec", kwlist,
&id, &code, &shared)) {
return NULL;
}
const char *expected = "a string, a function, or a code object";
if (PyUnicode_Check(code)) {
- code = (PyObject *)convert_script_arg(code, MODULE_NAME ".exec",
+ code = (PyObject *)convert_script_arg(code, MODULE_NAME_STR ".exec",
"argument 2", expected);
}
else {
- code = (PyObject *)convert_code_arg(code, MODULE_NAME ".exec",
+ code = (PyObject *)convert_code_arg(code, MODULE_NAME_STR ".exec",
"argument 2", expected);
}
if (code == NULL) {
PyObject *id, *script;
PyObject *shared = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "OU|O:" MODULE_NAME ".run_string", kwlist,
+ "OU|O:" MODULE_NAME_STR ".run_string", kwlist,
&id, &script, &shared)) {
return NULL;
}
- script = (PyObject *)convert_script_arg(script, MODULE_NAME ".exec",
+ script = (PyObject *)convert_script_arg(script, MODULE_NAME_STR ".exec",
"argument 2", "a string");
if (script == NULL) {
return NULL;
\n\
Execute the provided string in the identified interpreter.\n\
\n\
-(See " MODULE_NAME ".exec().");
+(See " MODULE_NAME_STR ".exec().");
static PyObject *
interp_run_func(PyObject *self, PyObject *args, PyObject *kwds)
PyObject *id, *func;
PyObject *shared = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "OO|O:" MODULE_NAME ".run_func", kwlist,
+ "OO|O:" MODULE_NAME_STR ".run_func", kwlist,
&id, &func, &shared)) {
return NULL;
}
- PyCodeObject *code = convert_code_arg(func, MODULE_NAME ".exec",
+ PyCodeObject *code = convert_code_arg(func, MODULE_NAME_STR ".exec",
"argument 2",
"a function or a code object");
if (code == NULL) {
Code objects are also supported. In both cases, closures and args\n\
are not supported. Methods and other callables are not supported either.\n\
\n\
-(See " MODULE_NAME ".exec().");
+(See " MODULE_NAME_STR ".exec().");
static PyObject *
static struct PyModuleDef moduledef = {
.m_base = PyModuleDef_HEAD_INIT,
- .m_name = MODULE_NAME,
+ .m_name = MODULE_NAME_STR,
.m_doc = module_doc,
.m_size = sizeof(module_state),
.m_methods = module_functions,
};
PyMODINIT_FUNC
-PyInit__xxsubinterpreters(void)
+MODINIT_FUNC_NAME(void)
{
return PyModuleDef_Init(&moduledef);
}
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_namespace.h" //_PyNamespace_New()
#include "pycore_pyerrors.h" // _PyErr_Clear()
-#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_typeobject.h" // _PyType_GetModuleName()
#include "pycore_weakref.h" // _PyWeakref_GET_REF()
/* exceptions */
/**************/
-/* InterpreterError extends Exception */
-
-static PyTypeObject _PyExc_InterpreterError = {
- PyVarObject_HEAD_INIT(NULL, 0)
- .tp_name = "InterpreterError",
- .tp_doc = PyDoc_STR("An interpreter was not found."),
- //.tp_base = (PyTypeObject *)PyExc_BaseException,
-};
-PyObject *PyExc_InterpreterError = (PyObject *)&_PyExc_InterpreterError;
-
-/* InterpreterNotFoundError extends InterpreterError */
-
-static PyTypeObject _PyExc_InterpreterNotFoundError = {
- PyVarObject_HEAD_INIT(NULL, 0)
- .tp_name = "InterpreterNotFoundError",
- .tp_doc = PyDoc_STR("An interpreter was not found."),
- .tp_base = &_PyExc_InterpreterError,
-};
-PyObject *PyExc_InterpreterNotFoundError = (PyObject *)&_PyExc_InterpreterNotFoundError;
-
-/* lifecycle */
-
-static int
-init_exceptions(PyInterpreterState *interp)
-{
- _PyExc_InterpreterError.tp_base = (PyTypeObject *)PyExc_BaseException;
- if (_PyStaticType_InitBuiltin(interp, &_PyExc_InterpreterError) < 0) {
- return -1;
- }
- if (_PyStaticType_InitBuiltin(interp, &_PyExc_InterpreterNotFoundError) < 0) {
- return -1;
- }
- return 0;
-}
-
-static void
-fini_exceptions(PyInterpreterState *interp)
-{
- _PyStaticType_Dealloc(interp, &_PyExc_InterpreterNotFoundError);
- _PyStaticType_Dealloc(interp, &_PyExc_InterpreterError);
-}
+static int init_exceptions(PyInterpreterState *);
+static void fini_exceptions(PyInterpreterState *);
+static int _init_not_shareable_error_type(PyInterpreterState *);
+static void _fini_not_shareable_error_type(PyInterpreterState *);
+static PyObject * _get_not_shareable_error_type(PyInterpreterState *);
+#include "crossinterp_exceptions.h"
/***************************/
_Py_CallInInterpreter(PyInterpreterState *interp,
_Py_simple_func func, void *arg)
{
- if (interp == _PyThreadState_GetCurrent()->interp) {
+ if (interp == PyInterpreterState_Get()) {
return func(arg);
}
// XXX Emit a warning if this fails?
_Py_CallInInterpreterAndRawFree(PyInterpreterState *interp,
_Py_simple_func func, void *arg)
{
- if (interp == _PyThreadState_GetCurrent()->interp) {
+ if (interp == PyInterpreterState_Get()) {
int res = func(arg);
PyMem_RawFree(arg);
return res;
/* cross-interpreter data */
/**************************/
+/* registry of {type -> crossinterpdatafunc} */
+
+/* For now we use a global registry of shareable classes. An
+ alternative would be to add a tp_* slot for a class's
+ crossinterpdatafunc. It would be simpler and more efficient. */
+
+static void xid_lookup_init(PyInterpreterState *);
+static void xid_lookup_fini(PyInterpreterState *);
+static crossinterpdatafunc lookup_getdata(PyInterpreterState *, PyObject *);
+#include "crossinterp_data_lookup.h"
+
+
+/* lifecycle */
+
_PyCrossInterpreterData *
_PyCrossInterpreterData_New(void)
{
}
-/* exceptions */
-
-static PyStatus
-_init_not_shareable_error_type(PyInterpreterState *interp)
-{
- const char *name = "_interpreters.NotShareableError";
- PyObject *base = PyExc_ValueError;
- PyObject *ns = NULL;
- PyObject *exctype = PyErr_NewException(name, base, ns);
- if (exctype == NULL) {
- PyErr_Clear();
- return _PyStatus_ERR("could not initialize NotShareableError");
- }
-
- interp->xi.PyExc_NotShareableError = exctype;
- return _PyStatus_OK();
-}
-
-static void
-_fini_not_shareable_error_type(PyInterpreterState *interp)
-{
- Py_CLEAR(interp->xi.PyExc_NotShareableError);
-}
-
-static PyObject *
-_get_not_shareable_error_type(PyInterpreterState *interp)
-{
- assert(interp->xi.PyExc_NotShareableError != NULL);
- return interp->xi.PyExc_NotShareableError;
-}
-
-
/* defining cross-interpreter data */
static inline void
assert(data->data == NULL);
assert(data->obj == NULL);
*data = (_PyCrossInterpreterData){0};
- data->interpid = -1;
+ _PyCrossInterpreterData_INTERPID(data) = -1;
}
static inline void
// Ideally every object would know its owning interpreter.
// Until then, we have to rely on the caller to identify it
// (but we don't need it in all cases).
- data->interpid = (interp != NULL) ? interp->id : -1;
+ _PyCrossInterpreterData_INTERPID(data) = (interp != NULL)
+ ? PyInterpreterState_GetID(interp)
+ : -1;
data->new_object = new_object;
}
assert(data != NULL);
// This must be called in the owning interpreter.
assert(interp == NULL
- || data->interpid == -1
- || data->interpid == interp->id);
+ || _PyCrossInterpreterData_INTERPID(data) == -1
+ || _PyCrossInterpreterData_INTERPID(data) == PyInterpreterState_GetID(interp));
_xidata_clear(data);
}
// data->obj may be NULL, so we don't check it.
- if (data->interpid < 0) {
- _PyErr_SetString(tstate, PyExc_SystemError, "missing interp");
+ if (_PyCrossInterpreterData_INTERPID(data) < 0) {
+ PyErr_SetString(PyExc_SystemError, "missing interp");
return -1;
}
if (data->new_object == NULL) {
- _PyErr_SetString(tstate, PyExc_SystemError, "missing new_object func");
+ PyErr_SetString(PyExc_SystemError, "missing new_object func");
return -1;
}
return 0;
}
-static crossinterpdatafunc _lookup_getdata_from_registry(
- PyInterpreterState *, PyObject *);
-
-static crossinterpdatafunc
-_lookup_getdata(PyInterpreterState *interp, PyObject *obj)
-{
- /* Cross-interpreter objects are looked up by exact match on the class.
- We can reassess this policy when we move from a global registry to a
- tp_* slot. */
- return _lookup_getdata_from_registry(interp, obj);
-}
-
-crossinterpdatafunc
-_PyCrossInterpreterData_Lookup(PyObject *obj)
-{
- PyInterpreterState *interp = _PyInterpreterState_GET();
- return _lookup_getdata(interp, obj);
-}
-
static inline void
_set_xid_lookup_failure(PyInterpreterState *interp,
PyObject *obj, const char *msg)
int
_PyObject_CheckCrossInterpreterData(PyObject *obj)
{
- PyInterpreterState *interp = _PyInterpreterState_GET();
- crossinterpdatafunc getdata = _lookup_getdata(interp, obj);
+ PyInterpreterState *interp = PyInterpreterState_Get();
+ crossinterpdatafunc getdata = lookup_getdata(interp, obj);
if (getdata == NULL) {
if (!PyErr_Occurred()) {
_set_xid_lookup_failure(interp, obj, NULL);
int
_PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data)
{
- PyThreadState *tstate = _PyThreadState_GetCurrent();
-#ifdef Py_DEBUG
- // The caller must hold the GIL
- _Py_EnsureTstateNotNULL(tstate);
-#endif
+ PyThreadState *tstate = PyThreadState_Get();
PyInterpreterState *interp = tstate->interp;
// Reset data before re-populating.
*data = (_PyCrossInterpreterData){0};
- data->interpid = -1;
+ _PyCrossInterpreterData_INTERPID(data) = -1;
// Call the "getdata" func for the object.
Py_INCREF(obj);
- crossinterpdatafunc getdata = _lookup_getdata(interp, obj);
+ crossinterpdatafunc getdata = lookup_getdata(interp, obj);
if (getdata == NULL) {
Py_DECREF(obj);
if (!PyErr_Occurred()) {
}
// Fill in the blanks and validate the result.
- data->interpid = interp->id;
+ _PyCrossInterpreterData_INTERPID(data) = PyInterpreterState_GetID(interp);
if (_check_xidata(tstate, data) != 0) {
(void)_PyCrossInterpreterData_Release(data);
return -1;
}
// Switch to the original interpreter.
- PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interpid);
+ PyInterpreterState *interp = _PyInterpreterState_LookUpID(
+ _PyCrossInterpreterData_INTERPID(data));
if (interp == NULL) {
// The interpreter was already destroyed.
// This function shouldn't have been called.
}
-/* registry of {type -> crossinterpdatafunc} */
-
-/* For now we use a global registry of shareable classes. An
- alternative would be to add a tp_* slot for a class's
- crossinterpdatafunc. It would be simpler and more efficient. */
-
-static inline struct _xidregistry *
-_get_global_xidregistry(_PyRuntimeState *runtime)
-{
- return &runtime->xi.registry;
-}
-
-static inline struct _xidregistry *
-_get_xidregistry(PyInterpreterState *interp)
-{
- return &interp->xi.registry;
-}
-
-static inline struct _xidregistry *
-_get_xidregistry_for_type(PyInterpreterState *interp, PyTypeObject *cls)
-{
- struct _xidregistry *registry = _get_global_xidregistry(interp->runtime);
- if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
- registry = _get_xidregistry(interp);
- }
- return registry;
-}
-
-static int
-_xidregistry_add_type(struct _xidregistry *xidregistry,
- PyTypeObject *cls, crossinterpdatafunc getdata)
-{
- struct _xidregitem *newhead = PyMem_RawMalloc(sizeof(struct _xidregitem));
- if (newhead == NULL) {
- return -1;
- }
- *newhead = (struct _xidregitem){
- // We do not keep a reference, to avoid keeping the class alive.
- .cls = cls,
- .refcount = 1,
- .getdata = getdata,
- };
- if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
- // XXX Assign a callback to clear the entry from the registry?
- newhead->weakref = PyWeakref_NewRef((PyObject *)cls, NULL);
- if (newhead->weakref == NULL) {
- PyMem_RawFree(newhead);
- return -1;
- }
- }
- newhead->next = xidregistry->head;
- if (newhead->next != NULL) {
- newhead->next->prev = newhead;
- }
- xidregistry->head = newhead;
- return 0;
-}
-
-static struct _xidregitem *
-_xidregistry_remove_entry(struct _xidregistry *xidregistry,
- struct _xidregitem *entry)
-{
- struct _xidregitem *next = entry->next;
- if (entry->prev != NULL) {
- assert(entry->prev->next == entry);
- entry->prev->next = next;
- }
- else {
- assert(xidregistry->head == entry);
- xidregistry->head = next;
- }
- if (next != NULL) {
- next->prev = entry->prev;
- }
- Py_XDECREF(entry->weakref);
- PyMem_RawFree(entry);
- return next;
-}
-
-static void
-_xidregistry_clear(struct _xidregistry *xidregistry)
-{
- struct _xidregitem *cur = xidregistry->head;
- xidregistry->head = NULL;
- while (cur != NULL) {
- struct _xidregitem *next = cur->next;
- Py_XDECREF(cur->weakref);
- PyMem_RawFree(cur);
- cur = next;
- }
-}
-
-static void
-_xidregistry_lock(struct _xidregistry *registry)
-{
- if (registry->global) {
- PyMutex_Lock(®istry->mutex);
- }
- // else: Within an interpreter we rely on the GIL instead of a separate lock.
-}
-
-static void
-_xidregistry_unlock(struct _xidregistry *registry)
-{
- if (registry->global) {
- PyMutex_Unlock(®istry->mutex);
- }
-}
-
-static struct _xidregitem *
-_xidregistry_find_type(struct _xidregistry *xidregistry, PyTypeObject *cls)
-{
- struct _xidregitem *cur = xidregistry->head;
- while (cur != NULL) {
- if (cur->weakref != NULL) {
- // cur is/was a heap type.
- PyObject *registered = _PyWeakref_GET_REF(cur->weakref);
- if (registered == NULL) {
- // The weakly ref'ed object was freed.
- cur = _xidregistry_remove_entry(xidregistry, cur);
- continue;
- }
- assert(PyType_Check(registered));
- assert(cur->cls == (PyTypeObject *)registered);
- assert(cur->cls->tp_flags & Py_TPFLAGS_HEAPTYPE);
- Py_DECREF(registered);
- }
- if (cur->cls == cls) {
- return cur;
- }
- cur = cur->next;
- }
- return NULL;
-}
-
-int
-_PyCrossInterpreterData_RegisterClass(PyTypeObject *cls,
- crossinterpdatafunc getdata)
-{
- if (!PyType_Check(cls)) {
- PyErr_Format(PyExc_ValueError, "only classes may be registered");
- return -1;
- }
- if (getdata == NULL) {
- PyErr_Format(PyExc_ValueError, "missing 'getdata' func");
- return -1;
- }
-
- int res = 0;
- PyInterpreterState *interp = _PyInterpreterState_GET();
- struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls);
- _xidregistry_lock(xidregistry);
-
- struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
- if (matched != NULL) {
- assert(matched->getdata == getdata);
- matched->refcount += 1;
- goto finally;
- }
-
- res = _xidregistry_add_type(xidregistry, cls, getdata);
-
-finally:
- _xidregistry_unlock(xidregistry);
- return res;
-}
-
-int
-_PyCrossInterpreterData_UnregisterClass(PyTypeObject *cls)
-{
- int res = 0;
- PyInterpreterState *interp = _PyInterpreterState_GET();
- struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls);
- _xidregistry_lock(xidregistry);
-
- struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
- if (matched != NULL) {
- assert(matched->refcount > 0);
- matched->refcount -= 1;
- if (matched->refcount == 0) {
- (void)_xidregistry_remove_entry(xidregistry, matched);
- }
- res = 1;
- }
-
- _xidregistry_unlock(xidregistry);
- return res;
-}
-
-static crossinterpdatafunc
-_lookup_getdata_from_registry(PyInterpreterState *interp, PyObject *obj)
-{
- PyTypeObject *cls = Py_TYPE(obj);
-
- struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls);
- _xidregistry_lock(xidregistry);
-
- struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
- crossinterpdatafunc func = matched != NULL ? matched->getdata : NULL;
-
- _xidregistry_unlock(xidregistry);
- return func;
-}
-
-/* cross-interpreter data for builtin types */
-
-// bytes
-
-struct _shared_bytes_data {
- char *bytes;
- Py_ssize_t len;
-};
-
-static PyObject *
-_new_bytes_object(_PyCrossInterpreterData *data)
-{
- struct _shared_bytes_data *shared = (struct _shared_bytes_data *)(data->data);
- return PyBytes_FromStringAndSize(shared->bytes, shared->len);
-}
-
-static int
-_bytes_shared(PyThreadState *tstate, PyObject *obj,
- _PyCrossInterpreterData *data)
-{
- if (_PyCrossInterpreterData_InitWithSize(
- data, tstate->interp, sizeof(struct _shared_bytes_data), obj,
- _new_bytes_object
- ) < 0)
- {
- return -1;
- }
- struct _shared_bytes_data *shared = (struct _shared_bytes_data *)data->data;
- if (PyBytes_AsStringAndSize(obj, &shared->bytes, &shared->len) < 0) {
- _PyCrossInterpreterData_Clear(tstate->interp, data);
- return -1;
- }
- return 0;
-}
-
-// str
-
-struct _shared_str_data {
- int kind;
- const void *buffer;
- Py_ssize_t len;
-};
-
-static PyObject *
-_new_str_object(_PyCrossInterpreterData *data)
-{
- struct _shared_str_data *shared = (struct _shared_str_data *)(data->data);
- return PyUnicode_FromKindAndData(shared->kind, shared->buffer, shared->len);
-}
-
-static int
-_str_shared(PyThreadState *tstate, PyObject *obj,
- _PyCrossInterpreterData *data)
-{
- if (_PyCrossInterpreterData_InitWithSize(
- data, tstate->interp, sizeof(struct _shared_str_data), obj,
- _new_str_object
- ) < 0)
- {
- return -1;
- }
- struct _shared_str_data *shared = (struct _shared_str_data *)data->data;
- shared->kind = PyUnicode_KIND(obj);
- shared->buffer = PyUnicode_DATA(obj);
- shared->len = PyUnicode_GET_LENGTH(obj);
- return 0;
-}
-
-// int
-
-static PyObject *
-_new_long_object(_PyCrossInterpreterData *data)
-{
- return PyLong_FromSsize_t((Py_ssize_t)(data->data));
-}
-
-static int
-_long_shared(PyThreadState *tstate, PyObject *obj,
- _PyCrossInterpreterData *data)
-{
- /* Note that this means the size of shareable ints is bounded by
- * sys.maxsize. Hence on 32-bit architectures that is half the
- * size of maximum shareable ints on 64-bit.
- */
- Py_ssize_t value = PyLong_AsSsize_t(obj);
- if (value == -1 && PyErr_Occurred()) {
- if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
- PyErr_SetString(PyExc_OverflowError, "try sending as bytes");
- }
- return -1;
- }
- _PyCrossInterpreterData_Init(data, tstate->interp, (void *)value, NULL,
- _new_long_object);
- // data->obj and data->free remain NULL
- return 0;
-}
-
-// float
-
-static PyObject *
-_new_float_object(_PyCrossInterpreterData *data)
-{
- double * value_ptr = data->data;
- return PyFloat_FromDouble(*value_ptr);
-}
-
-static int
-_float_shared(PyThreadState *tstate, PyObject *obj,
- _PyCrossInterpreterData *data)
-{
- if (_PyCrossInterpreterData_InitWithSize(
- data, tstate->interp, sizeof(double), NULL,
- _new_float_object
- ) < 0)
- {
- return -1;
- }
- double *shared = (double *)data->data;
- *shared = PyFloat_AsDouble(obj);
- return 0;
-}
-
-// None
-
-static PyObject *
-_new_none_object(_PyCrossInterpreterData *data)
-{
- // XXX Singleton refcounts are problematic across interpreters...
- return Py_NewRef(Py_None);
-}
-
-static int
-_none_shared(PyThreadState *tstate, PyObject *obj,
- _PyCrossInterpreterData *data)
-{
- _PyCrossInterpreterData_Init(data, tstate->interp, NULL, NULL,
- _new_none_object);
- // data->data, data->obj and data->free remain NULL
- return 0;
-}
-
-// bool
-
-static PyObject *
-_new_bool_object(_PyCrossInterpreterData *data)
-{
- if (data->data){
- Py_RETURN_TRUE;
- }
- Py_RETURN_FALSE;
-}
-
-static int
-_bool_shared(PyThreadState *tstate, PyObject *obj,
- _PyCrossInterpreterData *data)
-{
- _PyCrossInterpreterData_Init(data, tstate->interp,
- (void *) (Py_IsTrue(obj) ? (uintptr_t) 1 : (uintptr_t) 0), NULL,
- _new_bool_object);
- // data->obj and data->free remain NULL
- return 0;
-}
-
-// tuple
-
-struct _shared_tuple_data {
- Py_ssize_t len;
- _PyCrossInterpreterData **data;
-};
-
-static PyObject *
-_new_tuple_object(_PyCrossInterpreterData *data)
-{
- struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(data->data);
- PyObject *tuple = PyTuple_New(shared->len);
- if (tuple == NULL) {
- return NULL;
- }
-
- for (Py_ssize_t i = 0; i < shared->len; i++) {
- PyObject *item = _PyCrossInterpreterData_NewObject(shared->data[i]);
- if (item == NULL){
- Py_DECREF(tuple);
- return NULL;
- }
- PyTuple_SET_ITEM(tuple, i, item);
- }
- return tuple;
-}
-
-static void
-_tuple_shared_free(void* data)
-{
- struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(data);
-#ifndef NDEBUG
- int64_t interpid = PyInterpreterState_GetID(_PyInterpreterState_GET());
-#endif
- for (Py_ssize_t i = 0; i < shared->len; i++) {
- if (shared->data[i] != NULL) {
- assert(shared->data[i]->interpid == interpid);
- _PyCrossInterpreterData_Release(shared->data[i]);
- PyMem_RawFree(shared->data[i]);
- shared->data[i] = NULL;
- }
- }
- PyMem_Free(shared->data);
- PyMem_RawFree(shared);
-}
-
-static int
-_tuple_shared(PyThreadState *tstate, PyObject *obj,
- _PyCrossInterpreterData *data)
-{
- Py_ssize_t len = PyTuple_GET_SIZE(obj);
- if (len < 0) {
- return -1;
- }
- struct _shared_tuple_data *shared = PyMem_RawMalloc(sizeof(struct _shared_tuple_data));
- if (shared == NULL){
- PyErr_NoMemory();
- return -1;
- }
-
- shared->len = len;
- shared->data = (_PyCrossInterpreterData **) PyMem_Calloc(shared->len, sizeof(_PyCrossInterpreterData *));
- if (shared->data == NULL) {
- PyErr_NoMemory();
- return -1;
- }
-
- for (Py_ssize_t i = 0; i < shared->len; i++) {
- _PyCrossInterpreterData *data = _PyCrossInterpreterData_New();
- if (data == NULL) {
- goto error; // PyErr_NoMemory already set
- }
- PyObject *item = PyTuple_GET_ITEM(obj, i);
-
- int res = -1;
- if (!_Py_EnterRecursiveCallTstate(tstate, " while sharing a tuple")) {
- res = _PyObject_GetCrossInterpreterData(item, data);
- _Py_LeaveRecursiveCallTstate(tstate);
- }
- if (res < 0) {
- PyMem_RawFree(data);
- goto error;
- }
- shared->data[i] = data;
- }
- _PyCrossInterpreterData_Init(
- data, tstate->interp, shared, obj, _new_tuple_object);
- data->free = _tuple_shared_free;
- return 0;
-
-error:
- _tuple_shared_free(shared);
- return -1;
-}
-
-// registration
-
-static void
-_register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry)
-{
- // None
- if (_xidregistry_add_type(xidregistry, (PyTypeObject *)PyObject_Type(Py_None), _none_shared) != 0) {
- Py_FatalError("could not register None for cross-interpreter sharing");
- }
-
- // int
- if (_xidregistry_add_type(xidregistry, &PyLong_Type, _long_shared) != 0) {
- Py_FatalError("could not register int for cross-interpreter sharing");
- }
-
- // bytes
- if (_xidregistry_add_type(xidregistry, &PyBytes_Type, _bytes_shared) != 0) {
- Py_FatalError("could not register bytes for cross-interpreter sharing");
- }
-
- // str
- if (_xidregistry_add_type(xidregistry, &PyUnicode_Type, _str_shared) != 0) {
- Py_FatalError("could not register str for cross-interpreter sharing");
- }
-
- // bool
- if (_xidregistry_add_type(xidregistry, &PyBool_Type, _bool_shared) != 0) {
- Py_FatalError("could not register bool for cross-interpreter sharing");
- }
-
- // float
- if (_xidregistry_add_type(xidregistry, &PyFloat_Type, _float_shared) != 0) {
- Py_FatalError("could not register float for cross-interpreter sharing");
- }
-
- // tuple
- if (_xidregistry_add_type(xidregistry, &PyTuple_Type, _tuple_shared) != 0) {
- Py_FatalError("could not register tuple for cross-interpreter sharing");
- }
-}
-
-/* registry lifecycle */
-
-static void
-_xidregistry_init(struct _xidregistry *registry)
-{
- if (registry->initialized) {
- return;
- }
- registry->initialized = 1;
-
- if (registry->global) {
- // Registering the builtins is cheap so we don't bother doing it lazily.
- assert(registry->head == NULL);
- _register_builtins_for_crossinterpreter_data(registry);
- }
-}
-
-static void
-_xidregistry_fini(struct _xidregistry *registry)
-{
- if (!registry->initialized) {
- return;
- }
- registry->initialized = 0;
-
- _xidregistry_clear(registry);
-}
-
-
/*************************/
/* convenience utilities */
/*************************/
return -1;
}
+// We accommodate backports here.
+#ifndef _Py_EMPTY_STR
+# define _Py_EMPTY_STR &_Py_STR(empty)
+#endif
static const char *
_format_TracebackException(PyObject *tbexc)
if (lines == NULL) {
return NULL;
}
- PyObject *formatted_obj = PyUnicode_Join(&_Py_STR(empty), lines);
+ assert(_Py_EMPTY_STR != NULL);
+ PyObject *formatted_obj = PyUnicode_Join(_Py_EMPTY_STR, lines);
Py_DECREF(lines);
if (formatted_obj == NULL) {
return NULL;
return 0;
}
if (p_interpid != NULL) {
- *p_interpid = item->data->interpid;
+ *p_interpid = _PyCrossInterpreterData_INTERPID(item->data);
}
return 1;
}
return;
}
- if (interpid == PyInterpreterState_GetID(_PyInterpreterState_GET())) {
+ if (interpid == PyInterpreterState_GetID(PyInterpreterState_Get())) {
_sharedns_free(ns);
}
else {
PyThreadState *prev = tstate;
if (interp != tstate->interp) {
tstate = PyThreadState_New(interp);
- tstate->_whence = _PyThreadState_WHENCE_EXEC;
+ _PyThreadState_SetWhence(tstate, _PyThreadState_WHENCE_EXEC);
// XXX Possible GILState issues?
session->prev_tstate = PyThreadState_Swap(tstate);
assert(session->prev_tstate == prev);
if (session == NULL) {
return;
}
- PyInterpreterState *interp = _PyInterpreterState_GET();
+ PyInterpreterState *interp = PyInterpreterState_Get();
if (PyErr_ExceptionMatches(_get_not_shareable_error_type(interp))) {
// We want to propagate the exception directly.
session->_error_override = _PyXI_ERR_NOT_SHAREABLE;
PyStatus
_PyXI_Init(PyInterpreterState *interp)
{
- PyStatus status;
-
- // Initialize the XID registry.
- if (_Py_IsMainInterpreter(interp)) {
- _xidregistry_init(_get_global_xidregistry(interp->runtime));
- }
- _xidregistry_init(_get_xidregistry(interp));
+ // Initialize the XID lookup state (e.g. registry).
+ xid_lookup_init(interp);
// Initialize exceptions (heap types).
- status = _init_not_shareable_error_type(interp);
- if (_PyStatus_EXCEPTION(status)) {
- return status;
+ if (_init_not_shareable_error_type(interp) < 0) {
+ return _PyStatus_ERR("failed to initialize NotShareableError");
}
return _PyStatus_OK();
// Finalize exceptions (heap types).
_fini_not_shareable_error_type(interp);
- // Finalize the XID registry.
- _xidregistry_fini(_get_xidregistry(interp));
- if (_Py_IsMainInterpreter(interp)) {
- _xidregistry_fini(_get_global_xidregistry(interp->runtime));
- }
+ // Finalize the XID lookup state (e.g. registry).
+ xid_lookup_fini(interp);
}
PyStatus
--- /dev/null
+
+static crossinterpdatafunc _lookup_getdata_from_registry(
+ PyInterpreterState *, PyObject *);
+
+static crossinterpdatafunc
+lookup_getdata(PyInterpreterState *interp, PyObject *obj)
+{
+ /* Cross-interpreter objects are looked up by exact match on the class.
+ We can reassess this policy when we move from a global registry to a
+ tp_* slot. */
+ return _lookup_getdata_from_registry(interp, obj);
+}
+
+crossinterpdatafunc
+_PyCrossInterpreterData_Lookup(PyObject *obj)
+{
+ PyInterpreterState *interp = PyInterpreterState_Get();
+ return lookup_getdata(interp, obj);
+}
+
+
+/***********************************************/
+/* a registry of {type -> crossinterpdatafunc} */
+/***********************************************/
+
+/* For now we use a global registry of shareable classes. An
+ alternative would be to add a tp_* slot for a class's
+ crossinterpdatafunc. It would be simpler and more efficient. */
+
+
+/* registry lifecycle */
+
+static void _register_builtins_for_crossinterpreter_data(struct _xidregistry *);
+
+static void
+_xidregistry_init(struct _xidregistry *registry)
+{
+ if (registry->initialized) {
+ return;
+ }
+ registry->initialized = 1;
+
+ if (registry->global) {
+ // Registering the builtins is cheap so we don't bother doing it lazily.
+ assert(registry->head == NULL);
+ _register_builtins_for_crossinterpreter_data(registry);
+ }
+}
+
+static void _xidregistry_clear(struct _xidregistry *);
+
+static void
+_xidregistry_fini(struct _xidregistry *registry)
+{
+ if (!registry->initialized) {
+ return;
+ }
+ registry->initialized = 0;
+
+ _xidregistry_clear(registry);
+}
+
+static inline struct _xidregistry * _get_global_xidregistry(_PyRuntimeState *);
+static inline struct _xidregistry * _get_xidregistry(PyInterpreterState *);
+
+static void
+xid_lookup_init(PyInterpreterState *interp)
+{
+ if (_Py_IsMainInterpreter(interp)) {
+ _xidregistry_init(_get_global_xidregistry(interp->runtime));
+ }
+ _xidregistry_init(_get_xidregistry(interp));
+}
+
+static void
+xid_lookup_fini(PyInterpreterState *interp)
+{
+ _xidregistry_fini(_get_xidregistry(interp));
+ if (_Py_IsMainInterpreter(interp)) {
+ _xidregistry_fini(_get_global_xidregistry(interp->runtime));
+ }
+}
+
+
+/* registry thread safety */
+
+static void
+_xidregistry_lock(struct _xidregistry *registry)
+{
+ if (registry->global) {
+ PyMutex_Lock(®istry->mutex);
+ }
+ // else: Within an interpreter we rely on the GIL instead of a separate lock.
+}
+
+static void
+_xidregistry_unlock(struct _xidregistry *registry)
+{
+ if (registry->global) {
+ PyMutex_Unlock(®istry->mutex);
+ }
+}
+
+
+/* accessing the registry */
+
+static inline struct _xidregistry *
+_get_global_xidregistry(_PyRuntimeState *runtime)
+{
+ return &runtime->xi.registry;
+}
+
+static inline struct _xidregistry *
+_get_xidregistry(PyInterpreterState *interp)
+{
+ return &interp->xi.registry;
+}
+
+static inline struct _xidregistry *
+_get_xidregistry_for_type(PyInterpreterState *interp, PyTypeObject *cls)
+{
+ struct _xidregistry *registry = _get_global_xidregistry(interp->runtime);
+ if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
+ registry = _get_xidregistry(interp);
+ }
+ return registry;
+}
+
+static struct _xidregitem * _xidregistry_remove_entry(
+ struct _xidregistry *, struct _xidregitem *);
+
+static struct _xidregitem *
+_xidregistry_find_type(struct _xidregistry *xidregistry, PyTypeObject *cls)
+{
+ struct _xidregitem *cur = xidregistry->head;
+ while (cur != NULL) {
+ if (cur->weakref != NULL) {
+ // cur is/was a heap type.
+ PyObject *registered = _PyWeakref_GET_REF(cur->weakref);
+ if (registered == NULL) {
+ // The weakly ref'ed object was freed.
+ cur = _xidregistry_remove_entry(xidregistry, cur);
+ continue;
+ }
+ assert(PyType_Check(registered));
+ assert(cur->cls == (PyTypeObject *)registered);
+ assert(cur->cls->tp_flags & Py_TPFLAGS_HEAPTYPE);
+ Py_DECREF(registered);
+ }
+ if (cur->cls == cls) {
+ return cur;
+ }
+ cur = cur->next;
+ }
+ return NULL;
+}
+
+static crossinterpdatafunc
+_lookup_getdata_from_registry(PyInterpreterState *interp, PyObject *obj)
+{
+ PyTypeObject *cls = Py_TYPE(obj);
+
+ struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls);
+ _xidregistry_lock(xidregistry);
+
+ struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
+ crossinterpdatafunc func = matched != NULL ? matched->getdata : NULL;
+
+ _xidregistry_unlock(xidregistry);
+ return func;
+}
+
+
+/* updating the registry */
+
+static int
+_xidregistry_add_type(struct _xidregistry *xidregistry,
+ PyTypeObject *cls, crossinterpdatafunc getdata)
+{
+ struct _xidregitem *newhead = PyMem_RawMalloc(sizeof(struct _xidregitem));
+ if (newhead == NULL) {
+ return -1;
+ }
+ *newhead = (struct _xidregitem){
+ // We do not keep a reference, to avoid keeping the class alive.
+ .cls = cls,
+ .refcount = 1,
+ .getdata = getdata,
+ };
+ if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
+ // XXX Assign a callback to clear the entry from the registry?
+ newhead->weakref = PyWeakref_NewRef((PyObject *)cls, NULL);
+ if (newhead->weakref == NULL) {
+ PyMem_RawFree(newhead);
+ return -1;
+ }
+ }
+ newhead->next = xidregistry->head;
+ if (newhead->next != NULL) {
+ newhead->next->prev = newhead;
+ }
+ xidregistry->head = newhead;
+ return 0;
+}
+
+static struct _xidregitem *
+_xidregistry_remove_entry(struct _xidregistry *xidregistry,
+ struct _xidregitem *entry)
+{
+ struct _xidregitem *next = entry->next;
+ if (entry->prev != NULL) {
+ assert(entry->prev->next == entry);
+ entry->prev->next = next;
+ }
+ else {
+ assert(xidregistry->head == entry);
+ xidregistry->head = next;
+ }
+ if (next != NULL) {
+ next->prev = entry->prev;
+ }
+ Py_XDECREF(entry->weakref);
+ PyMem_RawFree(entry);
+ return next;
+}
+
+static void
+_xidregistry_clear(struct _xidregistry *xidregistry)
+{
+ struct _xidregitem *cur = xidregistry->head;
+ xidregistry->head = NULL;
+ while (cur != NULL) {
+ struct _xidregitem *next = cur->next;
+ Py_XDECREF(cur->weakref);
+ PyMem_RawFree(cur);
+ cur = next;
+ }
+}
+
+int
+_PyCrossInterpreterData_RegisterClass(PyTypeObject *cls,
+ crossinterpdatafunc getdata)
+{
+ if (!PyType_Check(cls)) {
+ PyErr_Format(PyExc_ValueError, "only classes may be registered");
+ return -1;
+ }
+ if (getdata == NULL) {
+ PyErr_Format(PyExc_ValueError, "missing 'getdata' func");
+ return -1;
+ }
+
+ int res = 0;
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls);
+ _xidregistry_lock(xidregistry);
+
+ struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
+ if (matched != NULL) {
+ assert(matched->getdata == getdata);
+ matched->refcount += 1;
+ goto finally;
+ }
+
+ res = _xidregistry_add_type(xidregistry, cls, getdata);
+
+finally:
+ _xidregistry_unlock(xidregistry);
+ return res;
+}
+
+int
+_PyCrossInterpreterData_UnregisterClass(PyTypeObject *cls)
+{
+ int res = 0;
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _xidregistry *xidregistry = _get_xidregistry_for_type(interp, cls);
+ _xidregistry_lock(xidregistry);
+
+ struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
+ if (matched != NULL) {
+ assert(matched->refcount > 0);
+ matched->refcount -= 1;
+ if (matched->refcount == 0) {
+ (void)_xidregistry_remove_entry(xidregistry, matched);
+ }
+ res = 1;
+ }
+
+ _xidregistry_unlock(xidregistry);
+ return res;
+}
+
+
+/********************************************/
+/* cross-interpreter data for builtin types */
+/********************************************/
+
+// bytes
+
+struct _shared_bytes_data {
+ char *bytes;
+ Py_ssize_t len;
+};
+
+static PyObject *
+_new_bytes_object(_PyCrossInterpreterData *data)
+{
+ struct _shared_bytes_data *shared = (struct _shared_bytes_data *)(data->data);
+ return PyBytes_FromStringAndSize(shared->bytes, shared->len);
+}
+
+static int
+_bytes_shared(PyThreadState *tstate, PyObject *obj,
+ _PyCrossInterpreterData *data)
+{
+ if (_PyCrossInterpreterData_InitWithSize(
+ data, tstate->interp, sizeof(struct _shared_bytes_data), obj,
+ _new_bytes_object
+ ) < 0)
+ {
+ return -1;
+ }
+ struct _shared_bytes_data *shared = (struct _shared_bytes_data *)data->data;
+ if (PyBytes_AsStringAndSize(obj, &shared->bytes, &shared->len) < 0) {
+ _PyCrossInterpreterData_Clear(tstate->interp, data);
+ return -1;
+ }
+ return 0;
+}
+
+// str
+
+struct _shared_str_data {
+ int kind;
+ const void *buffer;
+ Py_ssize_t len;
+};
+
+static PyObject *
+_new_str_object(_PyCrossInterpreterData *data)
+{
+ struct _shared_str_data *shared = (struct _shared_str_data *)(data->data);
+ return PyUnicode_FromKindAndData(shared->kind, shared->buffer, shared->len);
+}
+
+static int
+_str_shared(PyThreadState *tstate, PyObject *obj,
+ _PyCrossInterpreterData *data)
+{
+ if (_PyCrossInterpreterData_InitWithSize(
+ data, tstate->interp, sizeof(struct _shared_str_data), obj,
+ _new_str_object
+ ) < 0)
+ {
+ return -1;
+ }
+ struct _shared_str_data *shared = (struct _shared_str_data *)data->data;
+ shared->kind = PyUnicode_KIND(obj);
+ shared->buffer = PyUnicode_DATA(obj);
+ shared->len = PyUnicode_GET_LENGTH(obj);
+ return 0;
+}
+
+// int
+
+static PyObject *
+_new_long_object(_PyCrossInterpreterData *data)
+{
+ return PyLong_FromSsize_t((Py_ssize_t)(data->data));
+}
+
+static int
+_long_shared(PyThreadState *tstate, PyObject *obj,
+ _PyCrossInterpreterData *data)
+{
+ /* Note that this means the size of shareable ints is bounded by
+ * sys.maxsize. Hence on 32-bit architectures that is half the
+ * size of maximum shareable ints on 64-bit.
+ */
+ Py_ssize_t value = PyLong_AsSsize_t(obj);
+ if (value == -1 && PyErr_Occurred()) {
+ if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+ PyErr_SetString(PyExc_OverflowError, "try sending as bytes");
+ }
+ return -1;
+ }
+ _PyCrossInterpreterData_Init(data, tstate->interp, (void *)value, NULL,
+ _new_long_object);
+ // data->obj and data->free remain NULL
+ return 0;
+}
+
+// float
+
+static PyObject *
+_new_float_object(_PyCrossInterpreterData *data)
+{
+ double * value_ptr = data->data;
+ return PyFloat_FromDouble(*value_ptr);
+}
+
+static int
+_float_shared(PyThreadState *tstate, PyObject *obj,
+ _PyCrossInterpreterData *data)
+{
+ if (_PyCrossInterpreterData_InitWithSize(
+ data, tstate->interp, sizeof(double), NULL,
+ _new_float_object
+ ) < 0)
+ {
+ return -1;
+ }
+ double *shared = (double *)data->data;
+ *shared = PyFloat_AsDouble(obj);
+ return 0;
+}
+
+// None
+
+static PyObject *
+_new_none_object(_PyCrossInterpreterData *data)
+{
+ // XXX Singleton refcounts are problematic across interpreters...
+ return Py_NewRef(Py_None);
+}
+
+static int
+_none_shared(PyThreadState *tstate, PyObject *obj,
+ _PyCrossInterpreterData *data)
+{
+ _PyCrossInterpreterData_Init(data, tstate->interp, NULL, NULL,
+ _new_none_object);
+ // data->data, data->obj and data->free remain NULL
+ return 0;
+}
+
+// bool
+
+static PyObject *
+_new_bool_object(_PyCrossInterpreterData *data)
+{
+ if (data->data){
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
+static int
+_bool_shared(PyThreadState *tstate, PyObject *obj,
+ _PyCrossInterpreterData *data)
+{
+ _PyCrossInterpreterData_Init(data, tstate->interp,
+ (void *) (Py_IsTrue(obj) ? (uintptr_t) 1 : (uintptr_t) 0), NULL,
+ _new_bool_object);
+ // data->obj and data->free remain NULL
+ return 0;
+}
+
+// tuple
+
+struct _shared_tuple_data {
+ Py_ssize_t len;
+ _PyCrossInterpreterData **data;
+};
+
+static PyObject *
+_new_tuple_object(_PyCrossInterpreterData *data)
+{
+ struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(data->data);
+ PyObject *tuple = PyTuple_New(shared->len);
+ if (tuple == NULL) {
+ return NULL;
+ }
+
+ for (Py_ssize_t i = 0; i < shared->len; i++) {
+ PyObject *item = _PyCrossInterpreterData_NewObject(shared->data[i]);
+ if (item == NULL){
+ Py_DECREF(tuple);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(tuple, i, item);
+ }
+ return tuple;
+}
+
+static void
+_tuple_shared_free(void* data)
+{
+ struct _shared_tuple_data *shared = (struct _shared_tuple_data *)(data);
+#ifndef NDEBUG
+ int64_t interpid = PyInterpreterState_GetID(_PyInterpreterState_GET());
+#endif
+ for (Py_ssize_t i = 0; i < shared->len; i++) {
+ if (shared->data[i] != NULL) {
+ assert(_PyCrossInterpreterData_INTERPID(shared->data[i]) == interpid);
+ _PyCrossInterpreterData_Release(shared->data[i]);
+ PyMem_RawFree(shared->data[i]);
+ shared->data[i] = NULL;
+ }
+ }
+ PyMem_Free(shared->data);
+ PyMem_RawFree(shared);
+}
+
+static int
+_tuple_shared(PyThreadState *tstate, PyObject *obj,
+ _PyCrossInterpreterData *data)
+{
+ Py_ssize_t len = PyTuple_GET_SIZE(obj);
+ if (len < 0) {
+ return -1;
+ }
+ struct _shared_tuple_data *shared = PyMem_RawMalloc(sizeof(struct _shared_tuple_data));
+ if (shared == NULL){
+ PyErr_NoMemory();
+ return -1;
+ }
+
+ shared->len = len;
+ shared->data = (_PyCrossInterpreterData **) PyMem_Calloc(shared->len, sizeof(_PyCrossInterpreterData *));
+ if (shared->data == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+
+ for (Py_ssize_t i = 0; i < shared->len; i++) {
+ _PyCrossInterpreterData *data = _PyCrossInterpreterData_New();
+ if (data == NULL) {
+ goto error; // PyErr_NoMemory already set
+ }
+ PyObject *item = PyTuple_GET_ITEM(obj, i);
+
+ int res = -1;
+ if (!_Py_EnterRecursiveCallTstate(tstate, " while sharing a tuple")) {
+ res = _PyObject_GetCrossInterpreterData(item, data);
+ _Py_LeaveRecursiveCallTstate(tstate);
+ }
+ if (res < 0) {
+ PyMem_RawFree(data);
+ goto error;
+ }
+ shared->data[i] = data;
+ }
+ _PyCrossInterpreterData_Init(
+ data, tstate->interp, shared, obj, _new_tuple_object);
+ data->free = _tuple_shared_free;
+ return 0;
+
+error:
+ _tuple_shared_free(shared);
+ return -1;
+}
+
+// registration
+
+static void
+_register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry)
+{
+ // None
+ if (_xidregistry_add_type(xidregistry, (PyTypeObject *)PyObject_Type(Py_None), _none_shared) != 0) {
+ Py_FatalError("could not register None for cross-interpreter sharing");
+ }
+
+ // int
+ if (_xidregistry_add_type(xidregistry, &PyLong_Type, _long_shared) != 0) {
+ Py_FatalError("could not register int for cross-interpreter sharing");
+ }
+
+ // bytes
+ if (_xidregistry_add_type(xidregistry, &PyBytes_Type, _bytes_shared) != 0) {
+ Py_FatalError("could not register bytes for cross-interpreter sharing");
+ }
+
+ // str
+ if (_xidregistry_add_type(xidregistry, &PyUnicode_Type, _str_shared) != 0) {
+ Py_FatalError("could not register str for cross-interpreter sharing");
+ }
+
+ // bool
+ if (_xidregistry_add_type(xidregistry, &PyBool_Type, _bool_shared) != 0) {
+ Py_FatalError("could not register bool for cross-interpreter sharing");
+ }
+
+ // float
+ if (_xidregistry_add_type(xidregistry, &PyFloat_Type, _float_shared) != 0) {
+ Py_FatalError("could not register float for cross-interpreter sharing");
+ }
+
+ // tuple
+ if (_xidregistry_add_type(xidregistry, &PyTuple_Type, _tuple_shared) != 0) {
+ Py_FatalError("could not register tuple for cross-interpreter sharing");
+ }
+}
--- /dev/null
+
+/* InterpreterError extends Exception */
+
+static PyTypeObject _PyExc_InterpreterError = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "interpreters.InterpreterError",
+ .tp_doc = PyDoc_STR("A cross-interpreter operation failed"),
+ //.tp_base = (PyTypeObject *)PyExc_BaseException,
+};
+PyObject *PyExc_InterpreterError = (PyObject *)&_PyExc_InterpreterError;
+
+/* InterpreterNotFoundError extends InterpreterError */
+
+static PyTypeObject _PyExc_InterpreterNotFoundError = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "interpreters.InterpreterNotFoundError",
+ .tp_doc = PyDoc_STR("An interpreter was not found"),
+ .tp_base = &_PyExc_InterpreterError,
+};
+PyObject *PyExc_InterpreterNotFoundError = (PyObject *)&_PyExc_InterpreterNotFoundError;
+
+/* NotShareableError extends ValueError */
+
+static int
+_init_not_shareable_error_type(PyInterpreterState *interp)
+{
+ const char *name = "interpreters.NotShareableError";
+ PyObject *base = PyExc_ValueError;
+ PyObject *ns = NULL;
+ PyObject *exctype = PyErr_NewException(name, base, ns);
+ if (exctype == NULL) {
+ return -1;
+ }
+
+ _PyInterpreterState_GetXIState(interp)->PyExc_NotShareableError = exctype;
+ return 0;
+}
+
+static void
+_fini_not_shareable_error_type(PyInterpreterState *interp)
+{
+ Py_CLEAR(_PyInterpreterState_GetXIState(interp)->PyExc_NotShareableError);
+}
+
+static PyObject *
+_get_not_shareable_error_type(PyInterpreterState *interp)
+{
+ assert(_PyInterpreterState_GetXIState(interp)->PyExc_NotShareableError != NULL);
+ return _PyInterpreterState_GetXIState(interp)->PyExc_NotShareableError;
+}
+
+
+/* lifecycle */
+
+static int
+init_exceptions(PyInterpreterState *interp)
+{
+ // builtin static types
+ _PyExc_InterpreterError.tp_base = (PyTypeObject *)PyExc_BaseException;
+ if (_PyStaticType_InitBuiltin(interp, &_PyExc_InterpreterError) < 0) {
+ return -1;
+ }
+ if (_PyStaticType_InitBuiltin(interp, &_PyExc_InterpreterNotFoundError) < 0) {
+ return -1;
+ }
+
+ // heap types
+ // We would call _init_not_shareable_error_type() here too,
+ // but that leads to ref leaks
+
+ return 0;
+}
+
+static void
+fini_exceptions(PyInterpreterState *interp)
+{
+ // Likewise with _fini_not_shareable_error_type().
+ _PyStaticType_Dealloc(interp, &_PyExc_InterpreterNotFoundError);
+ _PyStaticType_Dealloc(interp, &_PyExc_InterpreterError);
+}
Objects/exceptions.c - PyExc_BytesWarning -
Objects/exceptions.c - PyExc_ResourceWarning -
Objects/exceptions.c - PyExc_EncodingWarning -
-Python/crossinterp.c - _PyExc_InterpreterError -
-Python/crossinterp.c - _PyExc_InterpreterNotFoundError -
-Python/crossinterp.c - PyExc_InterpreterError -
-Python/crossinterp.c - PyExc_InterpreterNotFoundError -
+Python/crossinterp_exceptions.h - _PyExc_InterpreterError -
+Python/crossinterp_exceptions.h - _PyExc_InterpreterNotFoundError -
+Python/crossinterp_exceptions.h - PyExc_InterpreterError -
+Python/crossinterp_exceptions.h - PyExc_InterpreterNotFoundError -
##-----------------------
## singletons