Skip to content

bpo-40077: Convert _abc module to use PyType_FromSpec() #19202

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 8 commits into from
Mar 30, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Replace statically allocated types with heap allocated types by using
:c:func:`PyType_FromSpec`. Add a module state to store the _abc_data_typ Add
traverse, clear and free functions to the module.
101 changes: 73 additions & 28 deletions Modules/_abc.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,25 @@ _Py_IDENTIFIER(_abc_impl);
_Py_IDENTIFIER(__subclasscheck__);
_Py_IDENTIFIER(__subclasshook__);

typedef struct {
PyObject *_abc_data_type;
} _abcmodulestate;

/* A global counter that is incremented each time a class is
registered as a virtual subclass of anything. It forces the
negative cache to be cleared before its next use.
Note: this counter is private. Use `abc.get_cache_token()` for
external code. */
static unsigned long long abc_invalidation_counter = 0;
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 am not sure that the abc_invalidation_counter is okay to be shared as static from interpreter Isolation point of view.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move this into PyInterpreterState?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like it would be safe to put it in the module state. The purpose of the counter is to invalidate the module cache. Registering a class in module instance 1 should not invalidate module instance 2. What do you think?

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 agree, I 've updated the PR.


static inline _abcmodulestate*
get_abc_state(PyObject *module)
{
void *state = PyModule_GetState(module);
assert(state != NULL);
return (_abcmodulestate *)state;
}

