Skip to content

Commit cf3bbbb

Browse files
committed
atomic "memcpy"
1 parent 07c98cc commit cf3bbbb

File tree

3 files changed

+70
-11
lines changed

3 files changed

+70
-11
lines changed

Lib/test/libregrtest/tsan.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# chosen because they use threads and run in a reasonable amount of time.
33

44
TSAN_TESTS = [
5+
'test_array',
56
# TODO: enable more of test_capi once bugs are fixed (GH-116908, GH-116909).
67
'test_capi.test_mem',
78
'test_capi.test_pyatomic',

Lib/test/test_array.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,26 +1961,44 @@ def copy_back_and_forth(b, a, count):
19611961
a[0] = a[1]
19621962
a[1] = a[0]
19631963

1964+
def append_and_pop(b, a, count):
1965+
v = a[0]
1966+
b.wait()
1967+
for _ in range(count):
1968+
a.append(v)
1969+
a.pop()
1970+
1971+
def copy_random(b, a, count):
1972+
end = len(a) - 1
1973+
idxs = [(random.randint(0, end), random.randint(0, end)) for _ in range(count)]
1974+
b.wait()
1975+
for src, dst in idxs:
1976+
a[dst] = a[src]
1977+
19641978
def extend_range(b, a, count):
19651979
b.wait()
19661980
for _ in range(count):
19671981
a.extend(range(10))
19681982

1969-
def append_and_pop(b, a, count):
1983+
def extend_self(b, a, count):
1984+
c = a[:]
19701985
b.wait()
19711986
for _ in range(count):
1972-
a.append(1)
1973-
a.pop()
1987+
a.extend(c)
19741988

19751989
for tc in typecodes:
19761990
if tc in 'uw':
19771991
a = array.array(tc, 'ab')
19781992
else:
19791993
a = array.array(tc, [0, 1])
19801994

1981-
self.check([copy_back_and_forth] * 10, a, 100)
1982-
self.check([append_and_pop] * 10, a, 100)
1983-
self.check([copy_back_and_forth] * 10 + [extend_range], a, 10)
1995+
self.check([copy_back_and_forth] * 10, a, 100)
1996+
self.check([append_and_pop] * 10, a, 100)
1997+
self.check([copy_back_and_forth] * 10 + [extend_self], a, 100)
1998+
1999+
if tc not in 'uw':
2000+
self.check([copy_back_and_forth] * 10 + [extend_range], a, 100)
2001+
self.check([copy_random] * 10, a * 5, 100)
19842002

19852003

19862004
if __name__ == "__main__":

Modules/arraymodule.c

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,44 @@ arraydata_set_items(arraydata *data, char *newitems, Py_ssize_t newsize, int ite
197197
return data;
198198
}
199199

200+
#ifdef Py_GIL_DISABLED
201+
202+
static void
203+
atomic_itemcpy(void *dest, const void *src, size_t n, int itemsize)
204+
{
205+
if (itemsize == 1) {
206+
for (char *d = (char *) dest, *end = d + n, *s = (char *) src;
207+
d < end; d++, s++) {
208+
_Py_atomic_store_char_relaxed(d, _Py_atomic_load_char_relaxed(s));
209+
}
210+
}
211+
else if (itemsize == 2) {
212+
for (short *d = (short *) dest, *end = d + n, *s = (short *) src;
213+
d < end; d++, s++) {
214+
_Py_atomic_store_short_relaxed(d, _Py_atomic_load_short_relaxed(s));
215+
}
216+
}
217+
else if (itemsize == 4) {
218+
for (PY_UINT32_T *d = (PY_UINT32_T *) dest, *end = d + n, *s = (PY_UINT32_T *) src;
219+
d < end; d++, s++) {
220+
_Py_atomic_store_uint32_relaxed(d, _Py_atomic_load_uint32_relaxed(s));
221+
}
222+
}
223+
else if (itemsize == 8) {
224+
for (PY_UINT64_T *d = (PY_UINT64_T *) dest, *end = d + n, *s = (PY_UINT64_T *) src;
225+
d < end; d++, s++) {
226+
_Py_atomic_store_uint64_relaxed(d, _Py_atomic_load_uint64_relaxed(s));
227+
}
228+
}
229+
else {
230+
assert(false);
231+
}
232+
}
233+
234+
#endif
235+
200236
#ifndef Py_GIL_DISABLED
237+
201238
static arraydata *
202239
arraydata_realloc(arraydata *data, Py_ssize_t size, int itemsize)
203240
{
@@ -211,6 +248,7 @@ arraydata_realloc(arraydata *data, Py_ssize_t size, int itemsize)
211248
data->allocated = size;
212249
return data;
213250
}
251+
214252
#endif
215253

216254
static int
@@ -289,7 +327,11 @@ array_resize(arrayobject *self, Py_ssize_t newsize)
289327
}
290328
if (data != NULL) {
291329
Py_ssize_t size = Py_SIZE(self);
330+
#ifdef Py_GIL_DISABLED
331+
atomic_itemcpy(newdata->items, data->items, Py_MIN(size, newsize), itemsize);
332+
#else
292333
memcpy(newdata->items, data->items, Py_MIN(size, newsize) * itemsize);
334+
#endif
293335
arraydata_free(data, _PyObject_GC_IS_SHARED(self));
294336
}
295337
_Py_atomic_store_ptr_release(&self->data, newdata);
@@ -375,7 +417,7 @@ BB_setitem(char *items, Py_ssize_t i, PyObject *v)
375417
static PyObject *
376418
u_getitem(char *items, Py_ssize_t i)
377419
{
378-
#if WCHAR_MAX > 0xFFFF
420+
#if SIZEOF_WCHAR_T > 2
379421
return PyUnicode_FromOrdinal(
380422
(wchar_t) FT_ATOMIC_LOAD_INT_RELAXED(((wchar_t *) items)[i]));
381423
#else
@@ -416,7 +458,7 @@ u_setitem(char *items, Py_ssize_t i, PyObject *v)
416458
assert(len == 1);
417459

418460
if (i >= 0) {
419-
#if WCHAR_MAX > 0xFFFF
461+
#if SIZEOF_WCHAR_T > 2
420462
FT_ATOMIC_STORE_INT_RELAXED(((wchar_t *) items)[i], w);
421463
#else
422464
FT_ATOMIC_STORE_SHORT_RELAXED(((wchar_t *) items)[i], w);
@@ -960,9 +1002,7 @@ setarrayitem_maybe_locked(PyObject *op, Py_ssize_t i, PyObject *v)
9601002
if (!valid_index(i, data->allocated)) {
9611003
goto error;
9621004
}
963-
int ret = setarrayitem(ap, i, v, data->items);
964-
_Py_atomic_fence_release();
965-
return ret;
1005+
return setarrayitem(ap, i, v, data->items);
9661006

9671007
error:
9681008
PyErr_SetString(PyExc_IndexError, "array index out of range");

0 commit comments

Comments
 (0)