Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a yield in a foreach-iteration through an array within a lock crash the VS2010 compiler?

A question related to the C#/.NET compiler used in Visual Studio 2010: During the development of a project, a colleague encountered a situation where the VS2010 compiler would crash when using existing code inside a lock. We took the code apart line-by-line to eventually come to the conclusion that using a yield return inside a foreach through an array within a lock statement would crash the compiler. The issue can be reproduced with the following code:

using System;
using System.Collections.Generic;

namespace PlayGround
{
    public static class Program
    {
        private static readonly object SyncRoot = new object();

        private static IEnumerable<int> EnumerableWithLock()
        {
            lock (SyncRoot)
            {
                foreach (var i in new int[1])
                {
                    yield return i;
                }
            }
        }

        public static void Main()
        {
            foreach (var i in EnumerableWithLock())
            {
                Console.WriteLine(i);
            }
        }
    }
}

We continued to test this reproduction sample on Visual Studio 2013 and it did not exhibit the same problem. This compiler issue seems to be related to the compiler used in VS2010, and might or might not have the same problem in VS2012 (we do not have access to it for testing purposes). Furthermore, we have tested that using a regular for loop does not crash. The question therefore is, why does the VS2010 compiler crash? What is it doing that confuses it so much?

(Yes, this is mostly a question for interests-sake to learn about the compiler)

like image 623
Deathspike Avatar asked Jul 31 '14 10:07

Deathspike


1 Answers

Well, may be this does not resolve the issue but here is my research..

Theoretically, Jon Skeet says that yield creates no problems at all when used with locks as the lock is acquired and release before and after the first and last 'MoveNext' iteration block respectively.

More on that here.

When I personally tried your code, the compiler threw the following (internal) errors:

Error   6   Internal Compiler Error (0xc0000005 at address 1332695D): likely culprit is 'TRANSFORM'.

An internal error has occurred in the compiler. To work around this problem, try simplifying or changing the program near the locations listed below. Locations at the top of the list are closer to the point at which the internal error occurred. Errors such as this can be reported to Microsoft by using the /errorreport option.
    ConsoleApp1

But, the following modifications to the class worked:

public static class Program
{
    //private static readonly object SyncRoot = new object();

    //private static IEnumerable<int> EnumerableWithLock()
    //{
    //    lock (SyncRoot)
    //    {
    //        foreach (var i in new int[1])
    //        {
    //            yield return i;
    //        }
    //    }
    //}

    public static void Main()
    {
        SomeClass sc = new SomeClass();
        foreach (var i in sc.EnumerableWithLock())
        {
            Console.WriteLine(i);
        }
        Console.ReadLine();
    }
}

public class SomeClass
{
    private static readonly object SyncRoot = new object();
    int[] i = { 1, 2, 3, 4, 5 };
    List<int> retval = new List<int>();

    public IEnumerable<int> EnumerableWithLock()
    {
        lock (SyncRoot)
        {
            foreach (var number in i)
            {
                retval.Add(number);
            }
            return retval;
        }
    }
}

So may be its just a CSC bug or something more subtle.

like image 123
Vaibhav Avatar answered Oct 04 '22 01:10

Vaibhav