-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
bpo-32604: Expose the subinterpreters C-API in a "private" stdlib module. #1748
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
be93a28
6f7a120
941efdd
95e7342
92aeaac
b6e2f3a
c15e79b
8d809c7
55bb53c
cad0a91
1395c58
e0e8176
0eb4acc
3562084
8e7e724
f8173fb
84cea38
48a1a38
bbdadb0
9a75fd2
f38b138
0055e80
9030634
df53c6c
b27597f
b69a643
dc21da6
a4dad08
bc818e5
d80ffdb
f4b8819
c1b2a79
406d1ec
6a53db3
f1e91b0
e7c581a
24095ab
7ebae2f
6d562bb
237f743
3b0ca21
064f710
703ac97
e36e4d4
b973cc4
6b9e457
12efb00
310d0ac
8a24220
7c8ede9
47d75de
153bbec
79d31cd
3aad28b
db72825
6a0c0ff
9d50730
b31d130
91e7c8b
7d2cfea
73dad33
ee7141c
8ec5137
65d822e
2df5aff
9bb05cf
633a3d3
74e7241
bb8619a
d785573
f856035
ba94571
9065001
2ebc68c
48291af
39df95e
b07e928
1bfed0f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,6 +65,79 @@ PyAPI_FUNC(_PyInitError) _PyPathConfig_Calculate( | |
PyAPI_FUNC(void) _PyPathConfig_Clear(_PyPathConfig *config); | ||
|
||
|
||
/* interpreter state */ | ||
|
||
PyAPI_FUNC(PyInterpreterState *) _PyInterpreterState_LookUpID(PY_INT64_T); | ||
|
||
|
||
/* cross-interpreter data */ | ||
|
||
struct _xid; | ||
|
||
// _PyCrossInterpreterData is similar to Py_buffer as an effectively | ||
// opaque struct that holds data outside the object machinery. This | ||
// is necessary to pass between interpreters in the same process. | ||
typedef struct _xid { | ||
// data is the cross-interpreter-safe derivation of a Python object | ||
// (see _PyObject_GetCrossInterpreterData). It will be NULL if the | ||
// new_object func (below) encodes the data. | ||
void *data; | ||
// obj is the Python object from which the data was derived. This | ||
// is non-NULL only if the data remains bound to the object in some | ||
// way, such that the object must be "released" (via a decref) when | ||
// the data is released. In that case it is automatically | ||
// incref'ed (to match the automatic decref when releaed). | ||
PyObject *obj; | ||
// interp is the ID of the owning interpreter of the original | ||
// object. It corresponds to the active interpreter when | ||
// _PyObject_GetCrossInterpreterData() was called. This should only | ||
// be set by the cross-interpreter machinery. | ||
// | ||
// We use the ID rather than the PyInterpreterState to avoid issues | ||
// with deleted interpreters. | ||
int64_t interp; | ||
// new_object is a function that returns a new object in the current | ||
// interpreter given the data. The resulting object (a new | ||
// reference) will be equivalent to the original object. This field | ||
// is required. | ||
PyObject *(*new_object)(struct _xid *); | ||
// free is called when the data is released. If it is NULL then | ||
// nothing will be done to free the data. For some types this is | ||
// okay (e.g. bytes) and for those types this field should be set | ||
// to NULL. However, for most the data was allocated just for | ||
// cross-interpreter use, so it must be freed when | ||
// _PyCrossInterpreterData_Release is called or the memory will | ||
// leak. In that case, at the very least this field should be set | ||
// to PyMem_RawFree (the default if not explicitly set to NULL). | ||
// The call will happen with the original interpreter activated. | ||
void (*free)(void *); | ||
} _PyCrossInterpreterData; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The actual layout of the struct should go into an "internal" header file: "Include/pystate.h": typedef struct _xid _PyCrossInterpreterData; "Include/internal/pystate.h": struct _xid {
void *data;
// etc
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't touched Include/pystate.h. If you are suggesting that I move the typedef (but not the layout) there, I don't see any benefit to that for 3.7. This is not a public API for now. |
||
|
||
typedef int (*crossinterpdatafunc)(PyObject *, _PyCrossInterpreterData *); | ||
PyAPI_FUNC(int) _PyObject_CheckCrossInterpreterData(PyObject *); | ||
|
||
PyAPI_FUNC(int) _PyObject_GetCrossInterpreterData(PyObject *, _PyCrossInterpreterData *); | ||
PyAPI_FUNC(PyObject *) _PyCrossInterpreterData_NewObject(_PyCrossInterpreterData *); | ||
PyAPI_FUNC(void) _PyCrossInterpreterData_Release(_PyCrossInterpreterData *); | ||
|
||
/* cross-interpreter data registry */ | ||
|
||
/* 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. */ | ||
|
||
PyAPI_FUNC(int) _PyCrossInterpreterData_Register_Class(PyTypeObject *, crossinterpdatafunc); | ||
PyAPI_FUNC(crossinterpdatafunc) _PyCrossInterpreterData_Lookup(PyObject *); | ||
|
||
struct _xidregitem; | ||
|
||
struct _xidregitem { | ||
PyTypeObject *cls; | ||
crossinterpdatafunc getdata; | ||
struct _xidregitem *next; | ||
}; | ||
|
||
|
||
/* Full Python runtime state */ | ||
|
||
typedef struct pyruntimestate { | ||
|
@@ -86,6 +159,11 @@ typedef struct pyruntimestate { | |
using a Python int. */ | ||
int64_t next_id; | ||
} interpreters; | ||
// XXX Remove this field once we have a tp_* slot. | ||
struct _xidregistry { | ||
PyThread_type_lock mutex; | ||
struct _xidregitem *head; | ||
} xidregistry; | ||
|
||
#define NEXITFUNCS 32 | ||
void (*exitfuncs[NEXITFUNCS])(void); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something we'll need to keep an eye on here is not re-using interpreter IDs within the life of a process (otherwise we'll still run into issues with deleted interpreters).
Not that I've ever worked on systems where we introduced bugs by adding a freelist for objects where we assumed IDs would never be re-used or anything ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We picked int64_t so that it was both really large and would overflow to negative. When the next ID is negative PyInterpreterState_New() raises RuntimError.