mirror of
https://github.com/python/cpython.git
synced 2025-08-30 13:38:43 +00:00
gh-76785: Move the Cross-Interpreter Code to Its Own File (gh-111502)
This is partly to clear this stuff out of pystate.c, but also in preparation for moving some code out of _xxsubinterpretersmodule.c. This change also moves this stuff to the internal API (new: Include/internal/pycore_crossinterp.h). @vstinner did this previously and I undid it. Now I'm re-doing it. :/
This commit is contained in:
parent
7b153d14ef
commit
c6fe0869ab
18 changed files with 849 additions and 760 deletions
627
Python/crossinterp.c
Normal file
627
Python/crossinterp.c
Normal file
|
@ -0,0 +1,627 @@
|
|||
|
||||
/* API for managing interactions between isolated interpreters */
|
||||
|
||||
#include "Python.h"
|
||||
#include "pycore_ceval.h" // _Py_simple_func
|
||||
#include "pycore_crossinterp.h" // struct _xid
|
||||
#include "pycore_pyerrors.h" // _PyErr_Clear()
|
||||
#include "pycore_pystate.h" // _PyInterpreterState_GET()
|
||||
#include "pycore_weakref.h" // _PyWeakref_GET_REF()
|
||||
|
||||
|
||||
/***************************/
|
||||
/* cross-interpreter calls */
|
||||
/***************************/
|
||||
|
||||
int
|
||||
_Py_CallInInterpreter(PyInterpreterState *interp,
|
||||
_Py_simple_func func, void *arg)
|
||||
{
|
||||
if (interp == _PyThreadState_GetCurrent()->interp) {
|
||||
return func(arg);
|
||||
}
|
||||
// XXX Emit a warning if this fails?
|
||||
_PyEval_AddPendingCall(interp, (_Py_pending_call_func)func, arg, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_Py_CallInInterpreterAndRawFree(PyInterpreterState *interp,
|
||||
_Py_simple_func func, void *arg)
|
||||
{
|
||||
if (interp == _PyThreadState_GetCurrent()->interp) {
|
||||
int res = func(arg);
|
||||
PyMem_RawFree(arg);
|
||||
return res;
|
||||
}
|
||||
// XXX Emit a warning if this fails?
|
||||
_PyEval_AddPendingCall(interp, func, arg, _Py_PENDING_RAWFREE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**************************/
|
||||
/* cross-interpreter data */
|
||||
/**************************/
|
||||
|
||||
_PyCrossInterpreterData *
|
||||
_PyCrossInterpreterData_New(void)
|
||||
{
|
||||
_PyCrossInterpreterData *xid = PyMem_RawMalloc(
|
||||
sizeof(_PyCrossInterpreterData));
|
||||
if (xid == NULL) {
|
||||
PyErr_NoMemory();
|
||||
}
|
||||
return xid;
|
||||
}
|
||||
|
||||
void
|
||||
_PyCrossInterpreterData_Free(_PyCrossInterpreterData *xid)
|
||||
{
|
||||
PyInterpreterState *interp = PyInterpreterState_Get();
|
||||
_PyCrossInterpreterData_Clear(interp, xid);
|
||||
PyMem_RawFree(xid);
|
||||
}
|
||||
|
||||
|
||||
/* defining cross-interpreter data */
|
||||
|
||||
static inline void
|
||||
_xidata_init(_PyCrossInterpreterData *data)
|
||||
{
|
||||
// If the value is being reused
|
||||
// then _xidata_clear() should have been called already.
|
||||
assert(data->data == NULL);
|
||||
assert(data->obj == NULL);
|
||||
*data = (_PyCrossInterpreterData){0};
|
||||
data->interpid = -1;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_xidata_clear(_PyCrossInterpreterData *data)
|
||||
{
|
||||
// _PyCrossInterpreterData only has two members that need to be
|
||||
// cleaned up, if set: "data" must be freed and "obj" must be decref'ed.
|
||||
// In both cases the original (owning) interpreter must be used,
|
||||
// which is the caller's responsibility to ensure.
|
||||
if (data->data != NULL) {
|
||||
if (data->free != NULL) {
|
||||
data->free(data->data);
|
||||
}
|
||||
data->data = NULL;
|
||||
}
|
||||
Py_CLEAR(data->obj);
|
||||
}
|
||||
|
||||
void
|
||||
_PyCrossInterpreterData_Init(_PyCrossInterpreterData *data,
|
||||
PyInterpreterState *interp,
|
||||
void *shared, PyObject *obj,
|
||||
xid_newobjectfunc new_object)
|
||||
{
|
||||
assert(data != NULL);
|
||||
assert(new_object != NULL);
|
||||
_xidata_init(data);
|
||||
data->data = shared;
|
||||
if (obj != NULL) {
|
||||
assert(interp != NULL);
|
||||
// released in _PyCrossInterpreterData_Clear()
|
||||
data->obj = Py_NewRef(obj);
|
||||
}
|
||||
// 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;
|
||||
data->new_object = new_object;
|
||||
}
|
||||
|
||||
int
|
||||
_PyCrossInterpreterData_InitWithSize(_PyCrossInterpreterData *data,
|
||||
PyInterpreterState *interp,
|
||||
const size_t size, PyObject *obj,
|
||||
xid_newobjectfunc new_object)
|
||||
{
|
||||
assert(size > 0);
|
||||
// For now we always free the shared data in the same interpreter
|
||||
// where it was allocated, so the interpreter is required.
|
||||
assert(interp != NULL);
|
||||
_PyCrossInterpreterData_Init(data, interp, NULL, obj, new_object);
|
||||
data->data = PyMem_RawMalloc(size);
|
||||
if (data->data == NULL) {
|
||||
return -1;
|
||||
}
|
||||
data->free = PyMem_RawFree;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
_PyCrossInterpreterData_Clear(PyInterpreterState *interp,
|
||||
_PyCrossInterpreterData *data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
// This must be called in the owning interpreter.
|
||||
assert(interp == NULL
|
||||
|| data->interpid == -1
|
||||
|| data->interpid == interp->id);
|
||||
_xidata_clear(data);
|
||||
}
|
||||
|
||||
|
||||
/* using cross-interpreter data */
|
||||
|
||||
static int
|
||||
_check_xidata(PyThreadState *tstate, _PyCrossInterpreterData *data)
|
||||
{
|
||||
// data->data can be anything, including NULL, so we don't check it.
|
||||
|
||||
// data->obj may be NULL, so we don't check it.
|
||||
|
||||
if (data->interpid < 0) {
|
||||
_PyErr_SetString(tstate, PyExc_SystemError, "missing interp");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data->new_object == NULL) {
|
||||
_PyErr_SetString(tstate, PyExc_SystemError, "missing new_object func");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// data->free may be NULL, so we don't check it.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
crossinterpdatafunc _PyCrossInterpreterData_Lookup(PyObject *);
|
||||
|
||||
/* This is a separate func from _PyCrossInterpreterData_Lookup in order
|
||||
to keep the registry code separate. */
|
||||
static crossinterpdatafunc
|
||||
_lookup_getdata(PyObject *obj)
|
||||
{
|
||||
crossinterpdatafunc getdata = _PyCrossInterpreterData_Lookup(obj);
|
||||
if (getdata == NULL && PyErr_Occurred() == 0)
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"%S does not support cross-interpreter data", obj);
|
||||
return getdata;
|
||||
}
|
||||
|
||||
int
|
||||
_PyObject_CheckCrossInterpreterData(PyObject *obj)
|
||||
{
|
||||
crossinterpdatafunc getdata = _lookup_getdata(obj);
|
||||
if (getdata == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GetCurrent();
|
||||
#ifdef Py_DEBUG
|
||||
// The caller must hold the GIL
|
||||
_Py_EnsureTstateNotNULL(tstate);
|
||||
#endif
|
||||
PyInterpreterState *interp = tstate->interp;
|
||||
|
||||
// Reset data before re-populating.
|
||||
*data = (_PyCrossInterpreterData){0};
|
||||
data->interpid = -1;
|
||||
|
||||
// Call the "getdata" func for the object.
|
||||
Py_INCREF(obj);
|
||||
crossinterpdatafunc getdata = _lookup_getdata(obj);
|
||||
if (getdata == NULL) {
|
||||
Py_DECREF(obj);
|
||||
return -1;
|
||||
}
|
||||
int res = getdata(tstate, obj, data);
|
||||
Py_DECREF(obj);
|
||||
if (res != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Fill in the blanks and validate the result.
|
||||
data->interpid = interp->id;
|
||||
if (_check_xidata(tstate, data) != 0) {
|
||||
(void)_PyCrossInterpreterData_Release(data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *data)
|
||||
{
|
||||
return data->new_object(data);
|
||||
}
|
||||
|
||||
static int
|
||||
_call_clear_xidata(void *data)
|
||||
{
|
||||
_xidata_clear((_PyCrossInterpreterData *)data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_xidata_release(_PyCrossInterpreterData *data, int rawfree)
|
||||
{
|
||||
if ((data->data == NULL || data->free == NULL) && data->obj == NULL) {
|
||||
// Nothing to release!
|
||||
if (rawfree) {
|
||||
PyMem_RawFree(data);
|
||||
}
|
||||
else {
|
||||
data->data = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Switch to the original interpreter.
|
||||
PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interpid);
|
||||
if (interp == NULL) {
|
||||
// The interpreter was already destroyed.
|
||||
// This function shouldn't have been called.
|
||||
// XXX Someone leaked some memory...
|
||||
assert(PyErr_Occurred());
|
||||
if (rawfree) {
|
||||
PyMem_RawFree(data);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// "Release" the data and/or the object.
|
||||
if (rawfree) {
|
||||
return _Py_CallInInterpreterAndRawFree(interp, _call_clear_xidata, data);
|
||||
}
|
||||
else {
|
||||
return _Py_CallInInterpreter(interp, _call_clear_xidata, data);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
_PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
|
||||
{
|
||||
return _xidata_release(data, 0);
|
||||
}
|
||||
|
||||
int
|
||||
_PyCrossInterpreterData_ReleaseAndRawFree(_PyCrossInterpreterData *data)
|
||||
{
|
||||
return _xidata_release(data, 1);
|
||||
}
|
||||
|
||||
|
||||
/* 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 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;
|
||||
}
|
||||
|
||||
// This is used in pystate.c (for now).
|
||||
void
|
||||
_Py_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 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 inline struct _xidregistry *
|
||||
_get_xidregistry(PyInterpreterState *interp, PyTypeObject *cls)
|
||||
{
|
||||
struct _xidregistry *xidregistry = &interp->runtime->xidregistry;
|
||||
if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
|
||||
assert(interp->xidregistry.mutex == xidregistry->mutex);
|
||||
xidregistry = &interp->xidregistry;
|
||||
}
|
||||
return xidregistry;
|
||||
}
|
||||
|
||||
static void _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry);
|
||||
|
||||
static inline void
|
||||
_ensure_builtins_xid(PyInterpreterState *interp, struct _xidregistry *xidregistry)
|
||||
{
|
||||
if (xidregistry != &interp->xidregistry) {
|
||||
assert(xidregistry == &interp->runtime->xidregistry);
|
||||
if (xidregistry->head == NULL) {
|
||||
_register_builtins_for_crossinterpreter_data(xidregistry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(interp, cls);
|
||||
PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
|
||||
|
||||
_ensure_builtins_xid(interp, 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:
|
||||
PyThread_release_lock(xidregistry->mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
_PyCrossInterpreterData_UnregisterClass(PyTypeObject *cls)
|
||||
{
|
||||
int res = 0;
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
struct _xidregistry *xidregistry = _get_xidregistry(interp, cls);
|
||||
PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
PyThread_release_lock(xidregistry->mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* 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. */
|
||||
|
||||
crossinterpdatafunc
|
||||
_PyCrossInterpreterData_Lookup(PyObject *obj)
|
||||
{
|
||||
PyTypeObject *cls = Py_TYPE(obj);
|
||||
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
struct _xidregistry *xidregistry = _get_xidregistry(interp, cls);
|
||||
PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
|
||||
|
||||
_ensure_builtins_xid(interp, xidregistry);
|
||||
|
||||
struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
|
||||
crossinterpdatafunc func = matched != NULL ? matched->getdata : NULL;
|
||||
|
||||
PyThread_release_lock(xidregistry->mutex);
|
||||
return func;
|
||||
}
|
||||
|
||||
/* cross-interpreter data for builtin types */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
595
Python/pystate.c
595
Python/pystate.c
|
@ -16,7 +16,6 @@
|
|||
#include "pycore_pystate.h"
|
||||
#include "pycore_runtime_init.h" // _PyRuntimeState_INIT
|
||||
#include "pycore_sysmodule.h" // _PySys_Audit()
|
||||
#include "pycore_weakref.h" // _PyWeakref_GET_REF()
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
CAUTION
|
||||
|
@ -495,7 +494,8 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
|
|||
return _PyStatus_OK();
|
||||
}
|
||||
|
||||
static void _xidregistry_clear(struct _xidregistry *);
|
||||
// This is defined in crossinterp.c (for now).
|
||||
extern void _Py_xidregistry_clear(struct _xidregistry *);
|
||||
|
||||
void
|
||||
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
|
||||
|
@ -505,7 +505,7 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime)
|
|||
assert(runtime->object_state.interpreter_leaks == 0);
|
||||
#endif
|
||||
|
||||
_xidregistry_clear(&runtime->xidregistry);
|
||||
_Py_xidregistry_clear(&runtime->xidregistry);
|
||||
|
||||
if (gilstate_tss_initialized(runtime)) {
|
||||
gilstate_tss_fini(runtime);
|
||||
|
@ -948,7 +948,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
|
|||
Py_CLEAR(interp->sysdict);
|
||||
Py_CLEAR(interp->builtins);
|
||||
|
||||
_xidregistry_clear(&interp->xidregistry);
|
||||
_Py_xidregistry_clear(&interp->xidregistry);
|
||||
/* The lock is owned by the runtime, so we don't free it here. */
|
||||
interp->xidregistry.mutex = NULL;
|
||||
|
||||
|
@ -2415,593 +2415,6 @@ PyGILState_Release(PyGILState_STATE oldstate)
|
|||
}
|
||||
|
||||
|
||||
/**************************/
|
||||
/* cross-interpreter data */
|
||||
/**************************/
|
||||
|
||||
/* cross-interpreter data */
|
||||
|
||||
static inline void
|
||||
_xidata_init(_PyCrossInterpreterData *data)
|
||||
{
|
||||
// If the value is being reused
|
||||
// then _xidata_clear() should have been called already.
|
||||
assert(data->data == NULL);
|
||||
assert(data->obj == NULL);
|
||||
*data = (_PyCrossInterpreterData){0};
|
||||
data->interpid = -1;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_xidata_clear(_PyCrossInterpreterData *data)
|
||||
{
|
||||
// _PyCrossInterpreterData only has two members that need to be
|
||||
// cleaned up, if set: "data" must be freed and "obj" must be decref'ed.
|
||||
// In both cases the original (owning) interpreter must be used,
|
||||
// which is the caller's responsibility to ensure.
|
||||
if (data->data != NULL) {
|
||||
if (data->free != NULL) {
|
||||
data->free(data->data);
|
||||
}
|
||||
data->data = NULL;
|
||||
}
|
||||
Py_CLEAR(data->obj);
|
||||
}
|
||||
|
||||
void
|
||||
_PyCrossInterpreterData_Init(_PyCrossInterpreterData *data,
|
||||
PyInterpreterState *interp,
|
||||
void *shared, PyObject *obj,
|
||||
xid_newobjectfunc new_object)
|
||||
{
|
||||
assert(data != NULL);
|
||||
assert(new_object != NULL);
|
||||
_xidata_init(data);
|
||||
data->data = shared;
|
||||
if (obj != NULL) {
|
||||
assert(interp != NULL);
|
||||
// released in _PyCrossInterpreterData_Clear()
|
||||
data->obj = Py_NewRef(obj);
|
||||
}
|
||||
// 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;
|
||||
data->new_object = new_object;
|
||||
}
|
||||
|
||||
int
|
||||
_PyCrossInterpreterData_InitWithSize(_PyCrossInterpreterData *data,
|
||||
PyInterpreterState *interp,
|
||||
const size_t size, PyObject *obj,
|
||||
xid_newobjectfunc new_object)
|
||||
{
|
||||
assert(size > 0);
|
||||
// For now we always free the shared data in the same interpreter
|
||||
// where it was allocated, so the interpreter is required.
|
||||
assert(interp != NULL);
|
||||
_PyCrossInterpreterData_Init(data, interp, NULL, obj, new_object);
|
||||
data->data = PyMem_RawMalloc(size);
|
||||
if (data->data == NULL) {
|
||||
return -1;
|
||||
}
|
||||
data->free = PyMem_RawFree;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
_PyCrossInterpreterData_Clear(PyInterpreterState *interp,
|
||||
_PyCrossInterpreterData *data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
// This must be called in the owning interpreter.
|
||||
assert(interp == NULL || data->interpid == interp->id);
|
||||
_xidata_clear(data);
|
||||
}
|
||||
|
||||
static int
|
||||
_check_xidata(PyThreadState *tstate, _PyCrossInterpreterData *data)
|
||||
{
|
||||
// data->data can be anything, including NULL, so we don't check it.
|
||||
|
||||
// data->obj may be NULL, so we don't check it.
|
||||
|
||||
if (data->interpid < 0) {
|
||||
_PyErr_SetString(tstate, PyExc_SystemError, "missing interp");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data->new_object == NULL) {
|
||||
_PyErr_SetString(tstate, PyExc_SystemError, "missing new_object func");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// data->free may be NULL, so we don't check it.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
crossinterpdatafunc _PyCrossInterpreterData_Lookup(PyObject *);
|
||||
|
||||
/* This is a separate func from _PyCrossInterpreterData_Lookup in order
|
||||
to keep the registry code separate. */
|
||||
static crossinterpdatafunc
|
||||
_lookup_getdata(PyObject *obj)
|
||||
{
|
||||
crossinterpdatafunc getdata = _PyCrossInterpreterData_Lookup(obj);
|
||||
if (getdata == NULL && PyErr_Occurred() == 0)
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"%S does not support cross-interpreter data", obj);
|
||||
return getdata;
|
||||
}
|
||||
|
||||
int
|
||||
_PyObject_CheckCrossInterpreterData(PyObject *obj)
|
||||
{
|
||||
crossinterpdatafunc getdata = _lookup_getdata(obj);
|
||||
if (getdata == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data)
|
||||
{
|
||||
_PyRuntimeState *runtime = &_PyRuntime;
|
||||
PyThreadState *tstate = current_fast_get(runtime);
|
||||
#ifdef Py_DEBUG
|
||||
// The caller must hold the GIL
|
||||
_Py_EnsureTstateNotNULL(tstate);
|
||||
#endif
|
||||
PyInterpreterState *interp = tstate->interp;
|
||||
|
||||
// Reset data before re-populating.
|
||||
*data = (_PyCrossInterpreterData){0};
|
||||
data->interpid = -1;
|
||||
|
||||
// Call the "getdata" func for the object.
|
||||
Py_INCREF(obj);
|
||||
crossinterpdatafunc getdata = _lookup_getdata(obj);
|
||||
if (getdata == NULL) {
|
||||
Py_DECREF(obj);
|
||||
return -1;
|
||||
}
|
||||
int res = getdata(tstate, obj, data);
|
||||
Py_DECREF(obj);
|
||||
if (res != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Fill in the blanks and validate the result.
|
||||
data->interpid = interp->id;
|
||||
if (_check_xidata(tstate, data) != 0) {
|
||||
(void)_PyCrossInterpreterData_Release(data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *data)
|
||||
{
|
||||
return data->new_object(data);
|
||||
}
|
||||
|
||||
int
|
||||
_Py_CallInInterpreter(PyInterpreterState *interp,
|
||||
_Py_simple_func func, void *arg)
|
||||
{
|
||||
if (interp == current_fast_get(interp->runtime)->interp) {
|
||||
return func(arg);
|
||||
}
|
||||
// XXX Emit a warning if this fails?
|
||||
_PyEval_AddPendingCall(interp, (_Py_pending_call_func)func, arg, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_Py_CallInInterpreterAndRawFree(PyInterpreterState *interp,
|
||||
_Py_simple_func func, void *arg)
|
||||
{
|
||||
if (interp == current_fast_get(interp->runtime)->interp) {
|
||||
int res = func(arg);
|
||||
PyMem_RawFree(arg);
|
||||
return res;
|
||||
}
|
||||
// XXX Emit a warning if this fails?
|
||||
_PyEval_AddPendingCall(interp, func, arg, _Py_PENDING_RAWFREE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_call_clear_xidata(void *data)
|
||||
{
|
||||
_xidata_clear((_PyCrossInterpreterData *)data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_xidata_release(_PyCrossInterpreterData *data, int rawfree)
|
||||
{
|
||||
if ((data->data == NULL || data->free == NULL) && data->obj == NULL) {
|
||||
// Nothing to release!
|
||||
if (rawfree) {
|
||||
PyMem_RawFree(data);
|
||||
}
|
||||
else {
|
||||
data->data = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Switch to the original interpreter.
|
||||
PyInterpreterState *interp = _PyInterpreterState_LookUpID(data->interpid);
|
||||
if (interp == NULL) {
|
||||
// The interpreter was already destroyed.
|
||||
// This function shouldn't have been called.
|
||||
// XXX Someone leaked some memory...
|
||||
assert(PyErr_Occurred());
|
||||
if (rawfree) {
|
||||
PyMem_RawFree(data);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// "Release" the data and/or the object.
|
||||
if (rawfree) {
|
||||
return _Py_CallInInterpreterAndRawFree(interp, _call_clear_xidata, data);
|
||||
}
|
||||
else {
|
||||
return _Py_CallInInterpreter(interp, _call_clear_xidata, data);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
_PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
|
||||
{
|
||||
return _xidata_release(data, 0);
|
||||
}
|
||||
|
||||
int
|
||||
_PyCrossInterpreterData_ReleaseAndRawFree(_PyCrossInterpreterData *data)
|
||||
{
|
||||
return _xidata_release(data, 1);
|
||||
}
|
||||
|
||||
/* 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 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 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 inline struct _xidregistry *
|
||||
_get_xidregistry(PyInterpreterState *interp, PyTypeObject *cls)
|
||||
{
|
||||
struct _xidregistry *xidregistry = &interp->runtime->xidregistry;
|
||||
if (cls->tp_flags & Py_TPFLAGS_HEAPTYPE) {
|
||||
assert(interp->xidregistry.mutex == xidregistry->mutex);
|
||||
xidregistry = &interp->xidregistry;
|
||||
}
|
||||
return xidregistry;
|
||||
}
|
||||
|
||||
static void _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry);
|
||||
|
||||
static inline void
|
||||
_ensure_builtins_xid(PyInterpreterState *interp, struct _xidregistry *xidregistry)
|
||||
{
|
||||
if (xidregistry != &interp->xidregistry) {
|
||||
assert(xidregistry == &interp->runtime->xidregistry);
|
||||
if (xidregistry->head == NULL) {
|
||||
_register_builtins_for_crossinterpreter_data(xidregistry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(interp, cls);
|
||||
PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
|
||||
|
||||
_ensure_builtins_xid(interp, 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:
|
||||
PyThread_release_lock(xidregistry->mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
_PyCrossInterpreterData_UnregisterClass(PyTypeObject *cls)
|
||||
{
|
||||
int res = 0;
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
struct _xidregistry *xidregistry = _get_xidregistry(interp, cls);
|
||||
PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
PyThread_release_lock(xidregistry->mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* 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. */
|
||||
|
||||
crossinterpdatafunc
|
||||
_PyCrossInterpreterData_Lookup(PyObject *obj)
|
||||
{
|
||||
PyTypeObject *cls = Py_TYPE(obj);
|
||||
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
struct _xidregistry *xidregistry = _get_xidregistry(interp, cls);
|
||||
PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
|
||||
|
||||
_ensure_builtins_xid(interp, xidregistry);
|
||||
|
||||
struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
|
||||
crossinterpdatafunc func = matched != NULL ? matched->getdata : NULL;
|
||||
|
||||
PyThread_release_lock(xidregistry->mutex);
|
||||
return func;
|
||||
}
|
||||
|
||||
/* cross-interpreter data for builtin types */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************/
|
||||
/* Other API */
|
||||
/*************/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue