From: Stephen Toub Date: Tue, 17 Mar 2020 23:37:48 +0000 (-0400) Subject: Annotate System.Threading.Tasks.Dataflow for nullable reference types (#33644) X-Git-Tag: submit/tizen/20210909.063632~9110 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4fa8c1ec8f98e0f9e663e58fb4776673aa83b00a;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Annotate System.Threading.Tasks.Dataflow for nullable reference types (#33644) --- diff --git a/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.cs b/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.cs index 3fee580..b4ffb87 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.cs @@ -18,7 +18,7 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public bool Post(TInput item) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } + System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock? source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } } public sealed partial class BatchBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock @@ -31,14 +31,15 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } + [return: System.Diagnostics.CodeAnalysis.MaybeNull] T[] System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { throw null; } - System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } + System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock? source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } public void TriggerBatch() { } - public bool TryReceive(System.Predicate filter, out T[] item) { throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList items) { throw null; } + public bool TryReceive(System.Predicate? filter, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out T[]? item) { throw null; } + public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList? items) { throw null; } } public sealed partial class BatchedJoinBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Collections.Generic.IList>>, System.Threading.Tasks.Dataflow.ISourceBlock, System.Collections.Generic.IList>> { @@ -56,8 +57,8 @@ namespace System.Threading.Tasks.Dataflow void System.Threading.Tasks.Dataflow.ISourceBlock,System.Collections.Generic.IList>>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList>> target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock,System.Collections.Generic.IList>>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList>> target) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate, System.Collections.Generic.IList>> filter, out System.Tuple, System.Collections.Generic.IList> item) { throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList, System.Collections.Generic.IList>> items) { throw null; } + public bool TryReceive(System.Predicate, System.Collections.Generic.IList>>? filter, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Tuple, System.Collections.Generic.IList>? item) { throw null; } + public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList, System.Collections.Generic.IList>>? items) { throw null; } } public sealed partial class BatchedJoinBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Collections.Generic.IList, System.Collections.Generic.IList>>, System.Threading.Tasks.Dataflow.ISourceBlock, System.Collections.Generic.IList, System.Collections.Generic.IList>> { @@ -76,24 +77,25 @@ namespace System.Threading.Tasks.Dataflow void System.Threading.Tasks.Dataflow.ISourceBlock,System.Collections.Generic.IList,System.Collections.Generic.IList>>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList, System.Collections.Generic.IList>> target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock,System.Collections.Generic.IList,System.Collections.Generic.IList>>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock, System.Collections.Generic.IList, System.Collections.Generic.IList>> target) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate, System.Collections.Generic.IList, System.Collections.Generic.IList>> filter, out System.Tuple, System.Collections.Generic.IList, System.Collections.Generic.IList> item) { throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList, System.Collections.Generic.IList, System.Collections.Generic.IList>> items) { throw null; } + public bool TryReceive(System.Predicate, System.Collections.Generic.IList, System.Collections.Generic.IList>>? filter, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Tuple, System.Collections.Generic.IList, System.Collections.Generic.IList>? item) { throw null; } + public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList, System.Collections.Generic.IList, System.Collections.Generic.IList>>? items) { throw null; } } public sealed partial class BroadcastBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock { - public BroadcastBlock(System.Func cloningFunction) { } - public BroadcastBlock(System.Func cloningFunction, System.Threading.Tasks.Dataflow.DataflowBlockOptions dataflowBlockOptions) { } + public BroadcastBlock(System.Func? cloningFunction) { } + public BroadcastBlock(System.Func? cloningFunction, System.Threading.Tasks.Dataflow.DataflowBlockOptions dataflowBlockOptions) { } public System.Threading.Tasks.Task Completion { get { throw null; } } public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - bool System.Threading.Tasks.Dataflow.IReceivableSourceBlock.TryReceiveAll(out System.Collections.Generic.IList items) { throw null; } + bool System.Threading.Tasks.Dataflow.IReceivableSourceBlock.TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList? items) { throw null; } + [return: System.Diagnostics.CodeAnalysis.MaybeNull] T System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { throw null; } - System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } + System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock? source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate filter, out T item) { throw null; } + public bool TryReceive(System.Predicate? filter, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out T item) { throw null; } } public sealed partial class BufferBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock { @@ -104,13 +106,14 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } + [return: System.Diagnostics.CodeAnalysis.MaybeNull] T System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { throw null; } - System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } + System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock? source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate filter, out T item) { throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList items) { throw null; } + public bool TryReceive(System.Predicate? filter, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out T item) { throw null; } + public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList? items) { throw null; } } public static partial class DataflowBlock { @@ -138,7 +141,7 @@ namespace System.Threading.Tasks.Dataflow public static TOutput Receive(this System.Threading.Tasks.Dataflow.ISourceBlock source, System.TimeSpan timeout, System.Threading.CancellationToken cancellationToken) { throw null; } public static System.Threading.Tasks.Task SendAsync(this System.Threading.Tasks.Dataflow.ITargetBlock target, TInput item) { throw null; } public static System.Threading.Tasks.Task SendAsync(this System.Threading.Tasks.Dataflow.ITargetBlock target, TInput item, System.Threading.CancellationToken cancellationToken) { throw null; } - public static bool TryReceive(this System.Threading.Tasks.Dataflow.IReceivableSourceBlock source, out TOutput item) { throw null; } + public static bool TryReceive(this System.Threading.Tasks.Dataflow.IReceivableSourceBlock source, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TOutput item) { throw null; } } public partial class DataflowBlockOptions { @@ -164,7 +167,7 @@ namespace System.Threading.Tasks.Dataflow public DataflowMessageHeader(long id) { throw null; } public long Id { get { throw null; } } public bool IsValid { get { throw null; } } - public override bool Equals(object obj) { throw null; } + public override bool Equals(object? obj) { throw null; } public bool Equals(System.Threading.Tasks.Dataflow.DataflowMessageHeader other) { throw null; } public override int GetHashCode() { throw null; } public static bool operator ==(System.Threading.Tasks.Dataflow.DataflowMessageHeader left, System.Threading.Tasks.Dataflow.DataflowMessageHeader right) { throw null; } @@ -201,11 +204,12 @@ namespace System.Threading.Tasks.Dataflow } public partial interface IReceivableSourceBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.ISourceBlock { - bool TryReceive(System.Predicate filter, out TOutput item); - bool TryReceiveAll(out System.Collections.Generic.IList items); + bool TryReceive(System.Predicate? filter, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TOutput item); + bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList? items); } public partial interface ISourceBlock : System.Threading.Tasks.Dataflow.IDataflowBlock { + [return: System.Diagnostics.CodeAnalysis.MaybeNull] TOutput ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed); System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions); void ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target); @@ -213,7 +217,7 @@ namespace System.Threading.Tasks.Dataflow } public partial interface ITargetBlock : System.Threading.Tasks.Dataflow.IDataflowBlock { - System.Threading.Tasks.Dataflow.DataflowMessageStatus OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept); + System.Threading.Tasks.Dataflow.DataflowMessageStatus OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock? source, bool consumeToAccept); } public sealed partial class JoinBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock>, System.Threading.Tasks.Dataflow.ISourceBlock> { @@ -226,12 +230,12 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock> target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - System.Tuple System.Threading.Tasks.Dataflow.ISourceBlock>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target, out bool messageConsumed) { throw null; } + System.Tuple? System.Threading.Tasks.Dataflow.ISourceBlock>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate> filter, out System.Tuple item) { throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList> items) { throw null; } + public bool TryReceive(System.Predicate>? filter, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Tuple? item) { throw null; } + public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList>? items) { throw null; } } public sealed partial class JoinBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock>, System.Threading.Tasks.Dataflow.ISourceBlock> { @@ -245,12 +249,12 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock> target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - System.Tuple System.Threading.Tasks.Dataflow.ISourceBlock>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target, out bool messageConsumed) { throw null; } + System.Tuple? System.Threading.Tasks.Dataflow.ISourceBlock>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock> target) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate> filter, out System.Tuple item) { throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList> items) { throw null; } + public bool TryReceive(System.Predicate>? filter, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Tuple? item) { throw null; } + public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList>? items) { throw null; } } public sealed partial class TransformBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock { @@ -264,13 +268,14 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } + [return: System.Diagnostics.CodeAnalysis.MaybeNull] TOutput System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { throw null; } - System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } + System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock? source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate filter, out TOutput item) { throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList items) { throw null; } + public bool TryReceive(System.Predicate? filter, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TOutput item) { throw null; } + public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList? items) { throw null; } } public sealed partial class TransformManyBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock { @@ -284,28 +289,30 @@ namespace System.Threading.Tasks.Dataflow public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } + [return: System.Diagnostics.CodeAnalysis.MaybeNull] TOutput System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { throw null; } - System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } + System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock? source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate filter, out TOutput item) { throw null; } - public bool TryReceiveAll(out System.Collections.Generic.IList items) { throw null; } + public bool TryReceive(System.Predicate? filter, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TOutput item) { throw null; } + public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList? items) { throw null; } } public sealed partial class WriteOnceBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock { - public WriteOnceBlock(System.Func cloningFunction) { } - public WriteOnceBlock(System.Func cloningFunction, System.Threading.Tasks.Dataflow.DataflowBlockOptions dataflowBlockOptions) { } + public WriteOnceBlock(System.Func? cloningFunction) { } + public WriteOnceBlock(System.Func? cloningFunction, System.Threading.Tasks.Dataflow.DataflowBlockOptions dataflowBlockOptions) { } public System.Threading.Tasks.Task Completion { get { throw null; } } public void Complete() { } public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; } void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { } - bool System.Threading.Tasks.Dataflow.IReceivableSourceBlock.TryReceiveAll(out System.Collections.Generic.IList items) { throw null; } + bool System.Threading.Tasks.Dataflow.IReceivableSourceBlock.TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList? items) { throw null; } + [return: System.Diagnostics.CodeAnalysis.MaybeNull] T System.Threading.Tasks.Dataflow.ISourceBlock.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target, out bool messageConsumed) { throw null; } void System.Threading.Tasks.Dataflow.ISourceBlock.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { } bool System.Threading.Tasks.Dataflow.ISourceBlock.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock target) { throw null; } - System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock source, bool consumeToAccept) { throw null; } + System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock? source, bool consumeToAccept) { throw null; } public override string ToString() { throw null; } - public bool TryReceive(System.Predicate filter, out T item) { throw null; } + public bool TryReceive(System.Predicate? filter, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out T item) { throw null; } } } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.csproj b/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.csproj index 9a09a94..915b6a2 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.csproj +++ b/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.csproj @@ -1,6 +1,7 @@ netstandard2.0;netstandard1.0;netstandard1.1 + enable diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.cs index 73dd608..1753d40 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.cs @@ -133,7 +133,9 @@ namespace System.Threading.Tasks.Dataflow } /// +#pragma warning disable 8617 DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) +#pragma warning restore 8617 { // Validate arguments. Some targets may have a null source, but FilteredLinkPropagator // is an internal target that should only ever have source non-null. @@ -153,6 +155,7 @@ namespace System.Threading.Tasks.Dataflow } /// + [return: MaybeNull] T ISourceBlock.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) { // This message should have only made it to the target if it passes the filter, so we shouldn't need to check again. @@ -442,7 +445,7 @@ namespace System.Threading.Tasks.Dataflow { RunCompletionAction(state => { - try { ((SendAsyncSource)state).TrySetResult(true); } + try { ((SendAsyncSource)state!).TrySetResult(true); } catch (ObjectDisposedException) { } }, this, runAsync); } @@ -455,7 +458,7 @@ namespace System.Threading.Tasks.Dataflow { // The try/catch for ObjectDisposedException handles the case where the // user disposes of the returned task before we're done with it. - try { ((SendAsyncSource)state).TrySetResult(false); } + try { ((SendAsyncSource)state!).TrySetResult(false); } catch (ObjectDisposedException) { } }, this, runAsync); } @@ -467,7 +470,7 @@ namespace System.Threading.Tasks.Dataflow { RunCompletionAction(state => { - var tuple = (Tuple, Exception>)state; + var tuple = (Tuple, Exception>)state!; try { tuple.Item1.TrySetException(tuple.Item2); } catch (ObjectDisposedException) { } }, Tuple.Create, Exception>(this, exception), runAsync); @@ -479,7 +482,7 @@ namespace System.Threading.Tasks.Dataflow { RunCompletionAction(state => { - try { ((SendAsyncSource)state).TrySetCanceled(); } + try { ((SendAsyncSource)state!).TrySetCanceled(); } catch (ObjectDisposedException) { } }, this, runAsync); } @@ -493,7 +496,7 @@ namespace System.Threading.Tasks.Dataflow /// the target is calling to ConsumeMessage. We don't want to block the target indefinitely /// with any synchronous continuations off of the returned send async task. /// - private void RunCompletionAction(Action completionAction, object completionActionState, bool runAsync) + private void RunCompletionAction(Action completionAction, object completionActionState, bool runAsync) { Debug.Assert(completionAction != null, "Completion action to run is required."); @@ -527,21 +530,21 @@ namespace System.Threading.Tasks.Dataflow private void OfferToTargetAsync() { System.Threading.Tasks.Task.Factory.StartNew( - state => ((SendAsyncSource)state).OfferToTarget(), this, + state => ((SendAsyncSource)state!).OfferToTarget(), this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default); } /// Cached delegate used to cancel a send in response to a cancellation request. - private static readonly Action _cancellationCallback = CancellationHandler; + private static readonly Action _cancellationCallback = CancellationHandler; /// Attempts to cancel the source passed as state in response to a cancellation request. /// /// A weak reference to the SendAsyncSource. A weak reference is used to prevent the source /// from being rooted in a long-lived token. /// - private static void CancellationHandler(object state) + private static void CancellationHandler(object? state) { - SendAsyncSource source = Common.UnwrapWeakReference>(state); + SendAsyncSource? source = Common.UnwrapWeakReference>(state!); if (source != null) { Debug.Assert(source._cancellationState != CANCELLATION_STATE_NONE, @@ -619,6 +622,7 @@ namespace System.Threading.Tasks.Dataflow } /// Called by the target to consume the buffered message. + [return: MaybeNull] TOutput ISourceBlock.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) { // Validate arguments @@ -770,7 +774,7 @@ namespace System.Threading.Tasks.Dataflow /// This method does not wait until the source has an item to provide. /// It will return whether or not an element was available. /// - public static bool TryReceive(this IReceivableSourceBlock source, out TOutput item) + public static bool TryReceive(this IReceivableSourceBlock source, [MaybeNullWhen(false)] out TOutput item) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -1034,7 +1038,7 @@ namespace System.Threading.Tasks.Dataflow } /// Cancels a CancellationTokenSource passed as the object state argument. - private static readonly Action _cancelCts = state => ((CancellationTokenSource)state).Cancel(); + private static readonly Action _cancelCts = state => ((CancellationTokenSource)state!).Cancel(); /// Receives an item from the source by linking a temporary target from it. /// Specifies the type of data contained in the source. @@ -1113,20 +1117,21 @@ namespace System.Threading.Tasks.Dataflow /// The C# compiler will not cache this delegate by default due to it being a generic method on a non-generic class. internal static readonly TimerCallback CachedLinkingTimerCallback = state => { - var receiveTarget = (ReceiveTarget)state; + var receiveTarget = (ReceiveTarget)state!; receiveTarget.TryCleanupAndComplete(ReceiveCoreByLinkingCleanupReason.Timer); }; /// Cached delegate used in ReceiveCoreByLinking on the cancellation token. Passed the ReceiveTarget as the state argument. /// The C# compiler will not cache this delegate by default due to it being a generic method on a non-generic class. - internal static readonly Action CachedLinkingCancellationCallback = state => + internal static readonly Action CachedLinkingCancellationCallback = state => { - var receiveTarget = (ReceiveTarget)state; + var receiveTarget = (ReceiveTarget)state!; receiveTarget.TryCleanupAndComplete(ReceiveCoreByLinkingCleanupReason.Cancellation); }; /// The received value if we accepted a value from the source. - private T _receivedValue; + [AllowNull, MaybeNull] + private T _receivedValue = default!; /// The cancellation token source representing both external and internal cancellation. internal readonly CancellationTokenSource _cts = new CancellationTokenSource(); @@ -1137,11 +1142,11 @@ namespace System.Threading.Tasks.Dataflow /// The registration on the external token that cancels the internal token. internal CancellationTokenRegistration _regFromExternalCancellationToken; /// The timer that fires when the timeout has been exceeded. - internal Timer _timer; + internal Timer? _timer; /// The unlinker from removing this target from the source from which we're receiving. - internal IDisposable _unlink; + internal IDisposable? _unlink; /// The received exception if an error occurred. - internal Exception _receivedException; + internal Exception? _receivedException; /// Gets the sync obj used to synchronize all activity on this target. internal object IncomingLock { get { return _cts; } } @@ -1150,7 +1155,7 @@ namespace System.Threading.Tasks.Dataflow internal ReceiveTarget() { } /// Offers a message to be used to complete the TaskCompletionSource. - DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) + DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock? source, bool consumeToAccept) { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); @@ -1172,7 +1177,7 @@ namespace System.Threading.Tasks.Dataflow { // Accept the message if possible and complete this task with the message's value. bool consumed = true; - T acceptedValue = consumeToAccept ? source.ConsumeMessage(messageHeader, this, out consumed) : messageValue; + T acceptedValue = consumeToAccept ? source!.ConsumeMessage(messageHeader, this, out consumed) : messageValue; if (consumed) { status = DataflowMessageStatus.Accepted; @@ -1238,10 +1243,10 @@ namespace System.Threading.Tasks.Dataflow // completed, this is unnecessary, as the source should have already // emptied out its target registry, or at least be in the process of doing so. // We are racing with the linking code - only one can dispose of the unlinker. - IDisposable unlink = _unlink; + IDisposable? unlink = _unlink; if (reason != ReceiveCoreByLinkingCleanupReason.SourceCompletion && unlink != null) { - IDisposable disposableUnlink = Interlocked.CompareExchange(ref _unlink, null, unlink); + IDisposable? disposableUnlink = Interlocked.CompareExchange(ref _unlink, null, unlink); if (disposableUnlink != null) { // If an error occurs, fault the target and override the reason to @@ -1297,8 +1302,8 @@ namespace System.Threading.Tasks.Dataflow System.Threading.Tasks.Task.Factory.StartNew(state => { // Complete with the received value - var target = (ReceiveTarget)state; - try { target.TrySetResult(target._receivedValue); } + var target = (ReceiveTarget)state!; + try { target.TrySetResult(target._receivedValue!); } catch (ObjectDisposedException) { /* benign race if returned task is already disposed */ } }, this, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); break; @@ -1308,7 +1313,7 @@ namespace System.Threading.Tasks.Dataflow System.Threading.Tasks.Task.Factory.StartNew(state => { // Complete as canceled - var target = (ReceiveTarget)state; + var target = (ReceiveTarget)state!; try { target.TrySetCanceled(); } catch (ObjectDisposedException) { /* benign race if returned task is already disposed */ } }, this, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); @@ -1329,7 +1334,7 @@ namespace System.Threading.Tasks.Dataflow System.Threading.Tasks.Task.Factory.StartNew(state => { // Complete with the received exception - var target = (ReceiveTarget)state; + var target = (ReceiveTarget)state!; try { target.TrySetException(target._receivedException ?? new InvalidOperationException(SR.InvalidOperation_ErrorDuringCleanup)); } catch (ObjectDisposedException) { /* benign race if returned task is already disposed */ } }, this, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); @@ -1482,7 +1487,7 @@ namespace System.Threading.Tasks.Dataflow /// Cached continuation delegate that unregisters from cancellation and /// marshals the antecedent's result to the return value. /// - internal static readonly Func, object, bool> s_handleCompletion = (antecedent, state) => + internal static readonly Func, object?, bool> s_handleCompletion = (antecedent, state) => { var target = state as OutputAvailableAsyncTarget; Debug.Assert(target != null, "Expected non-null target"); @@ -1494,11 +1499,11 @@ namespace System.Threading.Tasks.Dataflow /// Cached delegate that cancels the target and unlinks the target from the source. /// Expects an OutputAvailableAsyncTarget as the state argument. /// - internal static readonly Action s_cancelAndUnlink = CancelAndUnlink; + internal static readonly Action s_cancelAndUnlink = CancelAndUnlink; /// Cancels the target and unlinks the target from the source. /// An OutputAvailableAsyncTarget. - private static void CancelAndUnlink(object state) + private static void CancelAndUnlink(object? state) { var target = state as OutputAvailableAsyncTarget; Debug.Assert(target != null, "Expected a non-null target"); @@ -1508,7 +1513,7 @@ namespace System.Threading.Tasks.Dataflow // Take advantage of this task and unlink from there to avoid doing the interlocked operation synchronously. System.Threading.Tasks.Task.Factory.StartNew(tgt => { - var thisTarget = (OutputAvailableAsyncTarget)tgt; + var thisTarget = (OutputAvailableAsyncTarget)tgt!; thisTarget.TrySetCanceled(); thisTarget.AttemptThreadSafeUnlink(); }, @@ -1519,7 +1524,7 @@ namespace System.Threading.Tasks.Dataflow internal void AttemptThreadSafeUnlink() { // A race is possible. Therefore use an interlocked operation. - IDisposable cachedUnlinker = _unlinker; + IDisposable? cachedUnlinker = _unlinker; if (cachedUnlinker != null && Interlocked.CompareExchange(ref _unlinker, null, cachedUnlinker) == cachedUnlinker) { cachedUnlinker.Dispose(); @@ -1527,12 +1532,12 @@ namespace System.Threading.Tasks.Dataflow } /// The IDisposable used to unlink this target from its source. - internal IDisposable _unlinker; + internal IDisposable? _unlinker; /// The registration used to unregister this target from the cancellation token. internal CancellationTokenRegistration _ctr; /// Completes the task when offered a message (but doesn't consume the message). - DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) + DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock? source, bool consumeToAccept) { if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); if (source == null) throw new ArgumentNullException(nameof(source)); @@ -1625,7 +1630,7 @@ namespace System.Threading.Tasks.Dataflow _target.Fault(exception); } /// - public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock source, bool consumeToAccept) + public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock? source, bool consumeToAccept) { return _target.OfferMessage(messageHeader, messageValue, source, consumeToAccept); } @@ -1640,7 +1645,7 @@ namespace System.Threading.Tasks.Dataflow } /// - public bool TryReceive(Predicate filter, out TOutput item) + public bool TryReceive(Predicate? filter, [MaybeNullWhen(false)] out TOutput item) { var receivableSource = _source as IReceivableSourceBlock; if (receivableSource != null) return receivableSource.TryReceive(filter, out item); @@ -1650,7 +1655,7 @@ namespace System.Threading.Tasks.Dataflow } /// - public bool TryReceiveAll(out IList items) + public bool TryReceiveAll([NotNullWhen(true)] out IList? items) { var receivableSource = _source as IReceivableSourceBlock; if (receivableSource != null) return receivableSource.TryReceiveAll(out items); @@ -1660,6 +1665,7 @@ namespace System.Threading.Tasks.Dataflow } /// + [return: MaybeNull] public TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) { return _source.ConsumeMessage(messageHeader, target, out messageConsumed); @@ -1905,7 +1911,7 @@ namespace System.Threading.Tasks.Dataflow private static Task ChooseCore( ISourceBlock source1, Action action1, ISourceBlock source2, Action action2, - ISourceBlock source3, Action action3, + ISourceBlock? source3, Action? action3, DataflowBlockOptions dataflowBlockOptions) { Debug.Assert(source1 != null && action1 != null, "The first source and action should not be null."); @@ -1919,13 +1925,13 @@ namespace System.Threading.Tasks.Dataflow return Common.CreateTaskFromCancellation(dataflowBlockOptions.CancellationToken); // Fast path: if any of the sources already has data available that can be received immediately. - Task resultTask; + Task? resultTask; try { TaskScheduler scheduler = dataflowBlockOptions.TaskScheduler; if (TryChooseFromSource(source1, action1, 0, scheduler, out resultTask) || TryChooseFromSource(source2, action2, 1, scheduler, out resultTask) || - (hasThirdSource && TryChooseFromSource(source3, action3, 2, scheduler, out resultTask))) + (hasThirdSource && TryChooseFromSource(source3!, action3!, 2, scheduler, out resultTask))) { return resultTask; } @@ -1952,7 +1958,7 @@ namespace System.Threading.Tasks.Dataflow /// true if this try attempt satisfies the choose operation; otherwise, false. private static bool TryChooseFromSource( ISourceBlock source, Action action, int branchId, TaskScheduler scheduler, - out Task task) + [NotNullWhen(true)] out Task? task) { // Validate arguments Debug.Assert(source != null, "Expected a non-null source"); @@ -1990,7 +1996,7 @@ namespace System.Threading.Tasks.Dataflow private static Task ChooseCoreByLinking( ISourceBlock source1, Action action1, ISourceBlock source2, Action action2, - ISourceBlock source3, Action action3, + ISourceBlock? source3, Action? action3, DataflowBlockOptions dataflowBlockOptions) { Debug.Assert(source1 != null && action1 != null, "The first source and action should not be null."); @@ -2015,7 +2021,7 @@ namespace System.Threading.Tasks.Dataflow branchTasks[1] = CreateChooseBranch(boxedCompleted, cts, scheduler, 1, source2, action2); if (hasThirdSource) { - branchTasks[2] = CreateChooseBranch(boxedCompleted, cts, scheduler, 2, source3, action3); + branchTasks[2] = CreateChooseBranch(boxedCompleted, cts, scheduler, 2, source3!, action3!); } // Asynchronously wait for all branches to complete, then complete @@ -2029,14 +2035,14 @@ namespace System.Threading.Tasks.Dataflow // of whether a branch completed successfully. Others may have been // canceled (or run but found they were not needed), and those // we just ignore. - List exceptions = null; + List? exceptions = null; int successfulBranchId = -1; foreach (Task task in tasks) { switch (task.Status) { case TaskStatus.Faulted: - Common.AddException(ref exceptions, task.Exception, unwrapInnerExceptions: true); + Common.AddException(ref exceptions, task.Exception!, unwrapInnerExceptions: true); break; case TaskStatus.RanToCompletion: int resultBranchId = task.Result; @@ -2150,9 +2156,9 @@ namespace System.Threading.Tasks.Dataflow /// Delegate used to invoke the action for a branch when that branch is activated /// on the fast path. /// - internal static readonly Func s_processBranchFunction = state => + internal static readonly Func s_processBranchFunction = state => { - Tuple, T, int> actionResultBranch = (Tuple, T, int>)state; + Tuple, T, int> actionResultBranch = (Tuple, T, int>)state!; actionResultBranch.Item1(actionResultBranch.Item2); return actionResultBranch.Item3; }; @@ -2176,13 +2182,13 @@ namespace System.Threading.Tasks.Dataflow Common.WireCancellationToComplete(cancellationToken, base.Task, state => { - var thisChooseTarget = (ChooseTarget)state; + var thisChooseTarget = (ChooseTarget)state!; lock (thisChooseTarget._completed) thisChooseTarget.TrySetCanceled(); }, this); } /// Called when this choice branch is being offered a message. - public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) + public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock? source, bool consumeToAccept) { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); @@ -2197,12 +2203,12 @@ namespace System.Threading.Tasks.Dataflow if (consumeToAccept) { bool consumed; - messageValue = source.ConsumeMessage(messageHeader, this, out consumed); + messageValue = source!.ConsumeMessage(messageHeader, this, out consumed); if (!consumed) return DataflowMessageStatus.NotAvailable; } // Store the result and signal our success - TrySetResult(messageValue); + TrySetResult(messageValue!); _completed.Value = Task; return DataflowMessageStatus.Accepted; } @@ -2295,9 +2301,9 @@ namespace System.Threading.Tasks.Dataflow /// Gets any exceptions from the source block. /// The aggregate exception of all errors, or null if everything completed successfully. - private AggregateException GetCompletionError() + private AggregateException? GetCompletionError() { - Task sourceCompletionTask = Common.GetPotentiallyNotSupportedCompletionTask(_source); + Task? sourceCompletionTask = Common.GetPotentiallyNotSupportedCompletionTask(_source); return sourceCompletionTask != null && sourceCompletionTask.IsFaulted ? sourceCompletionTask.Exception : null; } @@ -2311,10 +2317,10 @@ namespace System.Threading.Tasks.Dataflow if (observer == null) throw new ArgumentNullException(nameof(observer)); Common.ContractAssertMonitorStatus(_SubscriptionLock, held: false); - Task sourceCompletionTask = Common.GetPotentiallyNotSupportedCompletionTask(_source); + Task? sourceCompletionTask = Common.GetPotentiallyNotSupportedCompletionTask(_source); // Synchronize all observers for this source. - Exception error = null; + Exception? error = null; lock (_SubscriptionLock) { // Fast path for if everything is already done. We need to ensure that both @@ -2338,7 +2344,7 @@ namespace System.Threading.Tasks.Dataflow if (_observersState.Unlinker == null) { _observersState.Observers = ImmutableArray>.Empty; - return null; + return Disposables.Nop; } } @@ -2452,12 +2458,12 @@ namespace System.Threading.Tasks.Dataflow /// internal ImmutableArray> Observers = ImmutableArray>.Empty; /// Used to unlink the source from this target when the last observer is unsubscribed. - internal IDisposable Unlinker; + internal IDisposable? Unlinker; /// /// Temporary list to keep track of SendAsync tasks to TargetObservers with back pressure. /// This field gets instantiated on demand. It gets populated and cleared within an offering cycle. /// - private List> _tempSendAsyncTaskList; + private List>? _tempSendAsyncTaskList; /// Initializes the target instance. /// The owning observable. @@ -2472,22 +2478,22 @@ namespace System.Threading.Tasks.Dataflow // If the target block fails due to an unexpected exception (e.g. it calls back to the source and the source throws an error), // we fault currently registered observers and reset the observable. Target.Completion.ContinueWith( - (t, state) => ((ObserversState)state).NotifyObserversOfCompletion(t.Exception), this, + (t, state) => ((ObserversState)state!).NotifyObserversOfCompletion(t.Exception!), this, CancellationToken.None, Common.GetContinuationOptions(TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously), TaskScheduler.Default); // When the source completes, complete the target. Then when the target completes, // send completion messages to any observers still registered. - Task sourceCompletionTask = Common.GetPotentiallyNotSupportedCompletionTask(Observable._source); + Task? sourceCompletionTask = Common.GetPotentiallyNotSupportedCompletionTask(Observable._source); if (sourceCompletionTask != null) { sourceCompletionTask.ContinueWith((_1, state1) => { - var ti = (ObserversState)state1; + var ti = (ObserversState)state1!; ti.Target.Complete(); ti.Target.Completion.ContinueWith( - (_2, state2) => ((ObserversState)state2).NotifyObserversOfCompletion(), state1, + (_2, state2) => ((ObserversState)state2!).NotifyObserversOfCompletion(), state1, CancellationToken.None, Common.GetContinuationOptions(TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.ExecuteSynchronously), TaskScheduler.Default); @@ -2558,7 +2564,7 @@ namespace System.Threading.Tasks.Dataflow /// Non-null when an unexpected exception occurs during processing. Faults /// all subscribed observers and resets the observable back to its original condition. /// - private void NotifyObserversOfCompletion(Exception targetException = null) + private void NotifyObserversOfCompletion(Exception? targetException = null) { Debug.Assert(Target.Completion.IsCompleted, "The target must have already completed in order to notify of completion."); Common.ContractAssertMonitorStatus(Observable._SubscriptionLock, held: false); @@ -2579,7 +2585,7 @@ namespace System.Threading.Tasks.Dataflow if (currentObservers.Count > 0) { // Determine if we should fault or complete the observers - Exception error = targetException ?? Observable.GetCompletionError(); + Exception? error = targetException ?? Observable.GetCompletionError(); try { // Do it. @@ -2696,10 +2702,10 @@ namespace System.Threading.Tasks.Dataflow /// The type of the messages this block can accept. private class NullTargetBlock : ITargetBlock { - private Task _completion; + private Task? _completion; /// - DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock source, bool consumeToAccept) + DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock? source, bool consumeToAccept) { if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowMessageHeader.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowMessageHeader.cs index fcf8b45..25cd92f 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowMessageHeader.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowMessageHeader.cs @@ -57,7 +57,7 @@ namespace System.Threading.Tasks.Dataflow /// Checks boxed instances for equality by ID. /// A boxed instance. /// True if the instances are equal. False otherwise. - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is DataflowMessageHeader && this == (DataflowMessageHeader)obj; } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/IReceivableSourceBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/IReceivableSourceBlock.cs index 3bb2f54..9dc8439 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/IReceivableSourceBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/IReceivableSourceBlock.cs @@ -12,6 +12,7 @@ // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace System.Threading.Tasks.Dataflow { @@ -22,11 +23,11 @@ namespace System.Threading.Tasks.Dataflow // IMPLEMENT IMPLICITLY /// - bool TryReceive(Predicate filter, out TOutput item); + bool TryReceive(Predicate? filter, [MaybeNullWhen(false)] out TOutput item); // IMPLEMENT IMPLICITLY IF BLOCK SUPPORTS RECEIVING MORE THAN ONE ITEM, OTHERWISE EXPLICITLY /// - bool TryReceiveAll(out IList items); + bool TryReceiveAll([NotNullWhen(true)] out IList? items); } } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/ISourceBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/ISourceBlock.cs index 8b54066..8b2f9bc 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/ISourceBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/ISourceBlock.cs @@ -28,6 +28,7 @@ namespace System.Threading.Tasks.Dataflow // IMPLEMENT EXPLICITLY /// + [return: MaybeNull] TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed); /// diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/ITargetBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/ITargetBlock.cs index 1728d58..67eed5c 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/ITargetBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/ITargetBlock.cs @@ -20,6 +20,6 @@ namespace System.Threading.Tasks.Dataflow // IMPLEMENT EXPLICITLY /// - DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock source, bool consumeToAccept); + DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock? source, bool consumeToAccept); } } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/ActionBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/ActionBlock.cs index f687532..e81e34c 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/ActionBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/ActionBlock.cs @@ -26,9 +26,9 @@ namespace System.Threading.Tasks.Dataflow public sealed class ActionBlock : ITargetBlock, IDebuggerDisplay { /// The core implementation of this message block when in default mode. - private readonly TargetCore _defaultTarget; + private readonly TargetCore? _defaultTarget; /// The core implementation of this message block when in SPSC mode. - private readonly SpscTargetCore _spscTarget; + private readonly SpscTargetCore? _spscTarget; /// Initializes the with the specified . /// The action to invoke with each data element received. @@ -113,7 +113,7 @@ namespace System.Threading.Tasks.Dataflow // Handle async cancellation requests by declining on the target Common.WireCancellationToComplete( - dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore)state).Complete(exception: null, dropPendingMessages: true), _defaultTarget); + dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore)state!).Complete(exception: null, dropPendingMessages: true), _defaultTarget); } #if FEATURE_TRACING DataflowEtwProvider etwLog = DataflowEtwProvider.Log; @@ -131,6 +131,7 @@ namespace System.Threading.Tasks.Dataflow /// The message to be processed. private void ProcessMessage(Action action, KeyValuePair messageWithId) { + Debug.Assert(_defaultTarget != null); try { action(messageWithId.Key); @@ -154,10 +155,11 @@ namespace System.Threading.Tasks.Dataflow private void ProcessMessageWithTask(Func action, KeyValuePair messageWithId) { Debug.Assert(action != null, "action needed for processing"); + Debug.Assert(_defaultTarget != null); // Run the action to get the task that represents the operation's completion - Task task = null; - Exception caughtException = null; + Task? task = null; + Exception? caughtException = null; try { task = action(messageWithId.Key); @@ -188,7 +190,7 @@ namespace System.Threading.Tasks.Dataflow // Otherwise, join with the asynchronous operation when it completes. task.ContinueWith((completed, state) => { - ((ActionBlock)state).AsyncCompleteProcessMessageWithTask(completed); + ((ActionBlock)state!).AsyncCompleteProcessMessageWithTask(completed); }, this, CancellationToken.None, Common.GetContinuationOptions(TaskContinuationOptions.ExecuteSynchronously), TaskScheduler.Default); } } @@ -199,6 +201,7 @@ namespace System.Threading.Tasks.Dataflow { Debug.Assert(completed != null, "Need completed task for processing"); Debug.Assert(completed.IsCompleted, "The task to be processed must be completed by now."); + Debug.Assert(_defaultTarget != null); // If the task faulted, store its errors. We must add the exception before declining // and signaling completion, as the exception is part of the operation, and the completion conditions @@ -224,6 +227,7 @@ namespace System.Threading.Tasks.Dataflow } else { + Debug.Assert(_spscTarget != null); _spscTarget.Complete(exception: null); } } @@ -239,6 +243,7 @@ namespace System.Threading.Tasks.Dataflow } else { + Debug.Assert(_spscTarget != null); _spscTarget.Complete(exception); } } @@ -246,7 +251,7 @@ namespace System.Threading.Tasks.Dataflow /// public Task Completion { - get { return _defaultTarget != null ? _defaultTarget.Completion : _spscTarget.Completion; } + get { return _defaultTarget != null ? _defaultTarget.Completion : _spscTarget!.Completion; } } /// Posts an item to the . @@ -273,33 +278,33 @@ namespace System.Threading.Tasks.Dataflow return _defaultTarget != null ? _defaultTarget.OfferMessage(Common.SingleMessageHeader, item, null, false) == DataflowMessageStatus.Accepted : - _spscTarget.Post(item); + _spscTarget!.Post(item); } /// - DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock source, bool consumeToAccept) + DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock? source, bool consumeToAccept) { return _defaultTarget != null ? _defaultTarget.OfferMessage(messageHeader, messageValue, source, consumeToAccept) : - _spscTarget.OfferMessage(messageHeader, messageValue, source, consumeToAccept); + _spscTarget!.OfferMessage(messageHeader, messageValue, source, consumeToAccept); } /// public int InputCount { - get { return _defaultTarget != null ? _defaultTarget.InputCount : _spscTarget.InputCount; } + get { return _defaultTarget != null ? _defaultTarget.InputCount : _spscTarget!.InputCount; } } /// Gets the number of messages waiting to be processed. This must only be used from the debugger. private int InputCountForDebugger { - get { return _defaultTarget != null ? _defaultTarget.GetDebuggingInformation().InputCount : _spscTarget.InputCount; } + get { return _defaultTarget != null ? _defaultTarget.GetDebuggingInformation().InputCount : _spscTarget!.InputCount; } } /// public override string ToString() { - return Common.GetNameForDebugger(this, _defaultTarget != null ? _defaultTarget.DataflowBlockOptions : _spscTarget.DataflowBlockOptions); + return Common.GetNameForDebugger(this, _defaultTarget != null ? _defaultTarget.DataflowBlockOptions : _spscTarget!.DataflowBlockOptions); } /// The data to display in the debugger display attribute. @@ -308,7 +313,7 @@ namespace System.Threading.Tasks.Dataflow get { return string.Format("{0}, InputCount={1}", - Common.GetNameForDebugger(this, _defaultTarget != null ? _defaultTarget.DataflowBlockOptions : _spscTarget.DataflowBlockOptions), + Common.GetNameForDebugger(this, _defaultTarget != null ? _defaultTarget.DataflowBlockOptions : _spscTarget!.DataflowBlockOptions), InputCountForDebugger); } } @@ -321,9 +326,9 @@ namespace System.Threading.Tasks.Dataflow /// The action block being viewed. private readonly ActionBlock _actionBlock; /// The action block's default target being viewed. - private readonly TargetCore.DebuggingInformation _defaultDebugInfo; + private readonly TargetCore.DebuggingInformation? _defaultDebugInfo; /// The action block's SPSC target being viewed. - private readonly SpscTargetCore.DebuggingInformation _spscDebugInfo; + private readonly SpscTargetCore.DebuggingInformation? _spscDebugInfo; /// Initializes the debug view. /// The target being debugged. @@ -333,21 +338,21 @@ namespace System.Threading.Tasks.Dataflow _actionBlock = actionBlock; if (_actionBlock._defaultTarget != null) { - _defaultDebugInfo = actionBlock._defaultTarget.GetDebuggingInformation(); + _defaultDebugInfo = actionBlock._defaultTarget!.GetDebuggingInformation(); } else { - _spscDebugInfo = actionBlock._spscTarget.GetDebuggingInformation(); + _spscDebugInfo = actionBlock._spscTarget!.GetDebuggingInformation(); } } /// Gets the messages waiting to be processed. public IEnumerable InputQueue { - get { return _defaultDebugInfo != null ? _defaultDebugInfo.InputQueue : _spscDebugInfo.InputQueue; } + get { return _defaultDebugInfo != null ? _defaultDebugInfo.InputQueue : _spscDebugInfo!.InputQueue; } } /// Gets any postponed messages. - public QueuedMap, DataflowMessageHeader> PostponedMessages + public QueuedMap, DataflowMessageHeader>? PostponedMessages { get { return _defaultDebugInfo != null ? _defaultDebugInfo.PostponedMessages : null; } } @@ -355,23 +360,23 @@ namespace System.Threading.Tasks.Dataflow /// Gets the number of outstanding input operations. public int CurrentDegreeOfParallelism { - get { return _defaultDebugInfo != null ? _defaultDebugInfo.CurrentDegreeOfParallelism : _spscDebugInfo.CurrentDegreeOfParallelism; } + get { return _defaultDebugInfo != null ? _defaultDebugInfo.CurrentDegreeOfParallelism : _spscDebugInfo!.CurrentDegreeOfParallelism; } } /// Gets the ExecutionDataflowBlockOptions used to configure this block. public ExecutionDataflowBlockOptions DataflowBlockOptions { - get { return _defaultDebugInfo != null ? _defaultDebugInfo.DataflowBlockOptions : _spscDebugInfo.DataflowBlockOptions; } + get { return _defaultDebugInfo != null ? _defaultDebugInfo.DataflowBlockOptions : _spscDebugInfo!.DataflowBlockOptions; } } /// Gets whether the block is declining further messages. public bool IsDecliningPermanently { - get { return _defaultDebugInfo != null ? _defaultDebugInfo.IsDecliningPermanently : _spscDebugInfo.IsDecliningPermanently; } + get { return _defaultDebugInfo != null ? _defaultDebugInfo.IsDecliningPermanently : _spscDebugInfo!.IsDecliningPermanently; } } /// Gets whether the block is completed. public bool IsCompleted { - get { return _defaultDebugInfo != null ? _defaultDebugInfo.IsCompleted : _spscDebugInfo.IsCompleted; } + get { return _defaultDebugInfo != null ? _defaultDebugInfo.IsCompleted : _spscDebugInfo!.IsCompleted; } } /// Gets the block's Id. public int Id { get { return Common.GetBlockId(_actionBlock); } } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchBlock.cs index 8c18c71..48976ab 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchBlock.cs @@ -55,8 +55,8 @@ namespace System.Threading.Tasks.Dataflow dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone(); // Initialize bounding actions - Action, int> onItemsRemoved = null; - Func, T[], IList, int> itemCountingFunc = null; + Action, int>? onItemsRemoved = null; + Func, T[], IList?, int>? itemCountingFunc = null; if (dataflowBlockOptions.BoundedCapacity > 0) { onItemsRemoved = (owningSource, count) => ((BatchBlock)owningSource)._target.OnItemsRemoved(count); @@ -81,14 +81,14 @@ namespace System.Threading.Tasks.Dataflow // to handle multiple completion requests and to carry over only one. _source.Completion.ContinueWith((completed, state) => { - var thisBlock = ((BatchBlock)state) as IDataflowBlock; + var thisBlock = ((BatchBlock)state!) as IDataflowBlock; Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion."); - thisBlock.Fault(completed.Exception); + thisBlock.Fault(completed.Exception!); }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); // Handle async cancellation requests by declining on the target Common.WireCancellationToComplete( - dataflowBlockOptions.CancellationToken, _source.Completion, state => ((BatchBlockTargetCore)state).Complete(exception: null, dropPendingMessages: true, releaseReservedMessages: false), _target); + dataflowBlockOptions.CancellationToken, _source.Completion, state => ((BatchBlockTargetCore)state!).Complete(exception: null, dropPendingMessages: true, releaseReservedMessages: false), _target); #if FEATURE_TRACING DataflowEtwProvider etwLog = DataflowEtwProvider.Log; if (etwLog.IsEnabled()) @@ -127,13 +127,15 @@ namespace System.Threading.Tasks.Dataflow } /// - public bool TryReceive(Predicate filter, out T[] item) +#pragma warning disable CS8614 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470 + public bool TryReceive(Predicate? filter, [NotNullWhen(true)] out T[]? item) +#pragma warning restore CS8614 { return _source.TryReceive(filter, out item); } /// - public bool TryReceiveAll(out IList items) { return _source.TryReceiveAll(out items); } + public bool TryReceiveAll([NotNullWhen(true)] out IList? items) { return _source.TryReceiveAll(out items); } /// public int OutputCount { get { return _source.OutputCount; } } @@ -149,12 +151,13 @@ namespace System.Threading.Tasks.Dataflow public int BatchSize { get { return _target.BatchSize; } } /// - DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) + DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock? source, bool consumeToAccept) { return _target.OfferMessage(messageHeader, messageValue, source, consumeToAccept); } /// + [return: MaybeNull] T[] ISourceBlock.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) { return _source.ConsumeMessage(messageHeader, target, out messageConsumed); @@ -220,9 +223,9 @@ namespace System.Threading.Tasks.Dataflow public long BatchesCompleted { get { return _targetDebuggingInformation.NumberOfBatchesCompleted; } } /// Gets the task being used for input processing. - public Task TaskForInputProcessing { get { return _targetDebuggingInformation.TaskForInputProcessing; } } + public Task? TaskForInputProcessing { get { return _targetDebuggingInformation.TaskForInputProcessing; } } /// Gets the task being used for output processing. - public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } + public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } /// Gets the DataflowBlockOptions used to configure this block. public GroupingDataflowBlockOptions DataflowBlockOptions { get { return _targetDebuggingInformation.DataflowBlockOptions; } } @@ -236,11 +239,11 @@ namespace System.Threading.Tasks.Dataflow public int Id { get { return Common.GetBlockId(_batchBlock); } } /// Gets the messages postponed by this batch. - public QueuedMap, DataflowMessageHeader> PostponedMessages { get { return _targetDebuggingInformation.PostponedMessages; } } + public QueuedMap, DataflowMessageHeader>? PostponedMessages { get { return _targetDebuggingInformation.PostponedMessages; } } /// Gets the set of all targets linked from this block. public TargetRegistry LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } } /// Gets the set of all targets linked from this block. - public ITargetBlock NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } + public ITargetBlock? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } } /// Provides the core target implementation for a Batch. @@ -260,9 +263,9 @@ namespace System.Threading.Tasks.Dataflow /// The batch size. private readonly int _batchSize; /// State used when in non-greedy mode. - private readonly NonGreedyState _nonGreedyState; + private readonly NonGreedyState? _nonGreedyState; /// Bounding state for when the block is executing in bounded mode. - private readonly BoundingState _boundingState; + private readonly BoundingState? _boundingState; /// The options associated with this block. private readonly GroupingDataflowBlockOptions _dataflowBlockOptions; /// The action invoked with a completed batch. @@ -288,7 +291,7 @@ namespace System.Threading.Tasks.Dataflow /// This value may be read not under a lock, but it must only be written to protected by the IncomingLock. internal bool AcceptFewerThanBatchSize; /// The task used to process messages. - internal Task TaskForInputProcessing; + internal Task? TaskForInputProcessing; /// Initializes the NonGreedyState. /// The batch size used by the BatchBlock. @@ -356,7 +359,7 @@ namespace System.Threading.Tasks.Dataflow } /// - internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) + internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock? source, bool consumeToAccept) { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); @@ -380,7 +383,7 @@ namespace System.Threading.Tasks.Dataflow if (_dataflowBlockOptions.Greedy && (_boundingState == null || - (_boundingState.CountIsLessThanBound && _nonGreedyState.PostponedMessages.Count == 0 && _nonGreedyState.TaskForInputProcessing == null))) + (_boundingState.CountIsLessThanBound && _nonGreedyState!.PostponedMessages.Count == 0 && _nonGreedyState.TaskForInputProcessing == null))) { // Consume the message from the source if necessary if (consumeToAccept) @@ -393,7 +396,7 @@ namespace System.Threading.Tasks.Dataflow } // Once consumed, enqueue it. - _messages.Enqueue(messageValue); + _messages.Enqueue(messageValue!); if (_boundingState != null) _boundingState.CurrentCount += 1; // track this new item against our bound // Now start declining if the number of batches we've already made plus @@ -432,7 +435,7 @@ namespace System.Threading.Tasks.Dataflow /// In general, it is not safe to pass releaseReservedMessages:true, because releasing of reserved messages /// is done without taking a lock. We pass releaseReservedMessages:true only when an exception has been /// caught inside the message processing loop which is a single instance at any given moment. - internal void Complete(Exception exception, bool dropPendingMessages, bool releaseReservedMessages, bool revertProcessingState = false) + internal void Complete(Exception? exception, bool dropPendingMessages, bool releaseReservedMessages, bool revertProcessingState = false) { // Ensure that no new messages may be added lock (IncomingLock) @@ -531,10 +534,10 @@ namespace System.Threading.Tasks.Dataflow // completion task continuations in that case, do it in a separate task. Task.Factory.StartNew(thisTargetCore => { - var targetCore = (BatchBlockTargetCore)thisTargetCore; + var targetCore = (BatchBlockTargetCore)thisTargetCore!; // Release any postponed messages - List exceptions = null; + List? exceptions = null; if (targetCore._nonGreedyState != null) { // Note: No locks should be held at this point @@ -637,7 +640,7 @@ namespace System.Threading.Tasks.Dataflow // Create task and store into _taskForInputProcessing prior to scheduling the task // so that _taskForInputProcessing will be visibly set in the task loop. - _nonGreedyState.TaskForInputProcessing = new Task(thisBatchTarget => ((BatchBlockTargetCore)thisBatchTarget).ProcessMessagesLoopCore(), this, + _nonGreedyState!.TaskForInputProcessing = new Task(thisBatchTarget => ((BatchBlockTargetCore)thisBatchTarget!).ProcessMessagesLoopCore(), this, Common.GetCreationOptionsForTask(isReplacementReplica)); #if FEATURE_TRACING @@ -651,11 +654,11 @@ namespace System.Threading.Tasks.Dataflow #endif // Start the task handling scheduling exceptions - Exception exception = Common.StartTaskSafe(_nonGreedyState.TaskForInputProcessing, _dataflowBlockOptions.TaskScheduler); + Exception? exception = Common.StartTaskSafe(_nonGreedyState.TaskForInputProcessing, _dataflowBlockOptions.TaskScheduler); if (exception != null) { // Get out from under currently held locks. Complete re-acquires the locks it needs. - Task.Factory.StartNew(exc => Complete(exception: (Exception)exc, dropPendingMessages: true, releaseReservedMessages: true, revertProcessingState: true), + Task.Factory.StartNew(exc => Complete(exception: (Exception)exc!, dropPendingMessages: true, releaseReservedMessages: true, revertProcessingState: true), exception, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default); } } @@ -793,7 +796,7 @@ namespace System.Threading.Tasks.Dataflow KeyValuePair, DataflowMessageHeader> sourceAndMessage = postponedTemp[i]; if (sourceAndMessage.Key.ReserveMessage(sourceAndMessage.Value, _owningBatch)) { - var reservedMessage = new KeyValuePair(sourceAndMessage.Value, default(T)); + var reservedMessage = new KeyValuePair(sourceAndMessage.Value, default(T)!); var reservedSourceAndMessage = new KeyValuePair, KeyValuePair>(sourceAndMessage.Key, reservedMessage); reserved.Add(reservedSourceAndMessage); } @@ -811,7 +814,7 @@ namespace System.Threading.Tasks.Dataflow } // Release the lock. We must not hold it while calling Reserve/Consume/Release. if (sourceAndMessage.Key.ReserveMessage(sourceAndMessage.Value, _owningBatch)) { - var reservedMessage = new KeyValuePair(sourceAndMessage.Value, default(T)); + var reservedMessage = new KeyValuePair(sourceAndMessage.Value, default(T)!); var reservedSourceAndMessage = new KeyValuePair, KeyValuePair>(sourceAndMessage.Key, reservedMessage); reserved.Add(reservedSourceAndMessage); } @@ -908,7 +911,7 @@ namespace System.Threading.Tasks.Dataflow for (int i = 0; i < poppedInitially; i++) { KeyValuePair, DataflowMessageHeader> sourceAndMessage = postponedTemp[i]; - var reservedMessage = new KeyValuePair(sourceAndMessage.Value, default(T)); + var reservedMessage = new KeyValuePair(sourceAndMessage.Value, default(T)!); var reservedSourceAndMessage = new KeyValuePair, KeyValuePair>(sourceAndMessage.Key, reservedMessage); reserved.Add(reservedSourceAndMessage); } @@ -924,7 +927,7 @@ namespace System.Threading.Tasks.Dataflow if (!postponed.TryPop(out sourceAndMessage)) break; } // Release the lock. We must not hold it while calling Reserve/Consume/Release. - var reservedMessage = new KeyValuePair(sourceAndMessage.Value, default(T)); + var reservedMessage = new KeyValuePair(sourceAndMessage.Value, default(T)!); var reservedSourceAndMessage = new KeyValuePair, KeyValuePair>(sourceAndMessage.Key, reservedMessage); reserved.Add(reservedSourceAndMessage); } @@ -1007,7 +1010,7 @@ namespace System.Threading.Tasks.Dataflow throw new InvalidOperationException(SR.InvalidOperation_FailedToConsumeReservedMessage); } - var consumedMessage = new KeyValuePair(sourceAndMessage.Value.Key, consumedValue); + var consumedMessage = new KeyValuePair(sourceAndMessage.Value.Key, consumedValue!); var consumedSourceAndMessage = new KeyValuePair, KeyValuePair>(sourceAndMessage.Key, consumedMessage); reserved[i] = consumedSourceAndMessage; } @@ -1052,7 +1055,7 @@ namespace System.Threading.Tasks.Dataflow T consumedValue = sourceAndMessage.Key.ConsumeMessage(sourceAndMessage.Value.Key, _owningBatch, out consumed); if (consumed) { - var consumedMessage = new KeyValuePair(sourceAndMessage.Value.Key, consumedValue); + var consumedMessage = new KeyValuePair(sourceAndMessage.Value.Key, consumedValue!); var consumedSourceAndMessage = new KeyValuePair, KeyValuePair>(sourceAndMessage.Key, consumedMessage); reserved[i] = consumedSourceAndMessage; @@ -1087,7 +1090,7 @@ namespace System.Threading.Tasks.Dataflow Debug.Assert(_nonGreedyState != null, "Non-greedy state is required for non-greedy mode."); Debug.Assert(_nonGreedyState.ReservedSourcesTemp != null, "Should have been initialized"); - List exceptions = null; + List? exceptions = null; List, KeyValuePair>> reserved = _nonGreedyState.ReservedSourcesTemp; for (int i = 0; i < reserved.Count; i++) @@ -1139,7 +1142,7 @@ namespace System.Threading.Tasks.Dataflow /// Counts the input items in a single output item or in a list of output items. /// A single output item. Only considered if multipleOutputItems == null. /// A list of output items. May be null. - internal static int CountItems(T[] singleOutputItem, IList multipleOutputItems) + internal static int CountItems(T[] singleOutputItem, IList? multipleOutputItems) { // If multipleOutputItems == null, then singleOutputItem is the subject of counting if (multipleOutputItems == null) return singleOutputItem.Length; @@ -1181,9 +1184,9 @@ namespace System.Threading.Tasks.Dataflow /// Gets the messages waiting to be processed. public IEnumerable InputQueue { get { return _target._messages.ToList(); } } /// Gets the task being used for input processing. - public Task TaskForInputProcessing { get { return _target._nonGreedyState != null ? _target._nonGreedyState.TaskForInputProcessing : null; } } + public Task? TaskForInputProcessing { get { return _target._nonGreedyState != null ? _target._nonGreedyState.TaskForInputProcessing : null; } } /// Gets the collection of postponed messages. - public QueuedMap, DataflowMessageHeader> PostponedMessages { get { return _target._nonGreedyState != null ? _target._nonGreedyState.PostponedMessages : null; } } + public QueuedMap, DataflowMessageHeader>? PostponedMessages { get { return _target._nonGreedyState != null ? _target._nonGreedyState.PostponedMessages : null; } } /// Gets whether the block is declining further messages. public bool IsDecliningPermanently { get { return _target._decliningPermanently; } } /// Gets the DataflowBlockOptions used to configure this block. diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchedJoinBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchedJoinBlock.cs index 4b961f5..4e4f576 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchedJoinBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BatchedJoinBlock.cs @@ -99,14 +99,14 @@ namespace System.Threading.Tasks.Dataflow // to handle multiple completion requests and to carry over only one. _source.Completion.ContinueWith((completed, state) => { - var thisBlock = ((BatchedJoinBlock)state) as IDataflowBlock; + var thisBlock = ((BatchedJoinBlock)state!) as IDataflowBlock; Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion."); - thisBlock.Fault(completed.Exception); + thisBlock.Fault(completed.Exception!); }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); // Handle async cancellation requests by declining on the target Common.WireCancellationToComplete( - dataflowBlockOptions.CancellationToken, _source.Completion, state => ((BatchedJoinBlock)state).CompleteEachTarget(), this); + dataflowBlockOptions.CancellationToken, _source.Completion, state => ((BatchedJoinBlock)state!).CompleteEachTarget(), this); #if FEATURE_TRACING DataflowEtwProvider etwLog = DataflowEtwProvider.Log; if (etwLog.IsEnabled()) @@ -132,13 +132,15 @@ namespace System.Threading.Tasks.Dataflow } /// - public bool TryReceive(Predicate, IList>> filter, out Tuple, IList> item) +#pragma warning disable CS8614 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470 + public bool TryReceive(Predicate, IList>>? filter, [NotNullWhen(true)] out Tuple, IList>? item) +#pragma warning restore CS8614 { return _source.TryReceive(filter, out item); } /// - public bool TryReceiveAll(out IList, IList>> items) { return _source.TryReceiveAll(out items); } + public bool TryReceiveAll([NotNullWhen(true)] out IList, IList>>? items) { return _source.TryReceiveAll(out items); } /// public int OutputCount { get { return _source.OutputCount; } } @@ -173,6 +175,7 @@ namespace System.Threading.Tasks.Dataflow } /// + [return: MaybeNull] Tuple, IList> ISourceBlock, IList>>.ConsumeMessage( DataflowMessageHeader messageHeader, ITargetBlock, IList>> target, out bool messageConsumed) { @@ -254,7 +257,7 @@ namespace System.Threading.Tasks.Dataflow public ITargetBlock Target2 { get { return _batchedJoinBlock._target2; } } /// Gets the task being used for output processing. - public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } + public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } /// Gets the DataflowBlockOptions used to configure this block. public GroupingDataflowBlockOptions DataflowBlockOptions { get { return (GroupingDataflowBlockOptions)_sourceDebuggingInformation.DataflowBlockOptions; } } @@ -266,7 +269,7 @@ namespace System.Threading.Tasks.Dataflow /// Gets the set of all targets linked from this block. public TargetRegistry, IList>> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } } /// Gets the target that holds a reservation on the next message, if any. - public ITargetBlock, IList>> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } + public ITargetBlock, IList>>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } } } @@ -357,14 +360,14 @@ namespace System.Threading.Tasks.Dataflow // to handle multiple completion requests and to carry over only one. _source.Completion.ContinueWith((completed, state) => { - var thisBlock = ((BatchedJoinBlock)state) as IDataflowBlock; + var thisBlock = ((BatchedJoinBlock)state!) as IDataflowBlock; Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion."); - thisBlock.Fault(completed.Exception); + thisBlock.Fault(completed.Exception!); }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); // Handle async cancellation requests by declining on the target Common.WireCancellationToComplete( - dataflowBlockOptions.CancellationToken, _source.Completion, state => ((BatchedJoinBlock)state).CompleteEachTarget(), this); + dataflowBlockOptions.CancellationToken, _source.Completion, state => ((BatchedJoinBlock)state!).CompleteEachTarget(), this); #if FEATURE_TRACING DataflowEtwProvider etwLog = DataflowEtwProvider.Log; if (etwLog.IsEnabled()) @@ -393,13 +396,15 @@ namespace System.Threading.Tasks.Dataflow } /// - public bool TryReceive(Predicate, IList, IList>> filter, out Tuple, IList, IList> item) +#pragma warning disable CS8614 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470 + public bool TryReceive(Predicate, IList, IList>>? filter, [NotNullWhen(true)] out Tuple, IList, IList>? item) +#pragma warning restore CS8614 { return _source.TryReceive(filter, out item); } /// - public bool TryReceiveAll(out IList, IList, IList>> items) { return _source.TryReceiveAll(out items); } + public bool TryReceiveAll([NotNullWhen(true)] out IList, IList, IList>>? items) { return _source.TryReceiveAll(out items); } /// public int OutputCount { get { return _source.OutputCount; } } @@ -436,6 +441,7 @@ namespace System.Threading.Tasks.Dataflow } /// + [return: MaybeNull] Tuple, IList, IList> ISourceBlock, IList, IList>>.ConsumeMessage( DataflowMessageHeader messageHeader, ITargetBlock, IList, IList>> target, out bool messageConsumed) { @@ -520,7 +526,7 @@ namespace System.Threading.Tasks.Dataflow public ITargetBlock Target3 { get { return _batchedJoinBlock._target3; } } /// Gets the task being used for output processing. - public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } + public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } /// Gets the DataflowBlockOptions used to configure this block. public GroupingDataflowBlockOptions DataflowBlockOptions { get { return (GroupingDataflowBlockOptions)_sourceDebuggingInformation.DataflowBlockOptions; } } @@ -532,7 +538,7 @@ namespace System.Threading.Tasks.Dataflow /// Gets the set of all targets linked from this block. public TargetRegistry, IList, IList>> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } } /// Gets the target that holds a reservation on the next message, if any. - public ITargetBlock, IList, IList>> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } + public ITargetBlock, IList, IList>>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } } } } @@ -580,7 +586,7 @@ namespace System.Threading.Tasks.Dataflow.Internal } /// - public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) + public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock? source, bool consumeToAccept) { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); @@ -602,7 +608,7 @@ namespace System.Threading.Tasks.Dataflow.Internal messageValue = source.ConsumeMessage(messageHeader, this, out consumed); if (!consumed) return DataflowMessageStatus.NotAvailable; } - _messages.Add(messageValue); + _messages.Add(messageValue!); // If this message makes a batch, notify the shared resources that a batch has been completed if (--_sharedResources._remainingItemsInBatch == 0) _sharedResources._batchSizeReachedAction(); diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BroadcastBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BroadcastBlock.cs index ab47133..103a124 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BroadcastBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BroadcastBlock.cs @@ -39,7 +39,7 @@ namespace System.Threading.Tasks.Dataflow /// The source side. private readonly BroadcastingSourceCore _source; /// Bounding state for when the block is executing in bounded mode. - private readonly BoundingStateWithPostponedAndTask _boundingState; + private readonly BoundingStateWithPostponedAndTask? _boundingState; /// Whether all future messages should be declined. private bool _decliningPermanently; /// A task has reserved the right to run the completion routine. @@ -52,7 +52,7 @@ namespace System.Threading.Tasks.Dataflow /// The function to use to clone the data when offered to other blocks. /// This may be null to indicate that no cloning need be performed. /// - public BroadcastBlock(Func cloningFunction) : + public BroadcastBlock(Func? cloningFunction) : this(cloningFunction, DataflowBlockOptions.Default) { } @@ -63,7 +63,7 @@ namespace System.Threading.Tasks.Dataflow /// /// The options with which to configure this . /// The is null (Nothing in Visual Basic). - public BroadcastBlock(Func cloningFunction, DataflowBlockOptions dataflowBlockOptions) + public BroadcastBlock(Func? cloningFunction, DataflowBlockOptions dataflowBlockOptions) { // Validate arguments if (dataflowBlockOptions == null) throw new ArgumentNullException(nameof(dataflowBlockOptions)); @@ -72,7 +72,7 @@ namespace System.Threading.Tasks.Dataflow dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone(); // Initialize bounding state if necessary - Action onItemsRemoved = null; + Action? onItemsRemoved = null; if (dataflowBlockOptions.BoundedCapacity > 0) { Debug.Assert(dataflowBlockOptions.BoundedCapacity > 0, "Positive bounding count expected; should have been verified by options ctor"); @@ -89,14 +89,14 @@ namespace System.Threading.Tasks.Dataflow // to handle multiple completion requests and to carry over only one. _source.Completion.ContinueWith((completed, state) => { - var thisBlock = ((BroadcastBlock)state) as IDataflowBlock; + var thisBlock = ((BroadcastBlock)state!) as IDataflowBlock; Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion."); - thisBlock.Fault(completed.Exception); + thisBlock.Fault(completed.Exception!); }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); // Handle async cancellation requests by declining on the target Common.WireCancellationToComplete( - dataflowBlockOptions.CancellationToken, _source.Completion, state => ((BroadcastBlock)state).Complete(), this); + dataflowBlockOptions.CancellationToken, _source.Completion, state => ((BroadcastBlock)state!).Complete(), this); #if FEATURE_TRACING DataflowEtwProvider etwLog = DataflowEtwProvider.Log; if (etwLog.IsEnabled()) @@ -120,7 +120,7 @@ namespace System.Threading.Tasks.Dataflow CompleteCore(exception, storeExceptionEvenIfAlreadyCompleting: false); } - internal void CompleteCore(Exception exception, bool storeExceptionEvenIfAlreadyCompleting, bool revertProcessingState = false) + internal void CompleteCore(Exception? exception, bool storeExceptionEvenIfAlreadyCompleting, bool revertProcessingState = false) { Debug.Assert(storeExceptionEvenIfAlreadyCompleting || !revertProcessingState, "Indicating dirty processing state may only come with storeExceptionEvenIfAlreadyCompleting==true."); @@ -152,16 +152,16 @@ namespace System.Threading.Tasks.Dataflow public IDisposable LinkTo(ITargetBlock target, DataflowLinkOptions linkOptions) { return _source.LinkTo(target, linkOptions); } /// - public bool TryReceive(Predicate filter, out T item) { return _source.TryReceive(filter, out item); } + public bool TryReceive(Predicate? filter, [MaybeNullWhen(false)] out T item) { return _source.TryReceive(filter, out item); } /// - bool IReceivableSourceBlock.TryReceiveAll(out IList items) { return _source.TryReceiveAll(out items); } + bool IReceivableSourceBlock.TryReceiveAll([NotNullWhen(true)] out IList? items) { return _source.TryReceiveAll(out items); } /// public Task Completion { get { return _source.Completion; } } /// - DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) + DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock? source, bool consumeToAccept) { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); @@ -197,7 +197,7 @@ namespace System.Threading.Tasks.Dataflow } // Once consumed, pass it to the delegate - _source.AddMessage(messageValue); + _source.AddMessage(messageValue!); if (_boundingState != null) _boundingState.CurrentCount += 1; // track this new item against our bound return DataflowMessageStatus.Accepted; } @@ -255,7 +255,7 @@ namespace System.Threading.Tasks.Dataflow // Create task and store into _taskForInputProcessing prior to scheduling the task // so that _taskForInputProcessing will be visibly set in the task loop. _boundingState.TaskForInputProcessing = - new Task(state => ((BroadcastBlock)state).ConsumeMessagesLoopCore(), this, + new Task(state => ((BroadcastBlock)state!).ConsumeMessagesLoopCore(), this, Common.GetCreationOptionsForTask(isReplacementReplica)); #if FEATURE_TRACING @@ -269,11 +269,11 @@ namespace System.Threading.Tasks.Dataflow #endif // Start the task handling scheduling exceptions - Exception exception = Common.StartTaskSafe(_boundingState.TaskForInputProcessing, _source.DataflowBlockOptions.TaskScheduler); + Exception? exception = Common.StartTaskSafe(_boundingState.TaskForInputProcessing, _source.DataflowBlockOptions.TaskScheduler); if (exception != null) { // Get out from under currently held locks. Complete re-acquires the locks it needs. - Task.Factory.StartNew(exc => CompleteCore(exception: (Exception)exc, storeExceptionEvenIfAlreadyCompleting: true, revertProcessingState: true), + Task.Factory.StartNew(exc => CompleteCore(exception: (Exception)exc!, storeExceptionEvenIfAlreadyCompleting: true, revertProcessingState: true), exception, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default); } } @@ -356,7 +356,7 @@ namespace System.Threading.Tasks.Dataflow T consumedValue = sourceAndMessage.Key.ConsumeMessage(sourceAndMessage.Value, this, out consumed); if (consumed) { - _source.AddMessage(consumedValue); + _source.AddMessage(consumedValue!); return true; } } @@ -387,10 +387,10 @@ namespace System.Threading.Tasks.Dataflow { Task.Factory.StartNew(state => { - var thisBroadcastBlock = (BroadcastBlock)state; + var thisBroadcastBlock = (BroadcastBlock)state!; // Release any postponed messages - List exceptions = null; + List? exceptions = null; if (thisBroadcastBlock._boundingState != null) { // Note: No locks should be held at this point @@ -418,6 +418,7 @@ namespace System.Threading.Tasks.Dataflow } /// + [return: MaybeNull] T ISourceBlock.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) { return _source.ConsumeMessage(messageHeader, target, out messageConsumed); @@ -482,7 +483,7 @@ namespace System.Threading.Tasks.Dataflow public T Value { get { return _broadcastBlock.ValueForDebugger; } } /// Gets the task being used for output processing. - public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } + public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } /// Gets the DataflowBlockOptions used to configure this block. public DataflowBlockOptions DataflowBlockOptions { get { return _sourceDebuggingInformation.DataflowBlockOptions; } } @@ -496,7 +497,7 @@ namespace System.Threading.Tasks.Dataflow /// Gets the set of all targets linked from this block. public TargetRegistry LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } } /// Gets the set of all targets linked from this block. - public ITargetBlock NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } + public ITargetBlock? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } } /// Provides a core implementation for blocks that implement . @@ -514,7 +515,7 @@ namespace System.Threading.Tasks.Dataflow /// An action to be invoked on the owner block when an item is removed. /// This may be null if the owner block doesn't need to be notified. /// - private readonly Action _itemsRemovedAction; + private readonly Action? _itemsRemovedAction; /// Gets the object to use as the outgoing lock. private object OutgoingLock { get { return _completionTask; } } @@ -526,22 +527,23 @@ namespace System.Threading.Tasks.Dataflow /// The options used to configure this block's execution. private readonly DataflowBlockOptions _dataflowBlockOptions; /// The cloning function to use. - private readonly Func _cloningFunction; + private readonly Func? _cloningFunction; /// An indicator whether _currentMessage has a value. private bool _currentMessageIsValid; /// The message currently being broadcast. - private TOutput _currentMessage; + [AllowNull, MaybeNull] + private TOutput _currentMessage = default; /// The target that the next message is reserved for, or null if nothing is reserved. - private ITargetBlock _nextMessageReservedFor; + private ITargetBlock? _nextMessageReservedFor; /// Whether this block should again attempt to offer messages to targets. private bool _enableOffering; /// Whether all future messages should be declined. private bool _decliningPermanently; /// The task used to process the output and offer it to targets. - private Task _taskForOutputProcessing; + private Task? _taskForOutputProcessing; /// Exceptions that may have occurred and gone unhandled during processing. - private List _exceptions; + private List? _exceptions; /// Counter for message IDs unique within this source block. private long _nextMessageId = 1; // We are going to use this value before incrementing. /// Whether someone has reserved the right to call CompleteBlockOncePossible. @@ -554,9 +556,9 @@ namespace System.Threading.Tasks.Dataflow /// Action to invoke when an item is removed. internal BroadcastingSourceCore( BroadcastBlock owningSource, - Func cloningFunction, + Func? cloningFunction, DataflowBlockOptions dataflowBlockOptions, - Action itemsRemovedAction) + Action? itemsRemovedAction) { Debug.Assert(owningSource != null, "Must be associated with a broadcast block."); Debug.Assert(dataflowBlockOptions != null, "Options are required to configure this block."); @@ -572,7 +574,7 @@ namespace System.Threading.Tasks.Dataflow } /// - internal bool TryReceive(Predicate filter, out TOutput item) + internal bool TryReceive(Predicate? filter, [MaybeNullWhen(false)] out TOutput item) { // Take the lock only long enough to get the message, // synchronizing with other activities on the block. @@ -591,9 +593,9 @@ namespace System.Threading.Tasks.Dataflow // Clone and hand back a message if we have one and if it passes the filter. // (A null filter means all messages pass.) - if (isValid && (filter == null || filter(message))) + if (isValid && (filter == null || filter(message!))) { - item = CloneItem(message); + item = CloneItem(message!); return true; } else @@ -604,7 +606,7 @@ namespace System.Threading.Tasks.Dataflow } /// - internal bool TryReceiveAll(out IList items) + internal bool TryReceiveAll([NotNullWhen(true)] out IList? items) { // Try to receive the one item this block may have. // If we can, give back an array of one item. Otherwise, give back null. @@ -653,7 +655,7 @@ namespace System.Threading.Tasks.Dataflow // and take the necessary locks in a situation where we're sure it won't cause a problem. Task.Factory.StartNew(state => { - var thisSourceCore = (BroadcastingSourceCore)state; + var thisSourceCore = (BroadcastingSourceCore)state!; lock (thisSourceCore.OutgoingLock) { lock (thisSourceCore.ValueLock) @@ -698,7 +700,7 @@ namespace System.Threading.Tasks.Dataflow // Offer it to the target. // We must not increment the message ID here. We only do that when we populate _currentMessage, i.e. when we dequeue. bool useCloning = _cloningFunction != null; - DataflowMessageStatus result = target.OfferMessage(new DataflowMessageHeader(_nextMessageId), currentMessage, _owningSource, consumeToAccept: useCloning); + DataflowMessageStatus result = target.OfferMessage(new DataflowMessageHeader(_nextMessageId), currentMessage!, _owningSource, consumeToAccept: useCloning); // If accepted and the target was linked as "unlinkAfterOne", remove it if (result == DataflowMessageStatus.Accepted) @@ -770,14 +772,14 @@ namespace System.Threading.Tasks.Dataflow if (_itemsRemovedAction != null) _itemsRemovedAction(numDequeuedMessages); // Offer it to each target, unless a soleTarget was provided, which case just offer it to that one. - TargetRegistry.LinkedTargetInfo cur = _targetRegistry.FirstTargetNode; + TargetRegistry.LinkedTargetInfo? cur = _targetRegistry.FirstTargetNode; while (cur != null) { // Note that during OfferMessage, a target may call ConsumeMessage, which may unlink the target // if the target is registered as "once". Doing so will remove the target from the targets list. // As such, we avoid using an enumerator over _targetRegistry and instead walk from back to front, // so that if an element is removed, it won't affect the rest of our walk. - TargetRegistry.LinkedTargetInfo next = cur.Next; + TargetRegistry.LinkedTargetInfo? next = cur.Next; ITargetBlock target = cur.Target; OfferMessageToTarget(header, message, target); cur = next; @@ -842,7 +844,7 @@ namespace System.Threading.Tasks.Dataflow { // Create task and store into _taskForOutputProcessing prior to scheduling the task // so that _taskForOutputProcessing will be visibly set in the task loop. - _taskForOutputProcessing = new Task(thisSourceCore => ((BroadcastingSourceCore)thisSourceCore).OfferMessagesLoopCore(), this, + _taskForOutputProcessing = new Task(thisSourceCore => ((BroadcastingSourceCore)thisSourceCore!).OfferMessagesLoopCore(), this, Common.GetCreationOptionsForTask(isReplacementReplica)); #if FEATURE_TRACING @@ -855,7 +857,7 @@ namespace System.Threading.Tasks.Dataflow #endif // Start the task handling scheduling exceptions - Exception exception = Common.StartTaskSafe(_taskForOutputProcessing, _dataflowBlockOptions.TaskScheduler); + Exception? exception = Common.StartTaskSafe(_taskForOutputProcessing, _dataflowBlockOptions.TaskScheduler); if (exception != null) { // First, log the exception while the processing state is dirty which is preventing the block from completing. @@ -869,7 +871,7 @@ namespace System.Threading.Tasks.Dataflow // Re-take the locks on a separate thread. Task.Factory.StartNew(state => { - var thisSourceCore = (BroadcastingSourceCore)state; + var thisSourceCore = (BroadcastingSourceCore)state!; lock (thisSourceCore.OutgoingLock) { lock (thisSourceCore.ValueLock) @@ -959,7 +961,7 @@ namespace System.Threading.Tasks.Dataflow _completionReserved = true; // Run asynchronously to get out of the currently held locks - Task.Factory.StartNew(thisSourceCore => ((BroadcastingSourceCore)thisSourceCore).CompleteBlockOncePossible(), + Task.Factory.StartNew(thisSourceCore => ((BroadcastingSourceCore)thisSourceCore!).CompleteBlockOncePossible(), this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default); } @@ -969,8 +971,8 @@ namespace System.Threading.Tasks.Dataflow /// private void CompleteBlockOncePossible() { - TargetRegistry.LinkedTargetInfo linkedTargets; - List exceptions; + TargetRegistry.LinkedTargetInfo? linkedTargets; + List? exceptions; // Clear out the target registry and buffers to help avoid memory leaks. // We do not clear _currentMessage, which should remain as that message forever. @@ -1048,6 +1050,7 @@ namespace System.Threading.Tasks.Dataflow } /// + [return: MaybeNull] internal TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) { // Validate arguments @@ -1084,7 +1087,7 @@ namespace System.Threading.Tasks.Dataflow } messageConsumed = true; - return CloneItem(valueToClone); + return CloneItem(valueToClone!); } /// @@ -1145,7 +1148,7 @@ namespace System.Threading.Tasks.Dataflow // do this if _messages.Count == 0, as if it's > 0 the message will get overwritten // as part of the asynchronous offering, but for consistency we should always reoffer // the current message. - OfferMessageToTarget(messageHeader, messageToReoffer, target); + OfferMessageToTarget(messageHeader, messageToReoffer!, target); } } @@ -1223,11 +1226,11 @@ namespace System.Threading.Tasks.Dataflow /// Gets whether the source contains a current message. public bool HasValue { get { return _source._currentMessageIsValid; } } /// Gets the value of the source's current message. - public TOutput Value { get { return _source._currentMessage; } } + public TOutput Value { get { return _source._currentMessage!; } } /// Gets the messages available for receiving. public IEnumerable InputQueue { get { return _source._messages.ToList(); } } /// Gets the task being used for output processing. - public Task TaskForOutputProcessing { get { return _source._taskForOutputProcessing; } } + public Task? TaskForOutputProcessing { get { return _source._taskForOutputProcessing; } } /// Gets the DataflowBlockOptions used to configure this block. public DataflowBlockOptions DataflowBlockOptions { get { return _source._dataflowBlockOptions; } } @@ -1237,7 +1240,7 @@ namespace System.Threading.Tasks.Dataflow /// Gets the set of all targets linked from this block. public TargetRegistry LinkedTargets { get { return _source._targetRegistry; } } /// Gets the target that holds a reservation on the next message, if any. - public ITargetBlock NextMessageReservedFor { get { return _source._nextMessageReservedFor; } } + public ITargetBlock? NextMessageReservedFor { get { return _source._nextMessageReservedFor; } } } } } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BufferBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BufferBlock.cs index d9dce57..806ee15 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BufferBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/BufferBlock.cs @@ -28,7 +28,7 @@ namespace System.Threading.Tasks.Dataflow /// The core logic for the buffer block. private readonly SourceCore _source; /// The bounding state for when in bounding mode; null if not bounding. - private readonly BoundingStateWithPostponedAndTask _boundingState; + private readonly BoundingStateWithPostponedAndTask? _boundingState; /// Whether all future messages should be declined on the target. private bool _targetDecliningPermanently; /// A task has reserved the right to run the target's completion routine. @@ -52,7 +52,7 @@ namespace System.Threading.Tasks.Dataflow dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone(); // Initialize bounding state if necessary - Action, int> onItemsRemoved = null; + Action, int>? onItemsRemoved = null; if (dataflowBlockOptions.BoundedCapacity > 0) { onItemsRemoved = (owningSource, count) => ((BufferBlock)owningSource).OnItemsRemoved(count); @@ -70,14 +70,14 @@ namespace System.Threading.Tasks.Dataflow // to handle multiple completion requests and to carry over only one. _source.Completion.ContinueWith((completed, state) => { - var thisBlock = ((BufferBlock)state) as IDataflowBlock; + var thisBlock = ((BufferBlock)state!) as IDataflowBlock; Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion."); - thisBlock.Fault(completed.Exception); + thisBlock.Fault(completed.Exception!); }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); // Handle async cancellation requests by declining on the target Common.WireCancellationToComplete( - dataflowBlockOptions.CancellationToken, _source.Completion, owningSource => ((BufferBlock)owningSource).Complete(), this); + dataflowBlockOptions.CancellationToken, _source.Completion, owningSource => ((BufferBlock)owningSource!).Complete(), this); #if FEATURE_TRACING DataflowEtwProvider etwLog = DataflowEtwProvider.Log; if (etwLog.IsEnabled()) @@ -88,7 +88,7 @@ namespace System.Threading.Tasks.Dataflow } /// - DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) + DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock? source, bool consumeToAccept) { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); @@ -124,7 +124,7 @@ namespace System.Threading.Tasks.Dataflow } // Once consumed, pass it to the source - _source.AddMessage(messageValue); + _source.AddMessage(messageValue!); if (_boundingState != null) _boundingState.CurrentCount++; return DataflowMessageStatus.Accepted; @@ -154,7 +154,7 @@ namespace System.Threading.Tasks.Dataflow CompleteCore(exception, storeExceptionEvenIfAlreadyCompleting: false); } - private void CompleteCore(Exception exception, bool storeExceptionEvenIfAlreadyCompleting, bool revertProcessingState = false) + private void CompleteCore(Exception? exception, bool storeExceptionEvenIfAlreadyCompleting, bool revertProcessingState = false) { Debug.Assert(storeExceptionEvenIfAlreadyCompleting || !revertProcessingState, "Indicating dirty processing state may only come with storeExceptionEvenIfAlreadyCompleting==true."); @@ -186,10 +186,10 @@ namespace System.Threading.Tasks.Dataflow public IDisposable LinkTo(ITargetBlock target, DataflowLinkOptions linkOptions) { return _source.LinkTo(target, linkOptions); } /// - public bool TryReceive(Predicate filter, out T item) { return _source.TryReceive(filter, out item); } + public bool TryReceive(Predicate? filter, [MaybeNullWhen(false)] out T item) { return _source.TryReceive(filter, out item); } /// - public bool TryReceiveAll(out IList items) { return _source.TryReceiveAll(out items); } + public bool TryReceiveAll([NotNullWhen(true)] out IList? items) { return _source.TryReceiveAll(out items); } /// Gets the number of items currently stored in the buffer. public int Count { get { return _source.OutputCount; } } @@ -198,6 +198,7 @@ namespace System.Threading.Tasks.Dataflow public Task Completion { get { return _source.Completion; } } /// + [return: MaybeNull] T ISourceBlock.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) { return _source.ConsumeMessage(messageHeader, target, out messageConsumed); @@ -255,7 +256,7 @@ namespace System.Threading.Tasks.Dataflow // Create task and store into _taskForInputProcessing prior to scheduling the task // so that _taskForInputProcessing will be visibly set in the task loop. _boundingState.TaskForInputProcessing = - new Task(state => ((BufferBlock)state).ConsumeMessagesLoopCore(), this, + new Task(state => ((BufferBlock)state!).ConsumeMessagesLoopCore(), this, Common.GetCreationOptionsForTask(isReplacementReplica)); #if FEATURE_TRACING @@ -269,11 +270,11 @@ namespace System.Threading.Tasks.Dataflow #endif // Start the task handling scheduling exceptions - Exception exception = Common.StartTaskSafe(_boundingState.TaskForInputProcessing, _source.DataflowBlockOptions.TaskScheduler); + Exception? exception = Common.StartTaskSafe(_boundingState.TaskForInputProcessing, _source.DataflowBlockOptions.TaskScheduler); if (exception != null) { // Get out from under currently held locks. CompleteCore re-acquires the locks it needs. - Task.Factory.StartNew(exc => CompleteCore(exception: (Exception)exc, storeExceptionEvenIfAlreadyCompleting: true, revertProcessingState: true), + Task.Factory.StartNew(exc => CompleteCore(exception: (Exception)exc!, storeExceptionEvenIfAlreadyCompleting: true, revertProcessingState: true), exception, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default); } } @@ -358,7 +359,7 @@ namespace System.Threading.Tasks.Dataflow T consumedValue = sourceAndMessage.Key.ConsumeMessage(sourceAndMessage.Value, this, out consumed); if (consumed) { - _source.AddMessage(consumedValue); + _source.AddMessage(consumedValue!); return true; } } @@ -389,10 +390,10 @@ namespace System.Threading.Tasks.Dataflow { Task.Factory.StartNew(state => { - var thisBufferBlock = (BufferBlock)state; + var thisBufferBlock = (BufferBlock)state!; // Release any postponed messages - List exceptions = null; + List? exceptions = null; if (thisBufferBlock._boundingState != null) { // Note: No locks should be held at this point @@ -456,7 +457,7 @@ namespace System.Threading.Tasks.Dataflow } /// Gets the collection of postponed message headers. - public QueuedMap, DataflowMessageHeader> PostponedMessages + public QueuedMap, DataflowMessageHeader>? PostponedMessages { get { return _bufferBlock._boundingState != null ? _bufferBlock._boundingState.PostponedMessages : null; } } @@ -464,9 +465,9 @@ namespace System.Threading.Tasks.Dataflow public IEnumerable Queue { get { return _sourceDebuggingInformation.OutputQueue; } } /// The task used to process messages. - public Task TaskForInputProcessing { get { return _bufferBlock._boundingState != null ? _bufferBlock._boundingState.TaskForInputProcessing : null; } } + public Task? TaskForInputProcessing { get { return _bufferBlock._boundingState != null ? _bufferBlock._boundingState.TaskForInputProcessing : null; } } /// Gets the task being used for output processing. - public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } + public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } /// Gets the DataflowBlockOptions used to configure this block. public DataflowBlockOptions DataflowBlockOptions { get { return _sourceDebuggingInformation.DataflowBlockOptions; } } @@ -481,7 +482,7 @@ namespace System.Threading.Tasks.Dataflow /// Gets the set of all targets linked from this block. public TargetRegistry LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } } /// Gets the set of all targets linked from this block. - public ITargetBlock NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } + public ITargetBlock? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } } } } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/JoinBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/JoinBlock.cs index b5d4052..9d8b1ad 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/JoinBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/JoinBlock.cs @@ -57,7 +57,7 @@ namespace System.Threading.Tasks.Dataflow dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone(); // Initialize bounding state if necessary - Action>, int> onItemsRemoved = null; + Action>, int>? onItemsRemoved = null; if (dataflowBlockOptions.BoundedCapacity > 0) onItemsRemoved = (owningSource, count) => ((JoinBlock)owningSource)._sharedResources.OnItemsRemoved(count); // Configure the source @@ -93,14 +93,14 @@ namespace System.Threading.Tasks.Dataflow // to handle multiple completion requests and to carry over only one. _source.Completion.ContinueWith((completed, state) => { - var thisBlock = ((JoinBlock)state) as IDataflowBlock; + var thisBlock = ((JoinBlock)state!) as IDataflowBlock; Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion."); - thisBlock.Fault(completed.Exception); + thisBlock.Fault(completed.Exception!); }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); // Handle async cancellation requests by declining on the target Common.WireCancellationToComplete( - dataflowBlockOptions.CancellationToken, _source.Completion, state => ((JoinBlock)state)._sharedResources.CompleteEachTarget(), this); + dataflowBlockOptions.CancellationToken, _source.Completion, state => ((JoinBlock)state!)._sharedResources.CompleteEachTarget(), this); #if FEATURE_TRACING DataflowEtwProvider etwLog = DataflowEtwProvider.Log; if (etwLog.IsEnabled()) @@ -117,13 +117,15 @@ namespace System.Threading.Tasks.Dataflow } /// - public bool TryReceive(Predicate> filter, out Tuple item) +#pragma warning disable CS8614 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470 + public bool TryReceive(Predicate>? filter, [NotNullWhen(true)] out Tuple? item) +#pragma warning restore CS8614 { return _source.TryReceive(filter, out item); } /// - public bool TryReceiveAll(out IList> items) { return _source.TryReceiveAll(out items); } + public bool TryReceiveAll([NotNullWhen(true)] out IList>? items) { return _source.TryReceiveAll(out items); } /// public int OutputCount { get { return _source.OutputCount; } } @@ -164,7 +166,9 @@ namespace System.Threading.Tasks.Dataflow public ITargetBlock Target2 { get { return _target2; } } /// - Tuple ISourceBlock>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock> target, out bool messageConsumed) +#pragma warning disable CS8616 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470 + Tuple? ISourceBlock>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock> target, out bool messageConsumed) +#pragma warning restore CS8616 { return _source.ConsumeMessage(messageHeader, target, out messageConsumed); } @@ -223,9 +227,9 @@ namespace System.Threading.Tasks.Dataflow public long JoinsCreated { get { return _joinBlock._sharedResources._joinsCreated; } } /// Gets the task being used for input processing. - public Task TaskForInputProcessing { get { return _joinBlock._sharedResources._taskForInputProcessing; } } + public Task? TaskForInputProcessing { get { return _joinBlock._sharedResources._taskForInputProcessing; } } /// Gets the task being used for output processing. - public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } + public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } /// Gets the GroupingDataflowBlockOptions used to configure this block. public GroupingDataflowBlockOptions DataflowBlockOptions { get { return (GroupingDataflowBlockOptions)_sourceDebuggingInformation.DataflowBlockOptions; } } @@ -244,7 +248,7 @@ namespace System.Threading.Tasks.Dataflow /// Gets the set of all targets linked from this block. public TargetRegistry> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } } /// Gets the set of all targets linked from this block. - public ITargetBlock> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } + public ITargetBlock>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } } } @@ -287,7 +291,7 @@ namespace System.Threading.Tasks.Dataflow dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone(); // Initialize bounding state if necessary - Action>, int> onItemsRemoved = null; + Action>, int>? onItemsRemoved = null; if (dataflowBlockOptions.BoundedCapacity > 0) onItemsRemoved = (owningSource, count) => ((JoinBlock)owningSource)._sharedResources.OnItemsRemoved(count); // Configure the source @@ -321,14 +325,14 @@ namespace System.Threading.Tasks.Dataflow // to handle multiple completion requests and to carry over only one. _source.Completion.ContinueWith((completed, state) => { - var thisBlock = ((JoinBlock)state) as IDataflowBlock; + var thisBlock = ((JoinBlock)state!) as IDataflowBlock; Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion."); - thisBlock.Fault(completed.Exception); + thisBlock.Fault(completed.Exception!); }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); // Handle async cancellation requests by declining on the target Common.WireCancellationToComplete( - dataflowBlockOptions.CancellationToken, _source.Completion, state => ((JoinBlock)state)._sharedResources.CompleteEachTarget(), this); + dataflowBlockOptions.CancellationToken, _source.Completion, state => ((JoinBlock)state!)._sharedResources.CompleteEachTarget(), this); #if FEATURE_TRACING DataflowEtwProvider etwLog = DataflowEtwProvider.Log; if (etwLog.IsEnabled()) @@ -345,13 +349,15 @@ namespace System.Threading.Tasks.Dataflow } /// - public bool TryReceive(Predicate> filter, out Tuple item) +#pragma warning disable CS8614 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470 + public bool TryReceive(Predicate>? filter, [NotNullWhen(true)] out Tuple? item) +#pragma warning restore CS8614 { return _source.TryReceive(filter, out item); } /// - public bool TryReceiveAll(out IList> items) { return _source.TryReceiveAll(out items); } + public bool TryReceiveAll([NotNullWhen(true)] out IList>? items) { return _source.TryReceiveAll(out items); } /// public int OutputCount { get { return _source.OutputCount; } } @@ -397,7 +403,9 @@ namespace System.Threading.Tasks.Dataflow public ITargetBlock Target3 { get { return _target3; } } /// - Tuple ISourceBlock>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock> target, out bool messageConsumed) +#pragma warning disable CS8616 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470 + Tuple? ISourceBlock>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock> target, out bool messageConsumed) +#pragma warning restore CS8616 { return _source.ConsumeMessage(messageHeader, target, out messageConsumed); } @@ -456,9 +464,9 @@ namespace System.Threading.Tasks.Dataflow public long JoinsCreated { get { return _joinBlock._sharedResources._joinsCreated; } } /// Gets the task being used for input processing. - public Task TaskForInputProcessing { get { return _joinBlock._sharedResources._taskForInputProcessing; } } + public Task? TaskForInputProcessing { get { return _joinBlock._sharedResources._taskForInputProcessing; } } /// Gets the task being used for output processing. - public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } + public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } /// Gets the GroupingDataflowBlockOptions used to configure this block. public GroupingDataflowBlockOptions DataflowBlockOptions { get { return (GroupingDataflowBlockOptions)_sourceDebuggingInformation.DataflowBlockOptions; } } @@ -479,7 +487,7 @@ namespace System.Threading.Tasks.Dataflow /// Gets the set of all targets linked from this block. public TargetRegistry> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } } /// Gets the set of all targets linked from this block. - public ITargetBlock> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } + public ITargetBlock>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } } } } @@ -497,9 +505,9 @@ namespace System.Threading.Tasks.Dataflow.Internal /// A task representing the completion of the block. private readonly TaskCompletionSource _completionTask = new TaskCompletionSource(); /// Input messages for the next batch. - private readonly Queue _messages; + private readonly Queue? _messages; /// State used when in non-greedy mode. - private readonly NonGreedyState _nonGreedy; + private readonly NonGreedyState? _nonGreedy; /// Whether this target is declining future messages. private bool _decliningPermanently; @@ -540,9 +548,9 @@ namespace System.Threading.Tasks.Dataflow.Internal } else { - Debug.Assert(_nonGreedy.ConsumedMessage.Key, "A message must have been consumed by this point."); + Debug.Assert(_nonGreedy!.ConsumedMessage.Key, "A message must have been consumed by this point."); T value = _nonGreedy.ConsumedMessage.Value; - _nonGreedy.ConsumedMessage = new KeyValuePair(false, default(T)); + _nonGreedy.ConsumedMessage = new KeyValuePair(false, default(T)!); return value; } } @@ -570,7 +578,7 @@ namespace System.Threading.Tasks.Dataflow.Internal } else { - return _nonGreedy.ConsumedMessage.Key; + return _nonGreedy!.ConsumedMessage.Key; } } } @@ -591,7 +599,7 @@ namespace System.Threading.Tasks.Dataflow.Internal get { Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: true); - return !_sharedResources._dataflowBlockOptions.Greedy ? _nonGreedy.PostponedMessages.Count : _messages.Count; + return !_sharedResources._dataflowBlockOptions.Greedy ? _nonGreedy!.PostponedMessages.Count : _messages!.Count; } } @@ -604,7 +612,7 @@ namespace System.Threading.Tasks.Dataflow.Internal Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: true); // Note: If there is a tie, we must return true - int count = _messages.Count; + int count = _messages!.Count; foreach (JoinBlockTargetBase target in _sharedResources._targets) if (target != this && target.NumberOfMessagesAvailableOrPostponed > count) return false; // Strictly bigger! @@ -628,7 +636,7 @@ namespace System.Threading.Tasks.Dataflow.Internal // While we are holding the lock, try to pop a postponed message. // If there are no postponed messages, we can't do anything. - if (!_nonGreedy.PostponedMessages.TryPop(out next)) return false; + if (!_nonGreedy!.PostponedMessages.TryPop(out next)) return false; } // We'll bail out of this loop either when we have reserved a message (true) @@ -658,7 +666,7 @@ namespace System.Threading.Tasks.Dataflow.Internal { Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: false); Debug.Assert(!_sharedResources._dataflowBlockOptions.Greedy, "This is only used in non-greedy mode"); - Debug.Assert(_nonGreedy.ReservedMessage.Key != null, "This target must have a reserved message"); + Debug.Assert(_nonGreedy!.ReservedMessage.Key != null, "This target must have a reserved message"); bool consumed; T consumedValue = _nonGreedy.ReservedMessage.Key.ConsumeMessage(_nonGreedy.ReservedMessage.Value, this, out consumed); @@ -685,7 +693,7 @@ namespace System.Threading.Tasks.Dataflow.Internal { // Now that we've consumed it, store its data. Debug.Assert(!_nonGreedy.ConsumedMessage.Key, "There must be no other consumed message"); - _nonGreedy.ConsumedMessage = new KeyValuePair(true, consumedValue); + _nonGreedy.ConsumedMessage = new KeyValuePair(true, consumedValue!); // We don't account bounding per target in non-greedy mode. We do it once per batch (in the loop). CompleteIfLastJoinIsFeasible(); @@ -716,7 +724,7 @@ namespace System.Threading.Tasks.Dataflow.Internal hasTheHighestNumberOfMessagesAvailable = HasTheHighestNumberOfMessagesAvailable; bool boundingCapacityAvailable = _sharedResources._boundingState.CountIsLessThanBound || !hasTheHighestNumberOfMessagesAvailable; if (_decliningPermanently || _sharedResources._decliningPermanently || - !boundingCapacityAvailable || !_nonGreedy.PostponedMessages.TryPop(out next)) + !boundingCapacityAvailable || !_nonGreedy!.PostponedMessages.TryPop(out next)) return false; } @@ -729,7 +737,7 @@ namespace System.Threading.Tasks.Dataflow.Internal { // The ranking in highest number of available messages cannot have changed because this task is causing OfferMessage to postpone if (hasTheHighestNumberOfMessagesAvailable) _sharedResources._boundingState.CurrentCount += 1; // track this new item against our bound - _messages.Enqueue(consumedValue); + _messages!.Enqueue(consumedValue!); CompleteIfLastJoinIsFeasible(); return true; @@ -746,8 +754,8 @@ namespace System.Threading.Tasks.Dataflow.Internal { Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: true); int messageCount = _sharedResources._dataflowBlockOptions.Greedy ? - _messages.Count : - _nonGreedy.ConsumedMessage.Key ? 1 : 0; + _messages!.Count : + _nonGreedy!.ConsumedMessage.Key ? 1 : 0; if ((_sharedResources._joinsCreated + messageCount) >= _sharedResources._dataflowBlockOptions.ActualMaxNumberOfGroups) { _decliningPermanently = true; @@ -806,7 +814,7 @@ namespace System.Threading.Tasks.Dataflow.Internal } // Release any postponed messages - List exceptions = null; + List? exceptions = null; if (_nonGreedy != null) { // Note: No locks should be held at this point @@ -830,7 +838,7 @@ namespace System.Threading.Tasks.Dataflow.Internal } /// - DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) + DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock? source, bool consumeToAccept) { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); @@ -855,7 +863,7 @@ namespace System.Threading.Tasks.Dataflow.Internal (_sharedResources._boundingState == null || ((_sharedResources._boundingState.CountIsLessThanBound || !HasTheHighestNumberOfMessagesAvailable) && - _nonGreedy.PostponedMessages.Count == 0 && _sharedResources._taskForInputProcessing == null))) + _nonGreedy!.PostponedMessages.Count == 0 && _sharedResources._taskForInputProcessing == null))) { if (consumeToAccept) { @@ -866,7 +874,7 @@ namespace System.Threading.Tasks.Dataflow.Internal if (!consumed) return DataflowMessageStatus.NotAvailable; } if (_sharedResources._boundingState != null && HasTheHighestNumberOfMessagesAvailable) _sharedResources._boundingState.CurrentCount += 1; // track this new item against our bound - _messages.Enqueue(messageValue); + _messages!.Enqueue(messageValue!); CompleteIfLastJoinIsFeasible(); // Since we're in greedy mode, we can skip asynchronous processing and @@ -899,7 +907,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// In general, it is not safe to pass releaseReservedMessages:true, because releasing of reserved messages /// is done without taking a lock. We pass releaseReservedMessages:true only when an exception has been /// caught inside the message processing loop which is a single instance at any given moment. - internal override void CompleteCore(Exception exception, bool dropPendingMessages, bool releaseReservedMessages) + internal override void CompleteCore(Exception? exception, bool dropPendingMessages, bool releaseReservedMessages) { bool greedy = _sharedResources._dataflowBlockOptions.Greedy; lock (_sharedResources.IncomingLock) @@ -954,7 +962,7 @@ namespace System.Threading.Tasks.Dataflow.Internal internal Task CompletionTaskInternal { get { return _completionTask.Task; } } /// Gets the number of messages waiting to be processed. This must only be used from the debugger as it avoids taking necessary locks. - private int InputCountForDebugger { get { return _messages != null ? _messages.Count : _nonGreedy.ConsumedMessage.Key ? 1 : 0; } } + private int InputCountForDebugger { get { return _messages != null ? _messages.Count : _nonGreedy!.ConsumedMessage.Key ? 1 : 0; } } /// The data to display in the debugger display attribute. private object DebuggerDisplayContent @@ -986,7 +994,7 @@ namespace System.Threading.Tasks.Dataflow.Internal } /// Gets the messages waiting to be processed. - public IEnumerable InputQueue { get { return _joinBlockTarget._messages; } } + public IEnumerable? InputQueue { get { return _joinBlockTarget._messages; } } /// Gets whether the block is declining further messages. public bool IsDecliningPermanently { get { return _joinBlockTarget._decliningPermanently || _joinBlockTarget._sharedResources._decliningPermanently; } } } @@ -1023,7 +1031,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Access point to the corresponding API method. public void Complete() { CompleteCore(exception: null, dropPendingMessages: false, releaseReservedMessages: false); } /// Internal implementation of the corresponding API method. - internal abstract void CompleteCore(Exception exception, bool dropPendingMessages, bool releaseReservedMessages); + internal abstract void CompleteCore(Exception? exception, bool dropPendingMessages, bool releaseReservedMessages); /// Completes the target. internal abstract void CompleteOncePossible(); } @@ -1075,11 +1083,11 @@ namespace System.Threading.Tasks.Dataflow.Internal /// The options for the join. internal readonly GroupingDataflowBlockOptions _dataflowBlockOptions; /// Bounding state for when the block is executing in bounded mode. - internal readonly BoundingState _boundingState; + internal readonly BoundingState? _boundingState; /// Whether all targets should decline all further messages. internal bool _decliningPermanently; /// The task used to process messages. - internal Task _taskForInputProcessing; + internal Task? _taskForInputProcessing; /// Whether any exceptions have been generated and stored into the source core. internal bool _hasExceptions; /// The number of joins this block has created. @@ -1286,7 +1294,7 @@ namespace System.Threading.Tasks.Dataflow.Internal // Create task and store into _taskForInputProcessing prior to scheduling the task // so that _taskForInputProcessing will be visibly set in the task loop. - _taskForInputProcessing = new Task(thisSharedResources => ((JoinBlockTargetSharedResources)thisSharedResources).ProcessMessagesLoopCore(), this, + _taskForInputProcessing = new Task(thisSharedResources => ((JoinBlockTargetSharedResources)thisSharedResources!).ProcessMessagesLoopCore(), this, Common.GetCreationOptionsForTask(isReplacementReplica)); #if FEATURE_TRACING @@ -1300,7 +1308,7 @@ namespace System.Threading.Tasks.Dataflow.Internal #endif // Start the task handling scheduling exceptions - Exception exception = Common.StartTaskSafe(_taskForInputProcessing, _dataflowBlockOptions.TaskScheduler); + Exception? exception = Common.StartTaskSafe(_taskForInputProcessing, _dataflowBlockOptions.TaskScheduler); if (exception != null) { // All of the following actions must be performed under the lock. @@ -1354,7 +1362,7 @@ namespace System.Threading.Tasks.Dataflow.Internal // Complete each target asynchronously so as not to invoke synchronous continuations under a lock Task.Factory.StartNew(state => { - var sharedResources = (JoinBlockTargetSharedResources)state; + var sharedResources = (JoinBlockTargetSharedResources)state!; foreach (JoinBlockTargetBase target in sharedResources._targets) target.CompleteOncePossible(); }, this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default); } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformBlock.cs index 1dacf7e..c79e9db 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformBlock.cs @@ -28,7 +28,7 @@ namespace System.Threading.Tasks.Dataflow /// The target side. private readonly TargetCore _target; /// Buffer used to reorder outputs that may have completed out-of-order between the target half and the source half. - private readonly ReorderingBuffer _reorderingBuffer; + private readonly ReorderingBuffer? _reorderingBuffer; /// The source side. private readonly SourceCore _source; @@ -89,7 +89,7 @@ namespace System.Threading.Tasks.Dataflow /// The options with which to configure this . /// The and are both null (Nothing in Visual Basic). /// The is null (Nothing in Visual Basic). - private TransformBlock(Func transformSync, Func> transformAsync, ExecutionDataflowBlockOptions dataflowBlockOptions) + private TransformBlock(Func? transformSync, Func>? transformAsync, ExecutionDataflowBlockOptions dataflowBlockOptions) { if (transformSync == null && transformAsync == null) throw new ArgumentNullException("transform"); if (dataflowBlockOptions == null) throw new ArgumentNullException(nameof(dataflowBlockOptions)); @@ -100,7 +100,7 @@ namespace System.Threading.Tasks.Dataflow dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone(); // Initialize onItemsRemoved delegate if necessary - Action, int> onItemsRemoved = null; + Action, int>? onItemsRemoved = null; if (dataflowBlockOptions.BoundedCapacity > 0) onItemsRemoved = (owningSource, count) => ((TransformBlock)owningSource)._target.ChangeBoundingCount(-count); @@ -138,8 +138,8 @@ namespace System.Threading.Tasks.Dataflow // we know for certain that no more messages will need to be sent to the source. _target.Completion.ContinueWith((completed, state) => { - var sourceCore = (SourceCore)state; - if (completed.IsFaulted) sourceCore.AddAndUnwrapAggregateException(completed.Exception); + var sourceCore = (SourceCore)state!; + if (completed.IsFaulted) sourceCore.AddAndUnwrapAggregateException(completed.Exception!); sourceCore.Complete(); }, _source, CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default); @@ -149,14 +149,14 @@ namespace System.Threading.Tasks.Dataflow // to handle multiple completion requests and to carry over only one. _source.Completion.ContinueWith((completed, state) => { - var thisBlock = ((TransformBlock)state) as IDataflowBlock; + var thisBlock = ((TransformBlock)state!) as IDataflowBlock; Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion."); - thisBlock.Fault(completed.Exception); + thisBlock.Fault(completed.Exception!); }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); // Handle async cancellation requests by declining on the target Common.WireCancellationToComplete( - dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore)state).Complete(exception: null, dropPendingMessages: true), _target); + dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore)state!).Complete(exception: null, dropPendingMessages: true), _target); #if FEATURE_TRACING DataflowEtwProvider etwLog = DataflowEtwProvider.Log; if (etwLog.IsEnabled()) @@ -203,18 +203,18 @@ namespace System.Threading.Tasks.Dataflow { if (_target.DataflowBlockOptions.MaxDegreeOfParallelism == 1) { - _source.AddMessage(outputItem); + _source.AddMessage(outputItem!); } else { lock (ParallelSourceLock) { - _source.AddMessage(outputItem); + _source.AddMessage(outputItem!); } } } } - else _reorderingBuffer.AddItem(messageWithId.Value, outputItem, itemIsValid); + else _reorderingBuffer.AddItem(messageWithId.Value, outputItem!, itemIsValid); } } @@ -226,8 +226,8 @@ namespace System.Threading.Tasks.Dataflow Debug.Assert(transform != null, "Function to invoke is required."); // Run the transform function to get the task that represents the operation's completion - Task task = null; - Exception caughtException = null; + Task? task = null; + Exception? caughtException = null; try { task = transform(messageWithId.Key); @@ -257,7 +257,7 @@ namespace System.Threading.Tasks.Dataflow // Otherwise, join with the asynchronous operation when it completes. task.ContinueWith((completed, state) => { - var tuple = (Tuple, KeyValuePair>)state; + var tuple = (Tuple, KeyValuePair>)state!; tuple.Item1.AsyncCompleteProcessMessageWithTask(completed, tuple.Item2); }, Tuple.Create(this, messageWithId), CancellationToken.None, Common.GetContinuationOptions(TaskContinuationOptions.ExecuteSynchronously), TaskScheduler.Default); @@ -285,7 +285,7 @@ namespace System.Threading.Tasks.Dataflow case TaskStatus.Faulted: // We must add the exception before declining and signaling completion, as the exception // is part of the operation, and the completion conditions depend on this. - AggregateException aggregate = completed.Exception; + AggregateException aggregate = completed.Exception!; Common.StoreDataflowMessageValueIntoExceptionData(aggregate, messageWithId.Key, targetInnerExceptions: true); _target.Complete(aggregate, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: true); break; @@ -307,13 +307,13 @@ namespace System.Threading.Tasks.Dataflow { if (_target.DataflowBlockOptions.MaxDegreeOfParallelism == 1) { - _source.AddMessage(outputItem); + _source.AddMessage(outputItem!); } else { lock (ParallelSourceLock) { - _source.AddMessage(outputItem); + _source.AddMessage(outputItem!); } } } @@ -321,7 +321,7 @@ namespace System.Threading.Tasks.Dataflow // Otherwise, there's a reordering buffer, so add to it instead. // Even if something goes wrong, we need to update the // reordering buffer, so it knows that an item isn't missing. - else _reorderingBuffer.AddItem(messageWithId.Value, outputItem, itemIsValid: gotOutputItem); + else _reorderingBuffer.AddItem(messageWithId.Value, outputItem!, itemIsValid: gotOutputItem); // Let the target know that one of the asynchronous operations it launched has completed. _target.SignalOneAsyncMessageCompleted(); @@ -345,13 +345,13 @@ namespace System.Threading.Tasks.Dataflow } /// - public bool TryReceive(Predicate filter, out TOutput item) + public bool TryReceive(Predicate? filter, [MaybeNullWhen(false)] out TOutput item) { return _source.TryReceive(filter, out item); } /// - public bool TryReceiveAll(out IList items) { return _source.TryReceiveAll(out items); } + public bool TryReceiveAll([NotNullWhen(true)] out IList? items) { return _source.TryReceiveAll(out items); } /// public Task Completion { get { return _source.Completion; } } @@ -363,12 +363,13 @@ namespace System.Threading.Tasks.Dataflow public int OutputCount { get { return _source.OutputCount; } } /// - DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock source, bool consumeToAccept) + DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock? source, bool consumeToAccept) { return _target.OfferMessage(messageHeader, messageValue, source, consumeToAccept); } /// + [return: MaybeNull] TOutput ISourceBlock.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) { return _source.ConsumeMessage(messageHeader, target, out messageConsumed); @@ -431,14 +432,14 @@ namespace System.Threading.Tasks.Dataflow /// Gets the messages waiting to be processed. public IEnumerable InputQueue { get { return _targetDebuggingInformation.InputQueue; } } /// Gets any postponed messages. - public QueuedMap, DataflowMessageHeader> PostponedMessages { get { return _targetDebuggingInformation.PostponedMessages; } } + public QueuedMap, DataflowMessageHeader>? PostponedMessages { get { return _targetDebuggingInformation.PostponedMessages; } } /// Gets the messages waiting to be received. public IEnumerable OutputQueue { get { return _sourceDebuggingInformation.OutputQueue; } } /// Gets the number of outstanding input operations. public int CurrentDegreeOfParallelism { get { return _targetDebuggingInformation.CurrentDegreeOfParallelism; } } /// Gets the task being used for output processing. - public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } + public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } /// Gets the DataflowBlockOptions used to configure this block. public ExecutionDataflowBlockOptions DataflowBlockOptions { get { return _targetDebuggingInformation.DataflowBlockOptions; } } @@ -452,7 +453,7 @@ namespace System.Threading.Tasks.Dataflow /// Gets the set of all targets linked from this block. public TargetRegistry LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } } /// Gets the target that holds a reservation on the next message, if any. - public ITargetBlock NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } + public ITargetBlock? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } } } } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformManyBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformManyBlock.cs index 612ac2d..ef4b5df 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformManyBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformManyBlock.cs @@ -33,7 +33,7 @@ namespace System.Threading.Tasks.Dataflow /// Buffer used to reorder output sets that may have completed out-of-order between the target half and the source half. /// This specialized reordering buffer supports streaming out enumerables if the message is the next in line. /// - private readonly ReorderingBuffer> _reorderingBuffer; + private readonly ReorderingBuffer>? _reorderingBuffer; /// The source side. private readonly SourceCore _source; @@ -97,7 +97,7 @@ namespace System.Threading.Tasks.Dataflow /// The options with which to configure this . /// The and are both null (Nothing in Visual Basic). /// The is null (Nothing in Visual Basic). - private TransformManyBlock(Func> transformSync, Func>> transformAsync, ExecutionDataflowBlockOptions dataflowBlockOptions) + private TransformManyBlock(Func>? transformSync, Func>>? transformAsync, ExecutionDataflowBlockOptions dataflowBlockOptions) { // Validate arguments. It's ok for the filterFunction to be null, but not the other parameters. if (transformSync == null && transformAsync == null) throw new ArgumentNullException("transform"); @@ -109,7 +109,7 @@ namespace System.Threading.Tasks.Dataflow dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone(); // Initialize onItemsRemoved delegate if necessary - Action, int> onItemsRemoved = null; + Action, int>? onItemsRemoved = null; if (dataflowBlockOptions.BoundedCapacity > 0) onItemsRemoved = (owningSource, count) => ((TransformManyBlock)owningSource)._target.ChangeBoundingCount(-count); @@ -155,8 +155,8 @@ namespace System.Threading.Tasks.Dataflow // we know for certain that no more messages will need to be sent to the source. _target.Completion.ContinueWith((completed, state) => { - var sourceCore = (SourceCore)state; - if (completed.IsFaulted) sourceCore.AddAndUnwrapAggregateException(completed.Exception); + var sourceCore = (SourceCore)state!; + if (completed.IsFaulted) sourceCore.AddAndUnwrapAggregateException(completed.Exception!); sourceCore.Complete(); }, _source, CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default); @@ -166,14 +166,14 @@ namespace System.Threading.Tasks.Dataflow // to handle multiple completion requests and to carry over only one. _source.Completion.ContinueWith((completed, state) => { - var thisBlock = ((TransformManyBlock)state) as IDataflowBlock; + var thisBlock = ((TransformManyBlock)state!) as IDataflowBlock; Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion."); - thisBlock.Fault(completed.Exception); + thisBlock.Fault(completed.Exception!); }, this, CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); // Handle async cancellation requests by declining on the target Common.WireCancellationToComplete( - dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore)state).Complete(exception: null, dropPendingMessages: true), _target); + dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore)state!).Complete(exception: null, dropPendingMessages: true), _target); #if FEATURE_TRACING DataflowEtwProvider etwLog = DataflowEtwProvider.Log; if (etwLog.IsEnabled()) @@ -219,8 +219,8 @@ namespace System.Threading.Tasks.Dataflow Debug.Assert(function != null, "Function to invoke is required."); // Run the transform function to get the resulting task - Task> task = null; - Exception caughtException = null; + Task>? task = null; + Exception? caughtException = null; try { task = function(messageWithId.Key); @@ -263,7 +263,7 @@ namespace System.Threading.Tasks.Dataflow // scheduler as we'll be running user code through enumerating the returned enumerable. task.ContinueWith((completed, state) => { - var tuple = (Tuple, KeyValuePair>)state; + var tuple = (Tuple, KeyValuePair>)state!; tuple.Item1.AsyncCompleteProcessMessageWithTask(completed, tuple.Item2); }, Tuple.Create(this, messageWithId), CancellationToken.None, @@ -307,7 +307,7 @@ namespace System.Threading.Tasks.Dataflow case TaskStatus.Faulted: // We must add the exception before declining and signaling completion, as the exception // is part of the operation, and the completion conditions depend on this. - AggregateException aggregate = completed.Exception; + AggregateException aggregate = completed.Exception!; Common.StoreDataflowMessageValueIntoExceptionData(aggregate, messageWithId.Key, targetInnerExceptions: true); _target.Complete(aggregate, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: true); goto case TaskStatus.Canceled; @@ -331,7 +331,7 @@ namespace System.Threading.Tasks.Dataflow /// The message with id. /// The output items to be persisted. private void StoreOutputItems( - KeyValuePair messageWithId, IEnumerable outputItems) + KeyValuePair messageWithId, IEnumerable? outputItems) { // If there's a reordering buffer, pass the data along to it. // The reordering buffer will handle all details, including bounding. @@ -369,7 +369,7 @@ namespace System.Threading.Tasks.Dataflow /// Stores the next item using the reordering buffer. /// The ID of the item. /// The completed item. - private void StoreOutputItemsReordered(long id, IEnumerable item) + private void StoreOutputItemsReordered(long id, IEnumerable? item) { Debug.Assert(_reorderingBuffer != null, "Expected a reordering buffer"); Debug.Assert(id != Common.INVALID_REORDERING_ID, "This ID should never have been handed out."); @@ -381,7 +381,7 @@ namespace System.Threading.Tasks.Dataflow // Handle invalid items (null enumerables) by delegating to the base if (item == null) { - _reorderingBuffer.AddItem(id, null, false); + _reorderingBuffer.AddItem(id, null!, false); if (isBounded) target.ChangeBoundingCount(count: -1); return; } @@ -390,7 +390,7 @@ namespace System.Threading.Tasks.Dataflow // This avoids the cost of updating it once per output item (since each update requires synchronization). // Even if we're not bounding, we still want to determine whether the item is trusted so that we // can immediately dump it out once we take the lock if we're the next item. - IList itemAsTrustedList = item as TOutput[]; + IList? itemAsTrustedList = item as TOutput[]; if (itemAsTrustedList == null) itemAsTrustedList = item as List; if (itemAsTrustedList != null && isBounded) { @@ -407,7 +407,7 @@ namespace System.Threading.Tasks.Dataflow // By this point, either we're not the next item, in which case we need to make a copy of the // data and store it, or we are the next item and can store it immediately but we need to enumerate // the items and store them individually because we don't want to enumerate while holding a lock. - List itemCopy = null; + List? itemCopy = null; try { // If this is the next item, we can output it now. @@ -581,10 +581,10 @@ namespace System.Threading.Tasks.Dataflow public IDisposable LinkTo(ITargetBlock target, DataflowLinkOptions linkOptions) { return _source.LinkTo(target, linkOptions); } /// - public bool TryReceive(Predicate filter, out TOutput item) { return _source.TryReceive(filter, out item); } + public bool TryReceive(Predicate? filter, [MaybeNullWhen(false)] out TOutput item) { return _source.TryReceive(filter, out item); } /// - public bool TryReceiveAll(out IList items) { return _source.TryReceiveAll(out items); } + public bool TryReceiveAll([NotNullWhen(true)] out IList? items) { return _source.TryReceiveAll(out items); } /// public Task Completion { get { return _source.Completion; } } @@ -596,12 +596,13 @@ namespace System.Threading.Tasks.Dataflow public int OutputCount { get { return _source.OutputCount; } } /// - DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock source, bool consumeToAccept) + DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock? source, bool consumeToAccept) { return _target.OfferMessage(messageHeader, messageValue, source, consumeToAccept); } /// + [return: MaybeNull] TOutput ISourceBlock.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) { return _source.ConsumeMessage(messageHeader, target, out messageConsumed); @@ -664,14 +665,14 @@ namespace System.Threading.Tasks.Dataflow /// Gets the messages waiting to be processed. public IEnumerable InputQueue { get { return _targetDebuggingInformation.InputQueue; } } /// Gets any postponed messages. - public QueuedMap, DataflowMessageHeader> PostponedMessages { get { return _targetDebuggingInformation.PostponedMessages; } } + public QueuedMap, DataflowMessageHeader>? PostponedMessages { get { return _targetDebuggingInformation.PostponedMessages; } } /// Gets the messages waiting to be received. public IEnumerable OutputQueue { get { return _sourceDebuggingInformation.OutputQueue; } } /// Gets the number of input operations currently in flight. public int CurrentDegreeOfParallelism { get { return _targetDebuggingInformation.CurrentDegreeOfParallelism; } } /// Gets the task being used for output processing. - public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } + public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } } /// Gets the DataflowBlockOptions used to configure this block. public ExecutionDataflowBlockOptions DataflowBlockOptions { get { return _targetDebuggingInformation.DataflowBlockOptions; } } @@ -685,7 +686,7 @@ namespace System.Threading.Tasks.Dataflow /// Gets the set of all targets linked from this block. public TargetRegistry LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } } /// Gets the set of all targets linked from this block. - public ITargetBlock NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } + public ITargetBlock? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } } } } } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/WriteOnceBlock.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/WriteOnceBlock.cs index 8a0b29d..b588a7d 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/WriteOnceBlock.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/WriteOnceBlock.cs @@ -28,11 +28,11 @@ namespace System.Threading.Tasks.Dataflow /// A registry used to store all linked targets and information about them. private readonly TargetRegistry _targetRegistry; /// The cloning function. - private readonly Func _cloningFunction; + private readonly Func? _cloningFunction; /// The options used to configure this block's execution. private readonly DataflowBlockOptions _dataflowBlockOptions; /// Lazily initialized task completion source that produces the actual completion task when needed. - private TaskCompletionSource _lazyCompletionTaskSource; + private TaskCompletionSource? _lazyCompletionTaskSource; /// Whether all future messages should be declined. private bool _decliningPermanently; /// Whether block completion is disallowed. @@ -40,7 +40,8 @@ namespace System.Threading.Tasks.Dataflow /// The header of the singly-assigned value. private DataflowMessageHeader _header; /// The singly-assigned value. - private T _value; + [AllowNull, MaybeNull] + private T _value = default; /// Gets the object used as the value lock. private object ValueLock { get { return _targetRegistry; } } @@ -50,7 +51,7 @@ namespace System.Threading.Tasks.Dataflow /// The function to use to clone the data when offered to other blocks. /// This may be null to indicate that no cloning need be performed. /// - public WriteOnceBlock(Func cloningFunction) : + public WriteOnceBlock(Func? cloningFunction) : this(cloningFunction, DataflowBlockOptions.Default) { } @@ -61,7 +62,7 @@ namespace System.Threading.Tasks.Dataflow /// /// The options with which to configure this . /// The is null (Nothing in Visual Basic). - public WriteOnceBlock(Func cloningFunction, DataflowBlockOptions dataflowBlockOptions) + public WriteOnceBlock(Func? cloningFunction, DataflowBlockOptions dataflowBlockOptions) { // Validate arguments if (dataflowBlockOptions == null) throw new ArgumentNullException(nameof(dataflowBlockOptions)); @@ -94,7 +95,7 @@ namespace System.Threading.Tasks.Dataflow { // Handle async cancellation requests by declining on the target Common.WireCancellationToComplete( - dataflowBlockOptions.CancellationToken, _lazyCompletionTaskSource.Task, state => ((WriteOnceBlock)state).Complete(), this); + dataflowBlockOptions.CancellationToken, _lazyCompletionTaskSource.Task, state => ((WriteOnceBlock)state!).Complete(), this); } } #if FEATURE_TRACING @@ -110,7 +111,7 @@ namespace System.Threading.Tasks.Dataflow /// /// This must only be called once all of the completion conditions are met. /// - private void CompleteBlockAsync(IList exceptions) + private void CompleteBlockAsync(IList? exceptions) { Debug.Assert(_decliningPermanently, "We may get here only after we have started to decline permanently."); Debug.Assert(_completionReserved, "We may get here only after we have reserved completion."); @@ -121,7 +122,7 @@ namespace System.Threading.Tasks.Dataflow if (exceptions == null) { // Offer the message to any linked targets and complete the block asynchronously to avoid blocking the caller - var taskForOutputProcessing = new Task(state => ((WriteOnceBlock)state).OfferToTargetsAndCompleteBlock(), this, + var taskForOutputProcessing = new Task(state => ((WriteOnceBlock)state!).OfferToTargetsAndCompleteBlock(), this, Common.GetCreationOptionsForTask()); #if FEATURE_TRACING @@ -134,7 +135,7 @@ namespace System.Threading.Tasks.Dataflow #endif // Start the task handling scheduling exceptions - Exception exception = Common.StartTaskSafe(taskForOutputProcessing, _dataflowBlockOptions.TaskScheduler); + Exception? exception = Common.StartTaskSafe(taskForOutputProcessing, _dataflowBlockOptions.TaskScheduler); if (exception != null) CompleteCore(exception, storeExceptionEvenIfAlreadyCompleting: true); } else @@ -142,7 +143,7 @@ namespace System.Threading.Tasks.Dataflow // Complete the block asynchronously to avoid blocking the caller Task.Factory.StartNew(state => { - Tuple, IList> blockAndList = (Tuple, IList>)state; + Tuple, IList> blockAndList = (Tuple, IList>)state!; blockAndList.Item1.CompleteBlock(blockAndList.Item2); }, Tuple.Create(this, exceptions), CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default); @@ -159,7 +160,7 @@ namespace System.Threading.Tasks.Dataflow // could be faulty and throw an exception. OfferToTargets creates a // list of all such exceptions and returns it. // If _value is null, OfferToTargets does nothing. - List exceptions = OfferToTargets(); + List? exceptions = OfferToTargets(); CompleteBlock(exceptions); } @@ -167,7 +168,7 @@ namespace System.Threading.Tasks.Dataflow /// /// This is called only once. /// - private void CompleteBlock(IList exceptions) + private void CompleteBlock(IList? exceptions) { // Do not invoke the CompletionTaskSource property if there is a chance that _lazyCompletionTaskSource // has not been initialized yet and we may have to complete normally, because that would defeat the @@ -176,7 +177,7 @@ namespace System.Threading.Tasks.Dataflow Debug.Assert(_lazyCompletionTaskSource == null || !_lazyCompletionTaskSource.Task.IsCompleted, "The task completion source must not be completed. This must be the only thread that ever completes the block."); // Save the linked list of targets so that it could be traversed later to propagate completion - TargetRegistry.LinkedTargetInfo linkedTargets = _targetRegistry.ClearEntryPoints(); + TargetRegistry.LinkedTargetInfo? linkedTargets = _targetRegistry.ClearEntryPoints(); // Complete the block's completion task if (exceptions != null && exceptions.Count > 0) @@ -224,7 +225,7 @@ namespace System.Threading.Tasks.Dataflow CompleteCore(exception: null, storeExceptionEvenIfAlreadyCompleting: false); } - private void CompleteCore(Exception exception, bool storeExceptionEvenIfAlreadyCompleting) + private void CompleteCore(Exception? exception, bool storeExceptionEvenIfAlreadyCompleting) { Debug.Assert(exception != null || !storeExceptionEvenIfAlreadyCompleting, "When storeExceptionEvenIfAlreadyCompleting is set to true, an exception must be provided."); @@ -249,7 +250,7 @@ namespace System.Threading.Tasks.Dataflow // there's nothing more this block needs to do... complete it if we just reserved completion. if (thisThreadReservedCompletion) { - List exceptions = null; + List? exceptions = null; if (exception != null) { exceptions = new List(); @@ -261,7 +262,7 @@ namespace System.Threading.Tasks.Dataflow } /// - public bool TryReceive(Predicate filter, out T item) + public bool TryReceive(Predicate? filter, [MaybeNullWhen(false)] out T item) { // No need to take the outgoing lock, as we don't need to synchronize with other // targets, and _value only ever goes from null to non-null, not the other way around. @@ -269,9 +270,9 @@ namespace System.Threading.Tasks.Dataflow // If we have a value, give it up. All receives on a successfully // completed WriteOnceBlock will return true, as long as the message // passes the filter (all messages pass a null filter). - if (_header.IsValid && (filter == null || filter(_value))) + if (_header.IsValid && (filter == null || filter(_value!))) { - item = CloneItem(_value); + item = CloneItem(_value!); return true; } // Otherwise, nothing to receive @@ -283,7 +284,7 @@ namespace System.Threading.Tasks.Dataflow } /// - bool IReceivableSourceBlock.TryReceiveAll(out IList items) + bool IReceivableSourceBlock.TryReceiveAll([NotNullWhen(true)] out IList? items) { // Try to receive the one item this block may have. // If we can, give back an array of one item. Otherwise, @@ -327,7 +328,7 @@ namespace System.Threading.Tasks.Dataflow if (hasValue) { bool useCloning = _cloningFunction != null; - target.OfferMessage(_header, _value, this, consumeToAccept: useCloning); + target.OfferMessage(_header, _value!, this, consumeToAccept: useCloning); } // If completion propagation has been requested, do it safely. @@ -341,7 +342,7 @@ namespace System.Threading.Tasks.Dataflow public Task Completion { get { return CompletionTaskSource.Task; } } /// - DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) + DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock? source, bool consumeToAccept) { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); @@ -358,7 +359,7 @@ namespace System.Threading.Tasks.Dataflow if (consumeToAccept) { bool consumed; - messageValue = source.ConsumeMessage(messageHeader, this, out consumed); + messageValue = source!.ConsumeMessage(messageHeader, this, out consumed); if (!consumed) return DataflowMessageStatus.NotAvailable; } @@ -392,12 +393,12 @@ namespace System.Threading.Tasks.Dataflow if (_header.Id == messageHeader.Id) { messageConsumed = true; - return CloneItem(_value); + return CloneItem(_value!); } else { messageConsumed = false; - return default(T); + return default(T)!; } } @@ -433,7 +434,7 @@ namespace System.Threading.Tasks.Dataflow // or not, nor do we care about any exceptions which may emerge (they should just propagate). Debug.Assert(_header.IsValid, "A valid header is required."); bool useCloning = _cloningFunction != null; - target.OfferMessage(_header, _value, this, consumeToAccept: useCloning); + target.OfferMessage(_header, _value!, this, consumeToAccept: useCloning); } /// Clones the item. @@ -447,20 +448,20 @@ namespace System.Threading.Tasks.Dataflow } /// Offers the WriteOnceBlock's message to all targets. - private List OfferToTargets() + private List? OfferToTargets() { Common.ContractAssertMonitorStatus(ValueLock, held: false); // If there is a message, offer it to everyone. Return values // don't matter, because we only get one message and then complete, // and everyone who wants a copy can get a copy. - List exceptions = null; + List? exceptions = null; if (HasValue) { - TargetRegistry.LinkedTargetInfo cur = _targetRegistry.FirstTargetNode; + TargetRegistry.LinkedTargetInfo? cur = _targetRegistry.FirstTargetNode; while (cur != null) { - TargetRegistry.LinkedTargetInfo next = cur.Next; + TargetRegistry.LinkedTargetInfo? next = cur.Next; ITargetBlock target = cur.Target; try { @@ -469,14 +470,14 @@ namespace System.Threading.Tasks.Dataflow // the cloning function once we know they want the data. If there is no cloning // function, there's no reason for them to call back here. bool useCloning = _cloningFunction != null; - target.OfferMessage(_header, _value, this, consumeToAccept: useCloning); + target.OfferMessage(_header, _value!, this, consumeToAccept: useCloning); } catch (Exception exc) { // Track any erroneous exceptions that may occur // and return them to the caller so that they may // be logged in the completion task. - Common.StoreDataflowMessageValueIntoExceptionData(exc, _value); + Common.StoreDataflowMessageValueIntoExceptionData(exc, _value!); Common.AddException(ref exceptions, exc); } cur = next; @@ -506,6 +507,7 @@ namespace System.Threading.Tasks.Dataflow /// Gets whether the block is storing a value. private bool HasValue { get { return _header.IsValid; } } /// Gets the value being stored by the block. + [MaybeNull] private T Value { get { return _header.IsValid ? _value : default(T); } } /// @@ -545,6 +547,7 @@ namespace System.Threading.Tasks.Dataflow /// Gets whether the WriteOnceBlock has a value. public bool HasValue { get { return _writeOnceBlock.HasValue; } } /// Gets the WriteOnceBlock's value if it has one, or default(T) if it doesn't. + [MaybeNull] public T Value { get { return _writeOnceBlock.Value; } } /// Gets the DataflowBlockOptions used to configure this block. diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ActionOnDispose.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ActionOnDispose.cs index bb87f43..a3d0047 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ActionOnDispose.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ActionOnDispose.cs @@ -66,7 +66,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Second state argument. private readonly T2 _arg2; /// The action to run when disposed. Null if disposed. - private Action _action; + private Action? _action; /// Initializes the ActionOnDispose. /// The action to run when disposed. @@ -86,7 +86,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Invoke the action. void IDisposable.Dispose() { - Action toRun = _action; + Action? toRun = _action; if (toRun != null && Interlocked.CompareExchange(ref _action, null, toRun) == toRun) { @@ -106,7 +106,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Third state argument. private readonly T3 _arg3; /// The action to run when disposed. Null if disposed. - private Action _action; + private Action? _action; /// Initializes the ActionOnDispose. /// The action to run when disposed. @@ -128,7 +128,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Invoke the action. void IDisposable.Dispose() { - Action toRun = _action; + Action? toRun = _action; if (toRun != null && Interlocked.CompareExchange(ref _action, null, toRun) == toRun) { diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/Common.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/Common.cs index b915ca1..61bd0e8 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/Common.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/Common.cs @@ -71,7 +71,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Output state for the predicate in order to avoid closure allocations. /// True if the predicate was evaluated and it returned true. False otherwise. internal static bool TryKeepAliveUntil(KeepAlivePredicate predicate, - TStateIn stateIn, out TStateOut stateOut) + TStateIn stateIn, [MaybeNullWhen(false)] out TStateOut stateOut) { Debug.Assert(predicate != null, "Non-null predicate to execute is required."); const int ITERATION_LIMIT = 16; @@ -94,12 +94,12 @@ namespace System.Threading.Tasks.Dataflow.Internal /// The type of the data to be unwrapped. /// The weak reference. /// The T instance. + [return: MaybeNull] internal static T UnwrapWeakReference(object state) where T : class { var wr = state as WeakReference; Debug.Assert(wr != null, "Expected a WeakReference as the state argument"); - T item; - return wr.TryGetTarget(out item) ? item : null; + return wr.TryGetTarget(out T? item) ? item : null; } /// Gets an ID for the dataflow block. @@ -109,7 +109,7 @@ namespace System.Threading.Tasks.Dataflow.Internal { Debug.Assert(block != null, "Block required to extract an Id."); const int NOTASKID = 0; // tasks don't have 0 as ids - Task t = Common.GetPotentiallyNotSupportedCompletionTask(block); + Task? t = Common.GetPotentiallyNotSupportedCompletionTask(block); return t != null ? t.Id : NOTASKID; } @@ -121,7 +121,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// The name of the object. /// This is used from DebuggerDisplay attributes. internal static string GetNameForDebugger( - IDataflowBlock block, DataflowBlockOptions options = null) + IDataflowBlock block, DataflowBlockOptions? options = null) { Debug.Assert(block != null, "Should only be used with valid objects being displayed in the debugger."); Debug.Assert(options == null || options.NameFormat != null, "If options are provided, NameFormat must be valid."); @@ -179,7 +179,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// An action that will decline permanently on the state passed to it. /// The block on which to decline permanently. internal static void WireCancellationToComplete( - CancellationToken cancellationToken, Task completionTask, Action completeAction, object completeState) + CancellationToken cancellationToken, Task completionTask, Action completeAction, object completeState) { Debug.Assert(completionTask != null, "A task to wire up for completion is needed."); Debug.Assert(completeAction != null, "An action to invoke upon cancellation is required."); @@ -196,7 +196,7 @@ namespace System.Threading.Tasks.Dataflow.Internal else if (cancellationToken.CanBeCanceled) { CancellationTokenRegistration reg = cancellationToken.Register(completeAction, completeState); - completionTask.ContinueWith((completed, state) => ((CancellationTokenRegistration)state).Dispose(), + completionTask.ContinueWith((completed, state) => ((CancellationTokenRegistration)state!).Dispose(), reg, cancellationToken, Common.GetContinuationOptions(), TaskScheduler.Default); } } @@ -225,7 +225,7 @@ namespace System.Threading.Tasks.Dataflow.Internal Debug.Assert(exc != null, "The exception into which data should be stored must be provided."); // Get the string value to store - string strValue = messageValue as string; + string? strValue = messageValue as string; if (strValue == null && messageValue != null) { try @@ -295,7 +295,7 @@ namespace System.Threading.Tasks.Dataflow.Internal internal static void ThrowAsync(Exception error) { ExceptionDispatchInfo edi = ExceptionDispatchInfo.Capture(error); - ThreadPool.QueueUserWorkItem(state => { ((ExceptionDispatchInfo)state).Throw(); }, edi); + ThreadPool.QueueUserWorkItem(state => { ((ExceptionDispatchInfo)state!).Throw(); }, edi); } /// Adds the exception to the list, first initializing the list if the list is null. @@ -303,25 +303,25 @@ namespace System.Threading.Tasks.Dataflow.Internal /// The exception to add or whose inner exception(s) should be added. /// Unwrap and add the inner exception(s) rather than the specified exception directly. /// This method is not thread-safe, in that it manipulates without any synchronization. - internal static void AddException(ref List list, Exception exception, bool unwrapInnerExceptions = false) + internal static void AddException([NotNull] ref List? list, Exception exception, bool unwrapInnerExceptions = false) { Debug.Assert(exception != null, "An exception to add is required."); Debug.Assert(!unwrapInnerExceptions || exception.InnerException != null, "If unwrapping is requested, an inner exception is required."); // Make sure the list of exceptions is initialized (lazily). - if (list == null) list = new List(); + list ??= new List(); if (unwrapInnerExceptions) { - AggregateException aggregate = exception as AggregateException; + AggregateException? aggregate = exception as AggregateException; if (aggregate != null) { list.AddRange(aggregate.InnerExceptions); } else { - list.Add(exception.InnerException); + list.Add(exception.InnerException!); } } else list.Add(exception); @@ -346,7 +346,7 @@ namespace System.Threading.Tasks.Dataflow.Internal private static TaskCompletionSource CreateCachedTaskCompletionSource() { var tcs = new TaskCompletionSource(); - tcs.SetResult(default(T)); + tcs.SetResult(default(T)!); return tcs; } @@ -376,7 +376,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Gets the completion task of a block, and protects against common cases of the completion task not being implemented or supported. /// The block. /// The completion task, or null if the block's completion task is not implemented or supported. - internal static Task GetPotentiallyNotSupportedCompletionTask(IDataflowBlock block) + internal static Task? GetPotentiallyNotSupportedCompletionTask(IDataflowBlock block) { Debug.Assert(block != null, "We need a block from which to retrieve a cancellation task."); try @@ -444,7 +444,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Task to be started. /// TaskScheduler to schedule the task on. /// null on success, an exception reference on scheduling error. In the latter case, the task reference is nulled out. - internal static Exception StartTaskSafe(Task task, TaskScheduler scheduler) + internal static Exception? StartTaskSafe(Task task, TaskScheduler scheduler) { Debug.Assert(task != null, "Task to start is required."); Debug.Assert(scheduler != null, "Scheduler on which to start the task is required."); @@ -462,12 +462,12 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Task to be started. /// TaskScheduler to schedule the task on. /// null on success, an exception reference on scheduling error. In the latter case, the task reference is nulled out. - private static Exception StartTaskSafeCore(Task task, TaskScheduler scheduler) + private static Exception? StartTaskSafeCore(Task task, TaskScheduler scheduler) { Debug.Assert(task != null, "Task to start is needed."); Debug.Assert(scheduler != null, "Scheduler on which to start the task is required."); - Exception schedulingException = null; + Exception? schedulingException = null; try { @@ -479,7 +479,7 @@ namespace System.Threading.Tasks.Dataflow.Internal Debug.Assert(task.IsFaulted, "The task should have been faulted if it failed to start."); // Observe the task's exception - AggregateException ignoredTaskException = task.Exception; + _ = task.Exception; schedulingException = caughtException; } @@ -491,7 +491,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// No locks should be held at this time. Unfortunately we cannot assert that. internal static void ReleaseAllPostponedMessages(ITargetBlock target, QueuedMap, DataflowMessageHeader> postponedMessages, - ref List exceptions) + ref List? exceptions) { Debug.Assert(target != null, "There must be a subject target."); Debug.Assert(postponedMessages != null, "The stacked map of postponed messages must exist."); @@ -538,13 +538,13 @@ namespace System.Threading.Tasks.Dataflow.Internal /// The task whose completion is to be propagated. It must be completed. /// The block where completion is propagated. /// Handler for exceptions from the target. May be null which would propagate the exception to the caller. - internal static void PropagateCompletion(Task sourceCompletionTask, IDataflowBlock target, Action exceptionHandler) + internal static void PropagateCompletion(Task sourceCompletionTask, IDataflowBlock target, Action? exceptionHandler) { Debug.Assert(sourceCompletionTask != null, "sourceCompletionTask may not be null."); Debug.Assert(target != null, "The target where completion is to be propagated may not be null."); Debug.Assert(sourceCompletionTask.IsCompleted, "sourceCompletionTask must be completed in order to propagate its completion."); - AggregateException exception = sourceCompletionTask.IsFaulted ? sourceCompletionTask.Exception : null; + AggregateException? exception = sourceCompletionTask.IsFaulted ? sourceCompletionTask.Exception : null; try { @@ -565,7 +565,7 @@ namespace System.Threading.Tasks.Dataflow.Internal { Debug.Assert(sourceCompletionTask != null, "sourceCompletionTask may not be null."); Debug.Assert(target != null, "The target where completion is to be propagated may not be null."); - sourceCompletionTask.ContinueWith((task, state) => Common.PropagateCompletion(task, (IDataflowBlock)state, AsyncExceptionHandler), + sourceCompletionTask.ContinueWith((task, state) => Common.PropagateCompletion(task, (IDataflowBlock)state!, AsyncExceptionHandler), target, CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default); } @@ -588,7 +588,7 @@ namespace System.Threading.Tasks.Dataflow.Internal private static class CachedGenericDelegates { /// A function that returns the default value of T. - internal static readonly Func DefaultTResultFunc = () => default(T); + internal static readonly Func DefaultTResultFunc = () => default(T)!; /// /// A function to use as the body of ActionOnDispose in CreateUnlinkerShim. /// Passed a tuple of the sync obj, the target registry, and the target block as the state parameter. @@ -667,7 +667,7 @@ namespace System.Threading.Tasks.Dataflow.Internal internal class BoundingStateWithPostponedAndTask : BoundingStateWithPostponed { /// The task used to process messages. - internal Task TaskForInputProcessing; + internal Task? TaskForInputProcessing; /// Initializes the BoundingState. /// The positive bounded capacity. diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ConcurrentQueue.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ConcurrentQueue.cs index b4058ec..db5af01 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ConcurrentQueue.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ConcurrentQueue.cs @@ -11,6 +11,7 @@ // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +#nullable disable // old copy used only for netstandard1.0 build using System; using System.Collections; using System.Collections.Generic; diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/IProducerConsumerCollection.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/IProducerConsumerCollection.cs index 171f141..1177af8 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/IProducerConsumerCollection.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/IProducerConsumerCollection.cs @@ -11,6 +11,7 @@ // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +#nullable disable // copy used only for netstandard1.0 build using System.Collections; using System.Collections.Generic; using System.Diagnostics; diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ProducerConsumerQueues.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ProducerConsumerQueues.cs index 3897cc3..70c72e4 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ProducerConsumerQueues.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ProducerConsumerQueues.cs @@ -31,6 +31,7 @@ using System.Collections.Concurrent; #endif using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Internal; @@ -49,7 +50,7 @@ namespace System.Threading.Tasks /// The dequeued item. /// true if an item could be dequeued; otherwise, false. /// This method is meant to be thread-safe subject to the particular nature of the implementation. - bool TryDequeue(out T result); + bool TryDequeue([MaybeNullWhen(false)] out T result); /// Gets whether the collection is currently empty. /// This method may or may not be thread-safe. @@ -210,7 +211,7 @@ namespace System.Threading.Tasks /// Attempts to dequeue an item from the queue. /// The dequeued item. /// true if an item could be dequeued; otherwise, false. - public bool TryDequeue(out T result) + public bool TryDequeue([MaybeNullWhen(false)] out T result) { Segment segment = _head; T[] array = segment._array; @@ -220,7 +221,7 @@ namespace System.Threading.Tasks if (first != segment._state._lastCopy) { result = array[first]; - array[first] = default(T); // Clear the slot to release the element + array[first] = default(T)!; // Clear the slot to release the element segment._state._first = (first + 1) & (array.Length - 1); return true; } @@ -233,7 +234,7 @@ namespace System.Threading.Tasks /// The segment from which the item was dequeued. /// The dequeued item. /// true if an item could be dequeued; otherwise, false. - private bool TryDequeueSlow(ref Segment segment, ref T[] array, out T result) + private bool TryDequeueSlow(ref Segment segment, ref T[] array, [MaybeNullWhen(false)] out T result) { Debug.Assert(segment != null, "Expected a non-null segment."); Debug.Assert(array != null, "Expected a non-null item array."); @@ -260,7 +261,7 @@ namespace System.Threading.Tasks } result = array[first]; - array[first] = default(T); // Clear the slot to release the element + array[first] = default(T)!; // Clear the slot to release the element segment._state._first = (first + 1) & (segment._array.Length - 1); segment._state._lastCopy = segment._state._last; // Refresh _lastCopy to ensure that _first has not passed _lastCopy @@ -291,7 +292,7 @@ namespace System.Threading.Tasks /// The segment from which the item is peeked. /// The peeked item. /// true if an item could be peeked; otherwise, false. - private bool TryPeekSlow(ref Segment segment, ref T[] array, out T result) + private bool TryPeekSlow(ref Segment segment, ref T[] array, [MaybeNullWhen(false)] out T result) { Debug.Assert(segment != null, "Expected a non-null segment."); Debug.Assert(array != null, "Expected a non-null item array."); @@ -325,7 +326,7 @@ namespace System.Threading.Tasks /// The predicate that must return true for the item to be dequeued. If null, all items implicitly return true. /// The dequeued item. /// true if an item could be dequeued; otherwise, false. - public bool TryDequeueIf(Predicate predicate, out T result) + public bool TryDequeueIf(Predicate? predicate, [MaybeNullWhen(false)] out T result) { Segment segment = _head; T[] array = segment._array; @@ -337,7 +338,7 @@ namespace System.Threading.Tasks result = array[first]; if (predicate == null || predicate(result)) { - array[first] = default(T); // Clear the slot to release the element + array[first] = default(T)!; // Clear the slot to release the element segment._state._first = (first + 1) & (array.Length - 1); return true; } @@ -357,7 +358,7 @@ namespace System.Threading.Tasks /// The segment from which the item was dequeued. /// The dequeued item. /// true if an item could be dequeued; otherwise, false. - private bool TryDequeueIfSlow(Predicate predicate, ref Segment segment, ref T[] array, out T result) + private bool TryDequeueIfSlow(Predicate? predicate, ref Segment segment, ref T[] array, [MaybeNullWhen(false)] out T result) { Debug.Assert(segment != null, "Expected a non-null segment."); Debug.Assert(array != null, "Expected a non-null item array."); @@ -386,7 +387,7 @@ namespace System.Threading.Tasks result = array[first]; if (predicate == null || predicate(result)) { - array[first] = default(T); // Clear the slot to release the element + array[first] = default(T)!; // Clear the slot to release the element segment._state._first = (first + 1) & (segment._array.Length - 1); segment._state._lastCopy = segment._state._last; // Refresh _lastCopy to ensure that _first has not passed _lastCopy return true; @@ -422,7 +423,7 @@ namespace System.Threading.Tasks /// WARNING: This should only be used for debugging purposes. It is not safe to be used concurrently. public IEnumerator GetEnumerator() { - for (Segment segment = _head; segment != null; segment = segment._next) + for (Segment? segment = _head; segment != null; segment = segment._next) { for (int pt = segment._state._first; pt != segment._state._last; @@ -443,7 +444,7 @@ namespace System.Threading.Tasks get { int count = 0; - for (Segment segment = _head; segment != null; segment = segment._next) + for (Segment? segment = _head; segment != null; segment = segment._next) { int arraySize = segment._array.Length; int first, last; @@ -475,7 +476,7 @@ namespace System.Threading.Tasks private sealed class Segment { /// The next segment in the linked list of segments. - internal Segment _next; + internal Segment? _next; /// The data stored in this segment. internal readonly T[] _array; /// Details about the segment. diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/QueuedMap.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/QueuedMap.cs index ed4ef24..bcd745f 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/QueuedMap.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/QueuedMap.cs @@ -15,6 +15,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Threading.Tasks.Dataflow.Internal { @@ -28,7 +29,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// This type is not thread-safe. [DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy(typeof(EnumerableDebugView<,>))] - internal sealed class QueuedMap + internal sealed class QueuedMap where TKey: notnull { /// /// A queue structure that uses an array-based list to store its items @@ -108,7 +109,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Tries to dequeue an item. /// The item that is dequeued. - internal bool TryDequeue(out T item) + internal bool TryDequeue([MaybeNullWhen(false)] out T item) { // If the queue is empty, just initialize the output item and return false if (_headIndex == TERMINATOR_INDEX) @@ -124,7 +125,7 @@ namespace System.Threading.Tasks.Dataflow.Internal // Move the popped slot to the head of the free list int newHeadIndex = _storage[_headIndex].Key; - _storage[_headIndex] = new KeyValuePair(_freeIndex, default(T)); + _storage[_headIndex] = new KeyValuePair(_freeIndex, default(T)!); _freeIndex = _headIndex; _headIndex = newHeadIndex; if (_headIndex == TERMINATOR_INDEX) _tailIndex = TERMINATOR_INDEX; diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ReorderingBuffer.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ReorderingBuffer.cs index 15fc700..7181bf8 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ReorderingBuffer.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/ReorderingBuffer.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace System.Threading.Tasks.Dataflow.Internal @@ -66,7 +67,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// The ID of the item. /// The completed item. /// Specifies whether the item is valid (true) or just a placeholder (false). - internal void AddItem(long id, TOutput item, bool itemIsValid) + internal void AddItem(long id, [AllowNull] TOutput item, bool itemIsValid) { Debug.Assert(id != Common.INVALID_REORDERING_ID, "This ID should never have been handed out."); Common.ContractAssertMonitorStatus(ValueLock, held: false); @@ -84,7 +85,7 @@ namespace System.Threading.Tasks.Dataflow.Internal else { Debug.Assert((ulong)id > (ulong)_nextReorderedIdToOutput, "Duplicate id."); - _reorderingBuffer.Add(id, new KeyValuePair(itemIsValid, item)); + _reorderingBuffer.Add(id, new KeyValuePair(itemIsValid, item!)); } } } @@ -104,7 +105,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// true if the item was not added but is next in line. /// false if the item was not added and is not next in line. /// - internal bool? AddItemIfNextAndTrusted(long id, TOutput item, bool isTrusted) + internal bool? AddItemIfNextAndTrusted(long id, [AllowNull] TOutput item, bool isTrusted) { Debug.Assert(id != Common.INVALID_REORDERING_ID, "This ID should never have been handed out."); Common.ContractAssertMonitorStatus(ValueLock, held: false); @@ -132,20 +133,20 @@ namespace System.Threading.Tasks.Dataflow.Internal /// The id of the message to be ignored. public void IgnoreItem(long id) { - AddItem(id, default(TOutput), itemIsValid: false); + AddItem(id, default(TOutput)!, itemIsValid: false); } /// Outputs the item. The item must have already been confirmed to have the next ID. /// The item to output. /// Whether the item is valid. - private void OutputNextItem(TOutput theNextItem, bool itemIsValid) + private void OutputNextItem([AllowNull] TOutput theNextItem, bool itemIsValid) { Common.ContractAssertMonitorStatus(ValueLock, held: true); // Note that we're now looking for a different item, and pass this one through. // Then release any items which may be pending. _nextReorderedIdToOutput++; - if (itemIsValid) _outputAction(_owningSource, theNextItem); + if (itemIsValid) _outputAction(_owningSource, theNextItem!); // Try to get the next available item from the buffer and output it. Continue to do so // until we run out of items in the reordering buffer or don't yet have the next ID buffered. diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/SourceCore.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/SourceCore.cs index 57f701a..0b5899a 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/SourceCore.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/SourceCore.cs @@ -70,18 +70,18 @@ namespace System.Threading.Tasks.Dataflow.Internal /// An action to be invoked on the owner block when an item is removed. /// This may be null if the owner block doesn't need to be notified. /// - private readonly Action, int> _itemsRemovedAction; + private readonly Action, int>? _itemsRemovedAction; /// Item counting function - private readonly Func, TOutput, IList, int> _itemCountingFunc; + private readonly Func, TOutput, IList?, int>? _itemCountingFunc; // *** These fields are mutated during execution. /// The task used to process the output and offer it to targets. - private Task _taskForOutputProcessing; // protected by ValueLock + private Task? _taskForOutputProcessing; // protected by ValueLock /// Counter for message IDs unique within this source block. private PaddedInt64 _nextMessageId = new PaddedInt64 { Value = 1 }; // We are going to use this value before incrementing. Protected by ValueLock. /// The target that the next message is reserved for, or null if nothing is reserved. - private ITargetBlock _nextMessageReservedFor; // protected by OutgoingLock + private ITargetBlock? _nextMessageReservedFor; // protected by OutgoingLock /// Whether all future messages should be declined. private bool _decliningPermanently; // Protected by ValueLock /// Whether this block should again attempt to offer messages to targets. @@ -89,7 +89,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Whether someone has reserved the right to call CompleteBlockOncePossible. private bool _completionReserved; // Protected by OutgoingLock /// Exceptions that may have occurred and gone unhandled during processing. - private List _exceptions; // Protected by ValueLock, sometimes read with volatile reads + private List? _exceptions; // Protected by ValueLock, sometimes read with volatile reads /// Initializes the source core. /// The source utilizing this core. @@ -103,8 +103,8 @@ namespace System.Threading.Tasks.Dataflow.Internal internal SourceCore( ISourceBlock owningSource, DataflowBlockOptions dataflowBlockOptions, Action> completeAction, - Action, int> itemsRemovedAction = null, - Func, TOutput, IList, int> itemCountingFunc = null) + Action, int>? itemsRemovedAction = null, + Func, TOutput, IList?, int>? itemCountingFunc = null) { Debug.Assert(owningSource != null, "Core must be associated with a source."); Debug.Assert(dataflowBlockOptions != null, "Options must be provided to configure the core."); @@ -156,6 +156,7 @@ namespace System.Threading.Tasks.Dataflow.Internal } /// + [return: MaybeNull] internal TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) { // Validate arguments @@ -274,7 +275,7 @@ namespace System.Threading.Tasks.Dataflow.Internal internal Task Completion { get { return _completionTask.Task; } } /// - internal bool TryReceive(Predicate filter, out TOutput item) + internal bool TryReceive(Predicate? filter, [MaybeNullWhen(false)] out TOutput item) { item = default(TOutput); bool itemReceived = false; @@ -313,7 +314,7 @@ namespace System.Threading.Tasks.Dataflow.Internal // Notify the owner block that our count has decreased if (_itemsRemovedAction != null) { - int count = _itemCountingFunc != null ? _itemCountingFunc(_owningSource, item, null) : 1; + int count = _itemCountingFunc != null ? _itemCountingFunc(_owningSource, item!, null) : 1; _itemsRemovedAction(_owningSource, count); } } @@ -321,7 +322,7 @@ namespace System.Threading.Tasks.Dataflow.Internal } /// - internal bool TryReceiveAll(out IList items) + internal bool TryReceiveAll([NotNullWhen(true)] out IList? items) { items = null; int countReceived = 0; @@ -360,10 +361,12 @@ namespace System.Threading.Tasks.Dataflow.Internal // Notify the owner block that our count has decreased if (_itemsRemovedAction != null) { - int count = _itemCountingFunc != null ? _itemCountingFunc(_owningSource, default(TOutput), items) : countReceived; + int count = _itemCountingFunc != null ? _itemCountingFunc(_owningSource, default(TOutput)!, items) : countReceived; _itemsRemovedAction(_owningSource, count); } +#pragma warning disable CS8762 // Parameter may not have a null value when exiting in some condition. return true; +#pragma warning restore CS8762 } else return false; } @@ -423,7 +426,7 @@ namespace System.Threading.Tasks.Dataflow.Internal } else { - TOutput[] itemsAsArray = items as TOutput[]; + TOutput[]? itemsAsArray = items as TOutput[]; if (itemsAsArray != null) { for (int i = 0; i < itemsAsArray.Length; i++) @@ -511,7 +514,7 @@ namespace System.Threading.Tasks.Dataflow.Internal // and take the necessary locks in a situation where we're sure it won't cause a problem. Task.Factory.StartNew(state => { - var thisSourceCore = (SourceCore)state; + var thisSourceCore = (SourceCore)state!; lock (thisSourceCore.OutgoingLock) { lock (thisSourceCore.ValueLock) @@ -531,7 +534,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// The newly linked target, if OfferToTargets is being called to synchronously /// propagate to a target during a LinkTo operation. /// - private bool OfferToTargets(ITargetBlock linkToTarget = null) + private bool OfferToTargets(ITargetBlock? linkToTarget = null) { Common.ContractAssertMonitorStatus(OutgoingLock, held: true); Common.ContractAssertMonitorStatus(ValueLock, held: false); @@ -585,10 +588,10 @@ namespace System.Threading.Tasks.Dataflow.Internal // separately from cur.Next, in case cur.Next changes by cur being removed from the list. // No other node in the list should change, as we're protected by OutgoingLock. - TargetRegistry.LinkedTargetInfo cur = _targetRegistry.FirstTargetNode; + TargetRegistry.LinkedTargetInfo? cur = _targetRegistry.FirstTargetNode; while (cur != null) { - TargetRegistry.LinkedTargetInfo next = cur.Next; + TargetRegistry.LinkedTargetInfo? next = cur.Next; if (OfferMessageToTarget(header, message, cur.Target, out messageWasAccepted)) break; cur = next; } @@ -749,7 +752,7 @@ namespace System.Threading.Tasks.Dataflow.Internal { // Create task and store into _taskForOutputProcessing prior to scheduling the task // so that _taskForOutputProcessing will be visibly set in the task loop. - _taskForOutputProcessing = new Task(thisSourceCore => ((SourceCore)thisSourceCore).OfferMessagesLoopCore(), this, + _taskForOutputProcessing = new Task(thisSourceCore => ((SourceCore)thisSourceCore!).OfferMessagesLoopCore(), this, Common.GetCreationOptionsForTask(isReplacementReplica)); #if FEATURE_TRACING @@ -762,7 +765,7 @@ namespace System.Threading.Tasks.Dataflow.Internal #endif // Start the task handling scheduling exceptions - Exception exception = Common.StartTaskSafe(_taskForOutputProcessing, _dataflowBlockOptions.TaskScheduler); + Exception? exception = Common.StartTaskSafe(_taskForOutputProcessing, _dataflowBlockOptions.TaskScheduler); if (exception != null) { // First, log the exception while the processing state is dirty which is preventing the block from completing. @@ -776,7 +779,7 @@ namespace System.Threading.Tasks.Dataflow.Internal // Re-take the locks on a separate thread. Task.Factory.StartNew(state => { - var thisSourceCore = (SourceCore)state; + var thisSourceCore = (SourceCore)state!; lock (thisSourceCore.OutgoingLock) { lock (thisSourceCore.ValueLock) @@ -917,7 +920,7 @@ namespace System.Threading.Tasks.Dataflow.Internal // Get out from under currently held locks. This is to avoid // invoking synchronous continuations off of _completionTask.Task // while holding a lock. - Task.Factory.StartNew(state => ((SourceCore)state).CompleteBlockOncePossible(), + Task.Factory.StartNew(state => ((SourceCore)state!).CompleteBlockOncePossible(), this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default); } } @@ -928,8 +931,8 @@ namespace System.Threading.Tasks.Dataflow.Internal /// private void CompleteBlockOncePossible() { - TargetRegistry.LinkedTargetInfo linkedTargets; - List exceptions; + TargetRegistry.LinkedTargetInfo? linkedTargets; + List? exceptions; // Avoid completing while the code that caused this completion to occur is still holding a lock. // Clear out the target registry and buffers to help avoid memory leaks. @@ -1006,7 +1009,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Gets the messages available for receiving. internal IEnumerable OutputQueue { get { return _source._messages.ToList(); } } /// Gets the task being used for output processing. - internal Task TaskForOutputProcessing { get { return _source._taskForOutputProcessing; } } + internal Task? TaskForOutputProcessing { get { return _source._taskForOutputProcessing; } } /// Gets the DataflowBlockOptions used to configure this block. internal DataflowBlockOptions DataflowBlockOptions { get { return _source._dataflowBlockOptions; } } @@ -1017,7 +1020,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Gets the set of all targets linked from this block. internal TargetRegistry LinkedTargets { get { return _source._targetRegistry; } } /// Gets the target that holds a reservation on the next message, if any. - internal ITargetBlock NextMessageReservedFor { get { return _source._nextMessageReservedFor; } } + internal ITargetBlock? NextMessageReservedFor { get { return _source._nextMessageReservedFor; } } } } } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/SpscTargetCore.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/SpscTargetCore.cs index f742443..07187ac 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/SpscTargetCore.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/SpscTargetCore.cs @@ -53,7 +53,7 @@ namespace System.Threading.Tasks.Dataflow.Internal private readonly Action _action; /// Exceptions that may have occurred and gone unhandled during processing. This field is lazily initialized. - private volatile List _exceptions; + private volatile List? _exceptions; /// Whether to stop accepting new messages. private volatile bool _decliningPermanently; /// A task has reserved the right to run the completion routine. @@ -63,9 +63,9 @@ namespace System.Threading.Tasks.Dataflow.Internal /// and it should not be set to null once the block completes, as doing so would allow for races where the producer /// gets another consumer task queued even though the block has completed. /// - private volatile Task _activeConsumer; + private volatile Task? _activeConsumer; /// A task representing the completion of the block. This field is lazily initialized. - private TaskCompletionSource _completionTask; + private TaskCompletionSource? _completionTask; /// Initialize the SPSC target core. /// The owning target block. @@ -108,7 +108,7 @@ namespace System.Threading.Tasks.Dataflow.Internal } /// - internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock source, bool consumeToAccept) + internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock? source, bool consumeToAccept) { // If we're not required to go back to the source to consume the offered message, try fast path. return !consumeToAccept && Post(messageValue) ? @@ -122,7 +122,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// The source offering the message. This may be null. /// true if we need to call back to the source to consume the message; otherwise, false if we can simply accept it directly. /// The status of the message. - private DataflowMessageStatus OfferMessage_Slow(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock source, bool consumeToAccept) + private DataflowMessageStatus OfferMessage_Slow(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock? source, bool consumeToAccept) { // If we're declining permanently, let the caller know. if (_decliningPermanently) @@ -146,7 +146,7 @@ namespace System.Threading.Tasks.Dataflow.Internal } // See the "fast path" comments in Post - _messages.Enqueue(messageValue); + _messages.Enqueue(messageValue!); Interlocked.MemoryBarrier(); // ensure the read of _activeConsumer doesn't move up before the writes in Enqueue if (_activeConsumer == null) { @@ -164,7 +164,7 @@ namespace System.Threading.Tasks.Dataflow.Internal { // Create a new consumption task and try to set it as current as long as there's still no other task var newConsumer = new Task( - state => ((SpscTargetCore)state).ProcessMessagesLoopCore(), + state => ((SpscTargetCore)state!).ProcessMessagesLoopCore(), this, CancellationToken.None, Common.GetCreationOptionsForTask(isReplica)); if (Interlocked.CompareExchange(ref _activeConsumer, newConsumer, null) == null) { @@ -225,7 +225,7 @@ namespace System.Threading.Tasks.Dataflow.Internal if (!Common.IsCooperativeCancellation(exc)) { _decliningPermanently = true; // stop accepting from producers - Common.StoreDataflowMessageValueIntoExceptionData(exc, nextMessage, false); + Common.StoreDataflowMessageValueIntoExceptionData(exc, nextMessage!, false); StoreException(exc); } } @@ -254,7 +254,7 @@ namespace System.Threading.Tasks.Dataflow.Internal else { // Mark that we're exiting. - Task previousConsumer = Interlocked.Exchange(ref _activeConsumer, null); + Task? previousConsumer = Interlocked.Exchange(ref _activeConsumer, null); Debug.Assert(previousConsumer != null && previousConsumer.Id == Task.CurrentId, "The running task should have been denoted as the active task."); @@ -285,7 +285,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// completing, all invocations after the first are ignored. /// /// The exception to be stored. - internal void Complete(Exception exception) + internal void Complete(Exception? exception) { // If we're not yet declining permanently... if (!_decliningPermanently) diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/TargetCore.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/TargetCore.cs index f3aa761..58a8ada 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/TargetCore.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/TargetCore.cs @@ -71,9 +71,9 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Whether the block relies on the delegate to signal when an async operation has completed. private readonly TargetCoreOptions _targetCoreOptions; /// Bounding state for when the block is executing in bounded mode. - private readonly BoundingStateWithPostponed _boundingState; + private readonly BoundingStateWithPostponed? _boundingState; /// The reordering buffer used by the owner. May be null. - private readonly IReorderingBuffer _reorderingBuffer; + private readonly IReorderingBuffer? _reorderingBuffer; /// Gets the object used as the incoming lock. private object IncomingLock { get { return _messages; } } @@ -81,7 +81,7 @@ namespace System.Threading.Tasks.Dataflow.Internal // *** These fields are mutated during execution. /// Exceptions that may have occurred and gone unhandled during processing. - private List _exceptions; + private List? _exceptions; /// Whether to stop accepting new messages. private bool _decliningPermanently; /// The number of operations (including service tasks) currently running asynchronously. @@ -106,7 +106,7 @@ namespace System.Threading.Tasks.Dataflow.Internal internal TargetCore( ITargetBlock owningTarget, Action> callAction, - IReorderingBuffer reorderingBuffer, + IReorderingBuffer? reorderingBuffer, ExecutionDataflowBlockOptions dataflowBlockOptions, TargetCoreOptions targetCoreOptions) { @@ -137,7 +137,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// If true, an exception will be stored after _decliningPermanently has been set to true. /// If true, exception will be treated as an AggregateException. /// Indicates whether the processing state is dirty and has to be reverted. - internal void Complete(Exception exception, bool dropPendingMessages, bool storeExceptionEvenIfAlreadyCompleting = false, + internal void Complete(Exception? exception, bool dropPendingMessages, bool storeExceptionEvenIfAlreadyCompleting = false, bool unwrapInnerExceptions = false, bool revertProcessingState = false) { Debug.Assert(storeExceptionEvenIfAlreadyCompleting || !revertProcessingState, @@ -178,7 +178,7 @@ namespace System.Threading.Tasks.Dataflow.Internal } /// - internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock source, bool consumeToAccept) + internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock? source, bool consumeToAccept) { // Validate arguments if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader)); @@ -217,7 +217,7 @@ namespace System.Threading.Tasks.Dataflow.Internal long messageId = _nextAvailableInputMessageId.Value++; Debug.Assert(messageId != Common.INVALID_REORDERING_ID, "The assigned message ID is invalid."); if (_boundingState != null) _boundingState.CurrentCount += 1; // track this new item against our bound - _messages.Enqueue(new KeyValuePair(messageValue, messageId)); + _messages.Enqueue(new KeyValuePair(messageValue!, messageId)); ProcessAsyncIfNecessary(); return DataflowMessageStatus.Accepted; } @@ -369,7 +369,7 @@ namespace System.Threading.Tasks.Dataflow.Internal _numberOfOutstandingOperations++; if (UsesAsyncCompletion) _numberOfOutstandingServiceTasks++; - var taskForInputProcessing = new Task(thisTargetCore => ((TargetCore)thisTargetCore).ProcessMessagesLoopCore(), this, + var taskForInputProcessing = new Task(thisTargetCore => ((TargetCore)thisTargetCore!).ProcessMessagesLoopCore(), this, Common.GetCreationOptionsForTask(repeat)); #if FEATURE_TRACING @@ -383,11 +383,11 @@ namespace System.Threading.Tasks.Dataflow.Internal #endif // Start the task handling scheduling exceptions - Exception exception = Common.StartTaskSafe(taskForInputProcessing, _dataflowBlockOptions.TaskScheduler); + Exception? exception = Common.StartTaskSafe(taskForInputProcessing, _dataflowBlockOptions.TaskScheduler); if (exception != null) { // Get out from under currently held locks. Complete re-acquires the locks it needs. - Task.Factory.StartNew(exc => Complete(exception: (Exception)exc, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, + Task.Factory.StartNew(exc => Complete(exception: (Exception)exc!, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: false, revertProcessingState: true), exception, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default); } @@ -424,7 +424,7 @@ namespace System.Threading.Tasks.Dataflow.Internal lock (IncomingLock) { Debug.Assert( - _boundingState.OutstandingTransfers > 0 + _boundingState!.OutstandingTransfers > 0 && _boundingState.OutstandingTransfers <= _dataflowBlockOptions.ActualMaxDegreeOfParallelism, "Expected TryConsumePostponedMessage to have incremented the count and for the count to not exceed the DOP."); _boundingState.OutstandingTransfers--; // was incremented in TryConsumePostponedMessage @@ -638,7 +638,7 @@ namespace System.Threading.Tasks.Dataflow.Internal // We can consume a message to process if there's one to process and also if // if we have logical room within our bound for the message. - if (!_boundingState.CountIsLessThanBound || !_boundingState.PostponedMessages.TryPop(out element)) + if (!_boundingState!.CountIsLessThanBound || !_boundingState.PostponedMessages.TryPop(out element)) { if (countIncrementedExpectingToGetItem) { @@ -665,7 +665,7 @@ namespace System.Threading.Tasks.Dataflow.Internal TInput consumedValue = element.Key.ConsumeMessage(element.Value, _owningTarget, out consumed); if (consumed) { - result = new KeyValuePair(consumedValue, messageId); + result = new KeyValuePair(consumedValue!, messageId); return true; } else @@ -734,7 +734,7 @@ namespace System.Threading.Tasks.Dataflow.Internal // Get out from under currently held locks. This is to avoid // invoking synchronous continuations off of _completionSource.Task // while holding a lock. - Task.Factory.StartNew(state => ((TargetCore)state).CompleteBlockOncePossible(), + Task.Factory.StartNew(state => ((TargetCore)state!).CompleteBlockOncePossible(), this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default); } } @@ -770,7 +770,7 @@ namespace System.Threading.Tasks.Dataflow.Internal // It's ok to read _exceptions' content here, because // at this point no more exceptions can be generated and thus no one will // be writing to it. - _completionSource.TrySetException(Volatile.Read(ref _exceptions)); + _completionSource.TrySetException(Volatile.Read(ref _exceptions!)); } // If we completed with cancellation, finish in a canceled state else if (_dataflowBlockOptions.CancellationToken.IsCancellationRequested) @@ -851,7 +851,7 @@ namespace System.Threading.Tasks.Dataflow.Internal internal IEnumerable InputQueue { get { return _target._messages.Select(kvp => kvp.Key).ToList(); } } /// Gets any postponed messages. - internal QueuedMap, DataflowMessageHeader> PostponedMessages + internal QueuedMap, DataflowMessageHeader>? PostponedMessages { get { return _target._boundingState != null ? _target._boundingState.PostponedMessages : null; } } diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/TargetRegistry.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/TargetRegistry.cs index c75a644..954d183 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/TargetRegistry.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/TargetRegistry.cs @@ -52,9 +52,9 @@ namespace System.Threading.Tasks.Dataflow.Internal /// gets decremented after each successful propagation. internal int RemainingMessages; /// The previous node in the list. - internal LinkedTargetInfo Previous; + internal LinkedTargetInfo? Previous; /// The next node in the list. - internal LinkedTargetInfo Next; + internal LinkedTargetInfo? Next; } /// A reference to the owning source block. @@ -62,9 +62,9 @@ namespace System.Threading.Tasks.Dataflow.Internal /// A mapping of targets to information about them. private readonly Dictionary, LinkedTargetInfo> _targetInformation; /// The first node of an ordered list of targets. Messages should be offered to targets starting from First and following Next. - private LinkedTargetInfo _firstTarget; + private LinkedTargetInfo? _firstTarget; /// The last node of the ordered list of targets. This field is used purely as a perf optimization to avoid traversing the list for each Add. - private LinkedTargetInfo _lastTarget; + private LinkedTargetInfo? _lastTarget; /// Number of links with positive RemainingMessages counters. /// This is an optimization that allows us to skip dictionary lookup when this counter is 0. private int _linksWithRemainingMessages; @@ -86,7 +86,7 @@ namespace System.Threading.Tasks.Dataflow.Internal Debug.Assert(target != null, "The target that is supposed to be linked must not be null."); Debug.Assert(linkOptions != null, "The link options must not be null."); - LinkedTargetInfo targetInfo; + LinkedTargetInfo? targetInfo; // If the target already exists in the registry, replace it with a new NopLinkPropagator to maintain uniqueness if (_targetInformation.TryGetValue(target, out targetInfo)) target = new NopLinkPropagator(_owningSource, target); @@ -147,7 +147,7 @@ namespace System.Threading.Tasks.Dataflow.Internal Debug.Assert(!onlyIfReachedMaxMessages || _linksWithRemainingMessages > 0, "We shouldn't have ended on the slow path."); // If the target is registered... - LinkedTargetInfo node; + LinkedTargetInfo? node; if (_targetInformation.TryGetValue(target, out node)) { Debug.Assert(node != null, "The LinkedTargetInfo node referenced in the Dictionary must be non-null."); @@ -180,10 +180,10 @@ namespace System.Threading.Tasks.Dataflow.Internal } /// Clears the target registry entry points while allowing subsequent traversals of the linked list. - internal LinkedTargetInfo ClearEntryPoints() + internal LinkedTargetInfo? ClearEntryPoints() { // Save _firstTarget so we can return it - LinkedTargetInfo firstTarget = _firstTarget; + LinkedTargetInfo? firstTarget = _firstTarget; // Clear out the entry points _firstTarget = _lastTarget = null; @@ -196,7 +196,7 @@ namespace System.Threading.Tasks.Dataflow.Internal /// Propagated completion to the targets of the given linked list. /// The head of a saved linked list. - internal void PropagateCompletion(LinkedTargetInfo firstTarget) + internal void PropagateCompletion(LinkedTargetInfo? firstTarget) { Debug.Assert(_owningSource.Completion.IsCompleted, "The owning source must have completed before propagating completion."); @@ -204,14 +204,14 @@ namespace System.Threading.Tasks.Dataflow.Internal Task owningSourceCompletion = _owningSource.Completion; // Propagate completion to those targets that have requested it - for (LinkedTargetInfo node = firstTarget; node != null; node = node.Next) + for (LinkedTargetInfo? node = firstTarget; node != null; node = node.Next) { if (node.PropagateCompletion) Common.PropagateCompletion(owningSourceCompletion, node.Target, Common.AsyncExceptionHandler); } } /// Gets the first node of the ordered target list. - internal LinkedTargetInfo FirstTargetNode { get { return _firstTarget; } } + internal LinkedTargetInfo? FirstTargetNode { get { return _firstTarget; } } /// Adds a LinkedTargetInfo node to the doubly-linked list. /// The node to be added. @@ -257,8 +257,8 @@ namespace System.Threading.Tasks.Dataflow.Internal Debug.Assert(node != null, "Node to remove is required."); Debug.Assert(_firstTarget != null && _lastTarget != null, "Both first and last node must be non-null before RemoveFromList."); - LinkedTargetInfo previous = node.Previous; - LinkedTargetInfo next = node.Next; + LinkedTargetInfo? previous = node.Previous; + LinkedTargetInfo? next = node.Next; // Remove the node by linking the adjacent nodes if (node.Previous != null) @@ -290,7 +290,7 @@ namespace System.Threading.Tasks.Dataflow.Internal { var targets = new ITargetBlock[Count]; int i = 0; - for (LinkedTargetInfo node = _firstTarget; node != null; node = node.Next) + for (LinkedTargetInfo? node = _firstTarget; node != null; node = node.Next) { targets[i++] = node.Target; } @@ -325,13 +325,14 @@ namespace System.Threading.Tasks.Dataflow.Internal } /// - DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock source, bool consumeToAccept) + DataflowMessageStatus ITargetBlock.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock? source, bool consumeToAccept) { Debug.Assert(source == _owningSource, "Only valid to be used with the source for which it was created."); return _target.OfferMessage(messageHeader, messageValue, this, consumeToAccept); } /// + [return: MaybeNull] T ISourceBlock.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock target, out bool messageConsumed) { return _owningSource.ConsumeMessage(messageHeader, this, out messageConsumed); diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/Threading.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/Threading.cs index 2d2a99f..b34c41a 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/Threading.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Internal/Threading.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable disable // used only for netstandard1.0 build using System; using System.Collections.Generic; using System.Diagnostics; diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/System.Threading.Tasks.Dataflow.csproj b/src/libraries/System.Threading.Tasks.Dataflow/src/System.Threading.Tasks.Dataflow.csproj index c4319ee..9e20116 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/System.Threading.Tasks.Dataflow.csproj +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/System.Threading.Tasks.Dataflow.csproj @@ -1,10 +1,11 @@ - + $(DefineConstants);FEATURE_TRACING $(DefineConstants);USE_INTERNAL_CONCURRENT_COLLECTIONS $(DefineConstants);USE_INTERNAL_THREADING netstandard1.1;portable-net45+win8+wpa81 netstandard2.0;netstandard1.0;netstandard1.1 + enable diff --git a/src/libraries/System.Threading.Tasks.Dataflow/tests/Dataflow/DataflowBlockExtensionTests.cs b/src/libraries/System.Threading.Tasks.Dataflow/tests/Dataflow/DataflowBlockExtensionTests.cs index 2db6048..d022a31 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/tests/Dataflow/DataflowBlockExtensionTests.cs +++ b/src/libraries/System.Threading.Tasks.Dataflow/tests/Dataflow/DataflowBlockExtensionTests.cs @@ -306,10 +306,10 @@ namespace System.Threading.Tasks.Dataflow.Tests } // Validate sane behavior with a bad LinkTo - Assert.Null( - new DelegatePropagator { - LinkToDelegate = (_,__) => null - }.AsObservable().Subscribe(DataflowBlock.NullTarget().AsObserver())); + new DelegatePropagator + { + LinkToDelegate = (_, __) => null + }.AsObservable().Subscribe(DataflowBlock.NullTarget().AsObserver()).Dispose(); } [Fact]