interactive icon indicating copy to clipboard operation
interactive copied to clipboard

PlainTextFormatter<T> causes infinite loop when T is IEnumerable<T>

Open duyang76 opened this issue 1 year ago • 3 comments

I can replicate this issue with a bare minimum example: a class implementing IEnumerable of itself with an empty child list.

Version: 1.0.522904

  • OS
    • [ ] Windows 10
  • Frontend
    • [ ] Visual Studio Code

I cannot post from my company's internal network, so I'm typing from my phone and taking a screenshot below. But it is pretty obvious.

IMG20240805111604

duyang76 avatar Aug 05 '24 15:08 duyang76

Can someone take a look or provide some insight?

The class definition is below. It runs fine. But if I create an instance in polyglot notebook, the kernel crashes due to stack overflow. There is no infinite-loop in the code, just recursive call on the child nodes (which is empty). It seems that the .net interactive or polyglot causes infinite-loop somewhere.

public class TestIEnumerator : IEnumerable<TestIEnumerator>
{
	private List<TestIEnumerator> children;	
	public List<TestIEnumerator> Children
	{
		get
		{
			if (children==null) children = new List<TestIEnumerator>();
			return children;
		}
	}
	
	public IEnumerator<TestIEnumerator> GetEnumerator()
	{
		List<TestIEnumerator> childList = new List<TestIEnumerator>();
		recurseChildren(this, ref childList, 0);
		return childList.GetEnumerator();
	}
	
	private void recurseChildren(TestIEnumerator f, ref List<TestIEnumerator>coll, int lvl)
	{
		coll.Add(f);
		Console.WriteLine("RecurseChildren level: " + lvl);
		foreach(TestIEnumerator tmp in f.Children)
		{
			RecurseChildren(tmp, ref coll, lvl+1);
		}
	}
	
	System.Collection.IEnumerator System.Collection.IEnumerable.GetEnumerator()
	{
		return this.GetEnumerator();
	}
}

duyang76 avatar Aug 29 '24 15:08 duyang76

Can someone provide some thoughts?

duyang76 avatar Sep 11 '24 16:09 duyang76

I looked at the stack trace and source code, and found that the infinite loop is caused by the CreateForAnyEnumerable() in PlainTextFormatter{T}.cs (https://github.com/dotnet/interactive/blob/main/src/Microsoft.DotNet.Interactive.Formatting/PlainTextFormatter%7BT%7D.cs#L73). In particular, the IEnumerable<> look through around line 84. Even though FormatContext has limit for recursion depth, it does not apply here. So, it causes infinite loop if if T is IEnumerable<T>.

I am able to work around by overriding PlainTextFormatter<T>.Default to my own formatter. But what is the correct fix? Is the look-through still needed if T is IEnumerable<T>?

duyang76 avatar Sep 27 '24 14:09 duyang76