mirror of https://github.com/python/cpython
Issue #17936: Fix O(n**2) behaviour when adding or removing many subclasses of a given type.
This commit is contained in:
parent
dc6b933d23
commit
84745ab464
|
@ -10,6 +10,9 @@ Projected release date: 2013-11-24
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #17936: Fix O(n**2) behaviour when adding or removing many subclasses
|
||||||
|
of a given type.
|
||||||
|
|
||||||
- Issue #19428: zipimport now handles errors when reading truncated or invalid
|
- Issue #19428: zipimport now handles errors when reading truncated or invalid
|
||||||
ZIP archive.
|
ZIP archive.
|
||||||
|
|
||||||
|
|
|
@ -101,16 +101,17 @@ PyType_Modified(PyTypeObject *type)
|
||||||
needed.
|
needed.
|
||||||
*/
|
*/
|
||||||
PyObject *raw, *ref;
|
PyObject *raw, *ref;
|
||||||
Py_ssize_t i, n;
|
Py_ssize_t i;
|
||||||
|
|
||||||
if (!PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
|
if (!PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
raw = type->tp_subclasses;
|
raw = type->tp_subclasses;
|
||||||
if (raw != NULL) {
|
if (raw != NULL) {
|
||||||
n = PyList_GET_SIZE(raw);
|
assert(PyDict_CheckExact(raw));
|
||||||
for (i = 0; i < n; i++) {
|
i = 0;
|
||||||
ref = PyList_GET_ITEM(raw, i);
|
while (PyDict_Next(raw, &i, NULL, &ref)) {
|
||||||
|
assert(PyWeakref_CheckRef(ref));
|
||||||
ref = PyWeakref_GET_OBJECT(ref);
|
ref = PyWeakref_GET_OBJECT(ref);
|
||||||
if (ref != Py_None) {
|
if (ref != Py_None) {
|
||||||
PyType_Modified((PyTypeObject *)ref);
|
PyType_Modified((PyTypeObject *)ref);
|
||||||
|
@ -435,6 +436,7 @@ static int mro_internal(PyTypeObject *);
|
||||||
static int compatible_for_assignment(PyTypeObject *, PyTypeObject *, char *);
|
static int compatible_for_assignment(PyTypeObject *, PyTypeObject *, char *);
|
||||||
static int add_subclass(PyTypeObject*, PyTypeObject*);
|
static int add_subclass(PyTypeObject*, PyTypeObject*);
|
||||||
static void remove_subclass(PyTypeObject *, PyTypeObject *);
|
static void remove_subclass(PyTypeObject *, PyTypeObject *);
|
||||||
|
static void remove_all_subclasses(PyTypeObject *type, PyObject *bases);
|
||||||
static void update_all_slots(PyTypeObject *);
|
static void update_all_slots(PyTypeObject *);
|
||||||
|
|
||||||
typedef int (*update_callback)(PyTypeObject *, void *);
|
typedef int (*update_callback)(PyTypeObject *, void *);
|
||||||
|
@ -448,15 +450,15 @@ mro_subclasses(PyTypeObject *type, PyObject* temp)
|
||||||
{
|
{
|
||||||
PyTypeObject *subclass;
|
PyTypeObject *subclass;
|
||||||
PyObject *ref, *subclasses, *old_mro;
|
PyObject *ref, *subclasses, *old_mro;
|
||||||
Py_ssize_t i, n;
|
Py_ssize_t i;
|
||||||
|
|
||||||
subclasses = type->tp_subclasses;
|
subclasses = type->tp_subclasses;
|
||||||
if (subclasses == NULL)
|
if (subclasses == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
assert(PyList_Check(subclasses));
|
assert(PyDict_CheckExact(subclasses));
|
||||||
n = PyList_GET_SIZE(subclasses);
|
i = 0;
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
ref = PyList_GET_ITEM(subclasses, i);
|
while (PyDict_Next(subclasses, &i, NULL, &ref)) {
|
||||||
assert(PyWeakref_CheckRef(ref));
|
assert(PyWeakref_CheckRef(ref));
|
||||||
subclass = (PyTypeObject *)PyWeakref_GET_OBJECT(ref);
|
subclass = (PyTypeObject *)PyWeakref_GET_OBJECT(ref);
|
||||||
assert(subclass != NULL);
|
assert(subclass != NULL);
|
||||||
|
@ -575,13 +577,7 @@ type_set_bases(PyTypeObject *type, PyObject *value, void *context)
|
||||||
/* for now, sod that: just remove from all old_bases,
|
/* for now, sod that: just remove from all old_bases,
|
||||||
add to all new_bases */
|
add to all new_bases */
|
||||||
|
|
||||||
for (i = PyTuple_GET_SIZE(old_bases) - 1; i >= 0; i--) {
|
remove_all_subclasses(type, old_bases);
|
||||||
ob = PyTuple_GET_ITEM(old_bases, i);
|
|
||||||
if (PyType_Check(ob)) {
|
|
||||||
remove_subclass(
|
|
||||||
(PyTypeObject*)ob, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = PyTuple_GET_SIZE(value) - 1; i >= 0; i--) {
|
for (i = PyTuple_GET_SIZE(value) - 1; i >= 0; i--) {
|
||||||
ob = PyTuple_GET_ITEM(value, i);
|
ob = PyTuple_GET_ITEM(value, i);
|
||||||
|
@ -2733,10 +2729,14 @@ static void
|
||||||
type_dealloc(PyTypeObject *type)
|
type_dealloc(PyTypeObject *type)
|
||||||
{
|
{
|
||||||
PyHeapTypeObject *et;
|
PyHeapTypeObject *et;
|
||||||
|
PyObject *tp, *val, *tb;
|
||||||
|
|
||||||
/* Assert this is a heap-allocated type object */
|
/* Assert this is a heap-allocated type object */
|
||||||
assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE);
|
assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE);
|
||||||
_PyObject_GC_UNTRACK(type);
|
_PyObject_GC_UNTRACK(type);
|
||||||
|
PyErr_Fetch(&tp, &val, &tb);
|
||||||
|
remove_all_subclasses(type, type->tp_bases);
|
||||||
|
PyErr_Restore(tp, val, tb);
|
||||||
PyObject_ClearWeakRefs((PyObject *)type);
|
PyObject_ClearWeakRefs((PyObject *)type);
|
||||||
et = (PyHeapTypeObject *)type;
|
et = (PyHeapTypeObject *)type;
|
||||||
Py_XDECREF(type->tp_base);
|
Py_XDECREF(type->tp_base);
|
||||||
|
@ -2761,7 +2761,7 @@ static PyObject *
|
||||||
type_subclasses(PyTypeObject *type, PyObject *args_ignored)
|
type_subclasses(PyTypeObject *type, PyObject *args_ignored)
|
||||||
{
|
{
|
||||||
PyObject *list, *raw, *ref;
|
PyObject *list, *raw, *ref;
|
||||||
Py_ssize_t i, n;
|
Py_ssize_t i;
|
||||||
|
|
||||||
list = PyList_New(0);
|
list = PyList_New(0);
|
||||||
if (list == NULL)
|
if (list == NULL)
|
||||||
|
@ -2769,10 +2769,9 @@ type_subclasses(PyTypeObject *type, PyObject *args_ignored)
|
||||||
raw = type->tp_subclasses;
|
raw = type->tp_subclasses;
|
||||||
if (raw == NULL)
|
if (raw == NULL)
|
||||||
return list;
|
return list;
|
||||||
assert(PyList_Check(raw));
|
assert(PyDict_CheckExact(raw));
|
||||||
n = PyList_GET_SIZE(raw);
|
i = 0;
|
||||||
for (i = 0; i < n; i++) {
|
while (PyDict_Next(raw, &i, NULL, &ref)) {
|
||||||
ref = PyList_GET_ITEM(raw, i);
|
|
||||||
assert(PyWeakref_CheckRef(ref));
|
assert(PyWeakref_CheckRef(ref));
|
||||||
ref = PyWeakref_GET_OBJECT(ref);
|
ref = PyWeakref_GET_OBJECT(ref);
|
||||||
if (ref != Py_None) {
|
if (ref != Py_None) {
|
||||||
|
@ -2961,8 +2960,8 @@ type_clear(PyTypeObject *type)
|
||||||
class's dict; the cycle will be broken that way.
|
class's dict; the cycle will be broken that way.
|
||||||
|
|
||||||
tp_subclasses:
|
tp_subclasses:
|
||||||
A list of weak references can't be part of a cycle; and
|
A dict of weak references can't be part of a cycle; and
|
||||||
lists have their own tp_clear.
|
dicts have their own tp_clear.
|
||||||
|
|
||||||
slots (in PyHeapTypeObject):
|
slots (in PyHeapTypeObject):
|
||||||
A tuple of strings can't be part of a cycle.
|
A tuple of strings can't be part of a cycle.
|
||||||
|
@ -4353,51 +4352,57 @@ PyType_Ready(PyTypeObject *type)
|
||||||
static int
|
static int
|
||||||
add_subclass(PyTypeObject *base, PyTypeObject *type)
|
add_subclass(PyTypeObject *base, PyTypeObject *type)
|
||||||
{
|
{
|
||||||
Py_ssize_t i;
|
int result = -1;
|
||||||
int result;
|
PyObject *dict, *key, *newobj;
|
||||||
PyObject *list, *ref, *newobj;
|
|
||||||
|
|
||||||
list = base->tp_subclasses;
|
dict = base->tp_subclasses;
|
||||||
if (list == NULL) {
|
if (dict == NULL) {
|
||||||
base->tp_subclasses = list = PyList_New(0);
|
base->tp_subclasses = dict = PyDict_New();
|
||||||
if (list == NULL)
|
if (dict == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
assert(PyList_Check(list));
|
assert(PyDict_CheckExact(dict));
|
||||||
newobj = PyWeakref_NewRef((PyObject *)type, NULL);
|
key = PyLong_FromVoidPtr((void *) type);
|
||||||
if (newobj == NULL)
|
if (key == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
i = PyList_GET_SIZE(list);
|
newobj = PyWeakref_NewRef((PyObject *)type, NULL);
|
||||||
while (--i >= 0) {
|
if (newobj != NULL) {
|
||||||
ref = PyList_GET_ITEM(list, i);
|
result = PyDict_SetItem(dict, key, newobj);
|
||||||
assert(PyWeakref_CheckRef(ref));
|
Py_DECREF(newobj);
|
||||||
if (PyWeakref_GET_OBJECT(ref) == Py_None)
|
|
||||||
return PyList_SetItem(list, i, newobj);
|
|
||||||
}
|
}
|
||||||
result = PyList_Append(list, newobj);
|
Py_DECREF(key);
|
||||||
Py_DECREF(newobj);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
remove_subclass(PyTypeObject *base, PyTypeObject *type)
|
remove_subclass(PyTypeObject *base, PyTypeObject *type)
|
||||||
{
|
{
|
||||||
Py_ssize_t i;
|
PyObject *dict, *key;
|
||||||
PyObject *list, *ref;
|
|
||||||
|
|
||||||
list = base->tp_subclasses;
|
dict = base->tp_subclasses;
|
||||||
if (list == NULL) {
|
if (dict == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(PyList_Check(list));
|
assert(PyDict_CheckExact(dict));
|
||||||
i = PyList_GET_SIZE(list);
|
key = PyLong_FromVoidPtr((void *) type);
|
||||||
while (--i >= 0) {
|
if (key == NULL || PyDict_DelItem(dict, key)) {
|
||||||
ref = PyList_GET_ITEM(list, i);
|
/* This can happen if the type initialization errored out before
|
||||||
assert(PyWeakref_CheckRef(ref));
|
the base subclasses were updated (e.g. a non-str __qualname__
|
||||||
if (PyWeakref_GET_OBJECT(ref) == (PyObject*)type) {
|
was passed in the type dict). */
|
||||||
/* this can't fail, right? */
|
PyErr_Clear();
|
||||||
PySequence_DelItem(list, i);
|
}
|
||||||
return;
|
Py_XDECREF(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
remove_all_subclasses(PyTypeObject *type, PyObject *bases)
|
||||||
|
{
|
||||||
|
if (bases) {
|
||||||
|
Py_ssize_t i;
|
||||||
|
for (i = 0; i < PyTuple_GET_SIZE(bases); i++) {
|
||||||
|
PyObject *base = PyTuple_GET_ITEM(bases, i);
|
||||||
|
if (PyType_Check(base))
|
||||||
|
remove_subclass((PyTypeObject*) base, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6173,15 +6178,14 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *name,
|
||||||
{
|
{
|
||||||
PyTypeObject *subclass;
|
PyTypeObject *subclass;
|
||||||
PyObject *ref, *subclasses, *dict;
|
PyObject *ref, *subclasses, *dict;
|
||||||
Py_ssize_t i, n;
|
Py_ssize_t i;
|
||||||
|
|
||||||
subclasses = type->tp_subclasses;
|
subclasses = type->tp_subclasses;
|
||||||
if (subclasses == NULL)
|
if (subclasses == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
assert(PyList_Check(subclasses));
|
assert(PyDict_CheckExact(subclasses));
|
||||||
n = PyList_GET_SIZE(subclasses);
|
i = 0;
|
||||||
for (i = 0; i < n; i++) {
|
while (PyDict_Next(subclasses, &i, NULL, &ref)) {
|
||||||
ref = PyList_GET_ITEM(subclasses, i);
|
|
||||||
assert(PyWeakref_CheckRef(ref));
|
assert(PyWeakref_CheckRef(ref));
|
||||||
subclass = (PyTypeObject *)PyWeakref_GET_OBJECT(ref);
|
subclass = (PyTypeObject *)PyWeakref_GET_OBJECT(ref);
|
||||||
assert(subclass != NULL);
|
assert(subclass != NULL);
|
||||||
|
|
Loading…
Reference in New Issue