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<TInput>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<TInput> source, bool consumeToAccept) { throw null; }
+ System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<TInput>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<TInput>? source, bool consumeToAccept) { throw null; }
public override string ToString() { throw null; }
}
public sealed partial class BatchBlock<T> : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock<T, T[]>, System.Threading.Tasks.Dataflow.IReceivableSourceBlock<T[]>, System.Threading.Tasks.Dataflow.ISourceBlock<T[]>, System.Threading.Tasks.Dataflow.ITargetBlock<T>
public void Complete() { }
public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock<T[]> 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<T[]>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<T[]> target, out bool messageConsumed) { throw null; }
void System.Threading.Tasks.Dataflow.ISourceBlock<T[]>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<T[]> target) { }
bool System.Threading.Tasks.Dataflow.ISourceBlock<T[]>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<T[]> target) { throw null; }
- System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<T>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<T> source, bool consumeToAccept) { throw null; }
+ System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<T>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<T>? source, bool consumeToAccept) { throw null; }
public override string ToString() { throw null; }
public void TriggerBatch() { }
- public bool TryReceive(System.Predicate<T[]> filter, out T[] item) { throw null; }
- public bool TryReceiveAll(out System.Collections.Generic.IList<T[]> items) { throw null; }
+ public bool TryReceive(System.Predicate<T[]>? filter, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out T[]? item) { throw null; }
+ public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList<T[]>? items) { throw null; }
}
public sealed partial class BatchedJoinBlock<T1, T2> : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>>>, System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>>>
{
void System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<System.Collections.Generic.IList<T1>,System.Collections.Generic.IList<T2>>>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>>> target) { }
bool System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<System.Collections.Generic.IList<T1>,System.Collections.Generic.IList<T2>>>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>>> target) { throw null; }
public override string ToString() { throw null; }
- public bool TryReceive(System.Predicate<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>>> filter, out System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>> item) { throw null; }
- public bool TryReceiveAll(out System.Collections.Generic.IList<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>>> items) { throw null; }
+ public bool TryReceive(System.Predicate<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>>>? filter, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>>? item) { throw null; }
+ public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>>>? items) { throw null; }
}
public sealed partial class BatchedJoinBlock<T1, T2, T3> : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>, System.Collections.Generic.IList<T3>>>, System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>, System.Collections.Generic.IList<T3>>>
{
void System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<System.Collections.Generic.IList<T1>,System.Collections.Generic.IList<T2>,System.Collections.Generic.IList<T3>>>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>, System.Collections.Generic.IList<T3>>> target) { }
bool System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<System.Collections.Generic.IList<T1>,System.Collections.Generic.IList<T2>,System.Collections.Generic.IList<T3>>>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>, System.Collections.Generic.IList<T3>>> target) { throw null; }
public override string ToString() { throw null; }
- public bool TryReceive(System.Predicate<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>, System.Collections.Generic.IList<T3>>> filter, out System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>, System.Collections.Generic.IList<T3>> item) { throw null; }
- public bool TryReceiveAll(out System.Collections.Generic.IList<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>, System.Collections.Generic.IList<T3>>> items) { throw null; }
+ public bool TryReceive(System.Predicate<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>, System.Collections.Generic.IList<T3>>>? filter, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>, System.Collections.Generic.IList<T3>>? item) { throw null; }
+ public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList<System.Tuple<System.Collections.Generic.IList<T1>, System.Collections.Generic.IList<T2>, System.Collections.Generic.IList<T3>>>? items) { throw null; }
}
public sealed partial class BroadcastBlock<T> : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock<T, T>, System.Threading.Tasks.Dataflow.IReceivableSourceBlock<T>, System.Threading.Tasks.Dataflow.ISourceBlock<T>, System.Threading.Tasks.Dataflow.ITargetBlock<T>
{
- public BroadcastBlock(System.Func<T, T> cloningFunction) { }
- public BroadcastBlock(System.Func<T, T> cloningFunction, System.Threading.Tasks.Dataflow.DataflowBlockOptions dataflowBlockOptions) { }
+ public BroadcastBlock(System.Func<T, T>? cloningFunction) { }
+ public BroadcastBlock(System.Func<T, T>? 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<T> 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<T>.TryReceiveAll(out System.Collections.Generic.IList<T> items) { throw null; }
+ bool System.Threading.Tasks.Dataflow.IReceivableSourceBlock<T>.TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList<T>? items) { throw null; }
+ [return: System.Diagnostics.CodeAnalysis.MaybeNull]
T System.Threading.Tasks.Dataflow.ISourceBlock<T>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<T> target, out bool messageConsumed) { throw null; }
void System.Threading.Tasks.Dataflow.ISourceBlock<T>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<T> target) { }
bool System.Threading.Tasks.Dataflow.ISourceBlock<T>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<T> target) { throw null; }
- System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<T>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<T> source, bool consumeToAccept) { throw null; }
+ System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<T>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<T>? source, bool consumeToAccept) { throw null; }
public override string ToString() { throw null; }
- public bool TryReceive(System.Predicate<T> filter, out T item) { throw null; }
+ public bool TryReceive(System.Predicate<T>? filter, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out T item) { throw null; }
}
public sealed partial class BufferBlock<T> : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock<T, T>, System.Threading.Tasks.Dataflow.IReceivableSourceBlock<T>, System.Threading.Tasks.Dataflow.ISourceBlock<T>, System.Threading.Tasks.Dataflow.ITargetBlock<T>
{
public void Complete() { }
public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock<T> 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<T>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<T> target, out bool messageConsumed) { throw null; }
void System.Threading.Tasks.Dataflow.ISourceBlock<T>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<T> target) { }
bool System.Threading.Tasks.Dataflow.ISourceBlock<T>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<T> target) { throw null; }
- System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<T>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<T> source, bool consumeToAccept) { throw null; }
+ System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<T>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<T>? source, bool consumeToAccept) { throw null; }
public override string ToString() { throw null; }
- public bool TryReceive(System.Predicate<T> filter, out T item) { throw null; }
- public bool TryReceiveAll(out System.Collections.Generic.IList<T> items) { throw null; }
+ public bool TryReceive(System.Predicate<T>? filter, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out T item) { throw null; }
+ public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList<T>? items) { throw null; }
}
public static partial class DataflowBlock
{
public static TOutput Receive<TOutput>(this System.Threading.Tasks.Dataflow.ISourceBlock<TOutput> source, System.TimeSpan timeout, System.Threading.CancellationToken cancellationToken) { throw null; }
public static System.Threading.Tasks.Task<bool> SendAsync<TInput>(this System.Threading.Tasks.Dataflow.ITargetBlock<TInput> target, TInput item) { throw null; }
public static System.Threading.Tasks.Task<bool> SendAsync<TInput>(this System.Threading.Tasks.Dataflow.ITargetBlock<TInput> target, TInput item, System.Threading.CancellationToken cancellationToken) { throw null; }
- public static bool TryReceive<TOutput>(this System.Threading.Tasks.Dataflow.IReceivableSourceBlock<TOutput> source, out TOutput item) { throw null; }
+ public static bool TryReceive<TOutput>(this System.Threading.Tasks.Dataflow.IReceivableSourceBlock<TOutput> source, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TOutput item) { throw null; }
}
public partial class DataflowBlockOptions
{
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; }
}
public partial interface IReceivableSourceBlock<TOutput> : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>
{
- bool TryReceive(System.Predicate<TOutput> filter, out TOutput item);
- bool TryReceiveAll(out System.Collections.Generic.IList<TOutput> items);
+ bool TryReceive(System.Predicate<TOutput>? filter, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TOutput item);
+ bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList<TOutput>? items);
}
public partial interface ISourceBlock<out TOutput> : System.Threading.Tasks.Dataflow.IDataflowBlock
{
+ [return: System.Diagnostics.CodeAnalysis.MaybeNull]
TOutput ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<TOutput> target, out bool messageConsumed);
System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock<TOutput> target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions);
void ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<TOutput> target);
}
public partial interface ITargetBlock<in TInput> : System.Threading.Tasks.Dataflow.IDataflowBlock
{
- System.Threading.Tasks.Dataflow.DataflowMessageStatus OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<TInput> source, bool consumeToAccept);
+ System.Threading.Tasks.Dataflow.DataflowMessageStatus OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<TInput>? source, bool consumeToAccept);
}
public sealed partial class JoinBlock<T1, T2> : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock<System.Tuple<T1, T2>>, System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<T1, T2>>
{
public void Complete() { }
public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<T1, T2>> target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; }
void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { }
- System.Tuple<T1, T2> System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<T1,T2>>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<T1, T2>> target, out bool messageConsumed) { throw null; }
+ System.Tuple<T1, T2>? System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<T1,T2>>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<T1, T2>> target, out bool messageConsumed) { throw null; }
void System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<T1,T2>>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<T1, T2>> target) { }
bool System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<T1,T2>>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<T1, T2>> target) { throw null; }
public override string ToString() { throw null; }
- public bool TryReceive(System.Predicate<System.Tuple<T1, T2>> filter, out System.Tuple<T1, T2> item) { throw null; }
- public bool TryReceiveAll(out System.Collections.Generic.IList<System.Tuple<T1, T2>> items) { throw null; }
+ public bool TryReceive(System.Predicate<System.Tuple<T1, T2>>? filter, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Tuple<T1, T2>? item) { throw null; }
+ public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList<System.Tuple<T1, T2>>? items) { throw null; }
}
public sealed partial class JoinBlock<T1, T2, T3> : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock<System.Tuple<T1, T2, T3>>, System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<T1, T2, T3>>
{
public void Complete() { }
public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<T1, T2, T3>> target, System.Threading.Tasks.Dataflow.DataflowLinkOptions linkOptions) { throw null; }
void System.Threading.Tasks.Dataflow.IDataflowBlock.Fault(System.Exception exception) { }
- System.Tuple<T1, T2, T3> System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<T1,T2,T3>>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<T1, T2, T3>> target, out bool messageConsumed) { throw null; }
+ System.Tuple<T1, T2, T3>? System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<T1,T2,T3>>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<T1, T2, T3>> target, out bool messageConsumed) { throw null; }
void System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<T1,T2,T3>>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<T1, T2, T3>> target) { }
bool System.Threading.Tasks.Dataflow.ISourceBlock<System.Tuple<T1,T2,T3>>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<System.Tuple<T1, T2, T3>> target) { throw null; }
public override string ToString() { throw null; }
- public bool TryReceive(System.Predicate<System.Tuple<T1, T2, T3>> filter, out System.Tuple<T1, T2, T3> item) { throw null; }
- public bool TryReceiveAll(out System.Collections.Generic.IList<System.Tuple<T1, T2, T3>> items) { throw null; }
+ public bool TryReceive(System.Predicate<System.Tuple<T1, T2, T3>>? filter, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Tuple<T1, T2, T3>? item) { throw null; }
+ public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList<System.Tuple<T1, T2, T3>>? items) { throw null; }
}
public sealed partial class TransformBlock<TInput, TOutput> : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock<TInput, TOutput>, System.Threading.Tasks.Dataflow.IReceivableSourceBlock<TOutput>, System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>, System.Threading.Tasks.Dataflow.ITargetBlock<TInput>
{
public void Complete() { }
public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock<TOutput> 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<TOutput>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<TOutput> target, out bool messageConsumed) { throw null; }
void System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<TOutput> target) { }
bool System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<TOutput> target) { throw null; }
- System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<TInput>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<TInput> source, bool consumeToAccept) { throw null; }
+ System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<TInput>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<TInput>? source, bool consumeToAccept) { throw null; }
public override string ToString() { throw null; }
- public bool TryReceive(System.Predicate<TOutput> filter, out TOutput item) { throw null; }
- public bool TryReceiveAll(out System.Collections.Generic.IList<TOutput> items) { throw null; }
+ public bool TryReceive(System.Predicate<TOutput>? filter, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TOutput item) { throw null; }
+ public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList<TOutput>? items) { throw null; }
}
public sealed partial class TransformManyBlock<TInput, TOutput> : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock<TInput, TOutput>, System.Threading.Tasks.Dataflow.IReceivableSourceBlock<TOutput>, System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>, System.Threading.Tasks.Dataflow.ITargetBlock<TInput>
{
public void Complete() { }
public System.IDisposable LinkTo(System.Threading.Tasks.Dataflow.ITargetBlock<TOutput> 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<TOutput>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<TOutput> target, out bool messageConsumed) { throw null; }
void System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<TOutput> target) { }
bool System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<TOutput> target) { throw null; }
- System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<TInput>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<TInput> source, bool consumeToAccept) { throw null; }
+ System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<TInput>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, TInput messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<TInput>? source, bool consumeToAccept) { throw null; }
public override string ToString() { throw null; }
- public bool TryReceive(System.Predicate<TOutput> filter, out TOutput item) { throw null; }
- public bool TryReceiveAll(out System.Collections.Generic.IList<TOutput> items) { throw null; }
+ public bool TryReceive(System.Predicate<TOutput>? filter, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TOutput item) { throw null; }
+ public bool TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList<TOutput>? items) { throw null; }
}
public sealed partial class WriteOnceBlock<T> : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock<T, T>, System.Threading.Tasks.Dataflow.IReceivableSourceBlock<T>, System.Threading.Tasks.Dataflow.ISourceBlock<T>, System.Threading.Tasks.Dataflow.ITargetBlock<T>
{
- public WriteOnceBlock(System.Func<T, T> cloningFunction) { }
- public WriteOnceBlock(System.Func<T, T> cloningFunction, System.Threading.Tasks.Dataflow.DataflowBlockOptions dataflowBlockOptions) { }
+ public WriteOnceBlock(System.Func<T, T>? cloningFunction) { }
+ public WriteOnceBlock(System.Func<T, T>? 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<T> 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<T>.TryReceiveAll(out System.Collections.Generic.IList<T> items) { throw null; }
+ bool System.Threading.Tasks.Dataflow.IReceivableSourceBlock<T>.TryReceiveAll([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Collections.Generic.IList<T>? items) { throw null; }
+ [return: System.Diagnostics.CodeAnalysis.MaybeNull]
T System.Threading.Tasks.Dataflow.ISourceBlock<T>.ConsumeMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<T> target, out bool messageConsumed) { throw null; }
void System.Threading.Tasks.Dataflow.ISourceBlock<T>.ReleaseReservation(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<T> target) { }
bool System.Threading.Tasks.Dataflow.ISourceBlock<T>.ReserveMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, System.Threading.Tasks.Dataflow.ITargetBlock<T> target) { throw null; }
- System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<T>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<T> source, bool consumeToAccept) { throw null; }
+ System.Threading.Tasks.Dataflow.DataflowMessageStatus System.Threading.Tasks.Dataflow.ITargetBlock<T>.OfferMessage(System.Threading.Tasks.Dataflow.DataflowMessageHeader messageHeader, T messageValue, System.Threading.Tasks.Dataflow.ISourceBlock<T>? source, bool consumeToAccept) { throw null; }
public override string ToString() { throw null; }
- public bool TryReceive(System.Predicate<T> filter, out T item) { throw null; }
+ public bool TryReceive(System.Predicate<T>? filter, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out T item) { throw null; }
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard1.0;netstandard1.1</TargetFrameworks>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Include="System.Threading.Tasks.Dataflow.cs" />
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
+#pragma warning disable 8617
DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> 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.
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
T ISourceBlock<T>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> 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.
{
RunCompletionAction(state =>
{
- try { ((SendAsyncSource<TOutput>)state).TrySetResult(true); }
+ try { ((SendAsyncSource<TOutput>)state!).TrySetResult(true); }
catch (ObjectDisposedException) { }
}, this, runAsync);
}
{
// The try/catch for ObjectDisposedException handles the case where the
// user disposes of the returned task before we're done with it.
- try { ((SendAsyncSource<TOutput>)state).TrySetResult(false); }
+ try { ((SendAsyncSource<TOutput>)state!).TrySetResult(false); }
catch (ObjectDisposedException) { }
}, this, runAsync);
}
{
RunCompletionAction(state =>
{
- var tuple = (Tuple<SendAsyncSource<TOutput>, Exception>)state;
+ var tuple = (Tuple<SendAsyncSource<TOutput>, Exception>)state!;
try { tuple.Item1.TrySetException(tuple.Item2); }
catch (ObjectDisposedException) { }
}, Tuple.Create<SendAsyncSource<TOutput>, Exception>(this, exception), runAsync);
{
RunCompletionAction(state =>
{
- try { ((SendAsyncSource<TOutput>)state).TrySetCanceled(); }
+ try { ((SendAsyncSource<TOutput>)state!).TrySetCanceled(); }
catch (ObjectDisposedException) { }
}, this, runAsync);
}
/// 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.
/// </remarks>
- private void RunCompletionAction(Action<object> completionAction, object completionActionState, bool runAsync)
+ private void RunCompletionAction(Action<object?> completionAction, object completionActionState, bool runAsync)
{
Debug.Assert(completionAction != null, "Completion action to run is required.");
private void OfferToTargetAsync()
{
System.Threading.Tasks.Task.Factory.StartNew(
- state => ((SendAsyncSource<TOutput>)state).OfferToTarget(), this,
+ state => ((SendAsyncSource<TOutput>)state!).OfferToTarget(), this,
CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
}
/// <summary>Cached delegate used to cancel a send in response to a cancellation request.</summary>
- private static readonly Action<object> _cancellationCallback = CancellationHandler;
+ private static readonly Action<object?> _cancellationCallback = CancellationHandler;
/// <summary>Attempts to cancel the source passed as state in response to a cancellation request.</summary>
/// <param name="state">
/// A weak reference to the SendAsyncSource. A weak reference is used to prevent the source
/// from being rooted in a long-lived token.
/// </param>
- private static void CancellationHandler(object state)
+ private static void CancellationHandler(object? state)
{
- SendAsyncSource<TOutput> source = Common.UnwrapWeakReference<SendAsyncSource<TOutput>>(state);
+ SendAsyncSource<TOutput>? source = Common.UnwrapWeakReference<SendAsyncSource<TOutput>>(state!);
if (source != null)
{
Debug.Assert(source._cancellationState != CANCELLATION_STATE_NONE,
}
/// <summary>Called by the target to consume the buffered message.</summary>
+ [return: MaybeNull]
TOutput ISourceBlock<TOutput>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out bool messageConsumed)
{
// Validate arguments
/// This method does not wait until the source has an item to provide.
/// It will return whether or not an element was available.
/// </remarks>
- public static bool TryReceive<TOutput>(this IReceivableSourceBlock<TOutput> source, out TOutput item)
+ public static bool TryReceive<TOutput>(this IReceivableSourceBlock<TOutput> source, [MaybeNullWhen(false)] out TOutput item)
{
if (source == null) throw new ArgumentNullException(nameof(source));
}
/// <summary>Cancels a CancellationTokenSource passed as the object state argument.</summary>
- private static readonly Action<object> _cancelCts = state => ((CancellationTokenSource)state).Cancel();
+ private static readonly Action<object?> _cancelCts = state => ((CancellationTokenSource)state!).Cancel();
/// <summary>Receives an item from the source by linking a temporary target from it.</summary>
/// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
/// <remarks>The C# compiler will not cache this delegate by default due to it being a generic method on a non-generic class.</remarks>
internal static readonly TimerCallback CachedLinkingTimerCallback = state =>
{
- var receiveTarget = (ReceiveTarget<T>)state;
+ var receiveTarget = (ReceiveTarget<T>)state!;
receiveTarget.TryCleanupAndComplete(ReceiveCoreByLinkingCleanupReason.Timer);
};
/// <summary>Cached delegate used in ReceiveCoreByLinking on the cancellation token. Passed the ReceiveTarget as the state argument.</summary>
/// <remarks>The C# compiler will not cache this delegate by default due to it being a generic method on a non-generic class.</remarks>
- internal static readonly Action<object> CachedLinkingCancellationCallback = state =>
+ internal static readonly Action<object?> CachedLinkingCancellationCallback = state =>
{
- var receiveTarget = (ReceiveTarget<T>)state;
+ var receiveTarget = (ReceiveTarget<T>)state!;
receiveTarget.TryCleanupAndComplete(ReceiveCoreByLinkingCleanupReason.Cancellation);
};
/// <summary>The received value if we accepted a value from the source.</summary>
- private T _receivedValue;
+ [AllowNull, MaybeNull]
+ private T _receivedValue = default!;
/// <summary>The cancellation token source representing both external and internal cancellation.</summary>
internal readonly CancellationTokenSource _cts = new CancellationTokenSource();
/// <summary>The registration on the external token that cancels the internal token.</summary>
internal CancellationTokenRegistration _regFromExternalCancellationToken;
/// <summary>The timer that fires when the timeout has been exceeded.</summary>
- internal Timer _timer;
+ internal Timer? _timer;
/// <summary>The unlinker from removing this target from the source from which we're receiving.</summary>
- internal IDisposable _unlink;
+ internal IDisposable? _unlink;
/// <summary>The received exception if an error occurred.</summary>
- internal Exception _receivedException;
+ internal Exception? _receivedException;
/// <summary>Gets the sync obj used to synchronize all activity on this target.</summary>
internal object IncomingLock { get { return _cts; } }
internal ReceiveTarget() { }
/// <summary>Offers a message to be used to complete the TaskCompletionSource.</summary>
- DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, bool consumeToAccept)
+ DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T>? source, bool consumeToAccept)
{
// Validate arguments
if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader));
{
// 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;
// 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
System.Threading.Tasks.Task.Factory.StartNew(state =>
{
// Complete with the received value
- var target = (ReceiveTarget<T>)state;
- try { target.TrySetResult(target._receivedValue); }
+ var target = (ReceiveTarget<T>)state!;
+ try { target.TrySetResult(target._receivedValue!); }
catch (ObjectDisposedException) { /* benign race if returned task is already disposed */ }
}, this, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
break;
System.Threading.Tasks.Task.Factory.StartNew(state =>
{
// Complete as canceled
- var target = (ReceiveTarget<T>)state;
+ var target = (ReceiveTarget<T>)state!;
try { target.TrySetCanceled(); }
catch (ObjectDisposedException) { /* benign race if returned task is already disposed */ }
}, this, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
System.Threading.Tasks.Task.Factory.StartNew(state =>
{
// Complete with the received exception
- var target = (ReceiveTarget<T>)state;
+ var target = (ReceiveTarget<T>)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);
/// Cached continuation delegate that unregisters from cancellation and
/// marshals the antecedent's result to the return value.
/// </summary>
- internal static readonly Func<Task<bool>, object, bool> s_handleCompletion = (antecedent, state) =>
+ internal static readonly Func<Task<bool>, object?, bool> s_handleCompletion = (antecedent, state) =>
{
var target = state as OutputAvailableAsyncTarget<T>;
Debug.Assert(target != null, "Expected non-null target");
/// Cached delegate that cancels the target and unlinks the target from the source.
/// Expects an OutputAvailableAsyncTarget as the state argument.
/// </summary>
- internal static readonly Action<object> s_cancelAndUnlink = CancelAndUnlink;
+ internal static readonly Action<object?> s_cancelAndUnlink = CancelAndUnlink;
/// <summary>Cancels the target and unlinks the target from the source.</summary>
/// <param name="state">An OutputAvailableAsyncTarget.</param>
- private static void CancelAndUnlink(object state)
+ private static void CancelAndUnlink(object? state)
{
var target = state as OutputAvailableAsyncTarget<T>;
Debug.Assert(target != null, "Expected a non-null target");
// 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<T>)tgt;
+ var thisTarget = (OutputAvailableAsyncTarget<T>)tgt!;
thisTarget.TrySetCanceled();
thisTarget.AttemptThreadSafeUnlink();
},
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();
}
/// <summary>The IDisposable used to unlink this target from its source.</summary>
- internal IDisposable _unlinker;
+ internal IDisposable? _unlinker;
/// <summary>The registration used to unregister this target from the cancellation token.</summary>
internal CancellationTokenRegistration _ctr;
/// <summary>Completes the task when offered a message (but doesn't consume the message).</summary>
- DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, bool consumeToAccept)
+ DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T>? source, bool consumeToAccept)
{
if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader));
if (source == null) throw new ArgumentNullException(nameof(source));
_target.Fault(exception);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, bool consumeToAccept)
+ public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput>? source, bool consumeToAccept)
{
return _target.OfferMessage(messageHeader, messageValue, source, consumeToAccept);
}
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- public bool TryReceive(Predicate<TOutput> filter, out TOutput item)
+ public bool TryReceive(Predicate<TOutput>? filter, [MaybeNullWhen(false)] out TOutput item)
{
var receivableSource = _source as IReceivableSourceBlock<TOutput>;
if (receivableSource != null) return receivableSource.TryReceive(filter, out item);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- public bool TryReceiveAll(out IList<TOutput> items)
+ public bool TryReceiveAll([NotNullWhen(true)] out IList<TOutput>? items)
{
var receivableSource = _source as IReceivableSourceBlock<TOutput>;
if (receivableSource != null) return receivableSource.TryReceiveAll(out items);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
public TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out bool messageConsumed)
{
return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
private static Task<int> ChooseCore<T1, T2, T3>(
ISourceBlock<T1> source1, Action<T1> action1,
ISourceBlock<T2> source2, Action<T2> action2,
- ISourceBlock<T3> source3, Action<T3> action3,
+ ISourceBlock<T3>? source3, Action<T3>? action3,
DataflowBlockOptions dataflowBlockOptions)
{
Debug.Assert(source1 != null && action1 != null, "The first source and action should not be null.");
return Common.CreateTaskFromCancellation<int>(dataflowBlockOptions.CancellationToken);
// Fast path: if any of the sources already has data available that can be received immediately.
- Task<int> resultTask;
+ Task<int>? 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;
}
/// <returns>true if this try attempt satisfies the choose operation; otherwise, false.</returns>
private static bool TryChooseFromSource<T>(
ISourceBlock<T> source, Action<T> action, int branchId, TaskScheduler scheduler,
- out Task<int> task)
+ [NotNullWhen(true)] out Task<int>? task)
{
// Validate arguments
Debug.Assert(source != null, "Expected a non-null source");
private static Task<int> ChooseCoreByLinking<T1, T2, T3>(
ISourceBlock<T1> source1, Action<T1> action1,
ISourceBlock<T2> source2, Action<T2> action2,
- ISourceBlock<T3> source3, Action<T3> action3,
+ ISourceBlock<T3>? source3, Action<T3>? action3,
DataflowBlockOptions dataflowBlockOptions)
{
Debug.Assert(source1 != null && action1 != null, "The first source and action should not be null.");
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
// 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<Exception> exceptions = null;
+ List<Exception>? exceptions = null;
int successfulBranchId = -1;
foreach (Task<int> 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;
/// Delegate used to invoke the action for a branch when that branch is activated
/// on the fast path.
/// </summary>
- internal static readonly Func<object, int> s_processBranchFunction = state =>
+ internal static readonly Func<object?, int> s_processBranchFunction = state =>
{
- Tuple<Action<T>, T, int> actionResultBranch = (Tuple<Action<T>, T, int>)state;
+ Tuple<Action<T>, T, int> actionResultBranch = (Tuple<Action<T>, T, int>)state!;
actionResultBranch.Item1(actionResultBranch.Item2);
return actionResultBranch.Item3;
};
Common.WireCancellationToComplete(cancellationToken, base.Task,
state =>
{
- var thisChooseTarget = (ChooseTarget<T>)state;
+ var thisChooseTarget = (ChooseTarget<T>)state!;
lock (thisChooseTarget._completed) thisChooseTarget.TrySetCanceled();
}, this);
}
/// <summary>Called when this choice branch is being offered a message.</summary>
- public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, bool consumeToAccept)
+ public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T>? source, bool consumeToAccept)
{
// Validate arguments
if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader));
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;
}
/// <summary>Gets any exceptions from the source block.</summary>
/// <returns>The aggregate exception of all errors, or null if everything completed successfully.</returns>
- private AggregateException GetCompletionError()
+ private AggregateException? GetCompletionError()
{
- Task sourceCompletionTask = Common.GetPotentiallyNotSupportedCompletionTask(_source);
+ Task? sourceCompletionTask = Common.GetPotentiallyNotSupportedCompletionTask(_source);
return sourceCompletionTask != null && sourceCompletionTask.IsFaulted ?
sourceCompletionTask.Exception : null;
}
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
if (_observersState.Unlinker == null)
{
_observersState.Observers = ImmutableArray<IObserver<TOutput>>.Empty;
- return null;
+ return Disposables.Nop;
}
}
/// </summary>
internal ImmutableArray<IObserver<TOutput>> Observers = ImmutableArray<IObserver<TOutput>>.Empty;
/// <summary>Used to unlink the source from this target when the last observer is unsubscribed.</summary>
- internal IDisposable Unlinker;
+ internal IDisposable? Unlinker;
/// <summary>
/// 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.
/// </summary>
- private List<Task<bool>> _tempSendAsyncTaskList;
+ private List<Task<bool>>? _tempSendAsyncTaskList;
/// <summary>Initializes the target instance.</summary>
/// <param name="observable">The owning observable.</param>
// 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);
/// Non-null when an unexpected exception occurs during processing. Faults
/// all subscribed observers and resets the observable back to its original condition.
/// </param>
- 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);
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.
/// <typeparam name="TInput">The type of the messages this block can accept.</typeparam>
private class NullTargetBlock<TInput> : ITargetBlock<TInput>
{
- private Task _completion;
+ private Task? _completion;
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- DataflowMessageStatus ITargetBlock<TInput>.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, bool consumeToAccept)
+ DataflowMessageStatus ITargetBlock<TInput>.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput>? source, bool consumeToAccept)
{
if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader));
/// <summary>Checks boxed <see cref="DataflowMessageHeader"/> instances for equality by ID.</summary>
/// <param name="obj">A boxed <see cref="DataflowMessageHeader"/> instance.</param>
/// <returns>True if the instances are equal. False otherwise.</returns>
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is DataflowMessageHeader && this == (DataflowMessageHeader)obj;
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
namespace System.Threading.Tasks.Dataflow
{
// IMPLEMENT IMPLICITLY
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- bool TryReceive(Predicate<TOutput> filter, out TOutput item);
+ bool TryReceive(Predicate<TOutput>? filter, [MaybeNullWhen(false)] out TOutput item);
// IMPLEMENT IMPLICITLY IF BLOCK SUPPORTS RECEIVING MORE THAN ONE ITEM, OTHERWISE EXPLICITLY
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- bool TryReceiveAll(out IList<TOutput> items);
+ bool TryReceiveAll([NotNullWhen(true)] out IList<TOutput>? items);
}
}
// IMPLEMENT EXPLICITLY
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out bool messageConsumed);
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
// IMPLEMENT EXPLICITLY
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, bool consumeToAccept);
+ DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput>? source, bool consumeToAccept);
}
}
public sealed class ActionBlock<TInput> : ITargetBlock<TInput>, IDebuggerDisplay
{
/// <summary>The core implementation of this message block when in default mode.</summary>
- private readonly TargetCore<TInput> _defaultTarget;
+ private readonly TargetCore<TInput>? _defaultTarget;
/// <summary>The core implementation of this message block when in SPSC mode.</summary>
- private readonly SpscTargetCore<TInput> _spscTarget;
+ private readonly SpscTargetCore<TInput>? _spscTarget;
/// <summary>Initializes the <see cref="ActionBlock{T}"/> with the specified <see cref="System.Action{T}"/>.</summary>
/// <param name="action">The action to invoke with each data element received.</param>
// Handle async cancellation requests by declining on the target
Common.WireCancellationToComplete(
- dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore<TInput>)state).Complete(exception: null, dropPendingMessages: true), _defaultTarget);
+ dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore<TInput>)state!).Complete(exception: null, dropPendingMessages: true), _defaultTarget);
}
#if FEATURE_TRACING
DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
/// <param name="messageWithId">The message to be processed.</param>
private void ProcessMessage(Action<TInput> action, KeyValuePair<TInput, long> messageWithId)
{
+ Debug.Assert(_defaultTarget != null);
try
{
action(messageWithId.Key);
private void ProcessMessageWithTask(Func<TInput, Task> action, KeyValuePair<TInput, long> 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);
// Otherwise, join with the asynchronous operation when it completes.
task.ContinueWith((completed, state) =>
{
- ((ActionBlock<TInput>)state).AsyncCompleteProcessMessageWithTask(completed);
+ ((ActionBlock<TInput>)state!).AsyncCompleteProcessMessageWithTask(completed);
}, this, CancellationToken.None, Common.GetContinuationOptions(TaskContinuationOptions.ExecuteSynchronously), TaskScheduler.Default);
}
}
{
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
}
else
{
+ Debug.Assert(_spscTarget != null);
_spscTarget.Complete(exception: null);
}
}
}
else
{
+ Debug.Assert(_spscTarget != null);
_spscTarget.Complete(exception);
}
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
public Task Completion
{
- get { return _defaultTarget != null ? _defaultTarget.Completion : _spscTarget.Completion; }
+ get { return _defaultTarget != null ? _defaultTarget.Completion : _spscTarget!.Completion; }
}
/// <summary>Posts an item to the <see cref="System.Threading.Tasks.Dataflow.ITargetBlock{T}"/>.</summary>
return _defaultTarget != null ?
_defaultTarget.OfferMessage(Common.SingleMessageHeader, item, null, false) == DataflowMessageStatus.Accepted :
- _spscTarget.Post(item);
+ _spscTarget!.Post(item);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- DataflowMessageStatus ITargetBlock<TInput>.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, bool consumeToAccept)
+ DataflowMessageStatus ITargetBlock<TInput>.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput>? source, bool consumeToAccept)
{
return _defaultTarget != null ?
_defaultTarget.OfferMessage(messageHeader, messageValue, source, consumeToAccept) :
- _spscTarget.OfferMessage(messageHeader, messageValue, source, consumeToAccept);
+ _spscTarget!.OfferMessage(messageHeader, messageValue, source, consumeToAccept);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="InputCount"]/*' />
public int InputCount
{
- get { return _defaultTarget != null ? _defaultTarget.InputCount : _spscTarget.InputCount; }
+ get { return _defaultTarget != null ? _defaultTarget.InputCount : _spscTarget!.InputCount; }
}
/// <summary>Gets the number of messages waiting to be processed. This must only be used from the debugger.</summary>
private int InputCountForDebugger
{
- get { return _defaultTarget != null ? _defaultTarget.GetDebuggingInformation().InputCount : _spscTarget.InputCount; }
+ get { return _defaultTarget != null ? _defaultTarget.GetDebuggingInformation().InputCount : _spscTarget!.InputCount; }
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="ToString"]/*' />
public override string ToString()
{
- return Common.GetNameForDebugger(this, _defaultTarget != null ? _defaultTarget.DataflowBlockOptions : _spscTarget.DataflowBlockOptions);
+ return Common.GetNameForDebugger(this, _defaultTarget != null ? _defaultTarget.DataflowBlockOptions : _spscTarget!.DataflowBlockOptions);
}
/// <summary>The data to display in the debugger display attribute.</summary>
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);
}
}
/// <summary>The action block being viewed.</summary>
private readonly ActionBlock<TInput> _actionBlock;
/// <summary>The action block's default target being viewed.</summary>
- private readonly TargetCore<TInput>.DebuggingInformation _defaultDebugInfo;
+ private readonly TargetCore<TInput>.DebuggingInformation? _defaultDebugInfo;
/// <summary>The action block's SPSC target being viewed.</summary>
- private readonly SpscTargetCore<TInput>.DebuggingInformation _spscDebugInfo;
+ private readonly SpscTargetCore<TInput>.DebuggingInformation? _spscDebugInfo;
/// <summary>Initializes the debug view.</summary>
/// <param name="actionBlock">The target being debugged.</param>
_actionBlock = actionBlock;
if (_actionBlock._defaultTarget != null)
{
- _defaultDebugInfo = actionBlock._defaultTarget.GetDebuggingInformation();
+ _defaultDebugInfo = actionBlock._defaultTarget!.GetDebuggingInformation();
}
else
{
- _spscDebugInfo = actionBlock._spscTarget.GetDebuggingInformation();
+ _spscDebugInfo = actionBlock._spscTarget!.GetDebuggingInformation();
}
}
/// <summary>Gets the messages waiting to be processed.</summary>
public IEnumerable<TInput> InputQueue
{
- get { return _defaultDebugInfo != null ? _defaultDebugInfo.InputQueue : _spscDebugInfo.InputQueue; }
+ get { return _defaultDebugInfo != null ? _defaultDebugInfo.InputQueue : _spscDebugInfo!.InputQueue; }
}
/// <summary>Gets any postponed messages.</summary>
- public QueuedMap<ISourceBlock<TInput>, DataflowMessageHeader> PostponedMessages
+ public QueuedMap<ISourceBlock<TInput>, DataflowMessageHeader>? PostponedMessages
{
get { return _defaultDebugInfo != null ? _defaultDebugInfo.PostponedMessages : null; }
}
/// <summary>Gets the number of outstanding input operations.</summary>
public int CurrentDegreeOfParallelism
{
- get { return _defaultDebugInfo != null ? _defaultDebugInfo.CurrentDegreeOfParallelism : _spscDebugInfo.CurrentDegreeOfParallelism; }
+ get { return _defaultDebugInfo != null ? _defaultDebugInfo.CurrentDegreeOfParallelism : _spscDebugInfo!.CurrentDegreeOfParallelism; }
}
/// <summary>Gets the ExecutionDataflowBlockOptions used to configure this block.</summary>
public ExecutionDataflowBlockOptions DataflowBlockOptions
{
- get { return _defaultDebugInfo != null ? _defaultDebugInfo.DataflowBlockOptions : _spscDebugInfo.DataflowBlockOptions; }
+ get { return _defaultDebugInfo != null ? _defaultDebugInfo.DataflowBlockOptions : _spscDebugInfo!.DataflowBlockOptions; }
}
/// <summary>Gets whether the block is declining further messages.</summary>
public bool IsDecliningPermanently
{
- get { return _defaultDebugInfo != null ? _defaultDebugInfo.IsDecliningPermanently : _spscDebugInfo.IsDecliningPermanently; }
+ get { return _defaultDebugInfo != null ? _defaultDebugInfo.IsDecliningPermanently : _spscDebugInfo!.IsDecliningPermanently; }
}
/// <summary>Gets whether the block is completed.</summary>
public bool IsCompleted
{
- get { return _defaultDebugInfo != null ? _defaultDebugInfo.IsCompleted : _spscDebugInfo.IsCompleted; }
+ get { return _defaultDebugInfo != null ? _defaultDebugInfo.IsCompleted : _spscDebugInfo!.IsCompleted; }
}
/// <summary>Gets the block's Id.</summary>
public int Id { get { return Common.GetBlockId(_actionBlock); } }
dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
// Initialize bounding actions
- Action<ISourceBlock<T[]>, int> onItemsRemoved = null;
- Func<ISourceBlock<T[]>, T[], IList<T[]>, int> itemCountingFunc = null;
+ Action<ISourceBlock<T[]>, int>? onItemsRemoved = null;
+ Func<ISourceBlock<T[]>, T[], IList<T[]>?, int>? itemCountingFunc = null;
if (dataflowBlockOptions.BoundedCapacity > 0)
{
onItemsRemoved = (owningSource, count) => ((BatchBlock<T>)owningSource)._target.OnItemsRemoved(count);
// to handle multiple completion requests and to carry over only one.
_source.Completion.ContinueWith((completed, state) =>
{
- var thisBlock = ((BatchBlock<T>)state) as IDataflowBlock;
+ var thisBlock = ((BatchBlock<T>)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())
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- public bool TryReceive(Predicate<T[]> filter, out T[] item)
+#pragma warning disable CS8614 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470
+ public bool TryReceive(Predicate<T[]>? filter, [NotNullWhen(true)] out T[]? item)
+#pragma warning restore CS8614
{
return _source.TryReceive(filter, out item);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- public bool TryReceiveAll(out IList<T[]> items) { return _source.TryReceiveAll(out items); }
+ public bool TryReceiveAll([NotNullWhen(true)] out IList<T[]>? items) { return _source.TryReceiveAll(out items); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="OutputCount"]/*' />
public int OutputCount { get { return _source.OutputCount; } }
public int BatchSize { get { return _target.BatchSize; } }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, bool consumeToAccept)
+ DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T>? source, bool consumeToAccept)
{
return _target.OfferMessage(messageHeader, messageValue, source, consumeToAccept);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
T[] ISourceBlock<T[]>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<T[]> target, out bool messageConsumed)
{
return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
public long BatchesCompleted { get { return _targetDebuggingInformation.NumberOfBatchesCompleted; } }
/// <summary>Gets the task being used for input processing.</summary>
- public Task TaskForInputProcessing { get { return _targetDebuggingInformation.TaskForInputProcessing; } }
+ public Task? TaskForInputProcessing { get { return _targetDebuggingInformation.TaskForInputProcessing; } }
/// <summary>Gets the task being used for output processing.</summary>
- public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
+ public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
/// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
public GroupingDataflowBlockOptions DataflowBlockOptions { get { return _targetDebuggingInformation.DataflowBlockOptions; } }
public int Id { get { return Common.GetBlockId(_batchBlock); } }
/// <summary>Gets the messages postponed by this batch.</summary>
- public QueuedMap<ISourceBlock<T>, DataflowMessageHeader> PostponedMessages { get { return _targetDebuggingInformation.PostponedMessages; } }
+ public QueuedMap<ISourceBlock<T>, DataflowMessageHeader>? PostponedMessages { get { return _targetDebuggingInformation.PostponedMessages; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
public TargetRegistry<T[]> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
- public ITargetBlock<T[]> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
+ public ITargetBlock<T[]>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
}
/// <summary>Provides the core target implementation for a Batch.</summary>
/// <summary>The batch size.</summary>
private readonly int _batchSize;
/// <summary>State used when in non-greedy mode.</summary>
- private readonly NonGreedyState _nonGreedyState;
+ private readonly NonGreedyState? _nonGreedyState;
/// <summary>Bounding state for when the block is executing in bounded mode.</summary>
- private readonly BoundingState _boundingState;
+ private readonly BoundingState? _boundingState;
/// <summary>The options associated with this block.</summary>
private readonly GroupingDataflowBlockOptions _dataflowBlockOptions;
/// <summary>The action invoked with a completed batch.</summary>
/// <remarks>This value may be read not under a lock, but it must only be written to protected by the IncomingLock.</remarks>
internal bool AcceptFewerThanBatchSize;
/// <summary>The task used to process messages.</summary>
- internal Task TaskForInputProcessing;
+ internal Task? TaskForInputProcessing;
/// <summary>Initializes the NonGreedyState.</summary>
/// <param name="batchSize">The batch size used by the BatchBlock.</param>
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, bool consumeToAccept)
+ internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T>? source, bool consumeToAccept)
{
// Validate arguments
if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader));
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)
}
// 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
/// 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.</summary>
- 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)
// 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<Exception> exceptions = null;
+ List<Exception>? exceptions = null;
if (targetCore._nonGreedyState != null)
{
// Note: No locks should be held at this point
// 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
#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);
}
}
KeyValuePair<ISourceBlock<T>, DataflowMessageHeader> sourceAndMessage = postponedTemp[i];
if (sourceAndMessage.Key.ReserveMessage(sourceAndMessage.Value, _owningBatch))
{
- var reservedMessage = new KeyValuePair<DataflowMessageHeader, T>(sourceAndMessage.Value, default(T));
+ var reservedMessage = new KeyValuePair<DataflowMessageHeader, T>(sourceAndMessage.Value, default(T)!);
var reservedSourceAndMessage = new KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>(sourceAndMessage.Key, reservedMessage);
reserved.Add(reservedSourceAndMessage);
}
} // Release the lock. We must not hold it while calling Reserve/Consume/Release.
if (sourceAndMessage.Key.ReserveMessage(sourceAndMessage.Value, _owningBatch))
{
- var reservedMessage = new KeyValuePair<DataflowMessageHeader, T>(sourceAndMessage.Value, default(T));
+ var reservedMessage = new KeyValuePair<DataflowMessageHeader, T>(sourceAndMessage.Value, default(T)!);
var reservedSourceAndMessage = new KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>(sourceAndMessage.Key, reservedMessage);
reserved.Add(reservedSourceAndMessage);
}
for (int i = 0; i < poppedInitially; i++)
{
KeyValuePair<ISourceBlock<T>, DataflowMessageHeader> sourceAndMessage = postponedTemp[i];
- var reservedMessage = new KeyValuePair<DataflowMessageHeader, T>(sourceAndMessage.Value, default(T));
+ var reservedMessage = new KeyValuePair<DataflowMessageHeader, T>(sourceAndMessage.Value, default(T)!);
var reservedSourceAndMessage = new KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>(sourceAndMessage.Key, reservedMessage);
reserved.Add(reservedSourceAndMessage);
}
if (!postponed.TryPop(out sourceAndMessage)) break;
} // Release the lock. We must not hold it while calling Reserve/Consume/Release.
- var reservedMessage = new KeyValuePair<DataflowMessageHeader, T>(sourceAndMessage.Value, default(T));
+ var reservedMessage = new KeyValuePair<DataflowMessageHeader, T>(sourceAndMessage.Value, default(T)!);
var reservedSourceAndMessage = new KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>(sourceAndMessage.Key, reservedMessage);
reserved.Add(reservedSourceAndMessage);
}
throw new InvalidOperationException(SR.InvalidOperation_FailedToConsumeReservedMessage);
}
- var consumedMessage = new KeyValuePair<DataflowMessageHeader, T>(sourceAndMessage.Value.Key, consumedValue);
+ var consumedMessage = new KeyValuePair<DataflowMessageHeader, T>(sourceAndMessage.Value.Key, consumedValue!);
var consumedSourceAndMessage = new KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>(sourceAndMessage.Key, consumedMessage);
reserved[i] = consumedSourceAndMessage;
}
T consumedValue = sourceAndMessage.Key.ConsumeMessage(sourceAndMessage.Value.Key, _owningBatch, out consumed);
if (consumed)
{
- var consumedMessage = new KeyValuePair<DataflowMessageHeader, T>(sourceAndMessage.Value.Key, consumedValue);
+ var consumedMessage = new KeyValuePair<DataflowMessageHeader, T>(sourceAndMessage.Value.Key, consumedValue!);
var consumedSourceAndMessage = new KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>(sourceAndMessage.Key, consumedMessage);
reserved[i] = consumedSourceAndMessage;
Debug.Assert(_nonGreedyState != null, "Non-greedy state is required for non-greedy mode.");
Debug.Assert(_nonGreedyState.ReservedSourcesTemp != null, "Should have been initialized");
- List<Exception> exceptions = null;
+ List<Exception>? exceptions = null;
List<KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>> reserved = _nonGreedyState.ReservedSourcesTemp;
for (int i = 0; i < reserved.Count; i++)
/// <summary>Counts the input items in a single output item or in a list of output items.</summary>
/// <param name="singleOutputItem">A single output item. Only considered if multipleOutputItems == null.</param>
/// <param name="multipleOutputItems">A list of output items. May be null.</param>
- internal static int CountItems(T[] singleOutputItem, IList<T[]> multipleOutputItems)
+ internal static int CountItems(T[] singleOutputItem, IList<T[]>? multipleOutputItems)
{
// If multipleOutputItems == null, then singleOutputItem is the subject of counting
if (multipleOutputItems == null) return singleOutputItem.Length;
/// <summary>Gets the messages waiting to be processed.</summary>
public IEnumerable<T> InputQueue { get { return _target._messages.ToList(); } }
/// <summary>Gets the task being used for input processing.</summary>
- public Task TaskForInputProcessing { get { return _target._nonGreedyState != null ? _target._nonGreedyState.TaskForInputProcessing : null; } }
+ public Task? TaskForInputProcessing { get { return _target._nonGreedyState != null ? _target._nonGreedyState.TaskForInputProcessing : null; } }
/// <summary>Gets the collection of postponed messages.</summary>
- public QueuedMap<ISourceBlock<T>, DataflowMessageHeader> PostponedMessages { get { return _target._nonGreedyState != null ? _target._nonGreedyState.PostponedMessages : null; } }
+ public QueuedMap<ISourceBlock<T>, DataflowMessageHeader>? PostponedMessages { get { return _target._nonGreedyState != null ? _target._nonGreedyState.PostponedMessages : null; } }
/// <summary>Gets whether the block is declining further messages.</summary>
public bool IsDecliningPermanently { get { return _target._decliningPermanently; } }
/// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
// to handle multiple completion requests and to carry over only one.
_source.Completion.ContinueWith((completed, state) =>
{
- var thisBlock = ((BatchedJoinBlock<T1, T2>)state) as IDataflowBlock;
+ var thisBlock = ((BatchedJoinBlock<T1, T2>)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<T1, T2>)state).CompleteEachTarget(), this);
+ dataflowBlockOptions.CancellationToken, _source.Completion, state => ((BatchedJoinBlock<T1, T2>)state!).CompleteEachTarget(), this);
#if FEATURE_TRACING
DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
if (etwLog.IsEnabled())
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- public bool TryReceive(Predicate<Tuple<IList<T1>, IList<T2>>> filter, out Tuple<IList<T1>, IList<T2>> item)
+#pragma warning disable CS8614 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470
+ public bool TryReceive(Predicate<Tuple<IList<T1>, IList<T2>>>? filter, [NotNullWhen(true)] out Tuple<IList<T1>, IList<T2>>? item)
+#pragma warning restore CS8614
{
return _source.TryReceive(filter, out item);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- public bool TryReceiveAll(out IList<Tuple<IList<T1>, IList<T2>>> items) { return _source.TryReceiveAll(out items); }
+ public bool TryReceiveAll([NotNullWhen(true)] out IList<Tuple<IList<T1>, IList<T2>>>? items) { return _source.TryReceiveAll(out items); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="OutputCount"]/*' />
public int OutputCount { get { return _source.OutputCount; } }
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
Tuple<IList<T1>, IList<T2>> ISourceBlock<Tuple<IList<T1>, IList<T2>>>.ConsumeMessage(
DataflowMessageHeader messageHeader, ITargetBlock<Tuple<IList<T1>, IList<T2>>> target, out bool messageConsumed)
{
public ITargetBlock<T2> Target2 { get { return _batchedJoinBlock._target2; } }
/// <summary>Gets the task being used for output processing.</summary>
- public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
+ public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
/// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
public GroupingDataflowBlockOptions DataflowBlockOptions { get { return (GroupingDataflowBlockOptions)_sourceDebuggingInformation.DataflowBlockOptions; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
public TargetRegistry<Tuple<IList<T1>, IList<T2>>> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } }
/// <summary>Gets the target that holds a reservation on the next message, if any.</summary>
- public ITargetBlock<Tuple<IList<T1>, IList<T2>>> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
+ public ITargetBlock<Tuple<IList<T1>, IList<T2>>>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
}
}
// to handle multiple completion requests and to carry over only one.
_source.Completion.ContinueWith((completed, state) =>
{
- var thisBlock = ((BatchedJoinBlock<T1, T2, T3>)state) as IDataflowBlock;
+ var thisBlock = ((BatchedJoinBlock<T1, T2, T3>)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<T1, T2, T3>)state).CompleteEachTarget(), this);
+ dataflowBlockOptions.CancellationToken, _source.Completion, state => ((BatchedJoinBlock<T1, T2, T3>)state!).CompleteEachTarget(), this);
#if FEATURE_TRACING
DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
if (etwLog.IsEnabled())
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- public bool TryReceive(Predicate<Tuple<IList<T1>, IList<T2>, IList<T3>>> filter, out Tuple<IList<T1>, IList<T2>, IList<T3>> item)
+#pragma warning disable CS8614 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470
+ public bool TryReceive(Predicate<Tuple<IList<T1>, IList<T2>, IList<T3>>>? filter, [NotNullWhen(true)] out Tuple<IList<T1>, IList<T2>, IList<T3>>? item)
+#pragma warning restore CS8614
{
return _source.TryReceive(filter, out item);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- public bool TryReceiveAll(out IList<Tuple<IList<T1>, IList<T2>, IList<T3>>> items) { return _source.TryReceiveAll(out items); }
+ public bool TryReceiveAll([NotNullWhen(true)] out IList<Tuple<IList<T1>, IList<T2>, IList<T3>>>? items) { return _source.TryReceiveAll(out items); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="OutputCount"]/*' />
public int OutputCount { get { return _source.OutputCount; } }
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
Tuple<IList<T1>, IList<T2>, IList<T3>> ISourceBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>>.ConsumeMessage(
DataflowMessageHeader messageHeader, ITargetBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>> target, out bool messageConsumed)
{
public ITargetBlock<T3> Target3 { get { return _batchedJoinBlock._target3; } }
/// <summary>Gets the task being used for output processing.</summary>
- public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
+ public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
/// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
public GroupingDataflowBlockOptions DataflowBlockOptions { get { return (GroupingDataflowBlockOptions)_sourceDebuggingInformation.DataflowBlockOptions; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
public TargetRegistry<Tuple<IList<T1>, IList<T2>, IList<T3>>> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } }
/// <summary>Gets the target that holds a reservation on the next message, if any.</summary>
- public ITargetBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
+ public ITargetBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
}
}
}
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, bool consumeToAccept)
+ public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T>? source, bool consumeToAccept)
{
// Validate arguments
if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader));
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();
/// <summary>The source side.</summary>
private readonly BroadcastingSourceCore<T> _source;
/// <summary>Bounding state for when the block is executing in bounded mode.</summary>
- private readonly BoundingStateWithPostponedAndTask<T> _boundingState;
+ private readonly BoundingStateWithPostponedAndTask<T>? _boundingState;
/// <summary>Whether all future messages should be declined.</summary>
private bool _decliningPermanently;
/// <summary>A task has reserved the right to run the completion routine.</summary>
/// 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.
/// </param>
- public BroadcastBlock(Func<T, T> cloningFunction) :
+ public BroadcastBlock(Func<T, T>? cloningFunction) :
this(cloningFunction, DataflowBlockOptions.Default)
{ }
/// </param>
/// <param name="dataflowBlockOptions">The options with which to configure this <see cref="BroadcastBlock{T}"/>.</param>
/// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
- public BroadcastBlock(Func<T, T> cloningFunction, DataflowBlockOptions dataflowBlockOptions)
+ public BroadcastBlock(Func<T, T>? cloningFunction, DataflowBlockOptions dataflowBlockOptions)
{
// Validate arguments
if (dataflowBlockOptions == null) throw new ArgumentNullException(nameof(dataflowBlockOptions));
dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
// Initialize bounding state if necessary
- Action<int> onItemsRemoved = null;
+ Action<int>? onItemsRemoved = null;
if (dataflowBlockOptions.BoundedCapacity > 0)
{
Debug.Assert(dataflowBlockOptions.BoundedCapacity > 0, "Positive bounding count expected; should have been verified by options ctor");
// to handle multiple completion requests and to carry over only one.
_source.Completion.ContinueWith((completed, state) =>
{
- var thisBlock = ((BroadcastBlock<T>)state) as IDataflowBlock;
+ var thisBlock = ((BroadcastBlock<T>)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<T>)state).Complete(), this);
+ dataflowBlockOptions.CancellationToken, _source.Completion, state => ((BroadcastBlock<T>)state!).Complete(), this);
#if FEATURE_TRACING
DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
if (etwLog.IsEnabled())
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.");
public IDisposable LinkTo(ITargetBlock<T> target, DataflowLinkOptions linkOptions) { return _source.LinkTo(target, linkOptions); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- public bool TryReceive(Predicate<T> filter, out T item) { return _source.TryReceive(filter, out item); }
+ public bool TryReceive(Predicate<T>? filter, [MaybeNullWhen(false)] out T item) { return _source.TryReceive(filter, out item); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- bool IReceivableSourceBlock<T>.TryReceiveAll(out IList<T> items) { return _source.TryReceiveAll(out items); }
+ bool IReceivableSourceBlock<T>.TryReceiveAll([NotNullWhen(true)] out IList<T>? items) { return _source.TryReceiveAll(out items); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
public Task Completion { get { return _source.Completion; } }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, bool consumeToAccept)
+ DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T>? source, bool consumeToAccept)
{
// Validate arguments
if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader));
}
// 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;
}
// 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<T>)state).ConsumeMessagesLoopCore(), this,
+ new Task(state => ((BroadcastBlock<T>)state!).ConsumeMessagesLoopCore(), this,
Common.GetCreationOptionsForTask(isReplacementReplica));
#if FEATURE_TRACING
#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);
}
}
T consumedValue = sourceAndMessage.Key.ConsumeMessage(sourceAndMessage.Value, this, out consumed);
if (consumed)
{
- _source.AddMessage(consumedValue);
+ _source.AddMessage(consumedValue!);
return true;
}
}
{
Task.Factory.StartNew(state =>
{
- var thisBroadcastBlock = (BroadcastBlock<T>)state;
+ var thisBroadcastBlock = (BroadcastBlock<T>)state!;
// Release any postponed messages
- List<Exception> exceptions = null;
+ List<Exception>? exceptions = null;
if (thisBroadcastBlock._boundingState != null)
{
// Note: No locks should be held at this point
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
T ISourceBlock<T>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target, out bool messageConsumed)
{
return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
public T Value { get { return _broadcastBlock.ValueForDebugger; } }
/// <summary>Gets the task being used for output processing.</summary>
- public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
+ public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
/// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
public DataflowBlockOptions DataflowBlockOptions { get { return _sourceDebuggingInformation.DataflowBlockOptions; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
public TargetRegistry<T> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
- public ITargetBlock<T> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
+ public ITargetBlock<T>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
}
/// <summary>Provides a core implementation for blocks that implement <see cref="ISourceBlock{TOutput}"/>.</summary>
/// 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.
/// </summary>
- private readonly Action<int> _itemsRemovedAction;
+ private readonly Action<int>? _itemsRemovedAction;
/// <summary>Gets the object to use as the outgoing lock.</summary>
private object OutgoingLock { get { return _completionTask; } }
/// <summary>The options used to configure this block's execution.</summary>
private readonly DataflowBlockOptions _dataflowBlockOptions;
/// <summary>The cloning function to use.</summary>
- private readonly Func<TOutput, TOutput> _cloningFunction;
+ private readonly Func<TOutput, TOutput>? _cloningFunction;
/// <summary>An indicator whether _currentMessage has a value.</summary>
private bool _currentMessageIsValid;
/// <summary>The message currently being broadcast.</summary>
- private TOutput _currentMessage;
+ [AllowNull, MaybeNull]
+ private TOutput _currentMessage = default;
/// <summary>The target that the next message is reserved for, or null if nothing is reserved.</summary>
- private ITargetBlock<TOutput> _nextMessageReservedFor;
+ private ITargetBlock<TOutput>? _nextMessageReservedFor;
/// <summary>Whether this block should again attempt to offer messages to targets.</summary>
private bool _enableOffering;
/// <summary>Whether all future messages should be declined.</summary>
private bool _decliningPermanently;
/// <summary>The task used to process the output and offer it to targets.</summary>
- private Task _taskForOutputProcessing;
+ private Task? _taskForOutputProcessing;
/// <summary>Exceptions that may have occurred and gone unhandled during processing.</summary>
- private List<Exception> _exceptions;
+ private List<Exception>? _exceptions;
/// <summary>Counter for message IDs unique within this source block.</summary>
private long _nextMessageId = 1; // We are going to use this value before incrementing.
/// <summary>Whether someone has reserved the right to call CompleteBlockOncePossible.</summary>
/// <param name="itemsRemovedAction">Action to invoke when an item is removed.</param>
internal BroadcastingSourceCore(
BroadcastBlock<TOutput> owningSource,
- Func<TOutput, TOutput> cloningFunction,
+ Func<TOutput, TOutput>? cloningFunction,
DataflowBlockOptions dataflowBlockOptions,
- Action<int> itemsRemovedAction)
+ Action<int>? itemsRemovedAction)
{
Debug.Assert(owningSource != null, "Must be associated with a broadcast block.");
Debug.Assert(dataflowBlockOptions != null, "Options are required to configure this block.");
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- internal bool TryReceive(Predicate<TOutput> filter, out TOutput item)
+ internal bool TryReceive(Predicate<TOutput>? filter, [MaybeNullWhen(false)] out TOutput item)
{
// Take the lock only long enough to get the message,
// synchronizing with other activities on the block.
// 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
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- internal bool TryReceiveAll(out IList<TOutput> items)
+ internal bool TryReceiveAll([NotNullWhen(true)] out IList<TOutput>? 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.
// 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<TOutput>)state;
+ var thisSourceCore = (BroadcastingSourceCore<TOutput>)state!;
lock (thisSourceCore.OutgoingLock)
{
lock (thisSourceCore.ValueLock)
// 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)
if (_itemsRemovedAction != null) _itemsRemovedAction(numDequeuedMessages);
// Offer it to each target, unless a soleTarget was provided, which case just offer it to that one.
- TargetRegistry<TOutput>.LinkedTargetInfo cur = _targetRegistry.FirstTargetNode;
+ TargetRegistry<TOutput>.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<TOutput>.LinkedTargetInfo next = cur.Next;
+ TargetRegistry<TOutput>.LinkedTargetInfo? next = cur.Next;
ITargetBlock<TOutput> target = cur.Target;
OfferMessageToTarget(header, message, target);
cur = next;
{
// 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<TOutput>)thisSourceCore).OfferMessagesLoopCore(), this,
+ _taskForOutputProcessing = new Task(thisSourceCore => ((BroadcastingSourceCore<TOutput>)thisSourceCore!).OfferMessagesLoopCore(), this,
Common.GetCreationOptionsForTask(isReplacementReplica));
#if FEATURE_TRACING
#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.
// Re-take the locks on a separate thread.
Task.Factory.StartNew(state =>
{
- var thisSourceCore = (BroadcastingSourceCore<TOutput>)state;
+ var thisSourceCore = (BroadcastingSourceCore<TOutput>)state!;
lock (thisSourceCore.OutgoingLock)
{
lock (thisSourceCore.ValueLock)
_completionReserved = true;
// Run asynchronously to get out of the currently held locks
- Task.Factory.StartNew(thisSourceCore => ((BroadcastingSourceCore<TOutput>)thisSourceCore).CompleteBlockOncePossible(),
+ Task.Factory.StartNew(thisSourceCore => ((BroadcastingSourceCore<TOutput>)thisSourceCore!).CompleteBlockOncePossible(),
this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
}
/// </summary>
private void CompleteBlockOncePossible()
{
- TargetRegistry<TOutput>.LinkedTargetInfo linkedTargets;
- List<Exception> exceptions;
+ TargetRegistry<TOutput>.LinkedTargetInfo? linkedTargets;
+ List<Exception>? 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.
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
internal TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out bool messageConsumed)
{
// Validate arguments
}
messageConsumed = true;
- return CloneItem(valueToClone);
+ return CloneItem(valueToClone!);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
// 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);
}
}
/// <summary>Gets whether the source contains a current message.</summary>
public bool HasValue { get { return _source._currentMessageIsValid; } }
/// <summary>Gets the value of the source's current message.</summary>
- public TOutput Value { get { return _source._currentMessage; } }
+ public TOutput Value { get { return _source._currentMessage!; } }
/// <summary>Gets the messages available for receiving.</summary>
public IEnumerable<TOutput> InputQueue { get { return _source._messages.ToList(); } }
/// <summary>Gets the task being used for output processing.</summary>
- public Task TaskForOutputProcessing { get { return _source._taskForOutputProcessing; } }
+ public Task? TaskForOutputProcessing { get { return _source._taskForOutputProcessing; } }
/// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
public DataflowBlockOptions DataflowBlockOptions { get { return _source._dataflowBlockOptions; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
public TargetRegistry<TOutput> LinkedTargets { get { return _source._targetRegistry; } }
/// <summary>Gets the target that holds a reservation on the next message, if any.</summary>
- public ITargetBlock<TOutput> NextMessageReservedFor { get { return _source._nextMessageReservedFor; } }
+ public ITargetBlock<TOutput>? NextMessageReservedFor { get { return _source._nextMessageReservedFor; } }
}
}
}
/// <summary>The core logic for the buffer block.</summary>
private readonly SourceCore<T> _source;
/// <summary>The bounding state for when in bounding mode; null if not bounding.</summary>
- private readonly BoundingStateWithPostponedAndTask<T> _boundingState;
+ private readonly BoundingStateWithPostponedAndTask<T>? _boundingState;
/// <summary>Whether all future messages should be declined on the target.</summary>
private bool _targetDecliningPermanently;
/// <summary>A task has reserved the right to run the target's completion routine.</summary>
dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
// Initialize bounding state if necessary
- Action<ISourceBlock<T>, int> onItemsRemoved = null;
+ Action<ISourceBlock<T>, int>? onItemsRemoved = null;
if (dataflowBlockOptions.BoundedCapacity > 0)
{
onItemsRemoved = (owningSource, count) => ((BufferBlock<T>)owningSource).OnItemsRemoved(count);
// to handle multiple completion requests and to carry over only one.
_source.Completion.ContinueWith((completed, state) =>
{
- var thisBlock = ((BufferBlock<T>)state) as IDataflowBlock;
+ var thisBlock = ((BufferBlock<T>)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<T>)owningSource).Complete(), this);
+ dataflowBlockOptions.CancellationToken, _source.Completion, owningSource => ((BufferBlock<T>)owningSource!).Complete(), this);
#if FEATURE_TRACING
DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
if (etwLog.IsEnabled())
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, bool consumeToAccept)
+ DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T>? source, bool consumeToAccept)
{
// Validate arguments
if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader));
}
// Once consumed, pass it to the source
- _source.AddMessage(messageValue);
+ _source.AddMessage(messageValue!);
if (_boundingState != null) _boundingState.CurrentCount++;
return DataflowMessageStatus.Accepted;
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.");
public IDisposable LinkTo(ITargetBlock<T> target, DataflowLinkOptions linkOptions) { return _source.LinkTo(target, linkOptions); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- public bool TryReceive(Predicate<T> filter, out T item) { return _source.TryReceive(filter, out item); }
+ public bool TryReceive(Predicate<T>? filter, [MaybeNullWhen(false)] out T item) { return _source.TryReceive(filter, out item); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- public bool TryReceiveAll(out IList<T> items) { return _source.TryReceiveAll(out items); }
+ public bool TryReceiveAll([NotNullWhen(true)] out IList<T>? items) { return _source.TryReceiveAll(out items); }
/// <summary>Gets the number of items currently stored in the buffer.</summary>
public int Count { get { return _source.OutputCount; } }
public Task Completion { get { return _source.Completion; } }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
T ISourceBlock<T>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target, out bool messageConsumed)
{
return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
// 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<T>)state).ConsumeMessagesLoopCore(), this,
+ new Task(state => ((BufferBlock<T>)state!).ConsumeMessagesLoopCore(), this,
Common.GetCreationOptionsForTask(isReplacementReplica));
#if FEATURE_TRACING
#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);
}
}
T consumedValue = sourceAndMessage.Key.ConsumeMessage(sourceAndMessage.Value, this, out consumed);
if (consumed)
{
- _source.AddMessage(consumedValue);
+ _source.AddMessage(consumedValue!);
return true;
}
}
{
Task.Factory.StartNew(state =>
{
- var thisBufferBlock = (BufferBlock<T>)state;
+ var thisBufferBlock = (BufferBlock<T>)state!;
// Release any postponed messages
- List<Exception> exceptions = null;
+ List<Exception>? exceptions = null;
if (thisBufferBlock._boundingState != null)
{
// Note: No locks should be held at this point
}
/// <summary>Gets the collection of postponed message headers.</summary>
- public QueuedMap<ISourceBlock<T>, DataflowMessageHeader> PostponedMessages
+ public QueuedMap<ISourceBlock<T>, DataflowMessageHeader>? PostponedMessages
{
get { return _bufferBlock._boundingState != null ? _bufferBlock._boundingState.PostponedMessages : null; }
}
public IEnumerable<T> Queue { get { return _sourceDebuggingInformation.OutputQueue; } }
/// <summary>The task used to process messages.</summary>
- public Task TaskForInputProcessing { get { return _bufferBlock._boundingState != null ? _bufferBlock._boundingState.TaskForInputProcessing : null; } }
+ public Task? TaskForInputProcessing { get { return _bufferBlock._boundingState != null ? _bufferBlock._boundingState.TaskForInputProcessing : null; } }
/// <summary>Gets the task being used for output processing.</summary>
- public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
+ public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
/// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
public DataflowBlockOptions DataflowBlockOptions { get { return _sourceDebuggingInformation.DataflowBlockOptions; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
public TargetRegistry<T> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
- public ITargetBlock<T> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
+ public ITargetBlock<T>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
}
}
}
dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
// Initialize bounding state if necessary
- Action<ISourceBlock<Tuple<T1, T2>>, int> onItemsRemoved = null;
+ Action<ISourceBlock<Tuple<T1, T2>>, int>? onItemsRemoved = null;
if (dataflowBlockOptions.BoundedCapacity > 0) onItemsRemoved = (owningSource, count) => ((JoinBlock<T1, T2>)owningSource)._sharedResources.OnItemsRemoved(count);
// Configure the source
// to handle multiple completion requests and to carry over only one.
_source.Completion.ContinueWith((completed, state) =>
{
- var thisBlock = ((JoinBlock<T1, T2>)state) as IDataflowBlock;
+ var thisBlock = ((JoinBlock<T1, T2>)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<T1, T2>)state)._sharedResources.CompleteEachTarget(), this);
+ dataflowBlockOptions.CancellationToken, _source.Completion, state => ((JoinBlock<T1, T2>)state!)._sharedResources.CompleteEachTarget(), this);
#if FEATURE_TRACING
DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
if (etwLog.IsEnabled())
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- public bool TryReceive(Predicate<Tuple<T1, T2>> filter, out Tuple<T1, T2> item)
+#pragma warning disable CS8614 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470
+ public bool TryReceive(Predicate<Tuple<T1, T2>>? filter, [NotNullWhen(true)] out Tuple<T1, T2>? item)
+#pragma warning restore CS8614
{
return _source.TryReceive(filter, out item);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- public bool TryReceiveAll(out IList<Tuple<T1, T2>> items) { return _source.TryReceiveAll(out items); }
+ public bool TryReceiveAll([NotNullWhen(true)] out IList<Tuple<T1, T2>>? items) { return _source.TryReceiveAll(out items); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="OutputCount"]/*' />
public int OutputCount { get { return _source.OutputCount; } }
public ITargetBlock<T2> Target2 { get { return _target2; } }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
- Tuple<T1, T2> ISourceBlock<Tuple<T1, T2>>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target, out bool messageConsumed)
+#pragma warning disable CS8616 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470
+ Tuple<T1, T2>? ISourceBlock<Tuple<T1, T2>>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target, out bool messageConsumed)
+#pragma warning restore CS8616
{
return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
}
public long JoinsCreated { get { return _joinBlock._sharedResources._joinsCreated; } }
/// <summary>Gets the task being used for input processing.</summary>
- public Task TaskForInputProcessing { get { return _joinBlock._sharedResources._taskForInputProcessing; } }
+ public Task? TaskForInputProcessing { get { return _joinBlock._sharedResources._taskForInputProcessing; } }
/// <summary>Gets the task being used for output processing.</summary>
- public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
+ public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
/// <summary>Gets the GroupingDataflowBlockOptions used to configure this block.</summary>
public GroupingDataflowBlockOptions DataflowBlockOptions { get { return (GroupingDataflowBlockOptions)_sourceDebuggingInformation.DataflowBlockOptions; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
public TargetRegistry<Tuple<T1, T2>> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
- public ITargetBlock<Tuple<T1, T2>> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
+ public ITargetBlock<Tuple<T1, T2>>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
}
}
dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
// Initialize bounding state if necessary
- Action<ISourceBlock<Tuple<T1, T2, T3>>, int> onItemsRemoved = null;
+ Action<ISourceBlock<Tuple<T1, T2, T3>>, int>? onItemsRemoved = null;
if (dataflowBlockOptions.BoundedCapacity > 0) onItemsRemoved = (owningSource, count) => ((JoinBlock<T1, T2, T3>)owningSource)._sharedResources.OnItemsRemoved(count);
// Configure the source
// to handle multiple completion requests and to carry over only one.
_source.Completion.ContinueWith((completed, state) =>
{
- var thisBlock = ((JoinBlock<T1, T2, T3>)state) as IDataflowBlock;
+ var thisBlock = ((JoinBlock<T1, T2, T3>)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<T1, T2, T3>)state)._sharedResources.CompleteEachTarget(), this);
+ dataflowBlockOptions.CancellationToken, _source.Completion, state => ((JoinBlock<T1, T2, T3>)state!)._sharedResources.CompleteEachTarget(), this);
#if FEATURE_TRACING
DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
if (etwLog.IsEnabled())
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- public bool TryReceive(Predicate<Tuple<T1, T2, T3>> filter, out Tuple<T1, T2, T3> item)
+#pragma warning disable CS8614 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470
+ public bool TryReceive(Predicate<Tuple<T1, T2, T3>>? filter, [NotNullWhen(true)] out Tuple<T1, T2, T3>? item)
+#pragma warning restore CS8614
{
return _source.TryReceive(filter, out item);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- public bool TryReceiveAll(out IList<Tuple<T1, T2, T3>> items) { return _source.TryReceiveAll(out items); }
+ public bool TryReceiveAll([NotNullWhen(true)] out IList<Tuple<T1, T2, T3>>? items) { return _source.TryReceiveAll(out items); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="OutputCount"]/*' />
public int OutputCount { get { return _source.OutputCount; } }
public ITargetBlock<T3> Target3 { get { return _target3; } }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
- Tuple<T1, T2, T3> ISourceBlock<Tuple<T1, T2, T3>>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2, T3>> target, out bool messageConsumed)
+#pragma warning disable CS8616 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/42470
+ Tuple<T1, T2, T3>? ISourceBlock<Tuple<T1, T2, T3>>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2, T3>> target, out bool messageConsumed)
+#pragma warning restore CS8616
{
return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
}
public long JoinsCreated { get { return _joinBlock._sharedResources._joinsCreated; } }
/// <summary>Gets the task being used for input processing.</summary>
- public Task TaskForInputProcessing { get { return _joinBlock._sharedResources._taskForInputProcessing; } }
+ public Task? TaskForInputProcessing { get { return _joinBlock._sharedResources._taskForInputProcessing; } }
/// <summary>Gets the task being used for output processing.</summary>
- public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
+ public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
/// <summary>Gets the GroupingDataflowBlockOptions used to configure this block.</summary>
public GroupingDataflowBlockOptions DataflowBlockOptions { get { return (GroupingDataflowBlockOptions)_sourceDebuggingInformation.DataflowBlockOptions; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
public TargetRegistry<Tuple<T1, T2, T3>> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
- public ITargetBlock<Tuple<T1, T2, T3>> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
+ public ITargetBlock<Tuple<T1, T2, T3>>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
}
}
}
/// <summary>A task representing the completion of the block.</summary>
private readonly TaskCompletionSource<VoidResult> _completionTask = new TaskCompletionSource<VoidResult>();
/// <summary>Input messages for the next batch.</summary>
- private readonly Queue<T> _messages;
+ private readonly Queue<T>? _messages;
/// <summary>State used when in non-greedy mode.</summary>
- private readonly NonGreedyState _nonGreedy;
+ private readonly NonGreedyState? _nonGreedy;
/// <summary>Whether this target is declining future messages.</summary>
private bool _decliningPermanently;
}
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<bool, T>(false, default(T));
+ _nonGreedy.ConsumedMessage = new KeyValuePair<bool, T>(false, default(T)!);
return value;
}
}
}
else
{
- return _nonGreedy.ConsumedMessage.Key;
+ return _nonGreedy!.ConsumedMessage.Key;
}
}
}
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;
}
}
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!
// 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)
{
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);
{
// 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<bool, T>(true, consumedValue);
+ _nonGreedy.ConsumedMessage = new KeyValuePair<bool, T>(true, consumedValue!);
// We don't account bounding per target in non-greedy mode. We do it once per batch (in the loop).
CompleteIfLastJoinIsFeasible();
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;
}
{
// 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;
{
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;
}
// Release any postponed messages
- List<Exception> exceptions = null;
+ List<Exception>? exceptions = null;
if (_nonGreedy != null)
{
// Note: No locks should be held at this point
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, bool consumeToAccept)
+ DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T>? source, bool consumeToAccept)
{
// Validate arguments
if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader));
(_sharedResources._boundingState == null
||
((_sharedResources._boundingState.CountIsLessThanBound || !HasTheHighestNumberOfMessagesAvailable) &&
- _nonGreedy.PostponedMessages.Count == 0 && _sharedResources._taskForInputProcessing == null)))
+ _nonGreedy!.PostponedMessages.Count == 0 && _sharedResources._taskForInputProcessing == null)))
{
if (consumeToAccept)
{
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
/// 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.</summary>
- 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)
internal Task CompletionTaskInternal { get { return _completionTask.Task; } }
/// <summary>Gets the number of messages waiting to be processed. This must only be used from the debugger as it avoids taking necessary locks.</summary>
- 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; } }
/// <summary>The data to display in the debugger display attribute.</summary>
private object DebuggerDisplayContent
}
/// <summary>Gets the messages waiting to be processed.</summary>
- public IEnumerable<T> InputQueue { get { return _joinBlockTarget._messages; } }
+ public IEnumerable<T>? InputQueue { get { return _joinBlockTarget._messages; } }
/// <summary>Gets whether the block is declining further messages.</summary>
public bool IsDecliningPermanently { get { return _joinBlockTarget._decliningPermanently || _joinBlockTarget._sharedResources._decliningPermanently; } }
}
/// <summary>Access point to the corresponding API method.</summary>
public void Complete() { CompleteCore(exception: null, dropPendingMessages: false, releaseReservedMessages: false); }
/// <summary>Internal implementation of the corresponding API method.</summary>
- internal abstract void CompleteCore(Exception exception, bool dropPendingMessages, bool releaseReservedMessages);
+ internal abstract void CompleteCore(Exception? exception, bool dropPendingMessages, bool releaseReservedMessages);
/// <summary>Completes the target.</summary>
internal abstract void CompleteOncePossible();
}
/// <summary>The options for the join.</summary>
internal readonly GroupingDataflowBlockOptions _dataflowBlockOptions;
/// <summary>Bounding state for when the block is executing in bounded mode.</summary>
- internal readonly BoundingState _boundingState;
+ internal readonly BoundingState? _boundingState;
/// <summary>Whether all targets should decline all further messages.</summary>
internal bool _decliningPermanently;
/// <summary>The task used to process messages.</summary>
- internal Task _taskForInputProcessing;
+ internal Task? _taskForInputProcessing;
/// <summary>Whether any exceptions have been generated and stored into the source core.</summary>
internal bool _hasExceptions;
/// <summary>The number of joins this block has created.</summary>
// 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
#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.
// 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);
}
/// <summary>The target side.</summary>
private readonly TargetCore<TInput> _target;
/// <summary>Buffer used to reorder outputs that may have completed out-of-order between the target half and the source half.</summary>
- private readonly ReorderingBuffer<TOutput> _reorderingBuffer;
+ private readonly ReorderingBuffer<TOutput>? _reorderingBuffer;
/// <summary>The source side.</summary>
private readonly SourceCore<TOutput> _source;
/// <param name="dataflowBlockOptions">The options with which to configure this <see cref="TransformBlock{TInput,TOutput}"/>.</param>
/// <exception cref="System.ArgumentNullException">The <paramref name="transformSync"/> and <paramref name="transformAsync"/> are both null (Nothing in Visual Basic).</exception>
/// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
- private TransformBlock(Func<TInput, TOutput> transformSync, Func<TInput, Task<TOutput>> transformAsync, ExecutionDataflowBlockOptions dataflowBlockOptions)
+ private TransformBlock(Func<TInput, TOutput>? transformSync, Func<TInput, Task<TOutput>>? transformAsync, ExecutionDataflowBlockOptions dataflowBlockOptions)
{
if (transformSync == null && transformAsync == null) throw new ArgumentNullException("transform");
if (dataflowBlockOptions == null) throw new ArgumentNullException(nameof(dataflowBlockOptions));
dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
// Initialize onItemsRemoved delegate if necessary
- Action<ISourceBlock<TOutput>, int> onItemsRemoved = null;
+ Action<ISourceBlock<TOutput>, int>? onItemsRemoved = null;
if (dataflowBlockOptions.BoundedCapacity > 0)
onItemsRemoved = (owningSource, count) => ((TransformBlock<TInput, TOutput>)owningSource)._target.ChangeBoundingCount(-count);
// we know for certain that no more messages will need to be sent to the source.
_target.Completion.ContinueWith((completed, state) =>
{
- var sourceCore = (SourceCore<TOutput>)state;
- if (completed.IsFaulted) sourceCore.AddAndUnwrapAggregateException(completed.Exception);
+ var sourceCore = (SourceCore<TOutput>)state!;
+ if (completed.IsFaulted) sourceCore.AddAndUnwrapAggregateException(completed.Exception!);
sourceCore.Complete();
}, _source, CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default);
// to handle multiple completion requests and to carry over only one.
_source.Completion.ContinueWith((completed, state) =>
{
- var thisBlock = ((TransformBlock<TInput, TOutput>)state) as IDataflowBlock;
+ var thisBlock = ((TransformBlock<TInput, TOutput>)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<TInput>)state).Complete(exception: null, dropPendingMessages: true), _target);
+ dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore<TInput>)state!).Complete(exception: null, dropPendingMessages: true), _target);
#if FEATURE_TRACING
DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
if (etwLog.IsEnabled())
{
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);
}
}
Debug.Assert(transform != null, "Function to invoke is required.");
// Run the transform function to get the task that represents the operation's completion
- Task<TOutput> task = null;
- Exception caughtException = null;
+ Task<TOutput>? task = null;
+ Exception? caughtException = null;
try
{
task = transform(messageWithId.Key);
// Otherwise, join with the asynchronous operation when it completes.
task.ContinueWith((completed, state) =>
{
- var tuple = (Tuple<TransformBlock<TInput, TOutput>, KeyValuePair<TInput, long>>)state;
+ var tuple = (Tuple<TransformBlock<TInput, TOutput>, KeyValuePair<TInput, long>>)state!;
tuple.Item1.AsyncCompleteProcessMessageWithTask(completed, tuple.Item2);
}, Tuple.Create(this, messageWithId), CancellationToken.None,
Common.GetContinuationOptions(TaskContinuationOptions.ExecuteSynchronously), TaskScheduler.Default);
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;
{
if (_target.DataflowBlockOptions.MaxDegreeOfParallelism == 1)
{
- _source.AddMessage(outputItem);
+ _source.AddMessage(outputItem!);
}
else
{
lock (ParallelSourceLock)
{
- _source.AddMessage(outputItem);
+ _source.AddMessage(outputItem!);
}
}
}
// 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();
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- public bool TryReceive(Predicate<TOutput> filter, out TOutput item)
+ public bool TryReceive(Predicate<TOutput>? filter, [MaybeNullWhen(false)] out TOutput item)
{
return _source.TryReceive(filter, out item);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- public bool TryReceiveAll(out IList<TOutput> items) { return _source.TryReceiveAll(out items); }
+ public bool TryReceiveAll([NotNullWhen(true)] out IList<TOutput>? items) { return _source.TryReceiveAll(out items); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
public Task Completion { get { return _source.Completion; } }
public int OutputCount { get { return _source.OutputCount; } }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- DataflowMessageStatus ITargetBlock<TInput>.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, bool consumeToAccept)
+ DataflowMessageStatus ITargetBlock<TInput>.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput>? source, bool consumeToAccept)
{
return _target.OfferMessage(messageHeader, messageValue, source, consumeToAccept);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
TOutput ISourceBlock<TOutput>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out bool messageConsumed)
{
return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
/// <summary>Gets the messages waiting to be processed.</summary>
public IEnumerable<TInput> InputQueue { get { return _targetDebuggingInformation.InputQueue; } }
/// <summary>Gets any postponed messages.</summary>
- public QueuedMap<ISourceBlock<TInput>, DataflowMessageHeader> PostponedMessages { get { return _targetDebuggingInformation.PostponedMessages; } }
+ public QueuedMap<ISourceBlock<TInput>, DataflowMessageHeader>? PostponedMessages { get { return _targetDebuggingInformation.PostponedMessages; } }
/// <summary>Gets the messages waiting to be received.</summary>
public IEnumerable<TOutput> OutputQueue { get { return _sourceDebuggingInformation.OutputQueue; } }
/// <summary>Gets the number of outstanding input operations.</summary>
public int CurrentDegreeOfParallelism { get { return _targetDebuggingInformation.CurrentDegreeOfParallelism; } }
/// <summary>Gets the task being used for output processing.</summary>
- public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
+ public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
/// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
public ExecutionDataflowBlockOptions DataflowBlockOptions { get { return _targetDebuggingInformation.DataflowBlockOptions; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
public TargetRegistry<TOutput> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } }
/// <summary>Gets the target that holds a reservation on the next message, if any.</summary>
- public ITargetBlock<TOutput> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
+ public ITargetBlock<TOutput>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
}
}
}
/// 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.
/// </summary>
- private readonly ReorderingBuffer<IEnumerable<TOutput>> _reorderingBuffer;
+ private readonly ReorderingBuffer<IEnumerable<TOutput>>? _reorderingBuffer;
/// <summary>The source side.</summary>
private readonly SourceCore<TOutput> _source;
/// <param name="dataflowBlockOptions">The options with which to configure this <see cref="TransformManyBlock{TInput,TOutput}"/>.</param>
/// <exception cref="System.ArgumentNullException">The <paramref name="transformSync"/> and <paramref name="transformAsync"/> are both null (Nothing in Visual Basic).</exception>
/// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
- private TransformManyBlock(Func<TInput, IEnumerable<TOutput>> transformSync, Func<TInput, Task<IEnumerable<TOutput>>> transformAsync, ExecutionDataflowBlockOptions dataflowBlockOptions)
+ private TransformManyBlock(Func<TInput, IEnumerable<TOutput>>? transformSync, Func<TInput, Task<IEnumerable<TOutput>>>? 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");
dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
// Initialize onItemsRemoved delegate if necessary
- Action<ISourceBlock<TOutput>, int> onItemsRemoved = null;
+ Action<ISourceBlock<TOutput>, int>? onItemsRemoved = null;
if (dataflowBlockOptions.BoundedCapacity > 0)
onItemsRemoved = (owningSource, count) => ((TransformManyBlock<TInput, TOutput>)owningSource)._target.ChangeBoundingCount(-count);
// we know for certain that no more messages will need to be sent to the source.
_target.Completion.ContinueWith((completed, state) =>
{
- var sourceCore = (SourceCore<TOutput>)state;
- if (completed.IsFaulted) sourceCore.AddAndUnwrapAggregateException(completed.Exception);
+ var sourceCore = (SourceCore<TOutput>)state!;
+ if (completed.IsFaulted) sourceCore.AddAndUnwrapAggregateException(completed.Exception!);
sourceCore.Complete();
}, _source, CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default);
// to handle multiple completion requests and to carry over only one.
_source.Completion.ContinueWith((completed, state) =>
{
- var thisBlock = ((TransformManyBlock<TInput, TOutput>)state) as IDataflowBlock;
+ var thisBlock = ((TransformManyBlock<TInput, TOutput>)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<TInput>)state).Complete(exception: null, dropPendingMessages: true), _target);
+ dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore<TInput>)state!).Complete(exception: null, dropPendingMessages: true), _target);
#if FEATURE_TRACING
DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
if (etwLog.IsEnabled())
Debug.Assert(function != null, "Function to invoke is required.");
// Run the transform function to get the resulting task
- Task<IEnumerable<TOutput>> task = null;
- Exception caughtException = null;
+ Task<IEnumerable<TOutput>>? task = null;
+ Exception? caughtException = null;
try
{
task = function(messageWithId.Key);
// scheduler as we'll be running user code through enumerating the returned enumerable.
task.ContinueWith((completed, state) =>
{
- var tuple = (Tuple<TransformManyBlock<TInput, TOutput>, KeyValuePair<TInput, long>>)state;
+ var tuple = (Tuple<TransformManyBlock<TInput, TOutput>, KeyValuePair<TInput, long>>)state!;
tuple.Item1.AsyncCompleteProcessMessageWithTask(completed, tuple.Item2);
}, Tuple.Create(this, messageWithId),
CancellationToken.None,
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;
/// <param name="messageWithId">The message with id.</param>
/// <param name="outputItems">The output items to be persisted.</param>
private void StoreOutputItems(
- KeyValuePair<TInput, long> messageWithId, IEnumerable<TOutput> outputItems)
+ KeyValuePair<TInput, long> messageWithId, IEnumerable<TOutput>? outputItems)
{
// If there's a reordering buffer, pass the data along to it.
// The reordering buffer will handle all details, including bounding.
/// <summary>Stores the next item using the reordering buffer.</summary>
/// <param name="id">The ID of the item.</param>
/// <param name="item">The completed item.</param>
- private void StoreOutputItemsReordered(long id, IEnumerable<TOutput> item)
+ private void StoreOutputItemsReordered(long id, IEnumerable<TOutput>? item)
{
Debug.Assert(_reorderingBuffer != null, "Expected a reordering buffer");
Debug.Assert(id != Common.INVALID_REORDERING_ID, "This ID should never have been handed out.");
// 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;
}
// 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<TOutput> itemAsTrustedList = item as TOutput[];
+ IList<TOutput>? itemAsTrustedList = item as TOutput[];
if (itemAsTrustedList == null) itemAsTrustedList = item as List<TOutput>;
if (itemAsTrustedList != null && isBounded)
{
// 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<TOutput> itemCopy = null;
+ List<TOutput>? itemCopy = null;
try
{
// If this is the next item, we can output it now.
public IDisposable LinkTo(ITargetBlock<TOutput> target, DataflowLinkOptions linkOptions) { return _source.LinkTo(target, linkOptions); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- public bool TryReceive(Predicate<TOutput> filter, out TOutput item) { return _source.TryReceive(filter, out item); }
+ public bool TryReceive(Predicate<TOutput>? filter, [MaybeNullWhen(false)] out TOutput item) { return _source.TryReceive(filter, out item); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- public bool TryReceiveAll(out IList<TOutput> items) { return _source.TryReceiveAll(out items); }
+ public bool TryReceiveAll([NotNullWhen(true)] out IList<TOutput>? items) { return _source.TryReceiveAll(out items); }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
public Task Completion { get { return _source.Completion; } }
public int OutputCount { get { return _source.OutputCount; } }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- DataflowMessageStatus ITargetBlock<TInput>.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, bool consumeToAccept)
+ DataflowMessageStatus ITargetBlock<TInput>.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput>? source, bool consumeToAccept)
{
return _target.OfferMessage(messageHeader, messageValue, source, consumeToAccept);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
TOutput ISourceBlock<TOutput>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out bool messageConsumed)
{
return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
/// <summary>Gets the messages waiting to be processed.</summary>
public IEnumerable<TInput> InputQueue { get { return _targetDebuggingInformation.InputQueue; } }
/// <summary>Gets any postponed messages.</summary>
- public QueuedMap<ISourceBlock<TInput>, DataflowMessageHeader> PostponedMessages { get { return _targetDebuggingInformation.PostponedMessages; } }
+ public QueuedMap<ISourceBlock<TInput>, DataflowMessageHeader>? PostponedMessages { get { return _targetDebuggingInformation.PostponedMessages; } }
/// <summary>Gets the messages waiting to be received.</summary>
public IEnumerable<TOutput> OutputQueue { get { return _sourceDebuggingInformation.OutputQueue; } }
/// <summary>Gets the number of input operations currently in flight.</summary>
public int CurrentDegreeOfParallelism { get { return _targetDebuggingInformation.CurrentDegreeOfParallelism; } }
/// <summary>Gets the task being used for output processing.</summary>
- public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
+ public Task? TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
/// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
public ExecutionDataflowBlockOptions DataflowBlockOptions { get { return _targetDebuggingInformation.DataflowBlockOptions; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
public TargetRegistry<TOutput> LinkedTargets { get { return _sourceDebuggingInformation.LinkedTargets; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
- public ITargetBlock<TOutput> NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
+ public ITargetBlock<TOutput>? NextMessageReservedFor { get { return _sourceDebuggingInformation.NextMessageReservedFor; } }
}
}
}
/// <summary>A registry used to store all linked targets and information about them.</summary>
private readonly TargetRegistry<T> _targetRegistry;
/// <summary>The cloning function.</summary>
- private readonly Func<T, T> _cloningFunction;
+ private readonly Func<T, T>? _cloningFunction;
/// <summary>The options used to configure this block's execution.</summary>
private readonly DataflowBlockOptions _dataflowBlockOptions;
/// <summary>Lazily initialized task completion source that produces the actual completion task when needed.</summary>
- private TaskCompletionSource<VoidResult> _lazyCompletionTaskSource;
+ private TaskCompletionSource<VoidResult>? _lazyCompletionTaskSource;
/// <summary>Whether all future messages should be declined.</summary>
private bool _decliningPermanently;
/// <summary>Whether block completion is disallowed.</summary>
/// <summary>The header of the singly-assigned value.</summary>
private DataflowMessageHeader _header;
/// <summary>The singly-assigned value.</summary>
- private T _value;
+ [AllowNull, MaybeNull]
+ private T _value = default;
/// <summary>Gets the object used as the value lock.</summary>
private object ValueLock { get { return _targetRegistry; } }
/// 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.
/// </param>
- public WriteOnceBlock(Func<T, T> cloningFunction) :
+ public WriteOnceBlock(Func<T, T>? cloningFunction) :
this(cloningFunction, DataflowBlockOptions.Default)
{ }
/// </param>
/// <param name="dataflowBlockOptions">The options with which to configure this <see cref="WriteOnceBlock{T}"/>.</param>
/// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
- public WriteOnceBlock(Func<T, T> cloningFunction, DataflowBlockOptions dataflowBlockOptions)
+ public WriteOnceBlock(Func<T, T>? cloningFunction, DataflowBlockOptions dataflowBlockOptions)
{
// Validate arguments
if (dataflowBlockOptions == null) throw new ArgumentNullException(nameof(dataflowBlockOptions));
{
// Handle async cancellation requests by declining on the target
Common.WireCancellationToComplete(
- dataflowBlockOptions.CancellationToken, _lazyCompletionTaskSource.Task, state => ((WriteOnceBlock<T>)state).Complete(), this);
+ dataflowBlockOptions.CancellationToken, _lazyCompletionTaskSource.Task, state => ((WriteOnceBlock<T>)state!).Complete(), this);
}
}
#if FEATURE_TRACING
/// <remarks>
/// This must only be called once all of the completion conditions are met.
/// </remarks>
- private void CompleteBlockAsync(IList<Exception> exceptions)
+ private void CompleteBlockAsync(IList<Exception>? 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.");
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<T>)state).OfferToTargetsAndCompleteBlock(), this,
+ var taskForOutputProcessing = new Task(state => ((WriteOnceBlock<T>)state!).OfferToTargetsAndCompleteBlock(), this,
Common.GetCreationOptionsForTask());
#if FEATURE_TRACING
#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
// Complete the block asynchronously to avoid blocking the caller
Task.Factory.StartNew(state =>
{
- Tuple<WriteOnceBlock<T>, IList<Exception>> blockAndList = (Tuple<WriteOnceBlock<T>, IList<Exception>>)state;
+ Tuple<WriteOnceBlock<T>, IList<Exception>> blockAndList = (Tuple<WriteOnceBlock<T>, IList<Exception>>)state!;
blockAndList.Item1.CompleteBlock(blockAndList.Item2);
},
Tuple.Create(this, exceptions), CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
// 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<Exception> exceptions = OfferToTargets();
+ List<Exception>? exceptions = OfferToTargets();
CompleteBlock(exceptions);
}
/// <remarks>
/// This is called only once.
/// </remarks>
- private void CompleteBlock(IList<Exception> exceptions)
+ private void CompleteBlock(IList<Exception>? 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
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<T>.LinkedTargetInfo linkedTargets = _targetRegistry.ClearEntryPoints();
+ TargetRegistry<T>.LinkedTargetInfo? linkedTargets = _targetRegistry.ClearEntryPoints();
// Complete the block's completion task
if (exceptions != null && exceptions.Count > 0)
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.");
// there's nothing more this block needs to do... complete it if we just reserved completion.
if (thisThreadReservedCompletion)
{
- List<Exception> exceptions = null;
+ List<Exception>? exceptions = null;
if (exception != null)
{
exceptions = new List<Exception>();
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- public bool TryReceive(Predicate<T> filter, out T item)
+ public bool TryReceive(Predicate<T>? 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.
// 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
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- bool IReceivableSourceBlock<T>.TryReceiveAll(out IList<T> items)
+ bool IReceivableSourceBlock<T>.TryReceiveAll([NotNullWhen(true)] out IList<T>? items)
{
// Try to receive the one item this block may have.
// If we can, give back an array of one item. Otherwise,
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.
public Task Completion { get { return CompletionTaskSource.Task; } }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, bool consumeToAccept)
+ DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T>? source, bool consumeToAccept)
{
// Validate arguments
if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader));
if (consumeToAccept)
{
bool consumed;
- messageValue = source.ConsumeMessage(messageHeader, this, out consumed);
+ messageValue = source!.ConsumeMessage(messageHeader, this, out consumed);
if (!consumed) return DataflowMessageStatus.NotAvailable;
}
if (_header.Id == messageHeader.Id)
{
messageConsumed = true;
- return CloneItem(_value);
+ return CloneItem(_value!);
}
else
{
messageConsumed = false;
- return default(T);
+ return default(T)!;
}
}
// 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);
}
/// <summary>Clones the item.</summary>
}
/// <summary>Offers the WriteOnceBlock's message to all targets.</summary>
- private List<Exception> OfferToTargets()
+ private List<Exception>? 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<Exception> exceptions = null;
+ List<Exception>? exceptions = null;
if (HasValue)
{
- TargetRegistry<T>.LinkedTargetInfo cur = _targetRegistry.FirstTargetNode;
+ TargetRegistry<T>.LinkedTargetInfo? cur = _targetRegistry.FirstTargetNode;
while (cur != null)
{
- TargetRegistry<T>.LinkedTargetInfo next = cur.Next;
+ TargetRegistry<T>.LinkedTargetInfo? next = cur.Next;
ITargetBlock<T> target = cur.Target;
try
{
// 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;
/// <summary>Gets whether the block is storing a value.</summary>
private bool HasValue { get { return _header.IsValid; } }
/// <summary>Gets the value being stored by the block.</summary>
+ [MaybeNull]
private T Value { get { return _header.IsValid ? _value : default(T); } }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="ToString"]/*' />
/// <summary>Gets whether the WriteOnceBlock has a value.</summary>
public bool HasValue { get { return _writeOnceBlock.HasValue; } }
/// <summary>Gets the WriteOnceBlock's value if it has one, or default(T) if it doesn't.</summary>
+ [MaybeNull]
public T Value { get { return _writeOnceBlock.Value; } }
/// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
/// <summary>Second state argument.</summary>
private readonly T2 _arg2;
/// <summary>The action to run when disposed. Null if disposed.</summary>
- private Action<T1, T2> _action;
+ private Action<T1, T2>? _action;
/// <summary>Initializes the ActionOnDispose.</summary>
/// <param name="action">The action to run when disposed.</param>
/// <summary>Invoke the action.</summary>
void IDisposable.Dispose()
{
- Action<T1, T2> toRun = _action;
+ Action<T1, T2>? toRun = _action;
if (toRun != null &&
Interlocked.CompareExchange(ref _action, null, toRun) == toRun)
{
/// <summary>Third state argument.</summary>
private readonly T3 _arg3;
/// <summary>The action to run when disposed. Null if disposed.</summary>
- private Action<T1, T2, T3> _action;
+ private Action<T1, T2, T3>? _action;
/// <summary>Initializes the ActionOnDispose.</summary>
/// <param name="action">The action to run when disposed.</param>
/// <summary>Invoke the action.</summary>
void IDisposable.Dispose()
{
- Action<T1, T2, T3> toRun = _action;
+ Action<T1, T2, T3>? toRun = _action;
if (toRun != null &&
Interlocked.CompareExchange(ref _action, null, toRun) == toRun)
{
/// <param name="stateOut">Output state for the predicate in order to avoid closure allocations.</param>
/// <returns>True if the predicate was evaluated and it returned true. False otherwise.</returns>
internal static bool TryKeepAliveUntil<TStateIn, TStateOut>(KeepAlivePredicate<TStateIn, TStateOut> 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;
/// <typeparam name="T">The type of the data to be unwrapped.</typeparam>
/// <param name="state">The weak reference.</param>
/// <returns>The T instance.</returns>
+ [return: MaybeNull]
internal static T UnwrapWeakReference<T>(object state) where T : class
{
var wr = state as WeakReference<T>;
Debug.Assert(wr != null, "Expected a WeakReference<T> as the state argument");
- T item;
- return wr.TryGetTarget(out item) ? item : null;
+ return wr.TryGetTarget(out T? item) ? item : null;
}
/// <summary>Gets an ID for the dataflow block.</summary>
{
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;
}
/// <returns>The name of the object.</returns>
/// <remarks>This is used from DebuggerDisplay attributes.</remarks>
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.");
/// <param name="completeAction">An action that will decline permanently on the state passed to it.</param>
/// <param name="completeState">The block on which to decline permanently.</param>
internal static void WireCancellationToComplete(
- CancellationToken cancellationToken, Task completionTask, Action<object> completeAction, object completeState)
+ CancellationToken cancellationToken, Task completionTask, Action<object?> 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.");
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);
}
}
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
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);
}
/// <summary>Adds the exception to the list, first initializing the list if the list is null.</summary>
/// <param name="exception">The exception to add or whose inner exception(s) should be added.</param>
/// <param name="unwrapInnerExceptions">Unwrap and add the inner exception(s) rather than the specified exception directly.</param>
/// <remarks>This method is not thread-safe, in that it manipulates <paramref name="list"/> without any synchronization.</remarks>
- internal static void AddException(ref List<Exception> list, Exception exception, bool unwrapInnerExceptions = false)
+ internal static void AddException([NotNull] ref List<Exception>? 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<Exception>();
+ list ??= new List<Exception>();
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);
private static TaskCompletionSource<T> CreateCachedTaskCompletionSource<T>()
{
var tcs = new TaskCompletionSource<T>();
- tcs.SetResult(default(T));
+ tcs.SetResult(default(T)!);
return tcs;
}
/// <summary>Gets the completion task of a block, and protects against common cases of the completion task not being implemented or supported.</summary>
/// <param name="block">The block.</param>
/// <returns>The completion task, or null if the block's completion task is not implemented or supported.</returns>
- 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
/// <param name="task">Task to be started.</param>
/// <param name="scheduler">TaskScheduler to schedule the task on.</param>
/// <returns>null on success, an exception reference on scheduling error. In the latter case, the task reference is nulled out.</returns>
- 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.");
/// <param name="task">Task to be started.</param>
/// <param name="scheduler">TaskScheduler to schedule the task on.</param>
/// <returns>null on success, an exception reference on scheduling error. In the latter case, the task reference is nulled out.</returns>
- 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
{
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;
}
/// <remarks>No locks should be held at this time. Unfortunately we cannot assert that.</remarks>
internal static void ReleaseAllPostponedMessages<T>(ITargetBlock<T> target,
QueuedMap<ISourceBlock<T>, DataflowMessageHeader> postponedMessages,
- ref List<Exception> exceptions)
+ ref List<Exception>? exceptions)
{
Debug.Assert(target != null, "There must be a subject target.");
Debug.Assert(postponedMessages != null, "The stacked map of postponed messages must exist.");
/// <param name="sourceCompletionTask">The task whose completion is to be propagated. It must be completed.</param>
/// <param name="target">The block where completion is propagated.</param>
/// <param name="exceptionHandler">Handler for exceptions from the target. May be null which would propagate the exception to the caller.</param>
- internal static void PropagateCompletion(Task sourceCompletionTask, IDataflowBlock target, Action<Exception> exceptionHandler)
+ internal static void PropagateCompletion(Task sourceCompletionTask, IDataflowBlock target, Action<Exception>? 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
{
{
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);
}
private static class CachedGenericDelegates<T>
{
/// <summary>A function that returns the default value of T.</summary>
- internal static readonly Func<T> DefaultTResultFunc = () => default(T);
+ internal static readonly Func<T> DefaultTResultFunc = () => default(T)!;
/// <summary>
/// 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.
internal class BoundingStateWithPostponedAndTask<TInput> : BoundingStateWithPostponed<TInput>
{
/// <summary>The task used to process messages.</summary>
- internal Task TaskForInputProcessing;
+ internal Task? TaskForInputProcessing;
/// <summary>Initializes the BoundingState.</summary>
/// <param name="boundedCapacity">The positive bounded capacity.</param>
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+#nullable disable // old copy used only for netstandard1.0 build
using System;
using System.Collections;
using System.Collections.Generic;
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+#nullable disable // copy used only for netstandard1.0 build
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
#endif
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Internal;
/// <param name="result">The dequeued item.</param>
/// <returns>true if an item could be dequeued; otherwise, false.</returns>
/// <remarks>This method is meant to be thread-safe subject to the particular nature of the implementation.</remarks>
- bool TryDequeue(out T result);
+ bool TryDequeue([MaybeNullWhen(false)] out T result);
/// <summary>Gets whether the collection is currently empty.</summary>
/// <remarks>This method may or may not be thread-safe.</remarks>
/// <summary>Attempts to dequeue an item from the queue.</summary>
/// <param name="result">The dequeued item.</param>
/// <returns>true if an item could be dequeued; otherwise, false.</returns>
- public bool TryDequeue(out T result)
+ public bool TryDequeue([MaybeNullWhen(false)] out T result)
{
Segment segment = _head;
T[] array = segment._array;
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;
}
/// <param name="segment">The segment from which the item was dequeued.</param>
/// <param name="result">The dequeued item.</param>
/// <returns>true if an item could be dequeued; otherwise, false.</returns>
- 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.");
}
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
/// <param name="segment">The segment from which the item is peeked.</param>
/// <param name="result">The peeked item.</param>
/// <returns>true if an item could be peeked; otherwise, false.</returns>
- 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.");
/// <param name="predicate">The predicate that must return true for the item to be dequeued. If null, all items implicitly return true.</param>
/// <param name="result">The dequeued item.</param>
/// <returns>true if an item could be dequeued; otherwise, false.</returns>
- public bool TryDequeueIf(Predicate<T> predicate, out T result)
+ public bool TryDequeueIf(Predicate<T>? predicate, [MaybeNullWhen(false)] out T result)
{
Segment segment = _head;
T[] array = segment._array;
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;
}
/// <param name="segment">The segment from which the item was dequeued.</param>
/// <param name="result">The dequeued item.</param>
/// <returns>true if an item could be dequeued; otherwise, false.</returns>
- private bool TryDequeueIfSlow(Predicate<T> predicate, ref Segment segment, ref T[] array, out T result)
+ private bool TryDequeueIfSlow(Predicate<T>? 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.");
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;
/// <remarks>WARNING: This should only be used for debugging purposes. It is not safe to be used concurrently.</remarks>
public IEnumerator<T> 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;
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;
private sealed class Segment
{
/// <summary>The next segment in the linked list of segments.</summary>
- internal Segment _next;
+ internal Segment? _next;
/// <summary>The data stored in this segment.</summary>
internal readonly T[] _array;
/// <summary>Details about the segment.</summary>
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
namespace System.Threading.Tasks.Dataflow.Internal
{
/// <remarks>This type is not thread-safe.</remarks>
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(EnumerableDebugView<,>))]
- internal sealed class QueuedMap<TKey, TValue>
+ internal sealed class QueuedMap<TKey, TValue> where TKey: notnull
{
/// <summary>
/// A queue structure that uses an array-based list to store its items
/// <summary>Tries to dequeue an item.</summary>
/// <param name="item">The item that is dequeued.</param>
- 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)
// Move the popped slot to the head of the free list
int newHeadIndex = _storage[_headIndex].Key;
- _storage[_headIndex] = new KeyValuePair<int, T>(_freeIndex, default(T));
+ _storage[_headIndex] = new KeyValuePair<int, T>(_freeIndex, default(T)!);
_freeIndex = _headIndex;
_headIndex = newHeadIndex;
if (_headIndex == TERMINATOR_INDEX) _tailIndex = TERMINATOR_INDEX;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace System.Threading.Tasks.Dataflow.Internal
/// <param name="id">The ID of the item.</param>
/// <param name="item">The completed item.</param>
/// <param name="itemIsValid">Specifies whether the item is valid (true) or just a placeholder (false).</param>
- 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);
else
{
Debug.Assert((ulong)id > (ulong)_nextReorderedIdToOutput, "Duplicate id.");
- _reorderingBuffer.Add(id, new KeyValuePair<bool, TOutput>(itemIsValid, item));
+ _reorderingBuffer.Add(id, new KeyValuePair<bool, TOutput>(itemIsValid, item!));
}
}
}
/// 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.
/// </returns>
- 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);
/// <param name="id">The id of the message to be ignored.</param>
public void IgnoreItem(long id)
{
- AddItem(id, default(TOutput), itemIsValid: false);
+ AddItem(id, default(TOutput)!, itemIsValid: false);
}
/// <summary>Outputs the item. The item must have already been confirmed to have the next ID.</summary>
/// <param name="theNextItem">The item to output.</param>
/// <param name="itemIsValid">Whether the item is valid.</param>
- 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.
/// 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.
/// </summary>
- private readonly Action<ISourceBlock<TOutput>, int> _itemsRemovedAction;
+ private readonly Action<ISourceBlock<TOutput>, int>? _itemsRemovedAction;
/// <summary>Item counting function</summary>
- private readonly Func<ISourceBlock<TOutput>, TOutput, IList<TOutput>, int> _itemCountingFunc;
+ private readonly Func<ISourceBlock<TOutput>, TOutput, IList<TOutput>?, int>? _itemCountingFunc;
// *** These fields are mutated during execution.
/// <summary>The task used to process the output and offer it to targets.</summary>
- private Task _taskForOutputProcessing; // protected by ValueLock
+ private Task? _taskForOutputProcessing; // protected by ValueLock
/// <summary>Counter for message IDs unique within this source block.</summary>
private PaddedInt64 _nextMessageId = new PaddedInt64 { Value = 1 }; // We are going to use this value before incrementing. Protected by ValueLock.
/// <summary>The target that the next message is reserved for, or null if nothing is reserved.</summary>
- private ITargetBlock<TOutput> _nextMessageReservedFor; // protected by OutgoingLock
+ private ITargetBlock<TOutput>? _nextMessageReservedFor; // protected by OutgoingLock
/// <summary>Whether all future messages should be declined.</summary>
private bool _decliningPermanently; // Protected by ValueLock
/// <summary>Whether this block should again attempt to offer messages to targets.</summary>
/// <summary>Whether someone has reserved the right to call CompleteBlockOncePossible.</summary>
private bool _completionReserved; // Protected by OutgoingLock
/// <summary>Exceptions that may have occurred and gone unhandled during processing.</summary>
- private List<Exception> _exceptions; // Protected by ValueLock, sometimes read with volatile reads
+ private List<Exception>? _exceptions; // Protected by ValueLock, sometimes read with volatile reads
/// <summary>Initializes the source core.</summary>
/// <param name="owningSource">The source utilizing this core.</param>
internal SourceCore(
ISourceBlock<TOutput> owningSource, DataflowBlockOptions dataflowBlockOptions,
Action<ISourceBlock<TOutput>> completeAction,
- Action<ISourceBlock<TOutput>, int> itemsRemovedAction = null,
- Func<ISourceBlock<TOutput>, TOutput, IList<TOutput>, int> itemCountingFunc = null)
+ Action<ISourceBlock<TOutput>, int>? itemsRemovedAction = null,
+ Func<ISourceBlock<TOutput>, TOutput, IList<TOutput>?, 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.");
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
internal TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out bool messageConsumed)
{
// Validate arguments
internal Task Completion { get { return _completionTask.Task; } }
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
- internal bool TryReceive(Predicate<TOutput> filter, out TOutput item)
+ internal bool TryReceive(Predicate<TOutput>? filter, [MaybeNullWhen(false)] out TOutput item)
{
item = default(TOutput);
bool itemReceived = false;
// 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);
}
}
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
- internal bool TryReceiveAll(out IList<TOutput> items)
+ internal bool TryReceiveAll([NotNullWhen(true)] out IList<TOutput>? items)
{
items = null;
int countReceived = 0;
// 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;
}
}
else
{
- TOutput[] itemsAsArray = items as TOutput[];
+ TOutput[]? itemsAsArray = items as TOutput[];
if (itemsAsArray != null)
{
for (int i = 0; i < itemsAsArray.Length; i++)
// 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<TOutput>)state;
+ var thisSourceCore = (SourceCore<TOutput>)state!;
lock (thisSourceCore.OutgoingLock)
{
lock (thisSourceCore.ValueLock)
/// The newly linked target, if OfferToTargets is being called to synchronously
/// propagate to a target during a LinkTo operation.
/// </param>
- private bool OfferToTargets(ITargetBlock<TOutput> linkToTarget = null)
+ private bool OfferToTargets(ITargetBlock<TOutput>? linkToTarget = null)
{
Common.ContractAssertMonitorStatus(OutgoingLock, held: true);
Common.ContractAssertMonitorStatus(ValueLock, held: false);
// 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<TOutput>.LinkedTargetInfo cur = _targetRegistry.FirstTargetNode;
+ TargetRegistry<TOutput>.LinkedTargetInfo? cur = _targetRegistry.FirstTargetNode;
while (cur != null)
{
- TargetRegistry<TOutput>.LinkedTargetInfo next = cur.Next;
+ TargetRegistry<TOutput>.LinkedTargetInfo? next = cur.Next;
if (OfferMessageToTarget(header, message, cur.Target, out messageWasAccepted)) break;
cur = next;
}
{
// 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<TOutput>)thisSourceCore).OfferMessagesLoopCore(), this,
+ _taskForOutputProcessing = new Task(thisSourceCore => ((SourceCore<TOutput>)thisSourceCore!).OfferMessagesLoopCore(), this,
Common.GetCreationOptionsForTask(isReplacementReplica));
#if FEATURE_TRACING
#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.
// Re-take the locks on a separate thread.
Task.Factory.StartNew(state =>
{
- var thisSourceCore = (SourceCore<TOutput>)state;
+ var thisSourceCore = (SourceCore<TOutput>)state!;
lock (thisSourceCore.OutgoingLock)
{
lock (thisSourceCore.ValueLock)
// 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<TOutput>)state).CompleteBlockOncePossible(),
+ Task.Factory.StartNew(state => ((SourceCore<TOutput>)state!).CompleteBlockOncePossible(),
this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
}
}
/// </summary>
private void CompleteBlockOncePossible()
{
- TargetRegistry<TOutput>.LinkedTargetInfo linkedTargets;
- List<Exception> exceptions;
+ TargetRegistry<TOutput>.LinkedTargetInfo? linkedTargets;
+ List<Exception>? 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.
/// <summary>Gets the messages available for receiving.</summary>
internal IEnumerable<TOutput> OutputQueue { get { return _source._messages.ToList(); } }
/// <summary>Gets the task being used for output processing.</summary>
- internal Task TaskForOutputProcessing { get { return _source._taskForOutputProcessing; } }
+ internal Task? TaskForOutputProcessing { get { return _source._taskForOutputProcessing; } }
/// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
internal DataflowBlockOptions DataflowBlockOptions { get { return _source._dataflowBlockOptions; } }
/// <summary>Gets the set of all targets linked from this block.</summary>
internal TargetRegistry<TOutput> LinkedTargets { get { return _source._targetRegistry; } }
/// <summary>Gets the target that holds a reservation on the next message, if any.</summary>
- internal ITargetBlock<TOutput> NextMessageReservedFor { get { return _source._nextMessageReservedFor; } }
+ internal ITargetBlock<TOutput>? NextMessageReservedFor { get { return _source._nextMessageReservedFor; } }
}
}
}
private readonly Action<TInput> _action;
/// <summary>Exceptions that may have occurred and gone unhandled during processing. This field is lazily initialized.</summary>
- private volatile List<Exception> _exceptions;
+ private volatile List<Exception>? _exceptions;
/// <summary>Whether to stop accepting new messages.</summary>
private volatile bool _decliningPermanently;
/// <summary>A task has reserved the right to run the completion routine.</summary>
/// 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.
/// </summary>
- private volatile Task _activeConsumer;
+ private volatile Task? _activeConsumer;
/// <summary>A task representing the completion of the block. This field is lazily initialized.</summary>
- private TaskCompletionSource<VoidResult> _completionTask;
+ private TaskCompletionSource<VoidResult>? _completionTask;
/// <summary>Initialize the SPSC target core.</summary>
/// <param name="owningTarget">The owning target block.</param>
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, bool consumeToAccept)
+ internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput>? 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) ?
/// <param name="source">The source offering the message. This may be null.</param>
/// <param name="consumeToAccept">true if we need to call back to the source to consume the message; otherwise, false if we can simply accept it directly.</param>
/// <returns>The status of the message.</returns>
- private DataflowMessageStatus OfferMessage_Slow(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, bool consumeToAccept)
+ private DataflowMessageStatus OfferMessage_Slow(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput>? source, bool consumeToAccept)
{
// If we're declining permanently, let the caller know.
if (_decliningPermanently)
}
// 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)
{
{
// 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<TInput>)state).ProcessMessagesLoopCore(),
+ state => ((SpscTargetCore<TInput>)state!).ProcessMessagesLoopCore(),
this, CancellationToken.None, Common.GetCreationOptionsForTask(isReplica));
if (Interlocked.CompareExchange(ref _activeConsumer, newConsumer, null) == null)
{
if (!Common.IsCooperativeCancellation(exc))
{
_decliningPermanently = true; // stop accepting from producers
- Common.StoreDataflowMessageValueIntoExceptionData<TInput>(exc, nextMessage, false);
+ Common.StoreDataflowMessageValueIntoExceptionData<TInput>(exc, nextMessage!, false);
StoreException(exc);
}
}
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.");
/// completing, all invocations after the first are ignored.
/// </summary>
/// <param name="exception">The exception to be stored.</param>
- internal void Complete(Exception exception)
+ internal void Complete(Exception? exception)
{
// If we're not yet declining permanently...
if (!_decliningPermanently)
/// <summary>Whether the block relies on the delegate to signal when an async operation has completed.</summary>
private readonly TargetCoreOptions _targetCoreOptions;
/// <summary>Bounding state for when the block is executing in bounded mode.</summary>
- private readonly BoundingStateWithPostponed<TInput> _boundingState;
+ private readonly BoundingStateWithPostponed<TInput>? _boundingState;
/// <summary>The reordering buffer used by the owner. May be null.</summary>
- private readonly IReorderingBuffer _reorderingBuffer;
+ private readonly IReorderingBuffer? _reorderingBuffer;
/// <summary>Gets the object used as the incoming lock.</summary>
private object IncomingLock { get { return _messages; } }
// *** These fields are mutated during execution.
/// <summary>Exceptions that may have occurred and gone unhandled during processing.</summary>
- private List<Exception> _exceptions;
+ private List<Exception>? _exceptions;
/// <summary>Whether to stop accepting new messages.</summary>
private bool _decliningPermanently;
/// <summary>The number of operations (including service tasks) currently running asynchronously.</summary>
internal TargetCore(
ITargetBlock<TInput> owningTarget,
Action<KeyValuePair<TInput, long>> callAction,
- IReorderingBuffer reorderingBuffer,
+ IReorderingBuffer? reorderingBuffer,
ExecutionDataflowBlockOptions dataflowBlockOptions,
TargetCoreOptions targetCoreOptions)
{
/// <param name="storeExceptionEvenIfAlreadyCompleting">If true, an exception will be stored after _decliningPermanently has been set to true.</param>
/// <param name="unwrapInnerExceptions">If true, exception will be treated as an AggregateException.</param>
/// <param name="revertProcessingState">Indicates whether the processing state is dirty and has to be reverted.</param>
- 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,
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, bool consumeToAccept)
+ internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput>? source, bool consumeToAccept)
{
// Validate arguments
if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, nameof(messageHeader));
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<TInput, long>(messageValue, messageId));
+ _messages.Enqueue(new KeyValuePair<TInput, long>(messageValue!, messageId));
ProcessAsyncIfNecessary();
return DataflowMessageStatus.Accepted;
}
_numberOfOutstandingOperations++;
if (UsesAsyncCompletion) _numberOfOutstandingServiceTasks++;
- var taskForInputProcessing = new Task(thisTargetCore => ((TargetCore<TInput>)thisTargetCore).ProcessMessagesLoopCore(), this,
+ var taskForInputProcessing = new Task(thisTargetCore => ((TargetCore<TInput>)thisTargetCore!).ProcessMessagesLoopCore(), this,
Common.GetCreationOptionsForTask(repeat));
#if FEATURE_TRACING
#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);
}
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
// 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)
{
TInput consumedValue = element.Key.ConsumeMessage(element.Value, _owningTarget, out consumed);
if (consumed)
{
- result = new KeyValuePair<TInput, long>(consumedValue, messageId);
+ result = new KeyValuePair<TInput, long>(consumedValue!, messageId);
return true;
}
else
// 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<TInput>)state).CompleteBlockOncePossible(),
+ Task.Factory.StartNew(state => ((TargetCore<TInput>)state!).CompleteBlockOncePossible(),
this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
}
}
// 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)
internal IEnumerable<TInput> InputQueue { get { return _target._messages.Select(kvp => kvp.Key).ToList(); } }
/// <summary>Gets any postponed messages.</summary>
- internal QueuedMap<ISourceBlock<TInput>, DataflowMessageHeader> PostponedMessages
+ internal QueuedMap<ISourceBlock<TInput>, DataflowMessageHeader>? PostponedMessages
{
get { return _target._boundingState != null ? _target._boundingState.PostponedMessages : null; }
}
/// gets decremented after each successful propagation.</summary>
internal int RemainingMessages;
/// <summary>The previous node in the list.</summary>
- internal LinkedTargetInfo Previous;
+ internal LinkedTargetInfo? Previous;
/// <summary>The next node in the list.</summary>
- internal LinkedTargetInfo Next;
+ internal LinkedTargetInfo? Next;
}
/// <summary>A reference to the owning source block.</summary>
/// <summary>A mapping of targets to information about them.</summary>
private readonly Dictionary<ITargetBlock<T>, LinkedTargetInfo> _targetInformation;
/// <summary>The first node of an ordered list of targets. Messages should be offered to targets starting from First and following Next.</summary>
- private LinkedTargetInfo _firstTarget;
+ private LinkedTargetInfo? _firstTarget;
/// <summary>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.</summary>
- private LinkedTargetInfo _lastTarget;
+ private LinkedTargetInfo? _lastTarget;
/// <summary>Number of links with positive RemainingMessages counters.
/// This is an optimization that allows us to skip dictionary lookup when this counter is 0.</summary>
private int _linksWithRemainingMessages;
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);
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.");
}
/// <summary>Clears the target registry entry points while allowing subsequent traversals of the linked list.</summary>
- 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;
/// <summary>Propagated completion to the targets of the given linked list.</summary>
/// <param name="firstTarget">The head of a saved linked list.</param>
- internal void PropagateCompletion(LinkedTargetInfo firstTarget)
+ internal void PropagateCompletion(LinkedTargetInfo? firstTarget)
{
Debug.Assert(_owningSource.Completion.IsCompleted, "The owning source must have completed before propagating completion.");
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);
}
}
/// <summary>Gets the first node of the ordered target list.</summary>
- internal LinkedTargetInfo FirstTargetNode { get { return _firstTarget; } }
+ internal LinkedTargetInfo? FirstTargetNode { get { return _firstTarget; } }
/// <summary>Adds a LinkedTargetInfo node to the doubly-linked list.</summary>
/// <param name="node">The node to be added.</param>
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)
{
var targets = new ITargetBlock<T>[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;
}
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
- DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, bool consumeToAccept)
+ DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T>? 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);
}
/// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+ [return: MaybeNull]
T ISourceBlock<T>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target, out bool messageConsumed)
{
return _owningSource.ConsumeMessage(messageHeader, this, out messageConsumed);
// 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;
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DefineConstants Condition="'$(TargetFramework)' == 'netstandard1.1' or '$(TargetFramework)' == 'netstandard2.0'">$(DefineConstants);FEATURE_TRACING</DefineConstants>
<DefineConstants Condition="'$(TargetFramework)' == 'netstandard1.0'">$(DefineConstants);USE_INTERNAL_CONCURRENT_COLLECTIONS</DefineConstants>
<DefineConstants Condition="'$(TargetFramework)' == 'netstandard1.0' or '$(TargetFramework)' == 'netstandard1.1'">$(DefineConstants);USE_INTERNAL_THREADING</DefineConstants>
<PackageTargetFramework Condition="'$(TargetFramework)' == 'netstandard1.1'">netstandard1.1;portable-net45+win8+wpa81</PackageTargetFramework>
<TargetFrameworks>netstandard2.0;netstandard1.0;netstandard1.1</TargetFrameworks>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Include="Base\DataflowBlock.cs" />
}
// Validate sane behavior with a bad LinkTo
- Assert.Null(
- new DelegatePropagator<int, int> {
- LinkToDelegate = (_,__) => null
- }.AsObservable().Subscribe(DataflowBlock.NullTarget<int>().AsObserver()));
+ new DelegatePropagator<int, int>
+ {
+ LinkToDelegate = (_, __) => null
+ }.AsObservable().Subscribe(DataflowBlock.NullTarget<int>().AsObserver()).Dispose();
}
[Fact]