DotNetty icon indicating copy to clipboard operation
DotNetty copied to clipboard

Allocation-free Write(AndFlush)Async using ValueTask

Open maksimkim opened this issue 6 years ago • 0 comments

Motivation:

corefx 2.1 will introduce ability to implement allocation free async awaitables using existing ValueTask struct and new IValueTaskSource interface (https://github.com/dotnet/corefx/issues/27445). PR adopts this new feature for write apis to avoid unnecessary Task allocation on every WriteAsync call.

ValueTask backed by IValueTaskSource by design has lots of limitations (see corefx issue for details).

In this PR IValueTaskSource is mostly implemented by recyclable objects (ChannelOutboundBuffer.Entry, PendingWriteQueue.PendingWrite, AbstractChannelHandlerContext.WriteTask, etc). It means than once completed it is immediately recycled which has certain implication on how it can be used inside and outside DotNetty code:

  • Once ValueTask is returned by WriteAsync call it should be either immediately awaited or immediately converted to Task or never touched again (e.g. storing it as a class field is not allowed). Not following this restriction can't be verified by compiler and can cause runtime error (if it was recycled after completion) or unpredictable result (if it was reactivated from pool for different async operation).

  • WriteAndFlushAsync method doesn't follow guideline above (it calls WriteAsync then Flush and then returns ValueTask to a caller) that is why new overload of WriteAndFlushAsync is introduced. New notifyComplete bool parameter defines if caller is interested in completion notification (for the penalty of Task allocation) or not. In later case completed ValueTask is returned from WriteAndFlushAsync.

Modifications:

  • Corresponding projects reference new System.Threading.Tasks.Extensions package version from myget dotnet feed. Assumption is that by the time PR is reviewed new version is available on nuget.org.
  • New promise hierarchy is introduced (IPromise, AbstractPromise, AbstractRecyclablePromise) to provide similar capabilities as TaskCompletionSource but for ValueTask/IValueTaskSource pattern.
  • WriteAsync/WriteAndFlushAsync return ValueTask
  • WriteAndFlushAsync has new overload with notifyComplete bool parameter to opt out of Task allocation

Result:

  • If calling code is not interested in awaiting write completion no TaskCompletionSource/Task allocation happen (DotNetty.Transport.Tests.Performance.Sockets.TcpChannelPerfSpecs+TcpChannel_Duplex_Throughput_10_messages_per_flush has twice less Gen0 collections).

maksimkim avatar Mar 12 '18 19:03 maksimkim