summaryrefslogtreecommitdiffstats
path: root/Python/pystate.c
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2023-10-30 22:53:10 (GMT)
committerGitHub <noreply@github.com>2023-10-30 22:53:10 (GMT)
commitc6fe0869ab1d91525f88279f8567461082c0d3ce (patch)
treec7df8abe8b1948201be0b2e6cb4579cac026d907 /Python/pystate.c
parent7b153d14ef2e446986148ce7833a00bee26208f1 (diff)
downloadcpython-c6fe0869ab1d91525f88279f8567461082c0d3ce.zip
cpython-c6fe0869ab1d91525f88279f8567461082c0d3ce.tar.gz
cpython-c6fe0869ab1d91525f88279f8567461082c0d3ce.tar.bz2
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. :/
Diffstat (limited to 'Python/pystate.c')
-rw-r--r--Python/pystate.c595
1 files changed, 4 insertions, 591 deletions
diff --git a/Python/pystate.c b/Python/pystate.c
index c44a28c..d97a03c 100644
--- a/Python/pystate.c
+++ b/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 */
/*************/