Challenge: The problem of locking down tasks…
Join the DZone community and get the full member experience.
Join For FreeThe following code has very subtle bug:
public class AsyncQueue { private readonly Queue<int> items = new Queue<int>(); private volatile LinkedList<TaskCompletionSource<object>> waiters = new LinkedList<TaskCompletionSource<object>>(); public void Enqueue(int i) { lock (items) { items.Enqueue(i); while (waiters.First != null) { waiters.First.Value.TrySetResult(null); waiters.RemoveFirst(); } } } public async Task<IEnumerable<int>> DrainAsync() { while (true) { TaskCompletionSource<object> taskCompletionSource; lock (items) { if (items.Count > 0) { return YieldAllItems(); } taskCompletionSource = new TaskCompletionSource<object>(); waiters.AddLast(taskCompletionSource); } await taskCompletionSource.Task; } } private IEnumerable<int> YieldAllItems() { while (items.Count > 0) { yield return items.Dequeue(); } } }
I’ll even give you a hint, try to run the following client code:
for (int i = 0; i < 1000 * 1000; i++) { q.Enqueue(i); if (i%100 == 0) { Task.Factory.StartNew(async () => { foreach (var result in await q.DrainAsync()) { Console.WriteLine(result); } }); } }
Published at DZone with permission of Oren Eini, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments