Skip to content

Commit b4f3505

Browse files
gh-99741: Fix the Cross-Interpreter Data API (gh-99939)
There were some minor issues that showed up while I was working on porting _xxsubinterpreters to multi-phase init. This fixes them. #99741
1 parent 3c137dc commit b4f3505

File tree

3 files changed

+88
-25
lines changed

3 files changed

+88
-25
lines changed

Include/cpython/pystate.h

+1
Original file line numberDiff line numberDiff line change
@@ -403,4 +403,5 @@ PyAPI_FUNC(int) _PyObject_CheckCrossInterpreterData(PyObject *);
403403
typedef int (*crossinterpdatafunc)(PyObject *, _PyCrossInterpreterData *);
404404

405405
PyAPI_FUNC(int) _PyCrossInterpreterData_RegisterClass(PyTypeObject *, crossinterpdatafunc);
406+
PyAPI_FUNC(int) _PyCrossInterpreterData_UnregisterClass(PyTypeObject *);
406407
PyAPI_FUNC(crossinterpdatafunc) _PyCrossInterpreterData_Lookup(PyObject *);

Include/internal/pycore_interp.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,10 @@ extern void _PyInterpreterState_Clear(PyThreadState *tstate);
249249
struct _xidregitem;
250250

251251
struct _xidregitem {
252-
PyTypeObject *cls;
253-
crossinterpdatafunc getdata;
252+
struct _xidregitem *prev;
254253
struct _xidregitem *next;
254+
PyObject *cls; // weakref to a PyTypeObject
255+
crossinterpdatafunc getdata;
255256
};
256257

257258
PyAPI_FUNC(PyInterpreterState*) _PyInterpreterState_LookUpID(int64_t);

Python/pystate.c

+84-23
Original file line numberDiff line numberDiff line change
@@ -1878,8 +1878,9 @@ _release_xidata(void *arg)
18781878
_PyCrossInterpreterData *data = (_PyCrossInterpreterData *)arg;
18791879
if (data->free != NULL) {
18801880
data->free(data->data);
1881+
data->data = NULL;
18811882
}
1882-
Py_XDECREF(data->obj);
1883+
Py_CLEAR(data->obj);
18831884
}
18841885

18851886
static void
@@ -1899,6 +1900,8 @@ _call_in_interpreter(struct _gilstate_runtime_state *gilstate,
18991900
save_tstate = _PyThreadState_Swap(gilstate, tstate);
19001901
}
19011902

1903+
// XXX Once the GIL is per-interpreter, this should be called with the
1904+
// calling interpreter's GIL released and the target interpreter's held.
19021905
func(arg);
19031906

19041907
// Switch back.
@@ -1943,21 +1946,73 @@ _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *data)
19431946
crossinterpdatafunc. It would be simpler and more efficient. */
19441947

19451948
static int
1946-
_register_xidata(struct _xidregistry *xidregistry, PyTypeObject *cls,
1949+
_xidregistry_add_type(struct _xidregistry *xidregistry, PyTypeObject *cls,
19471950
crossinterpdatafunc getdata)
19481951
{
19491952
// Note that we effectively replace already registered classes
19501953
// rather than failing.
19511954
struct _xidregitem *newhead = PyMem_RawMalloc(sizeof(struct _xidregitem));
1952-
if (newhead == NULL)
1955+
if (newhead == NULL) {
19531956
return -1;
1954-
newhead->cls = cls;
1957+
}
1958+
// XXX Assign a callback to clear the entry from the registry?
1959+
newhead->cls = PyWeakref_NewRef((PyObject *)cls, NULL);
1960+
if (newhead->cls == NULL) {
1961+
PyMem_RawFree(newhead);
1962+
return -1;
1963+
}
19551964
newhead->getdata = getdata;
1965+
newhead->prev = NULL;
19561966
newhead->next = xidregistry->head;
1967+
if (newhead->next != NULL) {
1968+
newhead->next->prev = newhead;
1969+
}
19571970
xidregistry->head = newhead;
19581971
return 0;
19591972
}
19601973

1974+
static struct _xidregitem *
1975+
_xidregistry_remove_entry(struct _xidregistry *xidregistry,
1976+
struct _xidregitem *entry)
1977+
{
1978+
struct _xidregitem *next = entry->next;
1979+
if (entry->prev != NULL) {
1980+
assert(entry->prev->next == entry);
1981+
entry->prev->next = next;
1982+
}
1983+
else {
1984+
assert(xidregistry->head == entry);
1985+
xidregistry->head = next;
1986+
}
1987+
if (next != NULL) {
1988+
next->prev = entry->prev;
1989+
}
1990+
Py_DECREF(entry->cls);
1991+
PyMem_RawFree(entry);
1992+
return next;
1993+
}
1994+
1995+
static struct _xidregitem *
1996+
_xidregistry_find_type(struct _xidregistry *xidregistry, PyTypeObject *cls)
1997+
{
1998+
struct _xidregitem *cur = xidregistry->head;
1999+
while (cur != NULL) {
2000+
PyObject *registered = PyWeakref_GetObject(cur->cls);
2001+
if (registered == Py_None) {
2002+
// The weakly ref'ed object was freed.
2003+
cur = _xidregistry_remove_entry(xidregistry, cur);
2004+
}
2005+
else {
2006+
assert(PyType_Check(registered));
2007+
if (registered == (PyObject *)cls) {
2008+
return cur;
2009+
}
2010+
cur = cur->next;
2011+
}
2012+
}
2013+
return NULL;
2014+
}
2015+
19612016
static void _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry);
19622017

