Skip to content

Commit 9712cfe

Browse files
committed
Fixed Memory Leak with deleting and adding values
1 parent 7843457 commit 9712cfe

File tree

2 files changed

+59
-16
lines changed

2 files changed

+59
-16
lines changed

multidict/_multidict.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,6 @@ multidict_popone(MultiDictObject *self, PyObject *const *args,
674674
if (md_pop_one(self, key, &ret_val) < 0) {
675675
return NULL;
676676
}
677-
678677
ASSERT_CONSISTENT(self, false);
679678
if (ret_val == NULL) {
680679
if (_default != NULL) {
@@ -720,6 +719,8 @@ multidict_pop(MultiDictObject *self, PyObject *const *args, Py_ssize_t nargs,
720719
return NULL;
721720
}
722721
} else {
722+
// Py_DECREF(key);
723+
// printf("%zu\n", ret_val->ob_refcnt);
723724
return ret_val;
724725
}
725726
}
@@ -755,6 +756,8 @@ multidict_popall(MultiDictObject *self, PyObject *const *args,
755756
return NULL;
756757
}
757758
} else {
759+
// printf("%zu", key->ob_refcnt);
760+
// printf("%zu", ret_val->ob_refcnt);
758761
return ret_val;
759762
}
760763
}

multidict/_multilib/hashtable.h

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ static inline int
9191
_md_check_consistency(MultiDictObject *md, bool update);
9292
static inline int
9393
_md_dump(MultiDictObject *md);
94+
static inline int
95+
_md_refdump(MultiDictObject *md);
9496

9597
#define ASSERT_CONSISTENT(md, update) assert(_md_check_consistency(md, update))
9698
#else
@@ -102,12 +104,12 @@ _str_cmp(PyObject *s1, PyObject *s2)
102104
{
103105
PyObject *ret = PyUnicode_RichCompare(s1, s2, Py_EQ);
104106
if (Py_IsTrue(ret)) {
105-
Py_DECREF(ret);
107+
Py_XDECREF(ret);
106108
return 1;
107109
} else if (ret == NULL) {
108110
return -1;
109111
} else {
110-
Py_DECREF(ret);
112+
Py_XDECREF(ret);
111113
return 0;
112114
}
113115
}
@@ -481,7 +483,7 @@ static inline int
481483
_md_add_for_upd(MultiDictObject *md, Py_hash_t hash, PyObject *identity,
482484
PyObject *key, PyObject *value)
483485
{
484-
Py_INCREF(identity);
486+
// Py_INCREF(identity);
485487
Py_INCREF(key);
486488
Py_INCREF(value);
487489
return _md_add_for_upd_steal_refs(md, hash, identity, key, value);
@@ -500,6 +502,7 @@ md_add(MultiDictObject *md, PyObject *key, PyObject *value)
500502
}
501503
int ret = _md_add_with_hash(md, hash, identity, key, value);
502504
ASSERT_CONSISTENT(md, false);
505+
Py_DECREF(key);
503506
Py_DECREF(identity);
504507
return ret;
505508
fail:
@@ -1010,16 +1013,15 @@ md_pop_one(MultiDictObject *md, PyObject *key, PyObject **ret)
10101013
if (_md_del_at(md, iter.slot, entry) < 0) {
10111014
goto fail;
10121015
}
1013-
Py_DECREF(identity);
10141016
*ret = value;
10151017
md->version = NEXT_VERSION(md->state);
1016-
ASSERT_CONSISTENT(md, false);
1018+
Py_DECREF(identity);
10171019
return 1;
10181020
} else if (tmp < 0) {
10191021
goto fail;
10201022
}
10211023
}
1022-
1024+
Py_DECREF(identity);
10231025
ASSERT_CONSISTENT(md, false);
10241026
return 0;
10251027
fail:
@@ -1084,7 +1086,7 @@ md_pop_all(MultiDictObject *md, PyObject *key, PyObject **ret)
10841086
}
10851087

10861088
*ret = lst;
1087-
Py_DECREF(identity);
1089+
Py_XDECREF(identity);
10881090
ASSERT_CONSISTENT(md, false);
10891091
return lst != NULL;
10901092
fail:
@@ -1877,19 +1879,19 @@ md_traverse(MultiDictObject *md, visitproc visit, void *arg)
18771879
static inline int
18781880
md_clear(MultiDictObject *md)
18791881
{
1880-
if (md->used == 0) {
1881-
return 0;
1882-
}
1882+
// Remove This because sometimes tweaked multidicts or memory leaks speap
1883+
// through the cracks. if (md->used == 0) {
1884+
// return 0;
1885+
// }
18831886
md->version = NEXT_VERSION(md->state);
18841887

18851888
entry_t *entries = htkeys_entries(md->keys);
18861889
for (Py_ssize_t pos = 0; pos < md->keys->nentries; pos++) {
18871890
entry_t *entry = entries + pos;
1888-
if (entry->identity != NULL) {
1889-
Py_CLEAR(entry->identity);
1890-
Py_CLEAR(entry->key);
1891-
Py_CLEAR(entry->value);
1892-
}
1891+
// Py_CLEAR has null checks of it's own making it easier to free.
1892+
Py_CLEAR(entry->identity);
1893+
Py_CLEAR(entry->key);
1894+
Py_CLEAR(entry->value);
18931895
}
18941896

18951897
md->used = 0;
@@ -1995,6 +1997,44 @@ _md_dump(MultiDictObject *md)
19951997
printf("\n");
19961998
return 1;
19971999
}
2000+
2001+
static inline int
2002+
_md_refdump(MultiDictObject *md)
2003+
{
2004+
htkeys_t *keys = md->keys;
2005+
printf("Refcounts Dump %p [%zd from %zd usable %zd nentries %zd]\n",
2006+
(void *)md,
2007+
md->used,
2008+
htkeys_nslots(keys),
2009+
keys->usable,
2010+
keys->nentries);
2011+
for (Py_ssize_t i = 0; i < htkeys_nslots(keys); i++) {
2012+
Py_ssize_t ix = htkeys_get_index(keys, i);
2013+
printf(" %zd -> %zd\n", i, ix);
2014+
}
2015+
printf(" --------\n");
2016+
entry_t *entries = htkeys_entries(keys);
2017+
for (Py_ssize_t i = 0; i < keys->nentries; i++) {
2018+
entry_t *entry = &entries[i];
2019+
PyObject *identity = entry->identity;
2020+
PyObject *key = entry->key;
2021+
PyObject *value = entry->value;
2022+
if (identity == NULL) {
2023+
printf(" %zd [should be deleted]", i);
2024+
} else {
2025+
printf(" %zd h=%20zd", i, entry->hash);
2026+
}
2027+
if (key != NULL) {
2028+
printf(", k=%zd", key->ob_refcnt);
2029+
}
2030+
if (value != NULL) {
2031+
printf(", v=%zd", value->ob_refcnt);
2032+
}
2033+
}
2034+
printf("\n");
2035+
return 1;
2036+
}
2037+
19982038
#endif // NDEBUG
19992039

20002040
#ifdef __cplusplus

0 commit comments

Comments
 (0)