mirror of https://github.com/python/cpython
gh-76785: Improved Subinterpreters Compatibility with 3.12 (gh-115424)
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.
This commit is contained in:
parent
206f73dc5f
commit
514b1c91b8
|
@ -8,6 +8,15 @@ extern "C" {
|
|||
# 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
|
||||
|
|
|
@ -87,6 +87,11 @@ struct _xid {
|
|||
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 */
|
||||
|
||||
|
@ -101,6 +106,25 @@ PyAPI_FUNC(int) _PyCrossInterpreterData_InitWithSize(
|
|||
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 */
|
||||
|
||||
|
@ -170,6 +194,8 @@ extern void _PyXI_Fini(PyInterpreterState *interp);
|
|||
extern PyStatus _PyXI_InitTypes(PyInterpreterState *interp);
|
||||
extern void _PyXI_FiniTypes(PyInterpreterState *interp);
|
||||
|
||||
#define _PyInterpreterState_GetXIState(interp) (&(interp)->xi)
|
||||
|
||||
|
||||
/***************************/
|
||||
/* short-term data sharing */
|
||||
|
|
|
@ -13,6 +13,13 @@ extern "C" {
|
|||
#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.
|
||||
|
|
|
@ -1671,6 +1671,14 @@ Modules/pwdmodule.o: $(srcdir)/Modules/pwdmodule.c $(srcdir)/Modules/posixmodule
|
|||
|
||||
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)"' \
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
#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);
|
||||
}
|
|
@ -17,6 +17,8 @@
|
|||
#include <sched.h> // sched_yield()
|
||||
#endif
|
||||
|
||||
#include "_interpreters_common.h"
|
||||
|
||||
|
||||
/*
|
||||
This module has the following process-global state:
|
||||
|
@ -80,7 +82,9 @@ channel's queue, which are safely managed via the _PyCrossInterpreterData_*()
|
|||
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) \
|
||||
|
@ -101,7 +105,7 @@ static int
|
|||
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.
|
||||
|
@ -167,7 +171,7 @@ _get_current_interp(void)
|
|||
static PyObject *
|
||||
_get_current_module(void)
|
||||
{
|
||||
PyObject *name = PyUnicode_FromString(MODULE_NAME);
|
||||
PyObject *name = PyUnicode_FromString(MODULE_NAME_STR);
|
||||
if (name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -217,7 +221,7 @@ add_new_exception(PyObject *mod, const char *name, PyObject *base)
|
|||
}
|
||||
|
||||
#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,
|
||||
|
@ -299,7 +303,7 @@ _get_current_module_state(void)
|
|||
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);
|
||||
|
@ -784,7 +788,7 @@ _channelqueue_clear_interpreter(_channelqueue *queue, int64_t interpid)
|
|||
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;
|
||||
}
|
||||
|
@ -2126,7 +2130,7 @@ static PyStructSequence_Field channel_info_fields[] = {
|
|||
};
|
||||
|
||||
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,
|
||||
|
@ -2474,10 +2478,11 @@ struct _channelid_xid {
|
|||
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;
|
||||
}
|
||||
|
@ -2530,7 +2535,8 @@ _channelid_shared(PyThreadState *tstate, PyObject *obj,
|
|||
{
|
||||
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;
|
||||
|
@ -2601,7 +2607,7 @@ static PyType_Slot channelid_typeslots[] = {
|
|||
};
|
||||
|
||||
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),
|
||||
|
@ -2680,7 +2686,7 @@ _channelend_shared(PyThreadState *tstate, PyObject *obj,
|
|||
if (res < 0) {
|
||||
return -1;
|
||||
}
|
||||
data->new_object = _channelend_from_xid;
|
||||
_PyCrossInterpreterData_SET_NEW_OBJECT(data, _channelend_from_xid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3379,7 +3385,7 @@ module_free(void *mod)
|
|||
|
||||
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,
|
||||
|
@ -3390,7 +3396,7 @@ static struct PyModuleDef moduledef = {
|
|||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit__xxinterpchannels(void)
|
||||
MODINIT_FUNC_NAME(void)
|
||||
{
|
||||
return PyModuleDef_Init(&moduledef);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,12 @@
|
|||
#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) \
|
||||
|
@ -64,7 +68,7 @@ _get_current_interp(void)
|
|||
static PyObject *
|
||||
_get_current_module(void)
|
||||
{
|
||||
PyObject *name = PyUnicode_FromString(MODULE_NAME);
|
||||
PyObject *name = PyUnicode_FromString(MODULE_NAME_STR);
|
||||
if (name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -602,7 +606,7 @@ _queue_clear_interpreter(_queue *queue, int64_t interpid)
|
|||
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;
|
||||
}
|
||||
|
@ -1062,7 +1066,7 @@ set_external_queue_type(PyObject *module, PyTypeObject *queue_type)
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1130,7 +1134,7 @@ _queueid_xid_free(void *data)
|
|||
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;
|
||||
|
@ -1140,7 +1144,7 @@ _queueobj_from_xid(_PyCrossInterpreterData *data)
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -1181,7 +1185,7 @@ _queueobj_shared(PyThreadState *tstate, PyObject *queueobj,
|
|||
_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;
|
||||
}
|
||||
|
||||
|
@ -1670,7 +1674,7 @@ module_free(void *mod)
|
|||
|
||||
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,
|
||||
|
@ -1681,7 +1685,7 @@ static struct PyModuleDef moduledef = {
|
|||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit__xxinterpqueues(void)
|
||||
MODINIT_FUNC_NAME(void)
|
||||
{
|
||||
return PyModuleDef_Init(&moduledef);
|
||||
}
|
||||
|
|
|
@ -19,8 +19,12 @@
|
|||
#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 *
|
||||
|
@ -125,7 +129,7 @@ get_interpid_obj(PyInterpreterState *interp)
|
|||
static PyObject *
|
||||
_get_current_module(void)
|
||||
{
|
||||
PyObject *name = PyUnicode_FromString(MODULE_NAME);
|
||||
PyObject *name = PyUnicode_FromString(MODULE_NAME_STR);
|
||||
if (name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -152,16 +156,16 @@ typedef struct {
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -209,7 +213,7 @@ static PyType_Slot XIBufferViewType_slots[] = {
|
|||
};
|
||||
|
||||
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),
|
||||
|
@ -267,8 +271,7 @@ register_memoryview_xid(PyObject *mod, PyTypeObject **p_state)
|
|||
*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.
|
||||
|
@ -303,7 +306,7 @@ _get_current_module_state(void)
|
|||
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);
|
||||
|
@ -372,9 +375,7 @@ check_code_object(PyCodeObject *code)
|
|||
}
|
||||
// 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) {
|
||||
|
@ -602,7 +603,7 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds)
|
|||
|
||||
// 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);
|
||||
|
@ -691,7 +692,7 @@ static PyObject *
|
|||
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;
|
||||
|
@ -856,18 +857,18 @@ interp_exec(PyObject *self, PyObject *args, PyObject *kwds)
|
|||
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) {
|
||||
|
@ -908,12 +909,12 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds)
|
|||
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;
|
||||
|
@ -934,7 +935,7 @@ PyDoc_STRVAR(run_string_doc,
|
|||
\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)
|
||||
|
@ -943,12 +944,12 @@ 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) {
|
||||
|
@ -972,7 +973,7 @@ Execute the body of the provided function in the identified interpreter.\n\
|
|||
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 *
|
||||
|
@ -1166,7 +1167,7 @@ module_free(void *mod)
|
|||
|
||||
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,
|
||||
|
@ -1177,7 +1178,7 @@ static struct PyModuleDef moduledef = {
|
|||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit__xxsubinterpreters(void)
|
||||
MODINIT_FUNC_NAME(void)
|
||||
{
|
||||
return PyModuleDef_Init(&moduledef);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#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()
|
||||
|
||||
|
@ -16,47 +15,12 @@
|
|||
/* 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"
|
||||
|
||||
|
||||
/***************************/
|
||||
|
@ -67,7 +31,7 @@ int
|
|||
_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?
|
||||
|
@ -79,7 +43,7 @@ int
|
|||
_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;
|
||||
|
@ -94,6 +58,20 @@ _Py_CallInInterpreterAndRawFree(PyInterpreterState *interp,
|
|||
/* 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)
|
||||
{
|
||||
|
@ -114,38 +92,6 @@ _PyCrossInterpreterData_Free(_PyCrossInterpreterData *xid)
|
|||
}
|
||||
|
||||
|
||||
/* 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
|
||||
|
@ -156,7 +102,7 @@ _xidata_init(_PyCrossInterpreterData *data)
|
|||
assert(data->data == NULL);
|
||||
assert(data->obj == NULL);
|
||||
*data = (_PyCrossInterpreterData){0};
|
||||
data->interpid = -1;
|
||||
_PyCrossInterpreterData_INTERPID(data) = -1;
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -193,7 +139,9 @@ _PyCrossInterpreterData_Init(_PyCrossInterpreterData *data,
|
|||
// 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;
|
||||
}
|
||||
|
||||
|
@ -223,8 +171,8 @@ _PyCrossInterpreterData_Clear(PyInterpreterState *interp,
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -238,13 +186,13 @@ _check_xidata(PyThreadState *tstate, _PyCrossInterpreterData *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;
|
||||
}
|
||||
|
||||
|
@ -253,25 +201,6 @@ _check_xidata(PyThreadState *tstate, _PyCrossInterpreterData *data)
|
|||
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)
|
||||
|
@ -295,8 +224,8 @@ _set_xid_lookup_failure(PyInterpreterState *interp,
|
|||
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);
|
||||
|
@ -309,20 +238,16 @@ _PyObject_CheckCrossInterpreterData(PyObject *obj)
|
|||
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()) {
|
||||
|
@ -337,7 +262,7 @@ _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data)
|
|||
}
|
||||
|
||||
// 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;
|
||||
|
@ -374,7 +299,8 @@ _xidata_release(_PyCrossInterpreterData *data, int rawfree)
|
|||
}
|
||||
|
||||
// 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.
|
||||
|
@ -408,538 +334,6 @@ _PyCrossInterpreterData_ReleaseAndRawFree(_PyCrossInterpreterData *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 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 */
|
||||
/*************************/
|
||||
|
@ -1023,6 +417,10 @@ error:
|
|||
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)
|
||||
|
@ -1031,7 +429,8 @@ _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;
|
||||
|
@ -1600,7 +999,7 @@ _sharednsitem_has_value(_PyXI_namespace_item *item, int64_t *p_interpid)
|
|||
return 0;
|
||||
}
|
||||
if (p_interpid != NULL) {
|
||||
*p_interpid = item->data->interpid;
|
||||
*p_interpid = _PyCrossInterpreterData_INTERPID(item->data);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@ -1874,7 +1273,7 @@ _PyXI_FreeNamespace(_PyXI_namespace *ns)
|
|||
return;
|
||||
}
|
||||
|
||||
if (interpid == PyInterpreterState_GetID(_PyInterpreterState_GET())) {
|
||||
if (interpid == PyInterpreterState_GetID(PyInterpreterState_Get())) {
|
||||
_sharedns_free(ns);
|
||||
}
|
||||
else {
|
||||
|
@ -2014,7 +1413,7 @@ _enter_session(_PyXI_session *session, PyInterpreterState *interp)
|
|||
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);
|
||||
|
@ -2073,7 +1472,7 @@ _propagate_not_shareable_error(_PyXI_session *session)
|
|||
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;
|
||||
|
@ -2245,18 +1644,12 @@ _PyXI_Exit(_PyXI_session *session)
|
|||
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();
|
||||
|
@ -2271,11 +1664,8 @@ _PyXI_Fini(PyInterpreterState *interp)
|
|||
// 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
|
||||
|
|
|
@ -0,0 +1,594 @@
|
|||
|
||||
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");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
|
||||
/* 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);
|
||||
}
|
|
@ -292,10 +292,10 @@ Objects/exceptions.c - PyExc_UnicodeWarning -
|
|||
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
|
||||
|
|
Can't render this file because it has a wrong number of fields in line 4.
|
Loading…
Reference in New Issue