/* This object stores internal state for ABCs.
Note that we can use normal sets for caches,
since they are never iterated over. */
Expand All @@ -41,10 +53,12 @@ typedef struct {
static void
abc_data_dealloc(_abc_data *self)
{
PyTypeObject *tp = Py_TYPE(self);
Py_XDECREF(self->_abc_registry);
Py_XDECREF(self->_abc_cache);
Py_XDECREF(self->_abc_negative_cache);
Py_TYPE(self)->tp_free(self);
tp->tp_free(self);
Py_DECREF(tp);
}

static PyObject *
Expand All @@ -65,24 +79,30 @@ abc_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyDoc_STRVAR(abc_data_doc,
"Internal state held by ABC machinery.");

static PyTypeObject _abc_data_type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_abc_data", /*tp_name*/
sizeof(_abc_data), /*tp_basicsize*/
.tp_dealloc = (destructor)abc_data_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_alloc = PyType_GenericAlloc,
.tp_new = abc_data_new,
static PyType_Slot _abc_data_type_spec_slots[] = {
{Py_tp_doc, (void *)abc_data_doc},
{Py_tp_dealloc, abc_data_dealloc},
{Py_tp_alloc, PyType_GenericAlloc},
{Py_tp_new, abc_data_new},
{0, 0}
};

static PyType_Spec _abc_data_type_spec = {
.name = "_abc._abc_data",
.basicsize = sizeof(_abc_data),
.flags = Py_TPFLAGS_DEFAULT,
.slots = _abc_data_type_spec_slots,
};

static _abc_data *
_get_impl(PyObject *self)
_get_impl(PyObject *module, PyObject *self)
{
_abcmodulestate *state = get_abc_state(module);
PyObject *impl = _PyObject_GetAttrId(self, &PyId__abc_impl);
if (impl == NULL) {
return NULL;
}
if (!Py_IS_TYPE(impl, &_abc_data_type)) {
if (!Py_IS_TYPE(impl, (PyTypeObject *)state->_abc_data_type)) {
PyErr_SetString(PyExc_TypeError, "_abc_impl is set to a wrong type");
Py_DECREF(impl);
return NULL;
Expand Down Expand Up @@ -179,7 +199,7 @@ static PyObject *
_abc__reset_registry(PyObject *module, PyObject *self)
/*[clinic end generated code: output=92d591a43566cc10 input=12a0b7eb339ac35c]*/
{
_abc_data *impl = _get_impl(self);
_abc_data *impl = _get_impl(module, self);
if (impl == NULL) {
return NULL;
}
Expand All @@ -206,7 +226,7 @@ static PyObject *
_abc__reset_caches(PyObject *module, PyObject *self)
/*[clinic end generated code: output=f296f0d5c513f80c input=c0ac616fd8acfb6f]*/
{
_abc_data *impl = _get_impl(self);
_abc_data *impl = _get_impl(module, self);
if (impl == NULL) {
return NULL;
}
Expand Down Expand Up @@ -241,7 +261,7 @@ static PyObject *
_abc__get_dump(PyObject *module, PyObject *self)
/*[clinic end generated code: output=9d9569a8e2c1c443 input=2c5deb1bfe9e3c79]*/
{
_abc_data *impl = _get_impl(self);
_abc_data *impl = _get_impl(module, self);
if (impl == NULL) {
return NULL;
}
Expand Down Expand Up @@ -391,13 +411,14 @@ static PyObject *
_abc__abc_init(PyObject *module, PyObject *self)
/*[clinic end generated code: output=594757375714cda1 input=8d7fe470ff77f029]*/
{
_abcmodulestate *state = get_abc_state(module);
PyObject *data;
if (compute_abstract_methods(self) < 0) {
return NULL;
}

/* Set up inheritance registry. */
data = abc_data_new(&_abc_data_type, NULL, NULL);
data = abc_data_new((PyTypeObject *)state->_abc_data_type, NULL, NULL);
if (data == NULL) {
return NULL;
}
Expand Down Expand Up @@ -446,7 +467,7 @@ _abc__abc_register_impl(PyObject *module, PyObject *self, PyObject *subclass)
if (result < 0) {
return NULL;
}
_abc_data *impl = _get_impl(self);
_abc_data *impl = _get_impl(module, self);
if (impl == NULL) {
return NULL;
}
Expand Down Expand Up @@ -480,7 +501,7 @@ _abc__abc_instancecheck_impl(PyObject *module, PyObject *self,
/*[clinic end generated code: output=b8b5148f63b6b56f input=a4f4525679261084]*/
{
PyObject *subtype, *result = NULL, *subclass = NULL;
_abc_data *impl = _get_impl(self);
_abc_data *impl = _get_impl(module, self);
if (impl == NULL) {
return NULL;
}
Expand Down Expand Up @@ -576,7 +597,7 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
PyObject *ok, *subclasses = NULL, *result = NULL;
Py_ssize_t pos;
int incache;
_abc_data *impl = _get_impl(self);
_abc_data *impl = _get_impl(module, self);
if (impl == NULL) {
return NULL;
}
Expand Down Expand Up @@ -808,30 +829,54 @@ static struct PyMethodDef module_functions[] = {
};

static int
_abc_exec(PyObject *module)
_abcmodule_exec(PyObject *module)
{
if (PyType_Ready(&_abc_data_type) < 0) {
_abcmodulestate *state = get_abc_state(module);
state->_abc_data_type = PyType_FromSpec(&_abc_data_type_spec);
if (state->_abc_data_type == NULL) {
return -1;
}
_abc_data_type.tp_doc = abc_data_doc;

return 0;
}

static int
_abcmodule_traverse(PyObject *module, visitproc visit, void *arg)
{
_abcmodulestate *state = get_abc_state(module);
Py_VISIT(state->_abc_data_type);
return 0;
}

static PyModuleDef_Slot _abc_slots[] = {
{Py_mod_exec, _abc_exec},
static int
_abcmodule_clear(PyObject *module)
{
_abcmodulestate *state = get_abc_state(module);
Py_CLEAR(state->_abc_data_type);
return 0;
}

static void
_abcmodule_free(void *module)
{
_abcmodule_clear((PyObject *)module);
}

static PyModuleDef_Slot _abcmodule_slots[] = {
{Py_mod_exec, _abcmodule_exec},
{0, NULL}
};

static struct PyModuleDef _abcmodule = {
PyModuleDef_HEAD_INIT,
"_abc",
_abc__doc__,
0,
sizeof(_abcmodulestate),
module_functions,
_abc_slots,
NULL,
NULL,
NULL
_abcmodule_slots,
_abcmodule_traverse,
_abcmodule_clear,
_abcmodule_free,
};

PyMODINIT_FUNC
Expand Down