Index: Python/pythonrun.c =================================================================== --- Python/pythonrun.c (revision 75896) +++ Python/pythonrun.c (working copy) @@ -484,7 +484,6 @@ PyCFunction_Fini(); PyTuple_Fini(); PyList_Fini(); - PySet_Fini(); PyBytes_Fini(); PyByteArray_Fini(); PyLong_Fini(); @@ -493,6 +492,7 @@ /* Cleanup Unicode implementation */ _PyUnicode_Fini(); + PySet_Fini(); /* reset file system default encoding */ if (!Py_HasFileSystemDefaultEncoding) { Index: Include/setobject.h =================================================================== --- Include/setobject.h (revision 75896) +++ Include/setobject.h (working copy) @@ -89,6 +89,7 @@ PyAPI_FUNC(int) PySet_Contains(PyObject *anyset, PyObject *key); PyAPI_FUNC(int) PySet_Discard(PyObject *set, PyObject *key); PyAPI_FUNC(int) PySet_Add(PyObject *set, PyObject *key); +PyAPI_FUNC(PyObject *) _PySet_Add(PyObject *set, PyObject *key); PyAPI_FUNC(int) _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, long *hash); PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set); PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable); Index: Objects/unicodeobject.c =================================================================== --- Objects/unicodeobject.c (revision 75896) +++ Objects/unicodeobject.c (working copy) @@ -386,8 +386,8 @@ case SSTATE_INTERNED_MORTAL: /* revive dead object temporarily for DelItem */ - Py_REFCNT(unicode) = 3; - if (PyDict_DelItem(interned, (PyObject *)unicode) != 0) + Py_REFCNT(unicode) = 2; + if (PySet_Discard(interned, (PyObject *)unicode) == -1) Py_FatalError( "deletion of interned string failed"); break; @@ -9715,36 +9715,31 @@ if (PyUnicode_CHECK_INTERNED(s)) return; if (interned == NULL) { - interned = PyDict_New(); + interned = PySet_New(NULL); if (interned == NULL) { PyErr_Clear(); /* Don't leave an exception */ return; } } - /* It might be that the GetItem call fails even - though the key is present in the dictionary, - namely when this happens during a stack overflow. */ - Py_ALLOW_RECURSION - t = PyDict_GetItem(interned, (PyObject *)s); - Py_END_ALLOW_RECURSION - if (t) { - Py_INCREF(t); - Py_DECREF(*p); - *p = t; - return; - } - PyThreadState_GET()->recursion_critical = 1; - if (PyDict_SetItem(interned, (PyObject *)s, (PyObject *)s) < 0) { + t = _PySet_Add(interned, (PyObject *)s); + if (t == NULL) { PyErr_Clear(); PyThreadState_GET()->recursion_critical = 0; return; } PyThreadState_GET()->recursion_critical = 0; - /* The two references in interned are not counted by refcnt. - The deallocator will take care of this */ - Py_REFCNT(s) -= 2; + if (t != *p) { + Py_INCREF(t); + Py_DECREF(*p); + *p = t; + return; + } + /* A reference in interned are not counted by refcnt. + The string deallocator will take care of this */ + --Py_REFCNT(s); + PyUnicode_CHECK_INTERNED(s) = SSTATE_INTERNED_MORTAL; } @@ -9770,29 +9765,23 @@ void _Py_ReleaseInternedUnicodeStrings(void) { - PyObject *keys; PyUnicodeObject *s; - Py_ssize_t i, n; + Py_ssize_t n, pos; Py_ssize_t immortal_size = 0, mortal_size = 0; + long hash; - if (interned == NULL || !PyDict_Check(interned)) + if (interned == NULL || !PySet_Check(interned)) return; - keys = PyDict_Keys(interned); - if (keys == NULL || !PyList_Check(keys)) { - PyErr_Clear(); - return; - } /* Since _Py_ReleaseInternedUnicodeStrings() is intended to help a leak detector, interned unicode strings are not forcibly deallocated; rather, we give them their stolen references back, and then clear - and DECREF the interned dict. */ + and DECREF the interned set. */ - n = PyList_GET_SIZE(keys); + n = PySet_GET_SIZE(interned); fprintf(stderr, "releasing %" PY_FORMAT_SIZE_T "d interned strings\n", n); - for (i = 0; i < n; i++) { - s = (PyUnicodeObject *) PyList_GET_ITEM(keys, i); + while (_PySet_NextEntry(interned, &pos, (PyObject **)&s, &hash)) { switch (s->state) { case SSTATE_NOT_INTERNED: /* XXX Shouldn't happen */ @@ -9802,7 +9791,7 @@ immortal_size += s->length; break; case SSTATE_INTERNED_MORTAL: - Py_REFCNT(s) += 2; + Py_REFCNT(s) += 1; mortal_size += s->length; break; default: @@ -9813,8 +9802,7 @@ fprintf(stderr, "total size of all interned strings: " "%" PY_FORMAT_SIZE_T "d/%" PY_FORMAT_SIZE_T "d " "mortal/immortal\n", mortal_size, immortal_size); - Py_DECREF(keys); - PyDict_Clear(interned); + PySet_Clear(interned); Py_DECREF(interned); interned = NULL; } Index: Objects/setobject.c =================================================================== --- Objects/setobject.c (revision 75896) +++ Objects/setobject.c (working copy) @@ -209,8 +209,9 @@ Internal routine to insert a new key into the table. Used by the public insert routine. Eats a reference to key. +Returns borrowed reference to the inserted or found key or NULL on error. */ -static int +static PyObject * set_insert_key(register PySetObject *so, PyObject *key, long hash) { register setentry *entry; @@ -219,7 +220,7 @@ assert(so->lookup != NULL); entry = so->lookup(so, key, hash); if (entry == NULL) - return -1; + return NULL; if (entry->key == NULL) { /* UNUSED */ so->fill++; @@ -236,7 +237,7 @@ /* ACTIVE */ Py_DECREF(key); } - return 0; + return entry->key; } /* @@ -368,7 +369,7 @@ assert(so->fill <= so->mask); /* at least one empty slot */ n_used = so->used; Py_INCREF(entry->key); - if (set_insert_key(so, entry->key, entry->hash) == -1) { + if (set_insert_key(so, entry->key, entry->hash) == NULL) { Py_DECREF(entry->key); return -1; } @@ -377,28 +378,30 @@ return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4); } -static int +static PyObject * set_add_key(register PySetObject *so, PyObject *key) { register long hash; register Py_ssize_t n_used; + PyObject *ret; if (!PyUnicode_CheckExact(key) || (hash = ((PyUnicodeObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) - return -1; + return NULL; } assert(so->fill <= so->mask); /* at least one empty slot */ n_used = so->used; Py_INCREF(key); - if (set_insert_key(so, key, hash) == -1) { + if ((ret = set_insert_key(so, key, hash)) == NULL) { Py_DECREF(key); - return -1; + return NULL; } if (!(so->used > n_used && so->fill*3 >= (so->mask+1)*2)) - return 0; - return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4); + return ret; + return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4) == -1 + ? NULL : ret; } #define DISCARD_NOTFOUND 0 @@ -662,7 +665,7 @@ if (entry->key != NULL && entry->key != dummy) { Py_INCREF(entry->key); - if (set_insert_key(so, entry->key, entry->hash) == -1) { + if (set_insert_key(so, entry->key, entry->hash) == NULL) { Py_DECREF(entry->key); return -1; } @@ -954,7 +957,7 @@ return -1; while ((key = PyIter_Next(it)) != NULL) { - if (set_add_key(so, key) == -1) { + if (set_add_key(so, key) == NULL) { Py_DECREF(it); Py_DECREF(key); return -1; @@ -1827,9 +1830,11 @@ static PyObject * set_add(PySetObject *so, PyObject *key) { - if (set_add_key(so, key) == -1) + PyObject *ret; + if ((ret = set_add_key(so, key)) == NULL) return NULL; - Py_RETURN_NONE; + Py_INCREF(ret); + return ret; } PyDoc_STRVAR(add_doc, @@ -2298,6 +2303,12 @@ PyErr_BadInternalCall(); return -1; } + return set_add_key((PySetObject *)anyset, key) == NULL ? -1 : 0; +} + +PyObject * +_PySet_Add(PyObject *anyset, PyObject *key) +{ return set_add_key((PySetObject *)anyset, key); } Index: Lib/test/test_set.py =================================================================== --- Lib/test/test_set.py (revision 75896) +++ Lib/test/test_set.py (working copy) @@ -378,10 +378,14 @@ self.assertEqual(type(dup), self.basetype) def test_add(self): - self.s.add('Q') - self.assertTrue('Q' in self.s) + Q = 'Q', + r = self.s.add(Q) + self.assertTrue(Q in self.s) + self.assertTrue(Q is r) dup = self.s.copy() - self.s.add('Q') + Q = 'Q', + r = self.s.add(Q) + self.assertFalse(Q is r) self.assertEqual(self.s, dup) self.assertRaises(TypeError, self.s.add, [])