Skip to content

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
be93a28
Add the _interpreters module to the stdlib.
ericsnowcurrently Dec 14, 2016
6f7a120
Add create() and destroy().
ericsnowcurrently Dec 29, 2016
941efdd
Finish nearly all the create/destroy tests.
ericsnowcurrently Jan 1, 2017
95e7342
Add run_string().
ericsnowcurrently Dec 29, 2016
92aeaac
Get tricky tests working.
ericsnowcurrently Jan 2, 2017
b6e2f3a
Add a test for a still running interpreter when main exits.
ericsnowcurrently Jan 2, 2017
c15e79b
Add run_string_unrestricted().
ericsnowcurrently Jan 4, 2017
8d809c7
Exit out of the child process.
ericsnowcurrently Jan 4, 2017
55bb53c
Resolve several TODOs.
ericsnowcurrently Jan 4, 2017
cad0a91
Set up the execution namespace before switching threads.
ericsnowcurrently Jan 4, 2017
1395c58
Run in a copy of __main__.
ericsnowcurrently Jan 4, 2017
e0e8176
Close stdin and stdout after the proc finishes.
ericsnowcurrently Jan 4, 2017
0eb4acc
Clean up a test.
ericsnowcurrently Jan 4, 2017
3562084
Chain exceptions during cleanup.
ericsnowcurrently Jan 4, 2017
8e7e724
Finish the module docs.
ericsnowcurrently Jan 4, 2017
f8173fb
Fix docs.
ericsnowcurrently May 23, 2017
84cea38
Fix includes.
ericsnowcurrently Dec 5, 2017
48a1a38
Add _interpreters.is_shareable().
ericsnowcurrently Nov 28, 2017
bbdadb0
Add _PyObject_CheckShareable().
ericsnowcurrently Dec 4, 2017
9a75fd2
Add _PyCrossInterpreterData.
ericsnowcurrently Dec 4, 2017
f38b138
Use the shared data in run() safely.
ericsnowcurrently Dec 7, 2017
0055e80
Do not use a copy of the __main__ ns.
ericsnowcurrently Dec 7, 2017
9030634
Never return the execution namespace.
ericsnowcurrently Dec 8, 2017
df53c6c
Group sharing-related code.
ericsnowcurrently Dec 8, 2017
b27597f
Fix a refcount.
ericsnowcurrently Dec 8, 2017
b69a643
Add get_current() and enumerate().
ericsnowcurrently Dec 29, 2016
dc21da6
Add is_running().
ericsnowcurrently Dec 29, 2016
a4dad08
Add get_main().
ericsnowcurrently Jan 4, 2017
bc818e5
Fix an INCREF placement.
ericsnowcurrently Dec 12, 2017
d80ffdb
Fix error propagation.
ericsnowcurrently Dec 12, 2017
f4b8819
enumerate -> list_all.
ericsnowcurrently Dec 12, 2017
c1b2a79
Correctly handle a failed _shared_exception allocation.
ericsnowcurrently Dec 12, 2017
406d1ec
Add _interpreters.RunFailedError.
ericsnowcurrently Dec 12, 2017
6a53db3
Return a simple RunFailedError.
ericsnowcurrently Dec 12, 2017
f1e91b0
(mostly) Fix the unit tests.
ericsnowcurrently Dec 22, 2017
e7c581a
Add tests for shareable objects.
ericsnowcurrently Jan 8, 2018
24095ab
Add None as shareable.
ericsnowcurrently Dec 14, 2017
7ebae2f
Add a registry for shareable types.
ericsnowcurrently Jan 11, 2018
6d562bb
Fix (and clean up) the cross-interpreter-data code.
ericsnowcurrently Jan 12, 2018
237f743
Add basic channel code.
ericsnowcurrently Jan 12, 2018
3b0ca21
Stub out the other channel-related module functions.
ericsnowcurrently Jan 13, 2018
064f710
Drop _cidclass.classname.
ericsnowcurrently Jan 13, 2018
703ac97
Drop some outdated TODO comments.
ericsnowcurrently Jan 13, 2018
e36e4d4
Add a mutex for the registry.
ericsnowcurrently Jan 13, 2018
b973cc4
Add _PyChannelState.mutex.
ericsnowcurrently Jan 13, 2018
6b9e457
Add a mutex for operating on the global list of channels.
ericsnowcurrently Jan 13, 2018
12efb00
Clean up the locking a little.
ericsnowcurrently Jan 13, 2018
310d0ac
Implement _interpreters.channel_list_all().
ericsnowcurrently Jan 13, 2018
8a24220
Implement _interpreters.channel_close().
ericsnowcurrently Jan 14, 2018
7c8ede9
Add channel-related exceptions.
ericsnowcurrently Jan 15, 2018
47d75de
Add _interpreters.ChannelID.
ericsnowcurrently Jan 16, 2018
153bbec
Solidify the "close" story.
ericsnowcurrently Jan 16, 2018
79d31cd
Drop channel_list_interpreters().
ericsnowcurrently Jan 20, 2018
3aad28b
Register ChannelID as a cross-intepreter type.
ericsnowcurrently Jan 20, 2018
db72825
_interpreters -> _xxsubinterpreters.
ericsnowcurrently Jan 26, 2018
6a0c0ff
Fix an un-initialized variable.
ericsnowcurrently Jan 26, 2018
9d50730
NULL-terminate kwlist for PyArg_ParseTupleAndKeywords().
ericsnowcurrently Jan 26, 2018
b31d130
Drop docs for now.
ericsnowcurrently Jan 26, 2018
91e7c8b
Do not check for NULL from PyThreadState_Get().
ericsnowcurrently Jan 26, 2018
7d2cfea
Format "else" statements properly.
ericsnowcurrently Jan 26, 2018
73dad33
Always use braces with if statements.
ericsnowcurrently Jan 26, 2018
ee7141c
Check for a NULL return from PyMem_NEW().
ericsnowcurrently Jan 26, 2018
8ec5137
Return the error.
ericsnowcurrently Jan 26, 2018
65d822e
Always check the result of PyDict_SetItemString().
ericsnowcurrently Jan 26, 2018
2df5aff
Add typedefs.
ericsnowcurrently Jan 26, 2018
9bb05cf
_shared_exception -> _sharedexception
ericsnowcurrently Jan 26, 2018
633a3d3
Free memory in the error case.
ericsnowcurrently Jan 26, 2018
74e7241
Whitelist instead of blacklist for richcompare ops.
ericsnowcurrently Jan 26, 2018
bb8619a
Decref in the error case.
ericsnowcurrently Jan 26, 2018
d785573
Add a Misc/NEWS entry.
ericsnowcurrently Jan 26, 2018
f856035
Do not try to print a bytes object.
ericsnowcurrently Jan 27, 2018
ba94571
Drop some outdated comments.
ericsnowcurrently Jan 27, 2018
9065001
Wrap long lines.
ericsnowcurrently Jan 27, 2018
2ebc68c
Fix some compiler warnings.
ericsnowcurrently Jan 27, 2018
48291af
Always use PyMem_NEW().
ericsnowcurrently Jan 27, 2018
39df95e
Do not try to allocate too much space.
ericsnowcurrently Jan 27, 2018
b07e928
Add Windows build support.
ericsnowcurrently Jan 27, 2018
1bfed0f
Drop Windows build support (for now).
ericsnowcurrently Jan 30, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions Include/internal/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Contributor

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 ;)

Copy link
Member Author

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.

// 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;
Copy link
Member

Choose a reason for hiding this comment

The 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
}

Copy link
Member Author

Choose a reason for hiding this comment

The 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 {
Expand All @@ -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);
Expand Down
Loading