GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — When IterableWrapper iterates up to the middle of list, System.AccessViolationException occurs. by JakeJP · Pull Request #2280 · pythonnet/pythonnet

Conversation

@JakeJP

@JakeJP JakeJP commented Nov 7, 2023

Copy link
Copy Markdown
Contributor

System.AccessViolationException occurs when iteration quits in the middle of list before reaching the end.

Environment

  • Pythonnet version: 3.0.3
  • Python version: 3.12.0
  • Operating System: Windows 11
  • .NET Runtime: .NET Framework 4.8

Reproduce the problem

I'm trying to pass a list from python to .NET method via List Codec.

mylist = ["a","b","c"]
dotnet.method( mylist )

Conversion(decode) is successful and mylist is wrapped by ListWrapper.
.NET code receives the list like this.

void method( IList<string> mylist )
{
    mylist.First( s => s == "b" ); // <- here the crash with System.AccessViolationException
}

Using Enumerator of mylist, if enumerated to the end of the list, nothing bad happens.
However if iteration didn't go to the end of the list, AccessViolationException is thrown.
This case happens in the following code:

mylist.First( s => s == "b" ); // iteration stops at the 2nd item

similarly

foreach( var item in mylist )
{
    if( item == "b" ) break; // iteration stops at the 2nd item
}
mylist.Contains("b"); // iteration stops at the 2nd item
mylist.Any( s => s == "b"); // iteration stops at the 2nd item

The following code doesn't throw exception becuase it always iterates to the end of the list.

mylist.ToList();
mylist.ToArray();

Exception occurs because PyIter(ator) object is Dispose(d) outside of GIL protection only when iteration ends before reaching the end ( before MoveNext() returns null ).
In such a case, code process just slips out of GIL enclosure as the nature of C# scope and yield return and break.

solution for now

original code is like this:

        public IEnumerator<T> GetEnumerator()
        {
            PyIter iterObject;
            using (Py.GIL())
            {
                iterObject = PyIter.GetIter(pyObject);
            }

            using var _ = iterObject;
            while (true)
            {
                using var GIL = Py.GIL();

                if (!iterObject.MoveNext())
                {
                    iterObject.Dispose();
                    break;
                }
                yield return iterObject.Current.As<T>()!;
            }
        }

using var _ = iterObject; above the while loop is the problematic line.
Before iteration reaches the end, the executing process is somewhere out of the using scope.
When c# caller process stops iteration(Enumeration) for some reason, c# suddely calls
using var _ = iterObject; 's closing process which is iterObject.Dispose().
But here iterObject is not inside of GIL() protection. This is why exception occurs.
Solution is to make sure iterObject.Dispose() is always inside of GIL protection and
always executed whatever the caller process abandons the enumeration in the middle.

My suggestion is to use try catch pattern. Without catching any exception it may look odd. But this is one solution to make sure the execution of Dispose and GIL scope surrounding always.

        public IEnumerator<T> GetEnumerator()
        {
            PyIter iterObject;
            using (Py.GIL())
            {
                iterObject = PyIter.GetIter(pyObject);
            }
            try
            {
                while (true)
                {
                    using var _ = Py.GIL();
                    if (!iterObject.MoveNext())
                    {
                        break;
                    }
                    yield return iterObject.Current.As<T>()!;
                }
            }
            finally
            {
                using var _ = Py.GIL();
                iterObject.Dispose();
            }
        }

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@filmor

filmor commented Nov 7, 2023

Copy link
Copy Markdown
Member

Great catch, thank you very much!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

lostmsu
lostmsu requested changes Nov 7, 2023
View reviewed changes

@lostmsu lostmsu left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, revert whitespace changes (wrong line breaks?)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@JakeJP
To fix memory access exception when iteration breaks in the middle of…
0a84031
… the list before reaching end.
@JakeJP
JakeJP force-pushed the master branch from a6f27ab to 0a84031 Compare November 7, 2023 21:08
@JakeJP

JakeJP commented Nov 7, 2023

Copy link
Copy Markdown
Contributor Author

I amended the last commit with 'right' line breaks. Did it work propertly? Sorry I'm not used to this 'push request' workflow.

lostmsu reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

lostmsu
lostmsu approved these changes Nov 7, 2023
View reviewed changes
@lostmsu
lostmsu merged commit eef67db into pythonnet:master Nov 7, 2023
@dependabot dependabot Bot mentioned this pull request Jan 16, 2026
Bump pythonnet from 3.0.3 to 3.0.5 mattemangia/Deep3DStudio#99
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@lostmsu lostmsu lostmsu approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@JakeJP @filmor @lostmsu
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.