Skip to content

Commit e84805c

Browse files
committed
Restore memoryview pin helper signatures and release exhausted iterators
1 parent b68565c commit e84805c

2 files changed

Lines changed: 90 additions & 78 deletions

File tree

‎Lib/test/test_free_threading/test_memoryview.py‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,11 +277,21 @@ def iterator():
277277
self.ensure_exporter_free(buf)
278278

279279
def test_iterator_exhaustion_drops_last_reference(self):
280-
def loop():
281-
for _ in range(ROUNDS * ITERS):
282-
self.assertEqual(list(iter(memoryview(b"ab"))), [97, 98])
280+
for _ in range(ROUNDS):
281+
data = bytearray(range(64))
282+
iterator = iter(memoryview(data))
283+
values = []
284+
lock = threading.Lock()
285+
286+
def consume():
287+
local = list(iterator)
288+
with lock:
289+
values.extend(local)
283290

284-
threading_helper.run_concurrently(loop, nthreads=NTHREADS)
291+
threading_helper.run_concurrently(consume, nthreads=NTHREADS)
292+
self.assertEqual(sorted(values), list(data))
293+
data.extend(b"x")
294+
self.assertRaises(StopIteration, next, iterator)
285295

286296
def test_concurrent_hash(self):
287297
for _ in range(ROUNDS):

‎Objects/memoryobject.c‎

Lines changed: 76 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -825,32 +825,31 @@ mbuf_drop_export(_PyManagedBufferObject *mbuf)
825825
free the underlying buffer while the operation is running. The pin does
826826
not hold a reference to the managed buffer: the memoryview's own
827827
reference is kept until memory_dealloc(), and the caller holds a
828-
reference to the memoryview. On success, return the pinned managed
829-
buffer; pass it to memoryview_unpin() afterwards. On failure, set an
830-
exception and return NULL. */
831-
static inline _PyManagedBufferObject *
828+
reference to the memoryview through memoryview_unpin(self). Return 0
829+
on success and -1 with an exception on failure. */
830+
static inline int
832831
memoryview_pin(PyMemoryViewObject *self)
833832
{
834833
if (mv_has_flag(self, _Py_MEMORYVIEW_RELEASED)) {
835834
PyErr_SetString(PyExc_ValueError,
836835
"operation forbidden on released memoryview object");
837-
return NULL;
836+
return -1;
838837
}
839838
_PyManagedBufferObject *mbuf = self->mbuf;
840839
FT_ATOMIC_ADD_SSIZE(mbuf->exports, 1);
841840
if (BASE_INACCESSIBLE(self)) {
842841
mbuf_drop_export(mbuf);
843842
PyErr_SetString(PyExc_ValueError,
844843
"operation forbidden on released memoryview object");
845-
return NULL;
844+
return -1;
846845
}
847-
return mbuf;
846+
return 0;
848847
}
849848

850849
static inline void
851-
memoryview_unpin(_PyManagedBufferObject *mbuf)
850+
memoryview_unpin(PyMemoryViewObject *self)
852851
{
853-
mbuf_drop_export(mbuf);
852+
mbuf_drop_export(self->mbuf);
854853
}
855854

