Skip to content

Commit b1c6768

Browse files
committed
SET_ADD should not lock
1 parent 303043f commit b1c6768

File tree

5 files changed

+80
-108
lines changed

5 files changed

+80
-108
lines changed

Include/internal/pycore_setobject.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ PyAPI_FUNC(int) _PySet_Contains(PySetObject *so, PyObject *key);
3333
// Clears the set without acquiring locks. Used by _PyCode_Fini.
3434
extern void _PySet_ClearInternal(PySetObject *so);
3535

36+
PyAPI_FUNC(int) _PySet_AddTakeRef(PySetObject *so, PyObject *key);
37+
38+
PyAPI_FUNC(PyObject *)
39+
_PySet_FromStackRefSteal(const _PyStackRef *src, Py_ssize_t n);
40+
41+
42+
3643
#ifdef __cplusplus
3744
}
3845
#endif

Objects/setobject.c

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)
122122
static int set_table_resize(PySetObject *, Py_ssize_t);
123123

124124
static int
125-
set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
125+
set_add_entry_takeref(PySetObject *so, PyObject *key, Py_hash_t hash)
126126
{
127127
setentry *table;
128128
setentry *freeslot;
@@ -133,12 +133,6 @@ set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
133133
int probes;
134134
int cmp;
135135

136-
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
137-
138-
/* Pre-increment is necessary to prevent arbitrary code in the rich
139-
comparison from deallocating the key just before the insertion. */
140-
Py_INCREF(key);
141-
142136
restart:
143137

144138
mask = so->mask;
@@ -209,6 +203,57 @@ set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
209203
return -1;
210204
}
211205

206+
static int
207+
set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
208+
{
209+
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
210+
211+
/* Pre-increment is necessary to prevent arbitrary code in the rich
212+
comparison from deallocating the key just before the insertion. */
213+
Py_INCREF(key);
214+
215+
return set_add_entry_takeref(so, key, hash);
216+
}
217+
218+
int
219+
_PySet_AddTakeRef(PySetObject *so, PyObject *key)
220+
{
221+
Py_hash_t hash = _PyObject_HashFast(key);
222+
if (hash == -1) {
223+
return -1;
224+
}
225+
// We don't pre-increment here, the caller holds a strong
226+
// reference to the object which we are stealing.
227+
return set_add_entry_takeref(so, key, hash);
228+
}
229+
230+
PyObject *
231+
_PySet_FromStackRefSteal(const _PyStackRef *src, Py_ssize_t n)
232+
{
233+
PySetObject *set = (PySetObject *)PySet_New(NULL);
234+
if (n == 0) {
235+
return (PyObject *)set;
236+
}
237+
238+
if (set_table_resize(set, n*2) != 0) {
239+
return NULL;
240+
}
241+
242+
int err = 0;
243+
for (Py_ssize_t i = 0; i < n; i++) {
244+
if (err == 0) {
245+
err = _PySet_AddTakeRef(set, PyStackRef_AsPyObjectSteal(src[i]));
246+
} else {
247+
PyStackRef_CLOSE(src[i]);
248+
}
249+
}
250+
if (err) {
251+
Py_CLEAR(set);
252+
}
253+
254+
return (PyObject *)set;
255+
}
256+
212257
/*
213258
Internal routine used by set_table_resize() to insert an item which is
214259
known to be absent from the set. Besides the performance benefit,

Python/bytecodes.c

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -964,9 +964,8 @@ dummy_func(
964964
}
965965

966966
inst(SET_ADD, (set, unused[oparg-1], v -- set, unused[oparg-1])) {
967-
int err = PySet_Add(PyStackRef_AsPyObjectBorrow(set),
968-
PyStackRef_AsPyObjectBorrow(v));
969-
PyStackRef_CLOSE(v);
967+
int err = _PySet_AddTakeRef((PySetObject *)PyStackRef_AsPyObjectBorrow(set),
968+
PyStackRef_AsPyObjectSteal(v));
970969
ERROR_IF(err, error);
971970
}
972971

@@ -1899,22 +1898,11 @@ dummy_func(
18991898
}
19001899

19011900
inst(BUILD_SET, (values[oparg] -- set)) {
1902-
PyObject *set_o = PySet_New(NULL);
1901+
PyObject *set_o = _PySet_FromStackRefSteal(values, oparg);
19031902
if (set_o == NULL) {
1904-
DECREF_INPUTS();
1905-
ERROR_IF(true, error);
1906-
}
1907-
int err = 0;
1908-
for (int i = 0; i < oparg; i++) {
1909-
if (err == 0) {
1910-
err = PySet_Add(set_o, PyStackRef_AsPyObjectBorrow(values[i]));
1911-
}
1912-
}
1913-
DECREF_INPUTS();
1914-
if (err != 0) {
1915-
Py_DECREF(set_o);
19161903
ERROR_IF(true, error);
19171904
}
1905+
INPUTS_DEAD();
19181906
set = PyStackRef_FromPyObjectSteal(set_o);
19191907
}
19201908

Python/executor_cases.c.h

Lines changed: 9 additions & 42 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 8 additions & 43 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)