Skip to content

Instantly share code, notes, and snippets.

@aalmada
Created April 22, 2024 08:59
Show Gist options
  • Save aalmada/784ca7948e80ea8558e993050ff00ba2 to your computer and use it in GitHub Desktop.
Save aalmada/784ca7948e80ea8558e993050ff00ba2 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
var root = ParallelAll([
Invert(Repeat(WriteLineRange(0, 4), 2)),
WriteLineRange(10, 10),
]);
await foreach(var _ in root)
{
Console.WriteLine("-");
}
static async IAsyncEnumerable<BehaviorStatus> Succeed()
{
yield return BehaviorStatus.Succeeded;
}
static async IAsyncEnumerable<BehaviorStatus> Fail()
{
yield return BehaviorStatus.Failed;
}
static IAsyncEnumerable<BehaviorStatus> Sequence(IAsyncEnumerable<BehaviorStatus>[] children)
{
ArgumentNullException.ThrowIfNull(children);
return GetEnumerable(children);
static async IAsyncEnumerable<BehaviorStatus> GetEnumerable(IAsyncEnumerable<BehaviorStatus>[] children)
{
for(var index = 0; index < children.Length; index++)
{
var child = children[index];
await foreach(var status in child.ConfigureAwait(false))
{
switch(status)
{
case BehaviorStatus.Running:
yield return BehaviorStatus.Running;
break;
case BehaviorStatus.Succeeded:
goto childSucceeded;
case BehaviorStatus.Failed:
yield return BehaviorStatus.Failed;
yield break;
}
}
childSucceeded:
if(index < children.Length - 1)
yield return BehaviorStatus.Running;
}
yield return BehaviorStatus.Succeeded;
}
}
static IAsyncEnumerable<BehaviorStatus> Select(IAsyncEnumerable<BehaviorStatus>[] children)
{
ArgumentNullException.ThrowIfNull(children);
return GetEnumerable(children);
static async IAsyncEnumerable<BehaviorStatus> GetEnumerable(IAsyncEnumerable<BehaviorStatus>[] children)
{
for(var index = 0; index < children.Length; index++)
{
var child = children[index];
await foreach(var status in child.ConfigureAwait(false))
{
switch(status)
{
case BehaviorStatus.Running:
yield return BehaviorStatus.Running;
break;
case BehaviorStatus.Succeeded:
yield return BehaviorStatus.Succeeded;
yield break;
case BehaviorStatus.Failed:
goto childFailed;
}
}
childFailed:
if(index < children.Length - 1)
yield return BehaviorStatus.Running;
}
yield return BehaviorStatus.Failed;
}
}
static IAsyncEnumerable<BehaviorStatus> ParallelAny(IAsyncEnumerable<BehaviorStatus>[] children)
{
ArgumentNullException.ThrowIfNull(children);
return GetEnumerable(children);
static async IAsyncEnumerable<BehaviorStatus> GetEnumerable(IAsyncEnumerable<BehaviorStatus>[] children)
{
var enumerators = new IAsyncEnumerator<BehaviorStatus>[children.Length];
for (var index = 0; index < enumerators.Length && index < children.Length; index++)
enumerators[index] = children[index].GetAsyncEnumerator();
try
{
while(true)
{
foreach(var enumerator in enumerators)
{
await enumerator.MoveNextAsync().ConfigureAwait(false);
switch(enumerator.Current)
{
case BehaviorStatus.Succeeded:
yield return BehaviorStatus.Succeeded;
yield break;
case BehaviorStatus.Failed:
yield return BehaviorStatus.Failed;
yield break;
}
}
yield return BehaviorStatus.Running;
}
}
finally
{
foreach(var enumerator in enumerators)
await enumerator.DisposeAsync().ConfigureAwait(false);
}
}
}
static IAsyncEnumerable<BehaviorStatus> ParallelAll(IAsyncEnumerable<BehaviorStatus>[] children)
{
ArgumentNullException.ThrowIfNull(children);
return GetEnumerable(children);
static async IAsyncEnumerable<BehaviorStatus> GetEnumerable(IAsyncEnumerable<BehaviorStatus>[] children)
{
var enumerators = new BehaviorEnumerator[children.Length];
for (var index = 0; index < enumerators.Length && index < children.Length; index++)
enumerators[index] = new BehaviorEnumerator { Instance = children[index].GetAsyncEnumerator() };
try
{
var succeededCounter = 0;
while(true)
{
foreach(var enumerator in enumerators)
{
if(!enumerator.Succeeded)
{
await enumerator.Instance.MoveNextAsync().ConfigureAwait(false);
switch(enumerator.Instance.Current)
{
case BehaviorStatus.Succeeded:
enumerator.Succeeded = true;
succeededCounter++;
if(succeededCounter == children.Length)
{
yield return BehaviorStatus.Succeeded;
yield break;
}
break;
case BehaviorStatus.Failed:
yield return BehaviorStatus.Failed;
yield break;
}
}
}
yield return BehaviorStatus.Running;
}
}
finally
{
foreach(var enumerator in enumerators)
await enumerator.Instance.DisposeAsync().ConfigureAwait(false);
}
}
}
static IAsyncEnumerable<BehaviorStatus> Invert(IAsyncEnumerable<BehaviorStatus> child)
{
ArgumentNullException.ThrowIfNull(child);
return GetEnumerable(child);
static async IAsyncEnumerable<BehaviorStatus> GetEnumerable(IAsyncEnumerable<BehaviorStatus> child)
{
await foreach(var status in child.ConfigureAwait(false))
{
switch(status)
{
case BehaviorStatus.Running:
yield return BehaviorStatus.Running;
break;
case BehaviorStatus.Succeeded:
yield return BehaviorStatus.Failed;
yield break;
case BehaviorStatus.Failed:
yield return BehaviorStatus.Succeeded;
yield break;
}
}
}
}
static IAsyncEnumerable<BehaviorStatus> Repeat(IAsyncEnumerable<BehaviorStatus> child, int count)
{
ArgumentNullException.ThrowIfNull(child);
ArgumentOutOfRangeException.ThrowIfNegative(count);
return GetEnumerable(child, count);
static async IAsyncEnumerable<BehaviorStatus> GetEnumerable(IAsyncEnumerable<BehaviorStatus> child, int count)
{
for(var counter = 0; counter < count; counter++)
{
await foreach(var status in child.ConfigureAwait(false))
{
switch(status)
{
case BehaviorStatus.Running:
yield return BehaviorStatus.Running;
break;
case BehaviorStatus.Succeeded:
goto childSucceeded;
case BehaviorStatus.Failed:
yield return BehaviorStatus.Failed;
yield break;
}
}
childSucceeded:
if(counter < count - 1)
yield return BehaviorStatus.Running;
}
yield return BehaviorStatus.Succeeded;
}
}
static IAsyncEnumerable<BehaviorStatus> RepeatUntilFail(IAsyncEnumerable<BehaviorStatus> child)
{
ArgumentNullException.ThrowIfNull(child);
return GetEnumerable(child);
static async IAsyncEnumerable<BehaviorStatus> GetEnumerable(IAsyncEnumerable<BehaviorStatus> child)
{
while(true)
{
await foreach(var status in child.ConfigureAwait(false))
{
switch(status)
{
case BehaviorStatus.Running:
yield return BehaviorStatus.Running;
break;
case BehaviorStatus.Succeeded:
goto childSucceeded;
case BehaviorStatus.Failed:
yield return BehaviorStatus.Succeeded;
yield break;
}
}
childSucceeded:
yield return BehaviorStatus.Running;
}
}
}
static async IAsyncEnumerable<BehaviorStatus> WriteLine(string? message)
{
Console.WriteLine(message);
yield return BehaviorStatus.Succeeded;
}
static IAsyncEnumerable<BehaviorStatus> WriteLineRange(int start, int count)
{
ArgumentOutOfRangeException.ThrowIfNegative(count);
return GetEnumerable(start, count);
static async IAsyncEnumerable<BehaviorStatus> GetEnumerable(int start, int count)
{
var end = start + count;
for(var value = start; value < end; value++)
{
Console.WriteLine(value);
if(value < end - 1)
yield return BehaviorStatus.Running;
}
yield return BehaviorStatus.Succeeded;
}
}
enum BehaviorStatus { Running, Succeeded, Failed };
class BehaviorEnumerator
{
public required IAsyncEnumerator<BehaviorStatus> Instance { get; init; }
public bool Succeeded { get; set; } = false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment