mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-127085: fix some data races in memoryview in free-threading (#127412)
This commit is contained in:
parent
1d276ec6f8
commit
4937ba54c0
3 changed files with 52 additions and 8 deletions
|
|
@ -16,7 +16,8 @@
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
from itertools import product
|
from itertools import product
|
||||||
from test.support import import_helper
|
from test import support
|
||||||
|
from test.support import import_helper, threading_helper
|
||||||
|
|
||||||
|
|
||||||
class MyObject:
|
class MyObject:
|
||||||
|
|
@ -733,5 +734,28 @@ def test_picklebuffer_reference_loop(self):
|
||||||
self.assertIsNone(wr())
|
self.assertIsNone(wr())
|
||||||
|
|
||||||
|
|
||||||
|
@threading_helper.requires_working_threading()
|
||||||
|
@support.requires_resource("cpu")
|
||||||
|
class RacingTest(unittest.TestCase):
|
||||||
|
def test_racing_getbuf_and_releasebuf(self):
|
||||||
|
"""Repeatly access the memoryview for racing."""
|
||||||
|
from multiprocessing.managers import SharedMemoryManager
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
n = 100
|
||||||
|
with SharedMemoryManager() as smm:
|
||||||
|
obj = smm.ShareableList(range(100))
|
||||||
|
threads = []
|
||||||
|
for _ in range(n):
|
||||||
|
# Issue gh-127085, the `ShareableList.count` is just a convenient way to mess the `exports`
|
||||||
|
# counter of `memoryview`, this issue has no direct relation with `ShareableList`.
|
||||||
|
threads.append(Thread(target=obj.count, args=(1,)))
|
||||||
|
|
||||||
|
with threading_helper.start_threads(threads):
|
||||||
|
pass
|
||||||
|
|
||||||
|
del obj
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Fix race when exporting a buffer from a :class:`memoryview` object on the :term:`free-threaded <free threading>` build.
|
||||||
|
|
@ -1086,6 +1086,16 @@ PyBuffer_ToContiguous(void *buf, const Py_buffer *src, Py_ssize_t len, char orde
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline Py_ssize_t
|
||||||
|
get_exports(PyMemoryViewObject *buf)
|
||||||
|
{
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
return _Py_atomic_load_ssize_relaxed(&buf->exports);
|
||||||
|
#else
|
||||||
|
return buf->exports;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
/* Release/GC management */
|
/* Release/GC management */
|
||||||
|
|
@ -1098,7 +1108,7 @@ PyBuffer_ToContiguous(void *buf, const Py_buffer *src, Py_ssize_t len, char orde
|
||||||
static void
|
static void
|
||||||
_memory_release(PyMemoryViewObject *self)
|
_memory_release(PyMemoryViewObject *self)
|
||||||
{
|
{
|
||||||
assert(self->exports == 0);
|
assert(get_exports(self) == 0);
|
||||||
if (self->flags & _Py_MEMORYVIEW_RELEASED)
|
if (self->flags & _Py_MEMORYVIEW_RELEASED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -1119,15 +1129,16 @@ static PyObject *
|
||||||
memoryview_release_impl(PyMemoryViewObject *self)
|
memoryview_release_impl(PyMemoryViewObject *self)
|
||||||
/*[clinic end generated code: output=d0b7e3ba95b7fcb9 input=bc71d1d51f4a52f0]*/
|
/*[clinic end generated code: output=d0b7e3ba95b7fcb9 input=bc71d1d51f4a52f0]*/
|
||||||
{
|
{
|
||||||
if (self->exports == 0) {
|
Py_ssize_t exports = get_exports(self);
|
||||||
|
if (exports == 0) {
|
||||||
_memory_release(self);
|
_memory_release(self);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self->exports > 0) {
|
if (exports > 0) {
|
||||||
PyErr_Format(PyExc_BufferError,
|
PyErr_Format(PyExc_BufferError,
|
||||||
"memoryview has %zd exported buffer%s", self->exports,
|
"memoryview has %zd exported buffer%s", exports,
|
||||||
self->exports==1 ? "" : "s");
|
exports==1 ? "" : "s");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1140,7 +1151,7 @@ static void
|
||||||
memory_dealloc(PyObject *_self)
|
memory_dealloc(PyObject *_self)
|
||||||
{
|
{
|
||||||
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
|
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
|
||||||
assert(self->exports == 0);
|
assert(get_exports(self) == 0);
|
||||||
_PyObject_GC_UNTRACK(self);
|
_PyObject_GC_UNTRACK(self);
|
||||||
_memory_release(self);
|
_memory_release(self);
|
||||||
Py_CLEAR(self->mbuf);
|
Py_CLEAR(self->mbuf);
|
||||||
|
|
@ -1161,7 +1172,7 @@ static int
|
||||||
memory_clear(PyObject *_self)
|
memory_clear(PyObject *_self)
|
||||||
{
|
{
|
||||||
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
|
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
|
||||||
if (self->exports == 0) {
|
if (get_exports(self) == 0) {
|
||||||
_memory_release(self);
|
_memory_release(self);
|
||||||
Py_CLEAR(self->mbuf);
|
Py_CLEAR(self->mbuf);
|
||||||
}
|
}
|
||||||
|
|
@ -1589,7 +1600,11 @@ memory_getbuf(PyObject *_self, Py_buffer *view, int flags)
|
||||||
|
|
||||||
|
|
||||||
view->obj = Py_NewRef(self);
|
view->obj = Py_NewRef(self);
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
_Py_atomic_add_ssize(&self->exports, 1);
|
||||||
|
#else
|
||||||
self->exports++;
|
self->exports++;
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1598,7 +1613,11 @@ static void
|
||||||
memory_releasebuf(PyObject *_self, Py_buffer *view)
|
memory_releasebuf(PyObject *_self, Py_buffer *view)
|
||||||
{
|
{
|
||||||
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
|
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
|
||||||
|
#ifdef Py_GIL_DISABLED
|
||||||
|
_Py_atomic_add_ssize(&self->exports, -1);
|
||||||
|
#else
|
||||||
self->exports--;
|
self->exports--;
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
/* PyBuffer_Release() decrements view->obj after this function returns. */
|
/* PyBuffer_Release() decrements view->obj after this function returns. */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue