summaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/Objects/setobject.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tools/python3/Objects/setobject.c')
-rw-r--r--contrib/tools/python3/Objects/setobject.c1183
1 files changed, 702 insertions, 481 deletions
diff --git a/contrib/tools/python3/Objects/setobject.c b/contrib/tools/python3/Objects/setobject.c
index 763f9a3d204..891987e3519 100644
--- a/contrib/tools/python3/Objects/setobject.c
+++ b/contrib/tools/python3/Objects/setobject.c
@@ -2,7 +2,7 @@
/* set object implementation
Written and maintained by Raymond D. Hettinger <[email protected]>
- Derived from Lib/sets.py and Objects/dictobject.c.
+ Derived from Objects/dictobject.c.
The basic lookup function used by all operations.
This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.
@@ -32,8 +32,29 @@
*/
#include "Python.h"
-#include "pycore_object.h" // _PyObject_GC_UNTRACK()
-#include <stddef.h> // offsetof()
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
+#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION, Py_END_CRITICAL_SECTION
+#include "pycore_dict.h" // _PyDict_Contains_KnownHash()
+#include "pycore_modsupport.h" // _PyArg_NoKwnames()
+#include "pycore_object.h" // _PyObject_GC_UNTRACK()
+#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_SSIZE_RELAXED()
+#include "pycore_pyerrors.h" // _PyErr_SetKeyError()
+#include "pycore_setobject.h" // _PySet_NextEntry() definition
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
+#include <stddef.h> // offsetof()
+#include "clinic/setobject.c.h"
+
+/*[clinic input]
+class set "PySetObject *" "&PySet_Type"
+class frozenset "PySetObject *" "&PyFrozenSet_Type"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=97ad1d3e9f117079]*/
+
+/*[python input]
+class setobject_converter(self_converter):
+ type = "PySetObject *"
+[python start generated code]*/
+/*[python end generated code: output=da39a3ee5e6b4b0d input=33a44506d4d57793]*/
/* Object used as dummy key to fill deleted entries */
static PyObject _dummy_struct;
@@ -111,6 +132,8 @@ set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
int probes;
int cmp;
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+
/* Pre-increment is necessary to prevent arbitrary code in the rich
comparison from deallocating the key just before the insertion. */
Py_INCREF(key);
@@ -162,14 +185,17 @@ set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
found_unused_or_dummy:
if (freeslot == NULL)
goto found_unused;
- so->used++;
+ if (freeslot->hash != -1) {
+ goto restart;
+ }
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used + 1);
freeslot->key = key;
freeslot->hash = hash;
return 0;
found_unused:
so->fill++;
- so->used++;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used + 1);
entry->key = key;
entry->hash = hash;
if ((size_t)so->fill*5 < mask*3)
@@ -335,7 +361,7 @@ set_discard_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
old_key = entry->key;
entry->key = dummy;
entry->hash = -1;
- so->used--;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used - 1);
Py_DECREF(old_key);
return DISCARD_FOUND;
}
@@ -343,13 +369,9 @@ set_discard_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
static int
set_add_key(PySetObject *so, PyObject *key)
{
- Py_hash_t hash;
-
- if (!PyUnicode_CheckExact(key) ||
- (hash = _PyASCIIObject_CAST(key)->hash) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return -1;
+ Py_hash_t hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ return -1;
}
return set_add_entry(so, key, hash);
}
@@ -357,13 +379,9 @@ set_add_key(PySetObject *so, PyObject *key)
static int
set_contains_key(PySetObject *so, PyObject *key)
{
- Py_hash_t hash;
-
- if (!PyUnicode_CheckExact(key) ||
- (hash = _PyASCIIObject_CAST(key)->hash) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return -1;
+ Py_hash_t hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ return -1;
}
return set_contains_entry(so, key, hash);
}
@@ -371,13 +389,9 @@ set_contains_key(PySetObject *so, PyObject *key)
static int
set_discard_key(PySetObject *so, PyObject *key)
{
- Py_hash_t hash;
-
- if (!PyUnicode_CheckExact(key) ||
- (hash = _PyASCIIObject_CAST(key)->hash) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return -1;
+ Py_hash_t hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ return -1;
}
return set_discard_entry(so, key, hash);
}
@@ -387,10 +401,10 @@ set_empty_to_minsize(PySetObject *so)
{
memset(so->smalltable, 0, sizeof(so->smalltable));
so->fill = 0;
- so->used = 0;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, 0);
so->mask = PySet_MINSIZE - 1;
so->table = so->smalltable;
- so->hash = -1;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->hash, -1);
}
static int
@@ -488,8 +502,7 @@ set_dealloc(PySetObject *so)
/* bpo-31095: UnTrack is needed before calling any callbacks */
PyObject_GC_UnTrack(so);
Py_TRASHCAN_BEGIN(so, set_dealloc)
- if (so->weakreflist != NULL)
- PyObject_ClearWeakRefs((PyObject *) so);
+ FT_CLEAR_WEAKREFS((PyObject *) so, so->weakreflist);
for (entry = so->table; used > 0; entry++) {
if (entry->key && entry->key != dummy) {
@@ -504,7 +517,7 @@ set_dealloc(PySetObject *so)
}
static PyObject *
-set_repr(PySetObject *so)
+set_repr_lock_held(PySetObject *so)
{
PyObject *result=NULL, *keys, *listrepr, *tmp;
int status = Py_ReprEnter((PyObject*)so);
@@ -521,9 +534,18 @@ set_repr(PySetObject *so)
return PyUnicode_FromFormat("%s()", Py_TYPE(so)->tp_name);
}
- keys = PySequence_List((PyObject *)so);
- if (keys == NULL)
+ // gh-129967: avoid PySequence_List because it might re-lock the object
+ // lock or the GIL and allow something to clear the set from underneath us.
+ keys = PyList_New(so->used);
+ if (keys == NULL) {
goto done;
+ }
+
+ Py_ssize_t pos = 0, idx = 0;
+ setentry *entry;
+ while (set_next(so, &pos, &entry)) {
+ PyList_SET_ITEM(keys, idx++, Py_NewRef(entry->key));
+ }
/* repr(keys)[1:-1] */
listrepr = PyObject_Repr(keys);
@@ -548,14 +570,24 @@ done:
return result;
}
+static PyObject *
+set_repr(PySetObject *so)
+{
+ PyObject *result;
+ Py_BEGIN_CRITICAL_SECTION(so);
+ result = set_repr_lock_held(so);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
static Py_ssize_t
-set_len(PyObject *so)
+set_len(PySetObject *so)
{
- return ((PySetObject *)so)->used;
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED(so->used);
}
static int
-set_merge(PySetObject *so, PyObject *otherset)
+set_merge_lock_held(PySetObject *so, PyObject *otherset)
{
PySetObject *other;
PyObject *key;
@@ -565,6 +597,8 @@ set_merge(PySetObject *so, PyObject *otherset)
assert (PyAnySet_Check(so));
assert (PyAnySet_Check(otherset));
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(otherset);
other = (PySetObject*)otherset;
if (other == so || other->used == 0)
@@ -593,7 +627,7 @@ set_merge(PySetObject *so, PyObject *otherset)
}
}
so->fill = other->fill;
- so->used = other->used;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, other->used);
return 0;
}
@@ -602,7 +636,7 @@ set_merge(PySetObject *so, PyObject *otherset)
setentry *newtable = so->table;
size_t newmask = (size_t)so->mask;
so->fill = other->used;
- so->used = other->used;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, other->used);
for (i = other->mask + 1; i > 0 ; i--, other_entry++) {
key = other_entry->key;
if (key != NULL && key != dummy) {
@@ -625,8 +659,19 @@ set_merge(PySetObject *so, PyObject *otherset)
return 0;
}
+/*[clinic input]
+@critical_section
+set.pop
+ so: setobject
+
+Remove and return an arbitrary set element.
+
+Raises KeyError if the set is empty.
+[clinic start generated code]*/
+
static PyObject *
-set_pop(PySetObject *so, PyObject *Py_UNUSED(ignored))
+set_pop_impl(PySetObject *so)
+/*[clinic end generated code: output=4d65180f1271871b input=9296c84921125060]*/
{
/* Make sure the search finger is in bounds */
setentry *entry = so->table + (so->finger & so->mask);
@@ -645,14 +690,11 @@ set_pop(PySetObject *so, PyObject *Py_UNUSED(ignored))
key = entry->key;
entry->key = dummy;
entry->hash = -1;
- so->used--;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used - 1);
so->finger = entry - so->table + 1; /* next place to start */
return key;
}
-PyDoc_STRVAR(pop_doc, "Remove and return an arbitrary set element.\n\
-Raises KeyError if the set is empty.");
-
static int
set_traverse(PySetObject *so, visitproc visit, void *arg)
{
@@ -679,18 +721,15 @@ _shuffle_bits(Py_uhash_t h)
large primes with "interesting bit patterns" and that passed tests
for good collision statistics on a variety of problematic datasets
including powersets and graph structures (such as David Eppstein's
- graph recipes in Lib/test/test_set.py) */
+ graph recipes in Lib/test/test_set.py). */
static Py_hash_t
-frozenset_hash(PyObject *self)
+frozenset_hash_impl(PyObject *self)
{
- PySetObject *so = (PySetObject *)self;
+ PySetObject *so = _PySet_CAST(self);
Py_uhash_t hash = 0;
setentry *entry;
- if (so->hash != -1)
- return so->hash;
-
/* Xor-in shuffled bits from every entry's hash field because xor is
commutative and a frozenset hash should be independent of order.
@@ -723,7 +762,21 @@ frozenset_hash(PyObject *self)
if (hash == (Py_uhash_t)-1)
hash = 590923713UL;
- so->hash = hash;
+ return (Py_hash_t)hash;
+}
+
+static Py_hash_t
+frozenset_hash(PyObject *self)
+{
+ PySetObject *so = _PySet_CAST(self);
+ Py_uhash_t hash;
+
+ if (FT_ATOMIC_LOAD_SSIZE_RELAXED(so->hash) != -1) {
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED(so->hash);
+ }
+
+ hash = frozenset_hash_impl(self);
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->hash, hash);
return hash;
}
@@ -792,7 +845,7 @@ static PyMethodDef setiter_methods[] = {
static PyObject *setiter_iternext(setiterobject *si)
{
- PyObject *key;
+ PyObject *key = NULL;
Py_ssize_t i, mask;
setentry *entry;
PySetObject *so = si->si_set;
@@ -801,30 +854,35 @@ static PyObject *setiter_iternext(setiterobject *si)
return NULL;
assert (PyAnySet_Check(so));
- if (si->si_used != so->used) {
+ Py_ssize_t so_used = FT_ATOMIC_LOAD_SSIZE(so->used);
+ Py_ssize_t si_used = FT_ATOMIC_LOAD_SSIZE(si->si_used);
+ if (si_used != so_used) {
PyErr_SetString(PyExc_RuntimeError,
"Set changed size during iteration");
si->si_used = -1; /* Make this state sticky */
return NULL;
}
+ Py_BEGIN_CRITICAL_SECTION(so);
i = si->si_pos;
assert(i>=0);
entry = so->table;
mask = so->mask;
- while (i <= mask && (entry[i].key == NULL || entry[i].key == dummy))
+ while (i <= mask && (entry[i].key == NULL || entry[i].key == dummy)) {
i++;
+ }
+ if (i <= mask) {
+ key = Py_NewRef(entry[i].key);
+ }
+ Py_END_CRITICAL_SECTION();
si->si_pos = i+1;
- if (i > mask)
- goto fail;
+ if (key == NULL) {
+ si->si_set = NULL;
+ Py_DECREF(so);
+ return NULL;
+ }
si->len--;
- key = entry[i].key;
- return Py_NewRef(key);
-
-fail:
- si->si_set = NULL;
- Py_DECREF(so);
- return NULL;
+ return key;
}
PyTypeObject PySetIter_Type = {
@@ -863,52 +921,60 @@ PyTypeObject PySetIter_Type = {
static PyObject *
set_iter(PySetObject *so)
{
+ Py_ssize_t size = set_len(so);
setiterobject *si = PyObject_GC_New(setiterobject, &PySetIter_Type);
if (si == NULL)
return NULL;
si->si_set = (PySetObject*)Py_NewRef(so);
- si->si_used = so->used;
+ si->si_used = size;
si->si_pos = 0;
- si->len = so->used;
+ si->len = size;
_PyObject_GC_TRACK(si);
return (PyObject *)si;
}
static int
-set_update_internal(PySetObject *so, PyObject *other)
+set_update_dict_lock_held(PySetObject *so, PyObject *other)
{
- PyObject *key, *it;
+ assert(PyDict_CheckExact(other));
- if (PyAnySet_Check(other))
- return set_merge(so, other);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
- if (PyDict_CheckExact(other)) {
- PyObject *value;
- Py_ssize_t pos = 0;
- Py_hash_t hash;
- Py_ssize_t dictsize = PyDict_GET_SIZE(other);
-
- /* Do one big resize at the start, rather than
- * incrementally resizing as we insert new keys. Expect
- * that there will be no (or few) overlapping keys.
- */
- if (dictsize < 0)
+ /* Do one big resize at the start, rather than
+ * incrementally resizing as we insert new keys. Expect
+ * that there will be no (or few) overlapping keys.
+ */
+ Py_ssize_t dictsize = PyDict_GET_SIZE(other);
+ if ((so->fill + dictsize)*5 >= so->mask*3) {
+ if (set_table_resize(so, (so->used + dictsize)*2) != 0) {
return -1;
- if ((so->fill + dictsize)*5 >= so->mask*3) {
- if (set_table_resize(so, (so->used + dictsize)*2) != 0)
- return -1;
}
- while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
- if (set_add_entry(so, key, hash))
- return -1;
+ }
+
+ Py_ssize_t pos = 0;
+ PyObject *key;
+ PyObject *value;
+ Py_hash_t hash;
+ while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
+ if (set_add_entry(so, key, hash)) {
+ return -1;
}
- return 0;
}
+ return 0;
+}
- it = PyObject_GetIter(other);
- if (it == NULL)
+static int
+set_update_iterable_lock_held(PySetObject *so, PyObject *other)
+{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+
+ PyObject *it = PyObject_GetIter(other);
+ if (it == NULL) {
return -1;
+ }
+ PyObject *key;
while ((key = PyIter_Next(it)) != NULL) {
if (set_add_key(so, key)) {
Py_DECREF(it);
@@ -923,8 +989,80 @@ set_update_internal(PySetObject *so, PyObject *other)
return 0;
}
+static int
+set_update_lock_held(PySetObject *so, PyObject *other)
+{
+ if (PyAnySet_Check(other)) {
+ return set_merge_lock_held(so, other);
+ }
+ else if (PyDict_CheckExact(other)) {
+ return set_update_dict_lock_held(so, other);
+ }
+ return set_update_iterable_lock_held(so, other);
+}
+
+// set_update for a `so` that is only visible to the current thread
+static int
+set_update_local(PySetObject *so, PyObject *other)
+{
+ assert(Py_REFCNT(so) == 1);
+ if (PyAnySet_Check(other)) {
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(other);
+ rv = set_merge_lock_held(so, other);
+ Py_END_CRITICAL_SECTION();
+ return rv;
+ }
+ else if (PyDict_CheckExact(other)) {
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(other);
+ rv = set_update_dict_lock_held(so, other);
+ Py_END_CRITICAL_SECTION();
+ return rv;
+ }
+ return set_update_iterable_lock_held(so, other);
+}
+
+static int
+set_update_internal(PySetObject *so, PyObject *other)
+{
+ if (PyAnySet_Check(other)) {
+ if (Py_Is((PyObject *)so, other)) {
+ return 0;
+ }
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_merge_lock_held(so, other);
+ Py_END_CRITICAL_SECTION2();
+ return rv;
+ }
+ else if (PyDict_CheckExact(other)) {
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_update_dict_lock_held(so, other);
+ Py_END_CRITICAL_SECTION2();
+ return rv;
+ }
+ else {
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(so);
+ rv = set_update_iterable_lock_held(so, other);
+ Py_END_CRITICAL_SECTION();
+ return rv;
+ }
+}
+
+/*[clinic input]
+set.update
+ so: setobject
+ *others as args: object
+
+Update the set, adding elements from all others.
+[clinic start generated code]*/
+
static PyObject *
-set_update(PySetObject *so, PyObject *args)
+set_update_impl(PySetObject *so, PyObject *args)
+/*[clinic end generated code: output=34f6371704974c8a input=df4fe486e38cd337]*/
{
Py_ssize_t i;
@@ -936,9 +1074,6 @@ set_update(PySetObject *so, PyObject *args)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(update_doc,
-"Update a set with the union of itself and others.");
-
/* XXX Todo:
If aligned memory allocations become available, make the
set object 64 byte aligned so that most of the fields
@@ -964,7 +1099,7 @@ make_new_set(PyTypeObject *type, PyObject *iterable)
so->weakreflist = NULL;
if (iterable != NULL) {
- if (set_update_internal(so, iterable)) {
+ if (set_update_local(so, iterable)) {
Py_DECREF(so);
return NULL;
}
@@ -1060,7 +1195,9 @@ set_swap_bodies(PySetObject *a, PySetObject *b)
Py_hash_t h;
t = a->fill; a->fill = b->fill; b->fill = t;
- t = a->used; a->used = b->used; b->used = t;
+ t = a->used;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(a->used, b->used);
+ FT_ATOMIC_STORE_SSIZE_RELAXED(b->used, t);
t = a->mask; a->mask = b->mask; b->mask = t;
u = a->table;
@@ -1079,41 +1216,84 @@ set_swap_bodies(PySetObject *a, PySetObject *b)
if (PyType_IsSubtype(Py_TYPE(a), &PyFrozenSet_Type) &&
PyType_IsSubtype(Py_TYPE(b), &PyFrozenSet_Type)) {
- h = a->hash; a->hash = b->hash; b->hash = h;
+ h = FT_ATOMIC_LOAD_SSIZE_RELAXED(a->hash);
+ FT_ATOMIC_STORE_SSIZE_RELAXED(a->hash, FT_ATOMIC_LOAD_SSIZE_RELAXED(b->hash));
+ FT_ATOMIC_STORE_SSIZE_RELAXED(b->hash, h);
} else {
- a->hash = -1;
- b->hash = -1;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(a->hash, -1);
+ FT_ATOMIC_STORE_SSIZE_RELAXED(b->hash, -1);
}
}
+/*[clinic input]
+@critical_section
+set.copy
+ so: setobject
+
+Return a shallow copy of a set.
+[clinic start generated code]*/
+
static PyObject *
-set_copy(PySetObject *so, PyObject *Py_UNUSED(ignored))
+set_copy_impl(PySetObject *so)
+/*[clinic end generated code: output=c9223a1e1cc6b041 input=c169a4fbb8209257]*/
{
- return make_new_set_basetype(Py_TYPE(so), (PyObject *)so);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+ PyObject *copy = make_new_set_basetype(Py_TYPE(so), NULL);
+ if (copy == NULL) {
+ return NULL;
+ }
+ if (set_merge_lock_held((PySetObject *)copy, (PyObject *)so) < 0) {
+ Py_DECREF(copy);
+ return NULL;
+ }
+ return copy;
}
+/*[clinic input]
+@critical_section
+frozenset.copy
+ so: setobject
+
+Return a shallow copy of a set.
+[clinic start generated code]*/
+
static PyObject *
-frozenset_copy(PySetObject *so, PyObject *Py_UNUSED(ignored))
+frozenset_copy_impl(PySetObject *so)
+/*[clinic end generated code: output=b356263526af9e70 input=fbf5bef131268dd7]*/
{
if (PyFrozenSet_CheckExact(so)) {
return Py_NewRef(so);
}
- return set_copy(so, NULL);
+ return set_copy_impl(so);
}
-PyDoc_STRVAR(copy_doc, "Return a shallow copy of a set.");
+/*[clinic input]
+@critical_section
+set.clear
+ so: setobject
+
+Remove all elements from this set.
+[clinic start generated code]*/
static PyObject *
-set_clear(PySetObject *so, PyObject *Py_UNUSED(ignored))
+set_clear_impl(PySetObject *so)
+/*[clinic end generated code: output=4e71d5a83904161a input=c6f831b366111950]*/
{
set_clear_internal(so);
Py_RETURN_NONE;
}
-PyDoc_STRVAR(clear_doc, "Remove all elements from this set.");
+/*[clinic input]
+set.union
+ so: setobject
+ *others as args: object
+
+Return a new set with elements from the set and all others.
+[clinic start generated code]*/
static PyObject *
-set_union(PySetObject *so, PyObject *args)
+set_union_impl(PySetObject *so, PyObject *args)
+/*[clinic end generated code: output=2c83d05a446a1477 input=ddf088706e9577b2]*/
{
PySetObject *result;
PyObject *other;
@@ -1127,7 +1307,7 @@ set_union(PySetObject *so, PyObject *args)
other = PyTuple_GET_ITEM(args, i);
if ((PyObject *)so == other)
continue;
- if (set_update_internal(result, other)) {
+ if (set_update_local(result, other)) {
Py_DECREF(result);
return NULL;
}
@@ -1135,11 +1315,6 @@ set_union(PySetObject *so, PyObject *args)
return (PyObject *)result;
}
-PyDoc_STRVAR(union_doc,
- "Return the union of sets as a new set.\n\
-\n\
-(i.e. all elements that are in either set.)");
-
static PyObject *
set_or(PySetObject *so, PyObject *other)
{
@@ -1149,11 +1324,13 @@ set_or(PySetObject *so, PyObject *other)
Py_RETURN_NOTIMPLEMENTED;
result = (PySetObject *)set_copy(so, NULL);
- if (result == NULL)
+ if (result == NULL) {
return NULL;
- if ((PyObject *)so == other)
+ }
+ if (Py_Is((PyObject *)so, other)) {
return (PyObject *)result;
- if (set_update_internal(result, other)) {
+ }
+ if (set_update_local(result, other)) {
Py_DECREF(result);
return NULL;
}
@@ -1166,8 +1343,9 @@ set_ior(PySetObject *so, PyObject *other)
if (!PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
- if (set_update_internal(so, other))
+ if (set_update_internal(so, other)) {
return NULL;
+ }
return Py_NewRef(so);
}
@@ -1180,7 +1358,7 @@ set_intersection(PySetObject *so, PyObject *other)
int rv;
if ((PyObject *)so == other)
- return set_copy(so, NULL);
+ return set_copy_impl(so);
result = (PySetObject *)make_new_set_basetype(Py_TYPE(so), NULL);
if (result == NULL)
@@ -1254,18 +1432,31 @@ set_intersection(PySetObject *so, PyObject *other)
return NULL;
}
+/*[clinic input]
+set.intersection as set_intersection_multi
+ so: setobject
+ *others as args: object
+
+Return a new set with elements common to the set and all others.
+[clinic start generated code]*/
+
static PyObject *
-set_intersection_multi(PySetObject *so, PyObject *args)
+set_intersection_multi_impl(PySetObject *so, PyObject *args)
+/*[clinic end generated code: output=2406ef3387adbe2f input=0d9f3805ccbba6a4]*/
{
Py_ssize_t i;
- if (PyTuple_GET_SIZE(args) == 0)
+ if (PyTuple_GET_SIZE(args) == 0) {
return set_copy(so, NULL);
+ }
PyObject *result = Py_NewRef(so);
for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
PyObject *other = PyTuple_GET_ITEM(args, i);
- PyObject *newresult = set_intersection((PySetObject *)result, other);
+ PyObject *newresult;
+ Py_BEGIN_CRITICAL_SECTION2(result, other);
+ newresult = set_intersection((PySetObject *)result, other);
+ Py_END_CRITICAL_SECTION2();
if (newresult == NULL) {
Py_DECREF(result);
return NULL;
@@ -1275,11 +1466,6 @@ set_intersection_multi(PySetObject *so, PyObject *args)
return result;
}
-PyDoc_STRVAR(intersection_doc,
-"Return the intersection of two sets as a new set.\n\
-\n\
-(i.e. all elements that are in both sets.)");
-
static PyObject *
set_intersection_update(PySetObject *so, PyObject *other)
{
@@ -1293,28 +1479,42 @@ set_intersection_update(PySetObject *so, PyObject *other)
Py_RETURN_NONE;
}
+/*[clinic input]
+set.intersection_update as set_intersection_update_multi
+ so: setobject
+ *others as args: object
+
+Update the set, keeping only elements found in it and all others.
+[clinic start generated code]*/
+
static PyObject *
-set_intersection_update_multi(PySetObject *so, PyObject *args)
+set_intersection_update_multi_impl(PySetObject *so, PyObject *args)
+/*[clinic end generated code: output=251c1f729063609d input=223c1e086aa669a9]*/
{
PyObject *tmp;
- tmp = set_intersection_multi(so, args);
+ tmp = set_intersection_multi_impl(so, args);
if (tmp == NULL)
return NULL;
+ Py_BEGIN_CRITICAL_SECTION(so);
set_swap_bodies(so, (PySetObject *)tmp);
+ Py_END_CRITICAL_SECTION();
Py_DECREF(tmp);
Py_RETURN_NONE;
}
-PyDoc_STRVAR(intersection_update_doc,
-"Update a set with the intersection of itself and another.");
-
static PyObject *
set_and(PySetObject *so, PyObject *other)
{
if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
- return set_intersection(so, other);
+
+ PyObject *rv;
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_intersection(so, other);
+ Py_END_CRITICAL_SECTION2();
+
+ return rv;
}
static PyObject *
@@ -1324,15 +1524,30 @@ set_iand(PySetObject *so, PyObject *other)
if (!PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
+
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
result = set_intersection_update(so, other);
+ Py_END_CRITICAL_SECTION2();
+
if (result == NULL)
return NULL;
Py_DECREF(result);
return Py_NewRef(so);
}
+/*[clinic input]
+@critical_section so other
+set.isdisjoint
+ so: setobject
+ other: object
+ /
+
+Return True if two sets have a null intersection.
+[clinic start generated code]*/
+
static PyObject *
-set_isdisjoint(PySetObject *so, PyObject *other)
+set_isdisjoint_impl(PySetObject *so, PyObject *other)
+/*[clinic end generated code: output=273493f2d57c565e input=32f8dcab5e0fc7d6]*/
{
PyObject *key, *it, *tmp;
int rv;
@@ -1390,12 +1605,12 @@ set_isdisjoint(PySetObject *so, PyObject *other)
Py_RETURN_TRUE;
}
-PyDoc_STRVAR(isdisjoint_doc,
-"Return True if two sets have a null intersection.");
-
static int
set_difference_update_internal(PySetObject *so, PyObject *other)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
+
if ((PyObject *)so == other)
return set_clear_internal(so);
@@ -1451,28 +1666,39 @@ set_difference_update_internal(PySetObject *so, PyObject *other)
return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4);
}
+/*[clinic input]
+set.difference_update
+ so: setobject
+ *others as args: object
+
+Update the set, removing elements found in others.
+[clinic start generated code]*/
+
static PyObject *
-set_difference_update(PySetObject *so, PyObject *args)
+set_difference_update_impl(PySetObject *so, PyObject *args)
+/*[clinic end generated code: output=28685b2fc63e41c4 input=024e6baa6fbcbb3d]*/
{
Py_ssize_t i;
for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
PyObject *other = PyTuple_GET_ITEM(args, i);
- if (set_difference_update_internal(so, other))
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_difference_update_internal(so, other);
+ Py_END_CRITICAL_SECTION2();
+ if (rv) {
return NULL;
+ }
}
Py_RETURN_NONE;
}
-PyDoc_STRVAR(difference_update_doc,
-"Remove all elements of another set from this set.");
-
static PyObject *
set_copy_and_difference(PySetObject *so, PyObject *other)
{
PyObject *result;
- result = set_copy(so, NULL);
+ result = set_copy_impl(so);
if (result == NULL)
return NULL;
if (set_difference_update_internal((PySetObject *) result, other) == 0)
@@ -1557,23 +1783,39 @@ set_difference(PySetObject *so, PyObject *other)
return result;
}
+/*[clinic input]
+set.difference as set_difference_multi
+ so: setobject
+ *others as args: object
+
+Return a new set with elements in the set that are not in the others.
+[clinic start generated code]*/
+
static PyObject *
-set_difference_multi(PySetObject *so, PyObject *args)
+set_difference_multi_impl(PySetObject *so, PyObject *args)
+/*[clinic end generated code: output=3130c3bb3cac873d input=ba78ea5f099e58df]*/
{
Py_ssize_t i;
PyObject *result, *other;
- if (PyTuple_GET_SIZE(args) == 0)
+ if (PyTuple_GET_SIZE(args) == 0) {
return set_copy(so, NULL);
+ }
other = PyTuple_GET_ITEM(args, 0);
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
result = set_difference(so, other);
+ Py_END_CRITICAL_SECTION2();
if (result == NULL)
return NULL;
for (i=1 ; i<PyTuple_GET_SIZE(args) ; i++) {
other = PyTuple_GET_ITEM(args, i);
- if (set_difference_update_internal((PySetObject *)result, other)) {
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(other);
+ rv = set_difference_update_internal((PySetObject *)result, other);
+ Py_END_CRITICAL_SECTION();
+ if (rv) {
Py_DECREF(result);
return NULL;
}
@@ -1581,16 +1823,17 @@ set_difference_multi(PySetObject *so, PyObject *args)
return result;
}
-PyDoc_STRVAR(difference_doc,
-"Return the difference of two or more sets as a new set.\n\
-\n\
-(i.e. all elements that are in this set but not the others.)");
static PyObject *
set_sub(PySetObject *so, PyObject *other)
{
if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
- return set_difference(so, other);
+
+ PyObject *rv;
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_difference(so, other);
+ Py_END_CRITICAL_SECTION2();
+ return rv;
}
static PyObject *
@@ -1598,100 +1841,145 @@ set_isub(PySetObject *so, PyObject *other)
{
if (!PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
- if (set_difference_update_internal(so, other))
+
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_difference_update_internal(so, other);
+ Py_END_CRITICAL_SECTION2();
+ if (rv < 0) {
return NULL;
+ }
return Py_NewRef(so);
}
-static PyObject *
-set_symmetric_difference_update(PySetObject *so, PyObject *other)
+static int
+set_symmetric_difference_update_dict(PySetObject *so, PyObject *other)
{
- PySetObject *otherset;
- PyObject *key;
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
+
Py_ssize_t pos = 0;
+ PyObject *key, *value;
Py_hash_t hash;
- setentry *entry;
- int rv;
-
- if ((PyObject *)so == other)
- return set_clear(so, NULL);
-
- if (PyDict_CheckExact(other)) {
- PyObject *value;
- while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
- Py_INCREF(key);
- rv = set_discard_entry(so, key, hash);
- if (rv < 0) {
+ while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
+ Py_INCREF(key);
+ int rv = set_discard_entry(so, key, hash);
+ if (rv < 0) {
+ Py_DECREF(key);
+ return -1;
+ }
+ if (rv == DISCARD_NOTFOUND) {
+ if (set_add_entry(so, key, hash)) {
Py_DECREF(key);
- return NULL;
- }
- if (rv == DISCARD_NOTFOUND) {
- if (set_add_entry(so, key, hash)) {
- Py_DECREF(key);
- return NULL;
- }
+ return -1;
}
- Py_DECREF(key);
}
- Py_RETURN_NONE;
+ Py_DECREF(key);
}
+ return 0;
+}
- if (PyAnySet_Check(other)) {
- otherset = (PySetObject *)Py_NewRef(other);
- } else {
- otherset = (PySetObject *)make_new_set_basetype(Py_TYPE(so), other);
- if (otherset == NULL)
- return NULL;
- }
+static int
+set_symmetric_difference_update_set(PySetObject *so, PySetObject *other)
+{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
- while (set_next(otherset, &pos, &entry)) {
- key = entry->key;
- hash = entry->hash;
- Py_INCREF(key);
- rv = set_discard_entry(so, key, hash);
+ Py_ssize_t pos = 0;
+ setentry *entry;
+ while (set_next(other, &pos, &entry)) {
+ PyObject *key = Py_NewRef(entry->key);
+ Py_hash_t hash = entry->hash;
+ int rv = set_discard_entry(so, key, hash);
if (rv < 0) {
- Py_DECREF(otherset);
Py_DECREF(key);
- return NULL;
+ return -1;
}
if (rv == DISCARD_NOTFOUND) {
if (set_add_entry(so, key, hash)) {
- Py_DECREF(otherset);
Py_DECREF(key);
- return NULL;
+ return -1;
}
}
Py_DECREF(key);
}
- Py_DECREF(otherset);
- Py_RETURN_NONE;
+ return 0;
}
-PyDoc_STRVAR(symmetric_difference_update_doc,
-"Update a set with the symmetric difference of itself and another.");
+/*[clinic input]
+set.symmetric_difference_update
+ so: setobject
+ other: object
+ /
+
+Update the set, keeping only elements found in either set, but not in both.
+[clinic start generated code]*/
static PyObject *
-set_symmetric_difference(PySetObject *so, PyObject *other)
+set_symmetric_difference_update(PySetObject *so, PyObject *other)
+/*[clinic end generated code: output=fbb049c0806028de input=a50acf0365e1f0a5]*/
{
- PyObject *rv;
- PySetObject *otherset;
+ if (Py_Is((PyObject *)so, other)) {
+ return set_clear(so, NULL);
+ }
+
+ int rv;
+ if (PyDict_CheckExact(other)) {
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_symmetric_difference_update_dict(so, other);
+ Py_END_CRITICAL_SECTION2();
+ }
+ else if (PyAnySet_Check(other)) {
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_symmetric_difference_update_set(so, (PySetObject *)other);
+ Py_END_CRITICAL_SECTION2();
+ }
+ else {
+ PySetObject *otherset = (PySetObject *)make_new_set_basetype(Py_TYPE(so), other);
+ if (otherset == NULL) {
+ return NULL;
+ }
+
+ Py_BEGIN_CRITICAL_SECTION(so);
+ rv = set_symmetric_difference_update_set(so, otherset);
+ Py_END_CRITICAL_SECTION();
- otherset = (PySetObject *)make_new_set_basetype(Py_TYPE(so), other);
- if (otherset == NULL)
- return NULL;
- rv = set_symmetric_difference_update(otherset, (PyObject *)so);
- if (rv == NULL) {
Py_DECREF(otherset);
+ }
+ if (rv < 0) {
return NULL;
}
- Py_DECREF(rv);
- return (PyObject *)otherset;
+ Py_RETURN_NONE;
}
-PyDoc_STRVAR(symmetric_difference_doc,
-"Return the symmetric difference of two sets as a new set.\n\
-\n\
-(i.e. all elements that are in exactly one of the sets.)");
+/*[clinic input]
+@critical_section so other
+set.symmetric_difference
+ so: setobject
+ other: object
+ /
+
+Return a new set with elements in either the set or other but not both.
+[clinic start generated code]*/
+
+static PyObject *
+set_symmetric_difference_impl(PySetObject *so, PyObject *other)
+/*[clinic end generated code: output=270ee0b5d42b0797 input=624f6e7bbdf70db1]*/
+{
+ PySetObject *result = (PySetObject *)make_new_set_basetype(Py_TYPE(so), NULL);
+ if (result == NULL) {
+ return NULL;
+ }
+ if (set_update_lock_held(result, other) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ if (set_symmetric_difference_update_set(result, so) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ return (PyObject *)result;
+}
static PyObject *
set_xor(PySetObject *so, PyObject *other)
@@ -1715,8 +2003,19 @@ set_ixor(PySetObject *so, PyObject *other)
return Py_NewRef(so);
}
+/*[clinic input]
+@critical_section so other
+set.issubset
+ so: setobject
+ other: object
+ /
+
+Report whether another set contains this set.
+[clinic start generated code]*/
+
static PyObject *
-set_issubset(PySetObject *so, PyObject *other)
+set_issubset_impl(PySetObject *so, PyObject *other)
+/*[clinic end generated code: output=b2b59d5f314555ce input=f2a4fd0f2537758b]*/
{
setentry *entry;
Py_ssize_t pos = 0;
@@ -1749,14 +2048,19 @@ set_issubset(PySetObject *so, PyObject *other)
Py_RETURN_TRUE;
}
-PyDoc_STRVAR(issubset_doc,
-"issubset($self, other, /)\n\
---\n\
-\n\
-Test whether every element in the set is in other.");
+/*[clinic input]
+@critical_section so other
+set.issuperset
+ so: setobject
+ other: object
+ /
+
+Report whether this set contains another set.
+[clinic start generated code]*/
static PyObject *
-set_issuperset(PySetObject *so, PyObject *other)
+set_issuperset_impl(PySetObject *so, PyObject *other)
+/*[clinic end generated code: output=ecf00ce552c09461 input=5f2e1f262e6e4ccc]*/
{
if (PyAnySet_Check(other)) {
return set_issubset((PySetObject *)other, (PyObject *)so);
@@ -1785,12 +2089,6 @@ set_issuperset(PySetObject *so, PyObject *other)
Py_RETURN_TRUE;
}
-PyDoc_STRVAR(issuperset_doc,
-"issuperset($self, other, /)\n\
---\n\
-\n\
-Test whether every element in other is in the set.");
-
static PyObject *
set_richcompare(PySetObject *v, PyObject *w, int op)
{
@@ -1804,9 +2102,9 @@ set_richcompare(PySetObject *v, PyObject *w, int op)
case Py_EQ:
if (PySet_GET_SIZE(v) != PySet_GET_SIZE(w))
Py_RETURN_FALSE;
- if (v->hash != -1 &&
- ((PySetObject *)w)->hash != -1 &&
- v->hash != ((PySetObject *)w)->hash)
+ Py_hash_t v_hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(v->hash);
+ Py_hash_t w_hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(((PySetObject *)w)->hash);
+ if (v_hash != -1 && w_hash != -1 && v_hash != w_hash)
Py_RETURN_FALSE;
return set_issubset(v, w);
case Py_NE:
@@ -1834,21 +2132,29 @@ set_richcompare(PySetObject *v, PyObject *w, int op)
Py_RETURN_NOTIMPLEMENTED;
}
+/*[clinic input]
+@critical_section
+set.add
+ so: setobject
+ object as key: object
+ /
+
+Add an element to a set.
+
+This has no effect if the element is already present.
+[clinic start generated code]*/
+
static PyObject *
-set_add(PySetObject *so, PyObject *key)
+set_add_impl(PySetObject *so, PyObject *key)
+/*[clinic end generated code: output=4cc4a937f1425c96 input=03baf62cb0e66514]*/
{
if (set_add_key(so, key))
return NULL;
Py_RETURN_NONE;
}
-PyDoc_STRVAR(add_doc,
-"Add an element to a set.\n\
-\n\
-This has no effect if the element is already present.");
-
static int
-set_contains(PySetObject *so, PyObject *key)
+set_contains_lock_held(PySetObject *so, PyObject *key)
{
PyObject *tmpkey;
int rv;
@@ -1867,21 +2173,54 @@ set_contains(PySetObject *so, PyObject *key)
return rv;
}
+int
+_PySet_Contains(PySetObject *so, PyObject *key)
+{
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(so);
+ rv = set_contains_lock_held(so, key);
+ Py_END_CRITICAL_SECTION();
+ return rv;
+}
+
+/*[clinic input]
+@critical_section
+@coexist
+set.__contains__
+ so: setobject
+ object as key: object
+ /
+
+x.__contains__(y) <==> y in x.
+[clinic start generated code]*/
+
static PyObject *
-set_direct_contains(PySetObject *so, PyObject *key)
+set___contains___impl(PySetObject *so, PyObject *key)
+/*[clinic end generated code: output=b44863d034b3c70e input=4a7d568459617f24]*/
{
long result;
- result = set_contains(so, key);
+ result = set_contains_lock_held(so, key);
if (result < 0)
return NULL;
return PyBool_FromLong(result);
}
-PyDoc_STRVAR(contains_doc, "x.__contains__(y) <==> y in x.");
+/*[clinic input]
+@critical_section
+set.remove
+ so: setobject
+ object as key: object
+ /
+
+Remove an element from a set; it must be a member.
+
+If the element is not a member, raise a KeyError.
+[clinic start generated code]*/
static PyObject *
-set_remove(PySetObject *so, PyObject *key)
+set_remove_impl(PySetObject *so, PyObject *key)
+/*[clinic end generated code: output=0b9134a2a2200363 input=893e1cb1df98227a]*/
{
PyObject *tmpkey;
int rv;
@@ -1907,13 +2246,22 @@ set_remove(PySetObject *so, PyObject *key)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(remove_doc,
-"Remove an element from a set; it must be a member.\n\
-\n\
-If the element is not a member, raise a KeyError.");
+/*[clinic input]
+@critical_section
+set.discard
+ so: setobject
+ object as key: object
+ /
+
+Remove an element from a set if it is a member.
+
+Unlike set.remove(), the discard() method does not raise
+an exception when an element is missing from the set.
+[clinic start generated code]*/
static PyObject *
-set_discard(PySetObject *so, PyObject *key)
+set_discard_impl(PySetObject *so, PyObject *key)
+/*[clinic end generated code: output=eec3b687bf32759e input=861cb7fb69b4def0]*/
{
PyObject *tmpkey;
int rv;
@@ -1934,14 +2282,17 @@ set_discard(PySetObject *so, PyObject *key)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(discard_doc,
-"Remove an element from a set if it is a member.\n\
-\n\
-Unlike set.remove(), the discard() method does not raise\n\
-an exception when an element is missing from the set.");
+/*[clinic input]
+@critical_section
+set.__reduce__
+ so: setobject
+
+Return state information for pickling.
+[clinic start generated code]*/
static PyObject *
-set_reduce(PySetObject *so, PyObject *Py_UNUSED(ignored))
+set___reduce___impl(PySetObject *so)
+/*[clinic end generated code: output=9af7d0e029df87ee input=59405a4249e82f71]*/
{
PyObject *keys=NULL, *args=NULL, *result=NULL, *state=NULL;
@@ -1962,8 +2313,17 @@ done:
return result;
}
+/*[clinic input]
+@critical_section
+set.__sizeof__
+ so: setobject
+
+S.__sizeof__() -> size of S in memory, in bytes.
+[clinic start generated code]*/
+
static PyObject *
-set_sizeof(PySetObject *so, PyObject *Py_UNUSED(ignored))
+set___sizeof___impl(PySetObject *so)
+/*[clinic end generated code: output=4bfa3df7bd38ed88 input=09e1a09f168eaa23]*/
{
size_t res = _PyObject_SIZE(Py_TYPE(so));
if (so->table != so->smalltable) {
@@ -1972,19 +2332,29 @@ set_sizeof(PySetObject *so, PyObject *Py_UNUSED(ignored))
return PyLong_FromSize_t(res);
}
-PyDoc_STRVAR(sizeof_doc, "S.__sizeof__() -> size of S in memory, in bytes");
static int
set_init(PySetObject *self, PyObject *args, PyObject *kwds)
{
PyObject *iterable = NULL;
- if (!_PyArg_NoKeywords("set", kwds))
+ if (!_PyArg_NoKeywords("set", kwds))
return -1;
if (!PyArg_UnpackTuple(args, Py_TYPE(self)->tp_name, 0, 1, &iterable))
return -1;
+
+ if (Py_REFCNT(self) == 1 && self->fill == 0) {
+ self->hash = -1;
+ if (iterable == NULL) {
+ return 0;
+ }
+ return set_update_local(self, iterable);
+ }
+ Py_BEGIN_CRITICAL_SECTION(self);
if (self->fill)
set_clear_internal(self);
self->hash = -1;
+ Py_END_CRITICAL_SECTION();
+
if (iterable == NULL)
return 0;
return set_update_internal(self, iterable);
@@ -2013,70 +2383,39 @@ set_vectorcall(PyObject *type, PyObject * const*args,
}
static PySequenceMethods set_as_sequence = {
- set_len, /* sq_length */
+ (lenfunc)set_len, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
- (objobjproc)set_contains, /* sq_contains */
+ (objobjproc)_PySet_Contains, /* sq_contains */
};
/* set object ********************************************************/
-#ifdef Py_DEBUG
-static PyObject *test_c_api(PySetObject *so, PyObject *Py_UNUSED(ignored));
-
-PyDoc_STRVAR(test_c_api_doc, "Exercises C API. Returns True.\n\
-All is well if assertions don't fail.");
-#endif
-
static PyMethodDef set_methods[] = {
- {"add", (PyCFunction)set_add, METH_O,
- add_doc},
- {"clear", (PyCFunction)set_clear, METH_NOARGS,
- clear_doc},
- {"__contains__",(PyCFunction)set_direct_contains, METH_O | METH_COEXIST,
- contains_doc},
- {"copy", (PyCFunction)set_copy, METH_NOARGS,
- copy_doc},
- {"discard", (PyCFunction)set_discard, METH_O,
- discard_doc},
- {"difference", (PyCFunction)set_difference_multi, METH_VARARGS,
- difference_doc},
- {"difference_update", (PyCFunction)set_difference_update, METH_VARARGS,
- difference_update_doc},
- {"intersection",(PyCFunction)set_intersection_multi, METH_VARARGS,
- intersection_doc},
- {"intersection_update",(PyCFunction)set_intersection_update_multi, METH_VARARGS,
- intersection_update_doc},
- {"isdisjoint", (PyCFunction)set_isdisjoint, METH_O,
- isdisjoint_doc},
- {"issubset", (PyCFunction)set_issubset, METH_O,
- issubset_doc},
- {"issuperset", (PyCFunction)set_issuperset, METH_O,
- issuperset_doc},
- {"pop", (PyCFunction)set_pop, METH_NOARGS,
- pop_doc},
- {"__reduce__", (PyCFunction)set_reduce, METH_NOARGS,
- reduce_doc},
- {"remove", (PyCFunction)set_remove, METH_O,
- remove_doc},
- {"__sizeof__", (PyCFunction)set_sizeof, METH_NOARGS,
- sizeof_doc},
- {"symmetric_difference",(PyCFunction)set_symmetric_difference, METH_O,
- symmetric_difference_doc},
- {"symmetric_difference_update",(PyCFunction)set_symmetric_difference_update, METH_O,
- symmetric_difference_update_doc},
-#ifdef Py_DEBUG
- {"test_c_api", (PyCFunction)test_c_api, METH_NOARGS,
- test_c_api_doc},
-#endif
- {"union", (PyCFunction)set_union, METH_VARARGS,
- union_doc},
- {"update", (PyCFunction)set_update, METH_VARARGS,
- update_doc},
+ SET_ADD_METHODDEF
+ SET_CLEAR_METHODDEF
+ SET___CONTAINS___METHODDEF
+ SET_COPY_METHODDEF
+ SET_DISCARD_METHODDEF
+ SET_DIFFERENCE_MULTI_METHODDEF
+ SET_DIFFERENCE_UPDATE_METHODDEF
+ SET_INTERSECTION_MULTI_METHODDEF
+ SET_INTERSECTION_UPDATE_MULTI_METHODDEF
+ SET_ISDISJOINT_METHODDEF
+ SET_ISSUBSET_METHODDEF
+ SET_ISSUPERSET_METHODDEF
+ SET_POP_METHODDEF
+ SET___REDUCE___METHODDEF
+ SET_REMOVE_METHODDEF
+ SET___SIZEOF___METHODDEF
+ SET_SYMMETRIC_DIFFERENCE_METHODDEF
+ SET_SYMMETRIC_DIFFERENCE_UPDATE_METHODDEF
+ SET_UNION_METHODDEF
+ SET_UPDATE_METHODDEF
{"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
{NULL, NULL} /* sentinel */
};
@@ -2114,8 +2453,8 @@ static PyNumberMethods set_as_number = {
};
PyDoc_STRVAR(set_doc,
-"set() -> new empty set object\n\
-set(iterable) -> new set object\n\
+"set(iterable=(), /)\n\
+--\n\
\n\
Build an unordered collection of unique elements.");
@@ -2169,28 +2508,17 @@ PyTypeObject PySet_Type = {
static PyMethodDef frozenset_methods[] = {
- {"__contains__",(PyCFunction)set_direct_contains, METH_O | METH_COEXIST,
- contains_doc},
- {"copy", (PyCFunction)frozenset_copy, METH_NOARGS,
- copy_doc},
- {"difference", (PyCFunction)set_difference_multi, METH_VARARGS,
- difference_doc},
- {"intersection", (PyCFunction)set_intersection_multi, METH_VARARGS,
- intersection_doc},
- {"isdisjoint", (PyCFunction)set_isdisjoint, METH_O,
- isdisjoint_doc},
- {"issubset", (PyCFunction)set_issubset, METH_O,
- issubset_doc},
- {"issuperset", (PyCFunction)set_issuperset, METH_O,
- issuperset_doc},
- {"__reduce__", (PyCFunction)set_reduce, METH_NOARGS,
- reduce_doc},
- {"__sizeof__", (PyCFunction)set_sizeof, METH_NOARGS,
- sizeof_doc},
- {"symmetric_difference",(PyCFunction)set_symmetric_difference, METH_O,
- symmetric_difference_doc},
- {"union", (PyCFunction)set_union, METH_VARARGS,
- union_doc},
+ SET___CONTAINS___METHODDEF
+ FROZENSET_COPY_METHODDEF
+ SET_DIFFERENCE_MULTI_METHODDEF
+ SET_INTERSECTION_MULTI_METHODDEF
+ SET_ISDISJOINT_METHODDEF
+ SET_ISSUBSET_METHODDEF
+ SET_ISSUPERSET_METHODDEF
+ SET___REDUCE___METHODDEF
+ SET___SIZEOF___METHODDEF
+ SET_SYMMETRIC_DIFFERENCE_METHODDEF
+ SET_UNION_METHODDEF
{"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
{NULL, NULL} /* sentinel */
};
@@ -2215,8 +2543,8 @@ static PyNumberMethods frozenset_as_number = {
};
PyDoc_STRVAR(frozenset_doc,
-"frozenset() -> empty frozenset object\n\
-frozenset(iterable) -> frozenset object\n\
+"frozenset(iterable=(), /)\n\
+--\n\
\n\
Build an immutable unordered collection of unique elements.");
@@ -2288,7 +2616,7 @@ PySet_Size(PyObject *anyset)
PyErr_BadInternalCall();
return -1;
}
- return PySet_GET_SIZE(anyset);
+ return set_len((PySetObject *)anyset);
}
int
@@ -2298,7 +2626,14 @@ PySet_Clear(PyObject *set)
PyErr_BadInternalCall();
return -1;
}
- return set_clear_internal((PySetObject *)set);
+ (void)set_clear((PySetObject *)set, NULL);
+ return 0;
+}
+
+void
+_PySet_ClearInternal(PySetObject *so)
+{
+ (void)set_clear_internal(so);
}
int
@@ -2308,7 +2643,12 @@ PySet_Contains(PyObject *anyset, PyObject *key)
PyErr_BadInternalCall();
return -1;
}
- return set_contains_key((PySetObject *)anyset, key);
+
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(anyset);
+ rv = set_contains_key((PySetObject *)anyset, key);
+ Py_END_CRITICAL_SECTION();
+ return rv;
}
int
@@ -2318,7 +2658,12 @@ PySet_Discard(PyObject *set, PyObject *key)
PyErr_BadInternalCall();
return -1;
}
- return set_discard_key((PySetObject *)set, key);
+
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(set);
+ rv = set_discard_key((PySetObject *)set, key);
+ Py_END_CRITICAL_SECTION();
+ return rv;
}
int
@@ -2329,7 +2674,12 @@ PySet_Add(PyObject *anyset, PyObject *key)
PyErr_BadInternalCall();
return -1;
}
- return set_add_key((PySetObject *)anyset, key);
+
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(anyset);
+ rv = set_add_key((PySetObject *)anyset, key);
+ Py_END_CRITICAL_SECTION();
+ return rv;
}
int
@@ -2348,6 +2698,23 @@ _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash
return 1;
}
+int
+_PySet_NextEntryRef(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash)
+{
+ setentry *entry;
+
+ if (!PyAnySet_Check(set)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(set);
+ if (set_next((PySetObject *)set, pos, &entry) == 0)
+ return 0;
+ *key = Py_NewRef(entry->key);
+ *hash = entry->hash;
+ return 1;
+}
+
PyObject *
PySet_Pop(PyObject *set)
{
@@ -2371,148 +2738,6 @@ _PySet_Update(PyObject *set, PyObject *iterable)
/* Exported for the gdb plugin's benefit. */
PyObject *_PySet_Dummy = dummy;
-#ifdef Py_DEBUG
-
-/* Test code to be called with any three element set.
- Returns True and original set is restored. */
-
-#define assertRaises(call_return_value, exception) \
- do { \
- assert(call_return_value); \
- assert(PyErr_ExceptionMatches(exception)); \
- PyErr_Clear(); \
- } while(0)
-
-static PyObject *
-test_c_api(PySetObject *so, PyObject *Py_UNUSED(ignored))
-{
- Py_ssize_t count;
- const char *s;
- Py_ssize_t i;
- PyObject *elem=NULL, *dup=NULL, *t, *f, *dup2, *x=NULL;
- PyObject *ob = (PyObject *)so;
- Py_hash_t hash;
- PyObject *str;
-
- /* Verify preconditions */
- assert(PyAnySet_Check(ob));
- assert(PyAnySet_CheckExact(ob));
- assert(!PyFrozenSet_CheckExact(ob));
-
- /* so.clear(); so |= set("abc"); */
- str = PyUnicode_FromString("abc");
- if (str == NULL)
- return NULL;
- set_clear_internal(so);
- if (set_update_internal(so, str)) {
- Py_DECREF(str);
- return NULL;
- }
- Py_DECREF(str);
-
- /* Exercise type/size checks */
- assert(PySet_Size(ob) == 3);
- assert(PySet_GET_SIZE(ob) == 3);
-
- /* Raise TypeError for non-iterable constructor arguments */
- assertRaises(PySet_New(Py_None) == NULL, PyExc_TypeError);
- assertRaises(PyFrozenSet_New(Py_None) == NULL, PyExc_TypeError);
-
- /* Raise TypeError for unhashable key */
- dup = PySet_New(ob);
- assertRaises(PySet_Discard(ob, dup) == -1, PyExc_TypeError);
- assertRaises(PySet_Contains(ob, dup) == -1, PyExc_TypeError);
- assertRaises(PySet_Add(ob, dup) == -1, PyExc_TypeError);
-
- /* Exercise successful pop, contains, add, and discard */
- elem = PySet_Pop(ob);
- assert(PySet_Contains(ob, elem) == 0);
- assert(PySet_GET_SIZE(ob) == 2);
- assert(PySet_Add(ob, elem) == 0);
- assert(PySet_Contains(ob, elem) == 1);
- assert(PySet_GET_SIZE(ob) == 3);
- assert(PySet_Discard(ob, elem) == 1);
- assert(PySet_GET_SIZE(ob) == 2);
- assert(PySet_Discard(ob, elem) == 0);
- assert(PySet_GET_SIZE(ob) == 2);
-
- /* Exercise clear */
- dup2 = PySet_New(dup);
- assert(PySet_Clear(dup2) == 0);
- assert(PySet_Size(dup2) == 0);
- Py_DECREF(dup2);
-
- /* Raise SystemError on clear or update of frozen set */
- f = PyFrozenSet_New(dup);
- assertRaises(PySet_Clear(f) == -1, PyExc_SystemError);
- assertRaises(_PySet_Update(f, dup) == -1, PyExc_SystemError);
- assert(PySet_Add(f, elem) == 0);
- Py_INCREF(f);
- assertRaises(PySet_Add(f, elem) == -1, PyExc_SystemError);
- Py_DECREF(f);
- Py_DECREF(f);
-
- /* Exercise direct iteration */
- i = 0, count = 0;
- while (_PySet_NextEntry((PyObject *)dup, &i, &x, &hash)) {
- s = PyUnicode_AsUTF8(x);
- assert(s && (s[0] == 'a' || s[0] == 'b' || s[0] == 'c'));
- count++;
- }
- assert(count == 3);
-
- /* Exercise updates */
- dup2 = PySet_New(NULL);
- assert(_PySet_Update(dup2, dup) == 0);
- assert(PySet_Size(dup2) == 3);
- assert(_PySet_Update(dup2, dup) == 0);
- assert(PySet_Size(dup2) == 3);
- Py_DECREF(dup2);
-
- /* Raise SystemError when self argument is not a set or frozenset. */
- t = PyTuple_New(0);
- assertRaises(PySet_Size(t) == -1, PyExc_SystemError);
- assertRaises(PySet_Contains(t, elem) == -1, PyExc_SystemError);
- Py_DECREF(t);
-
- /* Raise SystemError when self argument is not a set. */
- f = PyFrozenSet_New(dup);
- assert(PySet_Size(f) == 3);
- assert(PyFrozenSet_CheckExact(f));
- assertRaises(PySet_Discard(f, elem) == -1, PyExc_SystemError);
- assertRaises(PySet_Pop(f) == NULL, PyExc_SystemError);
- Py_DECREF(f);
-
- /* Raise KeyError when popping from an empty set */
- assert(PyNumber_InPlaceSubtract(ob, ob) == ob);
- Py_DECREF(ob);
- assert(PySet_GET_SIZE(ob) == 0);
- assertRaises(PySet_Pop(ob) == NULL, PyExc_KeyError);
-
- /* Restore the set from the copy using the PyNumber API */
- assert(PyNumber_InPlaceOr(ob, dup) == ob);
- Py_DECREF(ob);
-
- /* Verify constructors accept NULL arguments */
- f = PySet_New(NULL);
- assert(f != NULL);
- assert(PySet_GET_SIZE(f) == 0);
- Py_DECREF(f);
- f = PyFrozenSet_New(NULL);
- assert(f != NULL);
- assert(PyFrozenSet_CheckExact(f));
- assert(PySet_GET_SIZE(f) == 0);
- Py_DECREF(f);
-
- Py_DECREF(elem);
- Py_DECREF(dup);
- Py_RETURN_TRUE;
-}
-
-#undef assertRaises
-
-#endif
-
/***** Dummy Struct *************************************************/
static PyObject *
@@ -2550,8 +2775,4 @@ static PyTypeObject _PySetDummy_Type = {
Py_TPFLAGS_DEFAULT, /*tp_flags */
};
-static PyObject _dummy_struct = {
- _PyObject_EXTRA_INIT
- { _Py_IMMORTAL_REFCNT },
- &_PySetDummy_Type
-};
+static PyObject _dummy_struct = _PyObject_HEAD_INIT(&_PySetDummy_Type);