-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Open
Description
public interface IMyGrain : IGrainWithGuidKey
{
IAsyncEnumerable<int> Generate(CancellationToken cancellationToken);
}
public class MyGrain : Grain, IMyGrain
{
public IAsyncEnumerable<int> Generate(CancellationToken cancellationToken)
{
var cw = Channel.CreateUnbounded<int>();
_ = G(cw.Writer, cancellationToken);
return cw.Reader.ReadAllAsync(cancellationToken);
}
private async Task G(ChannelWriter<int> writer, CancellationToken cancellationToken)
{
writer.TryWrite(1);
await Task.Delay(10_000, cancellationToken);
writer.TryWrite(2);
}
}
// on the client
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try
{
await foreach (var item in myGrain.Generate(cts.Token))
{
Console.WriteLine(item);
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation was cancelled.");
}
The grain doesn't implement the iterator, but instead returns a different IAsyncEnumerable from the ChannelReader. The TryWrite(2)
is always executed, the cancellationToken
parameter never triggers cancellation.
If the Generate method would use an iterator, then it would see cancellation, but not because the cts
from the client is cancelled, but because the AsyncEnumerableGrainExtension
canceles the EnumeratorState's CTS as part of the DisposeAsync
call to the enumerator when leaving the await foreach
. In the example reproduction, this is also happening, but to the enumerator returned from the channel's implementation and that's not the CancellationToken from the parameter.
Metadata
Metadata
Assignees
Labels
No labels