19632018
int
@@ -1973,19 +2028,32 @@ _PyCrossInterpreterData_RegisterClass(PyTypeObject *cls,
19732028
return -1;
19742029
}
19752030

1976-
// Make sure the class isn't ever deallocated.
1977-
Py_INCREF((PyObject *)cls);
1978-
19792031
struct _xidregistry *xidregistry = &_PyRuntime.xidregistry ;
19802032
PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
19812033
if (xidregistry->head == NULL) {
19822034
_register_builtins_for_crossinterpreter_data(xidregistry);
19832035
}
1984-
int res = _register_xidata(xidregistry, cls, getdata);
2036+
int res = _xidregistry_add_type(xidregistry, cls, getdata);
19852037
PyThread_release_lock(xidregistry->mutex);
19862038
return res;
19872039
}
19882040

2041+
int
2042+
_PyCrossInterpreterData_UnregisterClass(PyTypeObject *cls)
2043+
{
2044+
int res = 0;
2045+
struct _xidregistry *xidregistry = &_PyRuntime.xidregistry ;
2046+
PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
2047+
struct _xidregitem *matched = _xidregistry_find_type(xidregistry, cls);
2048+
if (matched != NULL) {
2049+
(void)_xidregistry_remove_entry(xidregistry, matched);
2050+
res = 1;
2051+
}
2052+
PyThread_release_lock(xidregistry->mutex);
2053+
return res;
2054+
}
2055+
2056+
19892057
/* Cross-interpreter objects are looked up by exact match on the class.
19902058
We can reassess this policy when we move from a global registry to a
19912059
tp_* slot. */
@@ -1995,22 +2063,15 @@ _PyCrossInterpreterData_Lookup(PyObject *obj)
19952063
{
19962064
struct _xidregistry *xidregistry = &_PyRuntime.xidregistry ;
19972065
PyObject *cls = PyObject_Type(obj);
1998-
crossinterpdatafunc getdata = NULL;
19992066
PyThread_acquire_lock(xidregistry->mutex, WAIT_LOCK);
2000-
struct _xidregitem *cur = xidregistry->head;
2001-
if (cur == NULL) {
2067+
if (xidregistry->head == NULL) {
20022068
_register_builtins_for_crossinterpreter_data(xidregistry);
2003-
cur = xidregistry->head;
2004-
}
2005-
for(; cur != NULL; cur = cur->next) {
2006-
if (cur->cls == (PyTypeObject *)cls) {
2007-
getdata = cur->getdata;
2008-
break;
2009-
}
20102069
}
2070+
struct _xidregitem *matched = _xidregistry_find_type(xidregistry,
2071+
(PyTypeObject *)cls);
20112072
Py_DECREF(cls);
20122073
PyThread_release_lock(xidregistry->mutex);
2013-
return getdata;
2074+
return matched != NULL ? matched->getdata : NULL;
20142075
}
20152076

20162077
/* cross-interpreter data for builtin types */
@@ -2116,22 +2177,22 @@ static void
21162177
_register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry)
21172178
{
21182179
// None
2119-
if (_register_xidata(xidregistry, (PyTypeObject *)PyObject_Type(Py_None), _none_shared) != 0) {
2180+
if (_xidregistry_add_type(xidregistry, (PyTypeObject *)PyObject_Type(Py_None), _none_shared) != 0) {
21202181
Py_FatalError("could not register None for cross-interpreter sharing");
21212182
}
21222183

21232184
// int
2124-
if (_register_xidata(xidregistry, &PyLong_Type, _long_shared) != 0) {
2185+
if (_xidregistry_add_type(xidregistry, &PyLong_Type, _long_shared) != 0) {
21252186
Py_FatalError("could not register int for cross-interpreter sharing");
21262187
}
21272188

21282189
// bytes
2129-
if (_register_xidata(xidregistry, &PyBytes_Type, _bytes_shared) != 0) {
2190+
if (_xidregistry_add_type(xidregistry, &PyBytes_Type, _bytes_shared) != 0) {
21302191
Py_FatalError("could not register bytes for cross-interpreter sharing");
21312192
}
21322193

21332194
// str
2134-
if (_register_xidata(xidregistry, &PyUnicode_Type, _str_shared) != 0) {
2195+
if (_xidregistry_add_type(xidregistry, &PyUnicode_Type, _str_shared) != 0) {
21352196
Py_FatalError("could not register str for cross-interpreter sharing");
21362197
}
21372198
}

0 commit comments

Comments
 (0)