856855
/* Expose a raw memory area as a view of contiguous bytes. flags can be
@@ -923,8 +922,7 @@ PyMemoryView_FromObjectAndFlags(PyObject *v, int flags)
923922

924923
if (PyMemoryView_Check(v)) {
925924
PyMemoryViewObject *mv = (PyMemoryViewObject *)v;
926-
_PyManagedBufferObject *mv_mbuf = memoryview_pin(mv);
927-
if (mv_mbuf == NULL) {
925+
if (memoryview_pin(mv) < 0) {
928926
return NULL;
929927
}
930928
PyObject *result;
@@ -934,9 +932,9 @@ PyMemoryView_FromObjectAndFlags(PyObject *v, int flags)
934932
result = NULL;
935933
}
936934
else {
937-
result = mbuf_add_view(mv_mbuf, &mv->view);
935+
result = mbuf_add_view(mv->mbuf, &mv->view);
938936
}
939-
memoryview_unpin(mv_mbuf);
937+
memoryview_unpin(mv);
940938
return result;
941939
}
942940
else if (PyObject_CheckBuffer(v)) {
@@ -1691,12 +1689,11 @@ memoryview_cast_impl(PyMemoryViewObject *self, PyObject *format,
16911689
PyObject *shape, int order)
16921690
/*[clinic end generated code: output=6410d87141f6bb56 input=4a1a2326c59caeb3]*/
16931691
{
1694-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
1695-
if (self_mbuf == NULL) {
1692+
if (memoryview_pin(self) < 0) {
16961693
return NULL;
16971694
}
16981695
PyObject *result = memoryview_cast_pinned(self, format, shape, order);
1699-
memoryview_unpin(self_mbuf);
1696+
memoryview_unpin(self);
17001697
return result;
17011698
}
17021699

@@ -1725,12 +1722,11 @@ static PyObject *
17251722
memoryview_toreadonly_impl(PyMemoryViewObject *self)
17261723
/*[clinic end generated code: output=2c7e056f04c99e62 input=dc06d20f19ba236f]*/
17271724
{
1728-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
1729-
if (self_mbuf == NULL) {
1725+
if (memoryview_pin(self) < 0) {
17301726
return NULL;
17311727
}
17321728
PyObject *result = memoryview_toreadonly_pinned(self);
1733-
memoryview_unpin(self_mbuf);
1729+
memoryview_unpin(self);
17341730
return result;
17351731
}
17361732

@@ -2540,12 +2536,11 @@ static PyObject *
25402536
memoryview_tolist_impl(PyMemoryViewObject *self)
25412537
/*[clinic end generated code: output=a6cda89214fd5a1b input=21e7d0c1860b211a]*/
25422538
{
2543-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
2544-
if (self_mbuf == NULL) {
2539+
if (memoryview_pin(self) < 0) {
25452540
return NULL;
25462541
}
25472542
PyObject *result = memoryview_tolist_pinned(self);
2548-
memoryview_unpin(self_mbuf);
2543+
memoryview_unpin(self);
25492544
return result;
25502545
}
25512546

@@ -2605,12 +2600,11 @@ static PyObject *
26052600
memoryview_tobytes_impl(PyMemoryViewObject *self, const char *order)
26062601
/*[clinic end generated code: output=1288b62560a32a23 input=119c70aa91791dc8]*/
26072602
{
2608-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
2609-
if (self_mbuf == NULL) {
2603+
if (memoryview_pin(self) < 0) {
26102604
return NULL;
26112605
}
26122606
PyObject *result = memoryview_tobytes_pinned(self, order);
2613-
memoryview_unpin(self_mbuf);
2607+
memoryview_unpin(self);
26142608
return result;
26152609
}
26162610

@@ -2684,12 +2678,11 @@ memoryview_hex_impl(PyMemoryViewObject *self, PyObject *sep,
26842678
Py_ssize_t bytes_per_sep)
26852679
/*[clinic end generated code: output=c9bb00c7a8e86056 input=3f1c5d08906e3b70]*/
26862680
{
2687-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
2688-
if (self_mbuf == NULL) {
2681+
if (memoryview_pin(self) < 0) {
26892682
return NULL;
26902683
}
26912684
PyObject *result = memoryview_hex_pinned(self, sep, bytes_per_sep);
2692-
memoryview_unpin(self_mbuf);
2685+
memoryview_unpin(self);
26932686
return result;
26942687
}
26952688

@@ -3080,38 +3073,35 @@ static PyObject *
30803073
memory_item(PyObject *_self, Py_ssize_t index)
30813074
{
30823075
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
3083-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
3084-
if (self_mbuf == NULL) {
3076+
if (memoryview_pin(self) < 0) {
30853077
return NULL;
30863078
}
30873079
PyObject *result = memory_item_pinned(_self, index);
3088-
memoryview_unpin(self_mbuf);
3080+
memoryview_unpin(self);
30893081
return result;
30903082
}
30913083

30923084
static PyObject *
30933085
memory_subscript(PyObject *_self, PyObject *key)
30943086
{
30953087
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
3096-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
3097-
if (self_mbuf == NULL) {
3088+
if (memoryview_pin(self) < 0) {
30983089
return NULL;
30993090
}
31003091
PyObject *result = memory_subscript_pinned(_self, key);
3101-
memoryview_unpin(self_mbuf);
3092+
memoryview_unpin(self);
31023093
return result;
31033094
}
31043095

31053096
static int
31063097
memory_ass_sub(PyObject *_self, PyObject *key, PyObject *value)
31073098
{
31083099
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
3109-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
3110-
if (self_mbuf == NULL) {
3100+
if (memoryview_pin(self) < 0) {
31113101
return -1;
31123102
}
31133103
int result = memory_ass_sub_pinned(_self, key, value);
3114-
memoryview_unpin(self_mbuf);
3104+
memoryview_unpin(self);
31153105
return result;
31163106
}
31173107

@@ -3182,12 +3172,11 @@ static PyObject *
31823172
memoryview_count_impl(PyMemoryViewObject *self, PyObject *value)
31833173
/*[clinic end generated code: output=a15cb19311985063 input=e3036ce1ed7d1823]*/
31843174
{
3185-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
3186-
if (self_mbuf == NULL) {
3175+
if (memoryview_pin(self) < 0) {
31873176
return NULL;
31883177
}
31893178
PyObject *result = memoryview_count_pinned(self, value);
3190-
memoryview_unpin(self_mbuf);
3179+
memoryview_unpin(self);
31913180
return result;
31923181
}
31933182

@@ -3282,12 +3271,11 @@ memoryview_index_impl(PyMemoryViewObject *self, PyObject *value,
32823271
Py_ssize_t start, Py_ssize_t stop)
32833272
/*[clinic end generated code: output=e0185e3819e549df input=0697a0165bf90b5a]*/
32843273
{
3285-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
3286-
if (self_mbuf == NULL) {
3274+
if (memoryview_pin(self) < 0) {
32873275
return NULL;
32883276
}
32893277
PyObject *result = memoryview_index_pinned(self, value, start, stop);
3290-
memoryview_unpin(self_mbuf);
3278+
memoryview_unpin(self);
32913279
return result;
32923280
}
32933281

@@ -3636,30 +3624,27 @@ static PyObject *
36363624
memory_richcompare(PyObject *v, PyObject *w, int op)
36373625
{
36383626
PyMemoryViewObject *self = (PyMemoryViewObject *)v;
3639-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
3640-
if (self_mbuf == NULL) {
3627+
if (memoryview_pin(self) < 0) {
36413628
/* Released memoryviews still compare by identity. */
36423629
PyErr_Clear();
36433630
return memory_richcompare_pinned(v, w, op);
36443631
}
36453632

36463633
PyMemoryViewObject *other = NULL;
3647-
_PyManagedBufferObject *other_mbuf = NULL;
36483634
if (PyMemoryView_Check(w) && w != v) {
36493635
other = (PyMemoryViewObject *)w;
3650-
other_mbuf = memoryview_pin(other);
3651-
if (other_mbuf == NULL) {
3636+
if (memoryview_pin(other) < 0) {
36523637
PyErr_Clear();
3653-
memoryview_unpin(self_mbuf);
3638+
memoryview_unpin(self);
36543639
return memory_richcompare_pinned(v, w, op);
36553640
}
36563641
}
36573642

36583643
PyObject *result = memory_richcompare_pinned(v, w, op);
36593644
if (other != NULL) {
3660-
memoryview_unpin(other_mbuf);
3645+
memoryview_unpin(other);
36613646
}
3662-
memoryview_unpin(self_mbuf);
3647+
memoryview_unpin(self);
36633648
return result;
36643649
}
36653650

@@ -3740,12 +3725,11 @@ memory_hash(PyObject *_self)
37403725
if (hash != -1) {
37413726
return hash;
37423727
}
3743-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
3744-
if (self_mbuf == NULL) {
3728+
if (memoryview_pin(self) < 0) {
37453729
return -1;
37463730
}
37473731
Py_hash_t result = memory_hash_pinned(_self);
3748-
memoryview_unpin(self_mbuf);
3732+
memoryview_unpin(self);
37493733
return result;
37503734
}
37513735

@@ -3884,12 +3868,11 @@ memory_contiguous(PyObject *_self, void *Py_UNUSED(ignored))
38843868
name(PyObject *_self, void *ignored) \
38853869
{ \
38863870
PyMemoryViewObject *self = (PyMemoryViewObject *)_self; \
3887-
_PyManagedBufferObject *mbuf = memoryview_pin(self); \
3888-
if (mbuf == NULL) { \
3871+
if (memoryview_pin(self) < 0) { \
38893872
return NULL; \
38903873
} \
38913874
PyObject *result = name##_pinned(_self, ignored); \
3892-
memoryview_unpin(mbuf); \
3875+
memoryview_unpin(self); \
38933876
return result; \
38943877
}
38953878

@@ -3996,37 +3979,57 @@ static PyObject *
39963979
memoryiter_next(PyObject *self)
39973980
{
39983981
memoryiterobject *it = (memoryiterobject *)self;
3999-
PyMemoryViewObject *seq = it->it_seq;
4000-
if (seq == NULL) {
3982+
PyMemoryViewObject *seq;
3983+
Py_ssize_t index;
3984+
3985+
Py_BEGIN_CRITICAL_SECTION(it);
3986+
seq = it->it_seq;
3987+
index = it->it_index;
3988+
if (seq != NULL) {
3989+
if (index >= it->it_length) {
3990+
/* Transfer the iterator's reference to seq before clearing it. */
3991+
it->it_seq = NULL;
3992+
}
3993+
else {
3994+
Py_INCREF(seq);
3995+
}
3996+
}
3997+
Py_END_CRITICAL_SECTION();
3998+
3999+
if (seq == NULL || index >= it->it_length) {
4000+
Py_XDECREF(seq);
40014001
return NULL;
40024002
}
40034003

4004-
if (it->it_index >= it->it_length) {
4005-
/* Preserve StopIteration for exhausted iterators over released views. */
4006-
#ifndef Py_GIL_DISABLED
4007-
/* Like list and tuple iterators, keep the reference in the
4008-
free-threaded build: clearing it here races with other threads
4009-
that are still using seq. */
4010-
it->it_seq = NULL;
4004+
/* Pin outside the iterator lock: a failed pin may release the buffer. */
4005+
if (memoryview_pin(seq) < 0) {
40114006
Py_DECREF(seq);
4012-
#endif
40134007
return NULL;
40144008
}
40154009

4016-
_PyManagedBufferObject *mbuf = memoryview_pin(seq);
4017-
if (mbuf == NULL) {
4010+
Py_BEGIN_CRITICAL_SECTION(it);
4011+
index = it->it_index;
4012+
if (index < it->it_length) {
4013+
it->it_index = index + 1;
4014+
}
4015+
Py_END_CRITICAL_SECTION();
4016+
if (index >= it->it_length) {
4017+
memoryview_unpin(seq);
4018+
Py_DECREF(seq);
40184019
return NULL;
40194020
}
4021+
40204022
Py_buffer *view = &seq->view;
40214023
char *ptr = (char *)view->buf;
40224024

4023-
ptr += view->strides[0] * it->it_index++;
4025+
ptr += view->strides[0] * index;
40244026
ptr = ADJUST_PTR(ptr, view->suboffsets, 0);
40254027
PyObject *result = NULL;
40264028
if (ptr != NULL) {
40274029
result = unpack_single(seq, ptr, it->it_fmt);
40284030
}
4029-
memoryview_unpin(mbuf);
4031+
memoryview_unpin(seq);
4032+
Py_DECREF(seq);
40304033
return result;
40314034
}
40324035

@@ -4072,12 +4075,11 @@ static PyObject *
40724075
memory_iter(PyObject *seq)
40734076
{
40744077
PyMemoryViewObject *self = (PyMemoryViewObject *)seq;
4075-
_PyManagedBufferObject *self_mbuf = memoryview_pin(self);
4076-
if (self_mbuf == NULL) {
4078+
if (memoryview_pin(self) < 0) {
40774079
return NULL;
40784080
}
40794081
PyObject *result = memory_iter_pinned(seq);
4080-
memoryview_unpin(self_mbuf);
4082+
memoryview_unpin(self);
40814083
return result;
40824084
}
40834085

0 commit comments

Comments
 (0)