Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
12 changes: 8 additions & 4 deletions src/runtime/CollectionWrappers/IterableWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@ public IEnumerator<T> GetEnumerator()
{
while (true)
{
using var _ = Py.GIL();
if (!iterObject.MoveNext())
T result;
using (Py.GIL())
{
break;
if (!iterObject.MoveNext())
{
break;
}
result = iterObject.Current.As<T>()!;
}
yield return iterObject.Current.As<T>()!;
yield return result;
}
}
finally
Expand Down
32 changes: 32 additions & 0 deletions src/testing/IterableRegressionTester.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Collections.Generic;

using Python.Runtime;

namespace Python.Test
{
public class IterableRegressionTester
{
public string PartialIterationDoesNotHoldGIL(IEnumerable<object> iterable)
{
var iterator = iterable.GetEnumerator();
try
{
if (!iterator.MoveNext())
{
return "empty";
}

if (DebugUtil.HaveInterpreterLock())
{
return "gil_held_after_partial_iteration";
}

return "ok";
}
finally
{
iterator.Dispose();
}
}
}
}
20 changes: 19 additions & 1 deletion tests/test_codec.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
import pytest
import Python.Runtime
import Python.Test as Test
from Python.Test import ListConversionTester, ListMember, CodecResetter
from Python.Test import (
CodecResetter,
IterableRegressionTester,
ListConversionTester,
ListMember,
)


@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -59,6 +64,19 @@ def test_iterable():
assert 3 == ob.GetLength2(iterable2)


def test_iterable_partial_iteration_does_not_hold_gil():
"""Test to ensure partial enumeration does not hold GIL"""
Python.Runtime.Codecs.IterableDecoder.Register()
probe = IterableRegressionTester()

result = probe.PartialIterationDoesNotHoldGIL(int_iterable())
assert result == "ok", (
"Partial iteration blocked managed callback thread; "
"IterableWrapper may still be yielding inside Py.GIL(). "
f"Probe result: {result}"
)


def test_sequence():
Python.Runtime.Codecs.SequenceDecoder.Register()
ob = ListConversionTester()
Expand Down