dotnet / dotnet/dotnet-api-docs
Document exceptions/behavior of ChannelWriter writing after writer is completed.
- Dominant language
- C#
- Stars
- 949
- Forks
- 1.7k
- Avg merge
- 3d 27m
- Merged PRs (30d)
- 49
Description
**Issue description**
The documentation for `ChannelWriter.(TryWrite|WaitToWriteAsync|WriteAsync)` does not say what happens in case the writer has been completed. In fact `WaitToWriteAsync` throws the exception set in `ChannelWriter.Complete(Exception)` if specified while returing `false` if no exception was specified. In contrast `WriteAsync` always throws a System.Threading.Channels.ChannelClosedException, optionally with the supplied exception as its InnerException. Neither of these are documented as throwing any exceptions.
The following program generates the same output on .NET 6.0 and .NET Framework 4.8
Test program source
```CSharp
using System;
using System.Threading.Channels;
using System.Threading.Tasks;
namespace ChannelTest;
public class Program
{
private static string GetExceptionDescriptionWithInnerExceptions(Exception e)
{
if (e.InnerException != null)
return $"{e.GetType().FullName}: {e.Message} ---> {GetExceptionDescriptionWithInnerExceptions(e.InnerException)}";
else
return $"{e.GetType().FullName}: {e.Message}";
}
private static void PrintResultOrError(string op, Func f)
=> PrintResultOrError(op, async () => { await f(); return "Success"; });
private static void PrintResultOrError(string op, Func f)
=> PrintResultOrError(op, () => Task.FromResult(f()));
private static void PrintResultOrError(string op, Func> f)
{
try
{
T result = f().GetAwaiter().GetResult();
Console.WriteLine($"{op}: {result}");
}
catch (Exception e)
{
Console.WriteLine($"{op} failed: {GetExceptionDescriptionWithInnerExceptions(e)}");
}
}
public static void Main(string[] args)
{
Console.WriteLine("== Write after complete, unbounded without exception ==");
TestChannelWriteAfterCompleted(false, null);
Console.WriteLine();
Console.WriteLine("== Write after complete, unbounded with exception ==");
TestChannelWriteAfterCompleted(false, new Exception("Foobar!"));
Console.WriteLine("== Write after complete, bounded without exception ==");
TestChannelWriteAfterCompleted(true, null);
Console.WriteLine();
Console.WriteLine("== Write after complete, bounded with exception ==");
TestChannelWriteAfterCompleted(true, new Exception("Foobar!"));
}
private static void TestChannelWriteAfterCompleted(bool bounded, Exception? exception)
{
Channel channel = bounded ?
Channel.CreateBounded(new BoundedChannelOptions(5) { FullMode = BoundedChannelFullMode.Wait, SingleReader = true, SingleWriter = true }) :
Channel.CreateUnbounded(new UnboundedChannelOptions { SingleReader = true, SingleWriter = true });
channel.Writer.Complete(exception);
PrintResultOrError("TryWrite", () => channel.Writer.TryWrite("Hello, World!"));
PrintResultOrError("WaitToWriteAsync", () => channel.Writer.WaitToWriteAsync().AsTask());
PrintResultOrError("WriteAsync", () => channel.Writer.WriteAsync("World").AsTask());
}
}
```
Test program output
```Console
== Write after complete, unbounded with exception ==
TryWrite: False
WaitToWriteAsync failed: System.Exception: Foobar!
WriteAsync failed: System.Threading.Channels.ChannelClosedException: The channel has been closed. ---> System.Exception: Foobar!
== Write after complete, bounded without exception ==
TryWrite: False
WaitToWriteAsync: False
WriteAsync failed: System.Threading.Channels.ChannelClosedException: The channel has been closed.
== Write after complete, bounded with exception ==
TryWrite: False
WaitToWriteAsync failed: System.Exception: Foobar!
WriteAsync failed: System.Threading.Channels.ChannelClosedException: The channel has been closed. ---> System.Exception: Foobar!
```
Documentation source: https://github.com/dotnet/dotnet-api-docs/blob/main/xml/System.Threading.Channels/ChannelWriter%601.xml
Contributor guide
Assessment
This issue has not been assessed yet.