Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion Lib/test/test_memoryview.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
import copy
import pickle
import struct
import multiprocessing.shared_memory
from threading import Thread

from test.support import import_helper
from test.support import import_helper, threading_helper


class MyObject:
Expand Down Expand Up @@ -679,5 +681,24 @@ def test_picklebuffer_reference_loop(self):
self.assertIsNone(wr())


@threading_helper.requires_working_threading()
class RacingTest(unittest.TestCase):
def test_racing_getbuf_and_releasebuf(self):
"""Repeatly access the memoryview for racing."""
n = 100

obj = multiprocessing.shared_memory.ShareableList("Uq..SeDAmB+EBrkLl.SG.Z+Z.ZdsV..wT+zLxKwdN\b")
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__":
unittest.main()
29 changes: 20 additions & 9 deletions Objects/memoryobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,16 @@ PyBuffer_ToContiguous(void *buf, const Py_buffer *src, Py_ssize_t len, char orde
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 */
Expand All @@ -1098,7 +1108,7 @@ PyBuffer_ToContiguous(void *buf, const Py_buffer *src, Py_ssize_t len, char orde
static void
_memory_release(PyMemoryViewObject *self)
{
assert(self->exports == 0);
assert(get_exports(self) == 0);
if (self->flags & _Py_MEMORYVIEW_RELEASED)
return;

Expand All @@ -1119,15 +1129,16 @@ static PyObject *
memoryview_release_impl(PyMemoryViewObject *self)
/*[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);
Py_RETURN_NONE;
}

if (self->exports > 0) {
if (exports > 0) {
PyErr_Format(PyExc_BufferError,
"memoryview has %zd exported buffer%s", self->exports,
self->exports==1 ? "" : "s");
"memoryview has %zd exported buffer%s", exports,
exports==1 ? "" : "s");
return NULL;
}

Expand All @@ -1140,7 +1151,7 @@ static void
memory_dealloc(PyObject *_self)
{
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
assert(self->exports == 0);
assert(get_exports(self) == 0);
_PyObject_GC_UNTRACK(self);
_memory_release(self);
Py_CLEAR(self->mbuf);
Expand All @@ -1161,7 +1172,7 @@ static int
memory_clear(PyObject *_self)
{
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
if (self->exports == 0) {
if (get_exports(self) == 0) {
_memory_release(self);
Py_CLEAR(self->mbuf);
}
Expand Down Expand Up @@ -1589,7 +1600,7 @@ memory_getbuf(PyObject *_self, Py_buffer *view, int flags)


view->obj = Py_NewRef(self);
self->exports++;
_Py_atomic_add_ssize(&self->exports, 1);

return 0;
}
Expand All @@ -1598,7 +1609,7 @@ static void
memory_releasebuf(PyObject *_self, Py_buffer *view)
{
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
self->exports--;
_Py_atomic_add_ssize(&self->exports, -1);
return;
/* PyBuffer_Release() decrements view->obj after this function returns. */
}
Expand Down
Loading