Ignore for a moment the absurdity of await
ing an Enumerable.Range
call. It's just there to elicit the crash-y behavior. It just as easily could be a method that's doing some network IO to build a collection of value objects. (Indeed, this was where I saw the crash occur.)
If I comment out the Interlocked.Exchange
line, the compiler doesn't crash.
public class Launcher
{
private static IEnumerable<int> _foo;
static void Main(string[] args)
{
DoWorkAsync().Wait();
}
private static async Task DoWorkAsync()
{
RefreshCache();
foreach (var element in _foo)
{
Console.WriteLine(element);
}
Console.ReadLine();
}
private static async void RefreshCache()
{
Interlocked.Exchange(ref _foo, await Cache());
}
private static async Task<IEnumerable<int>> Cache()
{
return Enumerable.Range(0, 10);
}
}
Changing RefreshCache()
to this keeps the compiler from crashing:
private static async void RefreshCache()
{
var foo = await Cache();
Interlocked.Exchange(ref _foo, foo);
}
Edit:
An even simpler, self contained reproduce provided by @Servy
public class Worker
{
private static int foo;
private static async void DoWork()
{
DoNothing(ref foo, await Task.FromResult(0));
}
private static void DoNothing(ref int item, int other)
{
}
}
ref
and not necessarilyInterlocked.Exchange
? – Taurusprivate static void DoNothing<T>(ref T item, T other) { }
instead ofInterlocked.Exchange
also crashed the compiler.private static void DoNothing(ref IEnumerable<int> item, IEnumerable<int> other) { }
also crashes the compiler, so it's not related to generics at all. – CanvassDoInterlock<T>(T foo) { Interlocked.Exchange(ref _foo, foo); }
and just call it from theasync
method? Does it still crash? – Locularref
parameter, a static field needs to be supplied as the reference to that field, the method needs to have a second (non-ref) argument that uses anawait
as the expression for the parameter. – CanvassInterlocked
. It's not special. You can see my smallest repo in the github link (and now in the question, it seems). – Canvassref
andawait
. If the referenced variable isn't a static field (i.e. being an instance field or a local) then it works fine, which seems particularly odd. So certain uses ofawait
with aref
method work fine. – Canvass