Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@ -0,0 +1,54 @@
//
// AssemblyInfo.cs
//
// Authors:
// Marek Safar (marek.safar@gmail.com)
//
// Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;
// General Information about the assembly
[assembly: AssemblyTitle ("System.Threading.Tasks.Dataflow.dll")]
[assembly: AssemblyDescription ("System.Threading.Tasks.Dataflow.dll")]
[assembly: AssemblyDefaultAlias ("System.Threading.Tasks.Dataflow.dll")]
[assembly: AssemblyCompany (Consts.MonoCompany)]
[assembly: AssemblyProduct (Consts.MonoProduct)]
[assembly: AssemblyCopyright (Consts.MonoCopyright)]
[assembly: AssemblyVersion (Consts.FxVersion)]
[assembly: SatelliteContractVersion (Consts.FxVersion)]
[assembly: AssemblyInformationalVersion (Consts.FxFileVersion)]
[assembly: AssemblyFileVersion (Consts.FxFileVersion)]
[assembly: NeutralResourcesLanguage ("en-US")]
[assembly: CLSCompliant (true)]
[assembly: AssemblyDelaySign (true)]
[assembly: AssemblyKeyFile ("../ecma.pub")]
[assembly: ComVisible (false)]

View File

@ -0,0 +1,12 @@
thisdir = class/System.Threading.Tasks.Dataflow
SUBDIRS =
include ../../build/rules.make
LIBRARY = System.Threading.Tasks.Dataflow.dll
include ../../build/library.make
LIB_MCS_FLAGS += -r:$(corlib) -r:System.Core.dll -r:System.dll
TEST_MCS_FLAGS = -r:System.Core.dll -r:System.dll

View File

@ -0,0 +1,49 @@
../../build/common/Consts.cs
../../build/common/Locale.cs
../../build/common/MonoTODOAttribute.cs
Assembly/AssemblyInfo.cs
System.Threading.Tasks.Dataflow/ExecutingMessageBox.cs
System.Threading.Tasks.Dataflow/DataflowBlockOptions.cs
System.Threading.Tasks.Dataflow/DataflowMessageHeader.cs
System.Threading.Tasks.Dataflow/DataflowMessageStatus.cs
System.Threading.Tasks.Dataflow/ExecutionDataflowBlockOptions.cs
System.Threading.Tasks.Dataflow/GroupingDataflowBlockOptions.cs
System.Threading.Tasks.Dataflow/DataflowLinkOptions.cs
System.Threading.Tasks.Dataflow/IDataflowBlock.cs
System.Threading.Tasks.Dataflow/IPropagatorBlock.cs
System.Threading.Tasks.Dataflow/IReceivableSourceBlock.cs
System.Threading.Tasks.Dataflow/ISourceBlock.cs
System.Threading.Tasks.Dataflow/ITargetBlock.cs
System.Threading.Tasks.Dataflow/CompletionHelper.cs
System.Threading.Tasks.Dataflow/MessageBox.cs
System.Threading.Tasks.Dataflow/OutgoingQueueBase.cs
System.Threading.Tasks.Dataflow/OutgoingQueue.cs
System.Threading.Tasks.Dataflow/BroadcastOutgoingQueue.cs
System.Threading.Tasks.Dataflow/PassingMessageBox.cs
System.Threading.Tasks.Dataflow/NameHelper.cs
System.Threading.Tasks.Dataflow/TargetCollection.cs
System.Threading.Tasks.Dataflow/JoinTarget.cs
../corlib/System.Threading/AtomicBoolean.cs
System.Threading.Tasks.Dataflow/ActionBlock.cs
System.Threading.Tasks.Dataflow/BatchBlock.cs
System.Threading.Tasks.Dataflow/BatchedJoinBlock.cs
System.Threading.Tasks.Dataflow/BatchedJoinBlock`3.cs
System.Threading.Tasks.Dataflow/BroadcastBlock.cs
System.Threading.Tasks.Dataflow/BufferBlock.cs
System.Threading.Tasks.Dataflow/ChooserBlock.cs
System.Threading.Tasks.Dataflow/DataflowBlock.cs
System.Threading.Tasks.Dataflow/JoinBlock.cs
System.Threading.Tasks.Dataflow/JoinBlock`3.cs
System.Threading.Tasks.Dataflow/ObservableDataflowBlock.cs
System.Threading.Tasks.Dataflow/ObserverDataflowBlock.cs
System.Threading.Tasks.Dataflow/PropagatorWrapperBlock.cs
System.Threading.Tasks.Dataflow/ReceiveBlock.cs
System.Threading.Tasks.Dataflow/TransformBlock.cs
System.Threading.Tasks.Dataflow/TransformManyBlock.cs
System.Threading.Tasks.Dataflow/WriteOnceBlock.cs
System.Threading.Tasks.Dataflow/SendBlock.cs
System.Threading.Tasks.Dataflow/PredicateBlock.cs
System.Threading.Tasks.Dataflow/OutputAvailableBlock.cs
System.Threading.Tasks.Dataflow/NullTargetBlock.cs
System.Threading.Tasks.Dataflow/AsyncExecutingMessageBox.cs
System.Threading.Tasks.Dataflow/ExecutingMessageBoxBase.cs

View File

@ -0,0 +1,150 @@
// ActionBlock.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System.Collections.Concurrent;
namespace System.Threading.Tasks.Dataflow {
public sealed class ActionBlock<TInput> : ITargetBlock<TInput> {
readonly CompletionHelper compHelper;
readonly BlockingCollection<TInput> messageQueue = new BlockingCollection<TInput> ();
readonly ExecutingMessageBoxBase<TInput> messageBox;
readonly Action<TInput> action;
readonly Func<TInput, Task> asyncAction;
readonly ExecutionDataflowBlockOptions dataflowBlockOptions;
ActionBlock (ExecutionDataflowBlockOptions dataflowBlockOptions)
{
if (dataflowBlockOptions == null)
throw new ArgumentNullException ("dataflowBlockOptions");
this.dataflowBlockOptions = dataflowBlockOptions;
this.compHelper = new CompletionHelper (dataflowBlockOptions);
}
public ActionBlock (Action<TInput> action)
: this (action, ExecutionDataflowBlockOptions.Default)
{
}
public ActionBlock (Action<TInput> action,
ExecutionDataflowBlockOptions dataflowBlockOptions)
: this (dataflowBlockOptions)
{
if (action == null)
throw new ArgumentNullException ("action");
this.action = action;
this.messageBox = new ExecutingMessageBox<TInput> (this, messageQueue, compHelper,
() => true, ProcessItem, () => { }, dataflowBlockOptions);
}
public ActionBlock (Func<TInput, Task> action)
: this (action, ExecutionDataflowBlockOptions.Default)
{
}
public ActionBlock (Func<TInput, Task> action,
ExecutionDataflowBlockOptions dataflowBlockOptions)
: this (dataflowBlockOptions)
{
if (action == null)
throw new ArgumentNullException ("action");
this.asyncAction = action;
this.messageBox = new AsyncExecutingMessageBox<TInput, Task> (
this, messageQueue, compHelper, () => true, AsyncProcessItem, null,
() => { }, dataflowBlockOptions);
}
DataflowMessageStatus ITargetBlock<TInput>.OfferMessage (
DataflowMessageHeader messageHeader, TInput messageValue,
ISourceBlock<TInput> source, bool consumeToAccept)
{
return messageBox.OfferMessage (
messageHeader, messageValue, source, consumeToAccept);
}
public bool Post (TInput item)
{
return messageBox.OfferMessage (
new DataflowMessageHeader (1), item, null, false)
== DataflowMessageStatus.Accepted;
}
/// <summary>
/// Processes one item from the queue if the action is synchronous.
/// </summary>
/// <returns>Returns whether an item was processed. Returns <c>false</c> if the queue is empty.</returns>
bool ProcessItem ()
{
TInput data;
bool dequeued = messageQueue.TryTake (out data);
if (dequeued)
action (data);
return dequeued;
}
/// <summary>
/// Processes one item from the queue if the action is asynchronous.
/// </summary>
/// <param name="task">The Task that was returned by the synchronous part of the action.</param>
/// <returns>Returns whether an item was processed. Returns <c>false</c> if the queue was empty.</returns>
bool AsyncProcessItem(out Task task)
{
TInput data;
bool dequeued = messageQueue.TryTake (out data);
if (dequeued)
task = asyncAction (data);
else
task = null;
return dequeued;
}
public void Complete ()
{
messageBox.Complete ();
}
void IDataflowBlock.Fault (Exception exception)
{
compHelper.RequestFault (exception);
}
public Task Completion {
get {
return compHelper.Completion;
}
}
public int InputCount {
get {
return messageQueue.Count;
}
}
public override string ToString ()
{
return NameHelper.GetName (this, dataflowBlockOptions);
}
}
}

View File

@ -0,0 +1,121 @@
// AsyncExecutingMessageBox.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System.Collections.Concurrent;
namespace System.Threading.Tasks.Dataflow {
/// <summary>
/// Message box for executing blocks with asynchrnous
/// (<see cref="Task"/>-returning) actions.
/// </summary>
/// <typeparam name="TInput">Type of the item the block is processing.</typeparam>
/// <typeparam name="TTask">Type of the Task the action is returning.</typeparam>
class AsyncExecutingMessageBox<TInput, TTask>
: ExecutingMessageBoxBase<TInput>
where TTask : Task {
/// <summary>
/// Represents executing synchrnous part of the action.
/// </summary>
/// <param name="task">The Task that was returned by the synchronous part of the action.</param>
/// <returns>Returns whether an item was processed. Returns <c>false</c> if the queue was empty.</returns>
public delegate bool AsyncProcessItem (out TTask task);
readonly AsyncProcessItem processItem;
readonly Action<TTask> processFinishedTask;
public AsyncExecutingMessageBox (
ITargetBlock<TInput> target, BlockingCollection<TInput> messageQueue,
CompletionHelper compHelper, Func<bool> externalCompleteTester,
AsyncProcessItem processItem, Action<TTask> processFinishedTask,
Action outgoingQueueComplete, ExecutionDataflowBlockOptions options)
: base (
target, messageQueue, compHelper, externalCompleteTester,
outgoingQueueComplete, options)
{
this.processItem = processItem;
this.processFinishedTask = processFinishedTask;
}
/// <summary>
/// Processes the input queue of the block.
/// </summary>
protected override void ProcessQueue ()
{
StartProcessQueue ();
ProcessQueueWithoutStart ();
}
/// <summary>
/// The part of <see cref="ProcessQueue"/> specific to asynchronous execution.
/// Handles scheduling continuation on the Task returned by the block's action
/// (or continuing synchrnously if possible).
/// </summary>
void ProcessQueueWithoutStart ()
{
// catch is needed here, if the Task-returning delegate throws exception itself
try {
int i = 0;
while (CanRun (i)) {
TTask task;
if (!processItem (out task))
break;
if (task == null || task.IsCanceled
|| (task.IsCompleted && !task.IsFaulted)) {
if (processFinishedTask != null)
processFinishedTask (task);
} else if (task.IsFaulted) {
CompHelper.RequestFault (task.Exception, false);
break;
} else {
task.ContinueWith (
t => TaskFinished ((TTask)t), Options.TaskScheduler);
return;
}
i++;
}
} catch (Exception e) {
CompHelper.RequestFault (e, false);
}
FinishProcessQueue ();
}
/// <summary>
/// Handles asynchronously finished Task, continues processing the queue.
/// </summary>
void TaskFinished (TTask task)
{
if (task.IsFaulted) {
CompHelper.RequestFault (task.Exception, false);
FinishProcessQueue ();
return;
}
if (processFinishedTask != null)
processFinishedTask (task);
ProcessQueueWithoutStart ();
}
}
}

View File

@ -0,0 +1,376 @@
// BatchBlock.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System.Collections.Generic;
using System.Collections.Concurrent;
namespace System.Threading.Tasks.Dataflow {
public sealed class BatchBlock<T> : IPropagatorBlock<T, T[]>, IReceivableSourceBlock<T[]> {
readonly CompletionHelper compHelper;
readonly BlockingCollection<T> messageQueue = new BlockingCollection<T> ();
readonly MessageBox<T> messageBox;
readonly GroupingDataflowBlockOptions dataflowBlockOptions;
readonly int batchSize;
int batchCount;
long numberOfGroups;
SpinLock batchCountLock;
readonly OutgoingQueue<T[]> outgoing;
SpinLock batchLock;
readonly AtomicBoolean nonGreedyProcessing = new AtomicBoolean ();
public BatchBlock (int batchSize) : this (batchSize, GroupingDataflowBlockOptions.Default)
{
}
public BatchBlock (int batchSize, GroupingDataflowBlockOptions dataflowBlockOptions)
{
if (batchSize <= 0)
throw new ArgumentOutOfRangeException ("batchSize", batchSize,
"The batchSize must be positive.");
if (dataflowBlockOptions == null)
throw new ArgumentNullException ("dataflowBlockOptions");
if (dataflowBlockOptions.BoundedCapacity != -1
&& batchSize > dataflowBlockOptions.BoundedCapacity)
throw new ArgumentOutOfRangeException ("batchSize",
"The batchSize must be smaller than the value of BoundedCapacity.");
this.batchSize = batchSize;
this.dataflowBlockOptions = dataflowBlockOptions;
this.compHelper = CompletionHelper.GetNew (dataflowBlockOptions);
Action<bool> processQueue;
Func<bool> canAccept;
if (dataflowBlockOptions.MaxNumberOfGroups == -1) {
processQueue = newItem => BatchProcess (newItem ? 1 : 0);
canAccept = null;
} else {
processQueue = _ => BatchProcess ();
canAccept = TryAdd;
}
this.messageBox = new PassingMessageBox<T> (this, messageQueue, compHelper,
() => outgoing.IsCompleted, processQueue, dataflowBlockOptions,
dataflowBlockOptions.Greedy, canAccept);
this.outgoing = new OutgoingQueue<T[]> (this, compHelper,
() => messageQueue.IsCompleted, messageBox.DecreaseCount,
dataflowBlockOptions, batch => batch.Length);
}
DataflowMessageStatus ITargetBlock<T>.OfferMessage (
DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source,
bool consumeToAccept)
{
return messageBox.OfferMessage (
messageHeader, messageValue, source, consumeToAccept);
}
public IDisposable LinkTo (ITargetBlock<T[]> target, DataflowLinkOptions linkOptions)
{
return outgoing.AddTarget (target, linkOptions);
}
T[] ISourceBlock<T[]>.ConsumeMessage (
DataflowMessageHeader messageHeader, ITargetBlock<T[]> target,
out bool messageConsumed)
{
return outgoing.ConsumeMessage (messageHeader, target, out messageConsumed);
}
void ISourceBlock<T[]>.ReleaseReservation (
DataflowMessageHeader messageHeader, ITargetBlock<T[]> target)
{
outgoing.ReleaseReservation (messageHeader, target);
}
bool ISourceBlock<T[]>.ReserveMessage (
DataflowMessageHeader messageHeader, ITargetBlock<T[]> target)
{
return outgoing.ReserveMessage (messageHeader, target);
}
public bool TryReceive (Predicate<T[]> filter, out T[] item)
{
return outgoing.TryReceive (filter, out item);
}
public bool TryReceiveAll (out IList<T[]> items)
{
return outgoing.TryReceiveAll (out items);
}
/// <summary>
/// Verifies whether <see cref="GroupingDataflowBlockOptions.MaxNumberOfGroups"/>
/// has been reached. If it did, <see cref="Complete"/>s the block.
/// </summary>
void VerifyMaxNumberOfGroups ()
{
if (dataflowBlockOptions.MaxNumberOfGroups == -1)
return;
bool shouldComplete;
bool lockTaken = false;
try {
batchCountLock.Enter (ref lockTaken);
shouldComplete = numberOfGroups >= dataflowBlockOptions.MaxNumberOfGroups;
} finally {
if (lockTaken)
batchCountLock.Exit ();
}
if (shouldComplete)
Complete ();
}
/// <summary>
/// Returns whether a new item can be accepted, and increments a counter if it can.
/// Only makes sense when <see cref="GroupingDataflowBlockOptions.MaxNumberOfGroups"/>
/// is not unbounded.
/// </summary>
bool TryAdd ()
{
bool lockTaken = false;
try {
batchCountLock.Enter (ref lockTaken);
if (numberOfGroups + batchCount / batchSize
>= dataflowBlockOptions.MaxNumberOfGroups)
return false;
batchCount++;
return true;
} finally {
if (lockTaken)
batchCountLock.Exit ();
}
}
public void TriggerBatch ()
{
if (dataflowBlockOptions.Greedy) {
int earlyBatchSize;
bool lockTaken = false;
try {
batchCountLock.Enter (ref lockTaken);
if (batchCount == 0)
return;
earlyBatchSize = batchCount;
batchCount = 0;
numberOfGroups++;
} finally {
if (lockTaken)
batchCountLock.Exit ();
}
MakeBatch (earlyBatchSize);
} else {
if (dataflowBlockOptions.BoundedCapacity == -1
|| outgoing.Count <= dataflowBlockOptions.BoundedCapacity)
EnsureNonGreedyProcessing (true);
}
}
/// <summary>
/// Decides whether to create a new batch or not.
/// </summary>
/// <param name="addedItems">
/// Number of newly added items. Used only with greedy processing.
/// </param>
void BatchProcess (int addedItems = 0)
{
if (dataflowBlockOptions.Greedy) {
bool makeBatch = false;
bool lockTaken = false;
try {
batchCountLock.Enter (ref lockTaken);
batchCount += addedItems;
if (batchCount >= batchSize) {
batchCount -= batchSize;
numberOfGroups++;
makeBatch = true;
}
} finally {
if (lockTaken)
batchCountLock.Exit ();
}
if (makeBatch)
MakeBatch (batchSize);
} else {
if (ShouldProcessNonGreedy ())
EnsureNonGreedyProcessing (false);
}
}
/// <summary>
/// Returns whether non-greedy creation of a batch should be started.
/// </summary>
bool ShouldProcessNonGreedy ()
{
// do we have enough items waiting and would the new batch fit?
return messageBox.PostponedMessagesCount >= batchSize
&& (dataflowBlockOptions.BoundedCapacity == -1
|| outgoing.Count + batchSize <= dataflowBlockOptions.BoundedCapacity);
}
/// <summary>
/// Creates a batch of the given size and adds the result to the output queue.
/// </summary>
void MakeBatch (int size)
{
T[] batch = new T[size];
// lock is necessary here to make sure items are in the correct order
bool taken = false;
try {
batchLock.Enter (ref taken);
for (int i = 0; i < size; ++i)
messageQueue.TryTake (out batch [i]);
} finally {
if (taken)
batchLock.Exit ();
}
outgoing.AddData (batch);
VerifyMaxNumberOfGroups ();
}
/// <summary>
/// Starts non-greedy creation of batches, if one doesn't already run.
/// </summary>
/// <param name="manuallyTriggered">Whether the batch was triggered by <see cref="TriggerBatch"/>.</param>
void EnsureNonGreedyProcessing (bool manuallyTriggered)
{
if (nonGreedyProcessing.TrySet ())
Task.Factory.StartNew (() => NonGreedyProcess (manuallyTriggered),
dataflowBlockOptions.CancellationToken,
TaskCreationOptions.PreferFairness,
dataflowBlockOptions.TaskScheduler);
}
/// <summary>
/// Creates batches in non-greedy mode,
/// making sure the whole batch is available by using reservations.
/// </summary>
/// <param name="manuallyTriggered">Whether the batch was triggered by <see cref="TriggerBatch"/>.</param>
void NonGreedyProcess (bool manuallyTriggered)
{
bool first = true;
do {
var reservations =
new List<Tuple<ISourceBlock<T>, DataflowMessageHeader>> ();
int expectedReservationsCount = messageBox.PostponedMessagesCount;
if (expectedReservationsCount == 0)
break;
bool gotReservation;
do {
var reservation = messageBox.ReserveMessage ();
gotReservation = reservation != null;
if (gotReservation)
reservations.Add (reservation);
} while (gotReservation && reservations.Count < batchSize);
int expectedSize = manuallyTriggered && first
? Math.Min (expectedReservationsCount, batchSize)
: batchSize;
if (reservations.Count < expectedSize) {
foreach (var reservation in reservations)
messageBox.RelaseReservation (reservation);
// some reservations failed, which most likely means the message
// was consumed by someone else and a new one will be offered soon;
// so postpone the batch, so that the other block has time to do that
// (MS .Net does something like this too)
if (manuallyTriggered && first) {
Task.Factory.StartNew (() => NonGreedyProcess (true),
dataflowBlockOptions.CancellationToken,
TaskCreationOptions.PreferFairness,
dataflowBlockOptions.TaskScheduler);
return;
}
} else {
T[] batch = new T[reservations.Count];
for (int i = 0; i < reservations.Count; i++)
batch [i] = messageBox.ConsumeReserved (reservations [i]);
outgoing.AddData (batch);
// non-greedy doesn't need lock
numberOfGroups++;
VerifyMaxNumberOfGroups ();
}
first = false;
} while (ShouldProcessNonGreedy ());
nonGreedyProcessing.Value = false;
if (ShouldProcessNonGreedy ())
EnsureNonGreedyProcessing (false);
}
public void Complete ()
{
messageBox.Complete ();
TriggerBatch ();
outgoing.Complete ();
}
void IDataflowBlock.Fault (Exception exception)
{
compHelper.RequestFault (exception);
}
public Task Completion {
get { return compHelper.Completion; }
}
public int OutputCount {
get { return outgoing.Count; }
}
public int BatchSize {
get { return batchSize; }
}
public override string ToString ()
{
return NameHelper.GetName (this, dataflowBlockOptions);
}
}
}

View File

@ -0,0 +1,270 @@
// BatchedJoinBlock.cs
//
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System.Collections.Generic;
namespace System.Threading.Tasks.Dataflow {
public sealed class BatchedJoinBlock<T1, T2> :
IReceivableSourceBlock<Tuple<IList<T1>, IList<T2>>> {
readonly GroupingDataflowBlockOptions options;
readonly CompletionHelper completionHelper;
readonly OutgoingQueue<Tuple<IList<T1>, IList<T2>>> outgoing;
SpinLock batchLock;
readonly JoinTarget<T1> target1;
readonly JoinTarget<T2> target2;
int batchCount;
long numberOfGroups;
SpinLock batchCountLock;
public BatchedJoinBlock (int batchSize)
: this (batchSize, GroupingDataflowBlockOptions.Default)
{
}
public BatchedJoinBlock (int batchSize,
GroupingDataflowBlockOptions dataflowBlockOptions)
{
if (batchSize <= 0)
throw new ArgumentOutOfRangeException (
"batchSize", batchSize, "The batchSize must be positive.");
if (dataflowBlockOptions == null)
throw new ArgumentNullException ("dataflowBlockOptions");
if (!dataflowBlockOptions.Greedy)
throw new ArgumentException (
"Greedy must be true for this dataflow block.", "dataflowBlockOptions");
if (dataflowBlockOptions.BoundedCapacity != DataflowBlockOptions.Unbounded)
throw new ArgumentException (
"BoundedCapacity must be Unbounded or -1 for this dataflow block.",
"dataflowBlockOptions");
BatchSize = batchSize;
options = dataflowBlockOptions;
completionHelper = CompletionHelper.GetNew (dataflowBlockOptions);
target1 = new JoinTarget<T1> (
this, SignalTarget, completionHelper, () => outgoing.IsCompleted,
dataflowBlockOptions, true, TryAdd);
target2 = new JoinTarget<T2> (
this, SignalTarget, completionHelper, () => outgoing.IsCompleted,
dataflowBlockOptions, true, TryAdd);
outgoing = new OutgoingQueue<Tuple<IList<T1>, IList<T2>>> (
this, completionHelper,
() => target1.Buffer.IsCompleted || target2.Buffer.IsCompleted,
_ =>
{
target1.DecreaseCount ();
target2.DecreaseCount ();
}, options);
}
public int BatchSize { get; private set; }
public ITargetBlock<T1> Target1 {
get { return target1; }
}
public ITargetBlock<T2> Target2 {
get { return target2; }
}
/// <summary>
/// Returns whether a new item can be accepted, and increments a counter if it can.
/// </summary>
bool TryAdd ()
{
bool lockTaken = false;
try {
batchCountLock.Enter (ref lockTaken);
if (options.MaxNumberOfGroups != -1
&& numberOfGroups + batchCount / BatchSize >= options.MaxNumberOfGroups)
return false;
batchCount++;
return true;
} finally {
if (lockTaken)
batchCountLock.Exit();
}
}
/// <summary>
/// Decides whether to create a new batch or not.
/// </summary>
void SignalTarget ()
{
bool lockTaken = false;
try {
batchCountLock.Enter (ref lockTaken);
if (batchCount < BatchSize)
return;
batchCount -= BatchSize;
numberOfGroups++;
} finally {
if (lockTaken)
batchCountLock.Exit();
}
MakeBatch (BatchSize);
}
/// <summary>
/// Creates a batch of the given size and adds the resulting batch to the output queue.
/// </summary>
void MakeBatch (int batchSize)
{
if (batchSize == 0)
return;
var list1 = new List<T1> ();
var list2 = new List<T2> ();
// lock is necessary here to make sure items are in the correct order
bool taken = false;
try {
batchLock.Enter (ref taken);
int i = 0;
T1 item1;
while (i < batchSize && target1.Buffer.TryTake (out item1)) {
list1.Add (item1);
i++;
}
T2 item2;
while (i < batchSize && target2.Buffer.TryTake (out item2)) {
list2.Add (item2);
i++;
}
if (i < batchSize)
throw new InvalidOperationException("Unexpected count of items.");
} finally {
if (taken)
batchLock.Exit ();
}
var batch = Tuple.Create<IList<T1>, IList<T2>> (list1, list2);
outgoing.AddData (batch);
VerifyMaxNumberOfGroups ();
}
/// <summary>
/// Verifies whether <see cref="GroupingDataflowBlockOptions.MaxNumberOfGroups"/>
/// has been reached. If it did, <see cref="Complete"/>s the block.
/// </summary>
void VerifyMaxNumberOfGroups ()
{
if (options.MaxNumberOfGroups == -1)
return;
bool shouldComplete;
bool lockTaken = false;
try {
batchCountLock.Enter (ref lockTaken);
shouldComplete = numberOfGroups >= options.MaxNumberOfGroups;
} finally {
if (lockTaken)
batchCountLock.Exit ();
}
if (shouldComplete)
Complete ();
}
public Task Completion {
get { return completionHelper.Completion; }
}
public void Complete ()
{
target1.Complete ();
target2.Complete ();
MakeBatch (batchCount);
outgoing.Complete ();
}
void IDataflowBlock.Fault (Exception exception)
{
completionHelper.RequestFault (exception);
}
Tuple<IList<T1>, IList<T2>> ISourceBlock<Tuple<IList<T1>, IList<T2>>>.ConsumeMessage (
DataflowMessageHeader messageHeader,
ITargetBlock<Tuple<IList<T1>, IList<T2>>> target,
out bool messageConsumed)
{
return outgoing.ConsumeMessage (messageHeader, target, out messageConsumed);
}
public IDisposable LinkTo (ITargetBlock<Tuple<IList<T1>, IList<T2>>> target,
DataflowLinkOptions linkOptions)
{
return outgoing.AddTarget(target, linkOptions);
}
void ISourceBlock<Tuple<IList<T1>, IList<T2>>>.ReleaseReservation (
DataflowMessageHeader messageHeader,
ITargetBlock<Tuple<IList<T1>, IList<T2>>> target)
{
outgoing.ReleaseReservation (messageHeader, target);
}
bool ISourceBlock<Tuple<IList<T1>, IList<T2>>>.ReserveMessage (
DataflowMessageHeader messageHeader,
ITargetBlock<Tuple<IList<T1>, IList<T2>>> target)
{
return outgoing.ReserveMessage (messageHeader, target);
}
public bool TryReceive (Predicate<Tuple<IList<T1>, IList<T2>>> filter,
out Tuple<IList<T1>, IList<T2>> item)
{
return outgoing.TryReceive (filter, out item);
}
public bool TryReceiveAll (out IList<Tuple<IList<T1>, IList<T2>>> items)
{
return outgoing.TryReceiveAll (out items);
}
public int OutputCount {
get { return outgoing.Count; }
}
public override string ToString ()
{
return NameHelper.GetName (this, options);
}
}
}

View File

@ -0,0 +1,294 @@
// BatchedJoinBlock.cs
//
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System.Collections.Generic;
namespace System.Threading.Tasks.Dataflow {
public sealed class BatchedJoinBlock<T1, T2, T3> :
IReceivableSourceBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>> {
readonly GroupingDataflowBlockOptions options;
readonly CompletionHelper completionHelper;
readonly OutgoingQueue<Tuple<IList<T1>, IList<T2>, IList<T3>>> outgoing;
SpinLock batchLock;
readonly JoinTarget<T1> target1;
readonly JoinTarget<T2> target2;
readonly JoinTarget<T3> target3;
int batchCount;
long numberOfGroups;
SpinLock batchCountLock;
public BatchedJoinBlock (int batchSize)
: this (batchSize, GroupingDataflowBlockOptions.Default)
{
}
public BatchedJoinBlock (int batchSize,
GroupingDataflowBlockOptions dataflowBlockOptions)
{
if (batchSize <= 0)
throw new ArgumentOutOfRangeException (
"batchSize", batchSize, "The batchSize must be positive.");
if (dataflowBlockOptions == null)
throw new ArgumentNullException ("dataflowBlockOptions");
if (!dataflowBlockOptions.Greedy)
throw new ArgumentException (
"Greedy must be true for this dataflow block.", "dataflowBlockOptions");
if (dataflowBlockOptions.BoundedCapacity != DataflowBlockOptions.Unbounded)
throw new ArgumentException (
"BoundedCapacity must be Unbounded or -1 for this dataflow block.",
"dataflowBlockOptions");
BatchSize = batchSize;
options = dataflowBlockOptions;
completionHelper = CompletionHelper.GetNew (options);
target1 = new JoinTarget<T1> (
this, SignalTarget, completionHelper, () => outgoing.IsCompleted,
dataflowBlockOptions, true, TryAdd);
target2 = new JoinTarget<T2> (
this, SignalTarget, completionHelper, () => outgoing.IsCompleted,
dataflowBlockOptions, true, TryAdd);
target3 = new JoinTarget<T3> (
this, SignalTarget, completionHelper, () => outgoing.IsCompleted,
dataflowBlockOptions, true, TryAdd);
outgoing = new OutgoingQueue<Tuple<IList<T1>, IList<T2>, IList<T3>>> (
this, completionHelper,
() => target1.Buffer.IsCompleted || target2.Buffer.IsCompleted
|| target3.Buffer.IsCompleted,
_ =>
{
target1.DecreaseCount ();
target2.DecreaseCount ();
target3.DecreaseCount ();
}, options);
}
public int BatchSize { get; private set; }
public ITargetBlock<T1> Target1 {
get { return target1; }
}
public ITargetBlock<T2> Target2 {
get { return target2; }
}
public ITargetBlock<T3> Target3 {
get { return target3; }
}
/// <summary>
/// Returns whether a new item can be accepted, and increments a counter if it can.
/// </summary>
bool TryAdd ()
{
bool lockTaken = false;
try {
batchCountLock.Enter (ref lockTaken);
if (options.MaxNumberOfGroups != -1
&& numberOfGroups + batchCount / BatchSize >= options.MaxNumberOfGroups)
return false;
batchCount++;
return true;
} finally {
if (lockTaken)
batchCountLock.Exit ();
}
}
/// <summary>
/// Decides whether to create a new batch or not.
/// </summary>
void SignalTarget ()
{
bool lockTaken = false;
try {
batchCountLock.Enter (ref lockTaken);
if (batchCount < BatchSize)
return;
batchCount -= BatchSize;
numberOfGroups++;
} finally {
if (lockTaken)
batchCountLock.Exit ();
}
MakeBatch (BatchSize);
}
/// <summary>
/// Creates a batch of the given size and adds the resulting batch to the output queue.
/// </summary>
void MakeBatch (int batchSize)
{
if (batchSize == 0)
return;
var list1 = new List<T1> ();
var list2 = new List<T2> ();
var list3 = new List<T3> ();
// lock is necessary here to make sure items are in the correct order
bool taken = false;
try {
batchLock.Enter (ref taken);
int i = 0;
T1 item1;
while (i < batchSize && target1.Buffer.TryTake (out item1)) {
list1.Add (item1);
i++;
}
T2 item2;
while (i < batchSize && target2.Buffer.TryTake (out item2)) {
list2.Add (item2);
i++;
}
T3 item3;
while (i < batchSize && target3.Buffer.TryTake (out item3)) {
list3.Add (item3);
i++;
}
if (i < batchSize)
throw new InvalidOperationException ("Unexpected count of items.");
} finally {
if (taken)
batchLock.Exit ();
}
var batch = Tuple.Create<IList<T1>, IList<T2>, IList<T3>> (list1, list2,
list3);
outgoing.AddData (batch);
VerifyMaxNumberOfGroups ();
}
/// <summary>
/// Verifies whether <see cref="GroupingDataflowBlockOptions.MaxNumberOfGroups"/>
/// has been reached. If it did, <see cref="Complete"/>s the block.
/// </summary>
void VerifyMaxNumberOfGroups ()
{
if (options.MaxNumberOfGroups == -1)
return;
bool shouldComplete;
bool lockTaken = false;
try {
batchCountLock.Enter (ref lockTaken);
shouldComplete = numberOfGroups >= options.MaxNumberOfGroups;
} finally {
if (lockTaken)
batchCountLock.Exit ();
}
if (shouldComplete)
Complete ();
}
public Task Completion
{
get { return completionHelper.Completion; }
}
public void Complete ()
{
target1.Complete ();
target2.Complete ();
target3.Complete ();
MakeBatch (batchCount);
outgoing.Complete ();
}
void IDataflowBlock.Fault (Exception exception)
{
completionHelper.RequestFault (exception);
}
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)
{
return outgoing.ConsumeMessage (messageHeader, target, out messageConsumed);
}
public IDisposable LinkTo (
ITargetBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>> target,
DataflowLinkOptions linkOptions)
{
return outgoing.AddTarget (target, linkOptions);
}
void ISourceBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>>.ReleaseReservation (
DataflowMessageHeader messageHeader,
ITargetBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>> target)
{
outgoing.ReleaseReservation (messageHeader, target);
}
bool ISourceBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>>.ReserveMessage (
DataflowMessageHeader messageHeader,
ITargetBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>> target)
{
return outgoing.ReserveMessage (messageHeader, target);
}
public bool TryReceive (
Predicate<Tuple<IList<T1>, IList<T2>, IList<T3>>> filter,
out Tuple<IList<T1>, IList<T2>, IList<T3>> item)
{
return outgoing.TryReceive (filter, out item);
}
public bool TryReceiveAll (
out IList<Tuple<IList<T1>, IList<T2>, IList<T3>>> items)
{
return outgoing.TryReceiveAll (out items);
}
public int OutputCount {
get { return outgoing.Count; }
}
public override string ToString ()
{
return NameHelper.GetName (this, options);
}
}
}

View File

@ -0,0 +1,146 @@
// BroadcastBlock.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System.Collections.Generic;
using System.Collections.Concurrent;
namespace System.Threading.Tasks.Dataflow {
public sealed class BroadcastBlock<T> : IPropagatorBlock<T, T>, IReceivableSourceBlock<T> {
readonly CompletionHelper compHelper;
readonly BlockingCollection<T> messageQueue = new BlockingCollection<T> ();
readonly MessageBox<T> messageBox;
readonly DataflowBlockOptions dataflowBlockOptions;
readonly Func<T, T> cloningFunction;
readonly BroadcastOutgoingQueue<T> outgoing;
public BroadcastBlock (Func<T, T> cloningFunction)
: this (cloningFunction, DataflowBlockOptions.Default)
{
}
public BroadcastBlock (Func<T, T> cloningFunction,
DataflowBlockOptions dataflowBlockOptions)
{
if (dataflowBlockOptions == null)
throw new ArgumentNullException ("dataflowBlockOptions");
this.cloningFunction = cloningFunction;
this.dataflowBlockOptions = dataflowBlockOptions;
this.compHelper = CompletionHelper.GetNew (dataflowBlockOptions);
this.messageBox = new PassingMessageBox<T> (this, messageQueue, compHelper,
() => outgoing.IsCompleted, _ => BroadcastProcess (), dataflowBlockOptions);
this.outgoing = new BroadcastOutgoingQueue<T> (this, compHelper,
() => messageQueue.IsCompleted, messageBox.DecreaseCount,
dataflowBlockOptions, cloningFunction != null);
}
DataflowMessageStatus ITargetBlock<T>.OfferMessage (
DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source,
bool consumeToAccept)
{
return messageBox.OfferMessage (messageHeader, messageValue, source,
consumeToAccept);
}
public IDisposable LinkTo (ITargetBlock<T> target, DataflowLinkOptions linkOptions)
{
if (linkOptions == null)
throw new ArgumentNullException("linkOptions");
return outgoing.AddTarget (target, linkOptions);
}
T ISourceBlock<T>.ConsumeMessage (DataflowMessageHeader messageHeader,
ITargetBlock<T> target,
out bool messageConsumed)
{
T message = outgoing.ConsumeMessage (
messageHeader, target, out messageConsumed);
if (messageConsumed && cloningFunction != null)
message = cloningFunction (message);
return message;
}
bool ISourceBlock<T>.ReserveMessage (DataflowMessageHeader messageHeader,
ITargetBlock<T> target)
{
return outgoing.ReserveMessage (messageHeader, target);
}
void ISourceBlock<T>.ReleaseReservation (DataflowMessageHeader messageHeader,
ITargetBlock<T> target)
{
outgoing.ReleaseReservation (messageHeader, target);
}
public bool TryReceive (Predicate<T> filter, out T item)
{
var received = outgoing.TryReceive (filter, out item);
if (received && cloningFunction != null)
item = cloningFunction (item);
return received;
}
bool IReceivableSourceBlock<T>.TryReceiveAll (out IList<T> items)
{
T item;
if (!TryReceive (null, out item)) {
items = null;
return false;
}
items = new[] { item };
return true;
}
/// <summary>
/// Moves items from the input queue to the output queue.
/// </summary>
void BroadcastProcess ()
{
T item;
while (messageQueue.TryTake (out item))
outgoing.AddData (item);
}
public void Complete ()
{
messageBox.Complete ();
outgoing.Complete ();
}
void IDataflowBlock.Fault (Exception exception)
{
compHelper.RequestFault (exception);
}
public Task Completion {
get { return compHelper.Completion; }
}
public override string ToString ()
{
return NameHelper.GetName (this, dataflowBlockOptions);
}
}
}

View File

@ -0,0 +1,212 @@
// BroadcastOutgoingQueue.cs
//
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System.Collections.Concurrent;
namespace System.Threading.Tasks.Dataflow {
/// <summary>
/// Version of <see cref="OutgoingQueueBase{T}"/> for broadcast blocks.
/// </summary>
class BroadcastOutgoingQueue<T> : OutgoingQueueBase<T> {
volatile bool hasCurrentItem;
// don't use directly, only through CurrentItem (and carefully)
T currentItem;
SpinLock currentItemLock = new SpinLock();
readonly BroadcastTargetCollection<T> targets;
protected override TargetCollectionBase<T> Targets {
get { return targets; }
}
readonly ConcurrentDictionary<Tuple<DataflowMessageHeader, ITargetBlock<T>>, T>
reservedMessages =
new ConcurrentDictionary<Tuple<DataflowMessageHeader, ITargetBlock<T>>, T>();
public BroadcastOutgoingQueue (
ISourceBlock<T> block, CompletionHelper compHelper,
Func<bool> externalCompleteTester, Action<int> decreaseItemsCount,
DataflowBlockOptions options, bool hasCloner)
: base (compHelper, externalCompleteTester, decreaseItemsCount, options)
{
targets = new BroadcastTargetCollection<T> (block, hasCloner);
}
/// <summary>
/// The current item that is to be sent to taget blocks.
/// </summary>
T CurrentItem {
get {
T item;
bool lockTaken = false;
try {
currentItemLock.Enter (ref lockTaken);
item = currentItem;
} finally {
if (lockTaken)
currentItemLock.Exit ();
}
return item;
}
set {
hasCurrentItem = true;
bool lockTaken = false;
try {
currentItemLock.Enter (ref lockTaken);
currentItem = value;
} finally {
if (lockTaken)
currentItemLock.Exit ();
}
}
}
/// <summary>
/// Takes an item from the queue and sets it as <see cref="CurrentItem"/>.
/// </summary>
public void DequeueItem ()
{
T item;
if (Outgoing.TryTake (out item)) {
DecreaseCounts (item);
targets.SetCurrentItem (item);
CurrentItem = item;
}
}
/// <summary>
/// Manages sending items to the target blocks.
/// </summary>
protected override void Process ()
{
do {
ForceProcessing = false;
DequeueItem ();
targets.OfferItemToTargets ();
} while (!Store.IsEmpty || targets.NeedsProcessing);
IsProcessing.Value = false;
// to guard against race condition
if (ForceProcessing)
EnsureProcessing ();
VerifyCompleteness ();
}
public T ConsumeMessage (DataflowMessageHeader messageHeader,
ITargetBlock<T> target, out bool messageConsumed)
{
if (!messageHeader.IsValid)
throw new ArgumentException ("The messageHeader is not valid.",
"messageHeader");
if (target == null)
throw new ArgumentNullException("target");
T item;
if (reservedMessages.TryRemove (Tuple.Create (messageHeader, target), out item)) {
messageConsumed = true;
return item;
}
// if we first retrieve CurrentItem and then check the header,
// there will be no race condition
item = CurrentItem;
if (!targets.VerifyHeader (messageHeader)) {
targets.UnpostponeTargetNotConsumed (target);
messageConsumed = false;
return default(T);
}
targets.UnpostponeTargetConsumed (target, messageHeader);
EnsureProcessing ();
messageConsumed = true;
return item;
}
public bool ReserveMessage (DataflowMessageHeader messageHeader,
ITargetBlock<T> target)
{
if (!messageHeader.IsValid)
throw new ArgumentException ("The messageHeader is not valid.",
"messageHeader");
if (target == null)
throw new ArgumentNullException("target");
T item = CurrentItem;
if (!targets.VerifyHeader (messageHeader)) {
targets.UnpostponeTargetNotConsumed (target);
EnsureProcessing ();
return false;
}
targets.ReserveTarget (target);
reservedMessages [Tuple.Create (messageHeader, target)] = item;
return true;
}
public void ReleaseReservation (DataflowMessageHeader messageHeader,
ITargetBlock<T> target)
{
if (!messageHeader.IsValid)
throw new ArgumentException ("The messageHeader is not valid.",
"messageHeader");
if (target == null)
throw new ArgumentNullException("target");
T item;
if (!reservedMessages.TryRemove (Tuple.Create (messageHeader, target), out item))
throw new InvalidOperationException (
"The target did not have the message reserved.");
targets.UnpostponeTargetNotConsumed (target);
EnsureProcessing ();
}
public bool TryReceive (Predicate<T> filter, out T retrievedItem)
{
retrievedItem = default(T);
if (!hasCurrentItem) {
return false;
}
T item = CurrentItem;
if (filter == null || filter(item)) {
retrievedItem = item;
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,132 @@
// BufferBlock.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System.Collections.Generic;
using System.Collections.Concurrent;
namespace System.Threading.Tasks.Dataflow {
public sealed class BufferBlock<T> : IPropagatorBlock<T, T>, IReceivableSourceBlock<T> {
readonly DataflowBlockOptions dataflowBlockOptions;
readonly CompletionHelper compHelper;
readonly MessageBox<T> messageBox;
readonly OutgoingQueue<T> outgoing;
readonly BlockingCollection<T> messageQueue = new BlockingCollection<T> ();
public BufferBlock () : this (DataflowBlockOptions.Default)
{
}
public BufferBlock (DataflowBlockOptions dataflowBlockOptions)
{
if (dataflowBlockOptions == null)
throw new ArgumentNullException ("dataflowBlockOptions");
this.dataflowBlockOptions = dataflowBlockOptions;
this.compHelper = CompletionHelper.GetNew (dataflowBlockOptions);
this.messageBox = new PassingMessageBox<T> (this, messageQueue, compHelper,
() => outgoing.IsCompleted, _ => ProcessQueue (), dataflowBlockOptions);
this.outgoing = new OutgoingQueue<T> (this, compHelper,
() => messageQueue.IsCompleted, messageBox.DecreaseCount,
dataflowBlockOptions);
}
DataflowMessageStatus ITargetBlock<T>.OfferMessage (
DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source,
bool consumeToAccept)
{
return messageBox.OfferMessage (messageHeader, messageValue, source, consumeToAccept);
}
public IDisposable LinkTo (ITargetBlock<T> target, DataflowLinkOptions linkOptions)
{
return outgoing.AddTarget (target, linkOptions);
}
T ISourceBlock<T>.ConsumeMessage (DataflowMessageHeader messageHeader,
ITargetBlock<T> target,
out bool messageConsumed)
{
return outgoing.ConsumeMessage (messageHeader, target, out messageConsumed);
}
bool ISourceBlock<T>.ReserveMessage (DataflowMessageHeader messageHeader,
ITargetBlock<T> target)
{
return outgoing.ReserveMessage (messageHeader, target);
}
void ISourceBlock<T>.ReleaseReservation (DataflowMessageHeader messageHeader,
ITargetBlock<T> target)
{
outgoing.ReleaseReservation (messageHeader, target);
}
public bool TryReceive (Predicate<T> filter, out T item)
{
return outgoing.TryReceive (filter, out item);
}
public bool TryReceiveAll (out IList<T> items)
{
return outgoing.TryReceiveAll (out items);
}
/// <summary>
/// Moves items from the input queue to the output queue.
/// </summary>
void ProcessQueue ()
{
T item;
while (messageQueue.TryTake (out item))
outgoing.AddData (item);
}
public void Complete ()
{
messageBox.Complete ();
outgoing.Complete ();
}
void IDataflowBlock.Fault (Exception exception)
{
compHelper.RequestFault (exception);
}
public Task Completion {
get {
return compHelper.Completion;
}
}
public int Count {
get {
return outgoing.Count;
}
}
public override string ToString ()
{
return NameHelper.GetName (this, dataflowBlockOptions);
}
}
}

View File

@ -0,0 +1,179 @@
// JoinBlock.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
namespace System.Threading.Tasks.Dataflow {
/// <summary>
/// Block used in all versions of <see cref="DataflowBlock.Choose"/>.
/// </summary>
class ChooserBlock<T1, T2, T3> {
/// <summary>
/// Target for one of the sources to choose from.
/// </summary>
class ChooseTarget<TMessage> : ITargetBlock<TMessage> {
readonly ChooserBlock<T1, T2, T3> chooserBlock;
readonly int index;
readonly Action<TMessage> action;
public ChooseTarget (ChooserBlock<T1, T2, T3> chooserBlock,
int index, Action<TMessage> action)
{
this.chooserBlock = chooserBlock;
this.index = index;
this.action = action;
}
public DataflowMessageStatus OfferMessage (
DataflowMessageHeader messageHeader, TMessage messageValue,
ISourceBlock<TMessage> source, bool consumeToAccept)
{
if (!chooserBlock.canAccept)
return DataflowMessageStatus.DecliningPermanently;
bool lockTaken = false;
try {
chooserBlock.messageLock.Enter (ref lockTaken);
if (!chooserBlock.canAccept)
return DataflowMessageStatus.DecliningPermanently;
if (consumeToAccept) {
bool consummed;
messageValue = source.ConsumeMessage (messageHeader, this, out consummed);
if (!consummed)
return DataflowMessageStatus.NotAvailable;
}
chooserBlock.canAccept = false;
} finally {
if (lockTaken)
chooserBlock.messageLock.Exit ();
}
chooserBlock.MessageArrived (index, action, messageValue);
return DataflowMessageStatus.Accepted;
}
public Task Completion {
get { return null; }
}
public void Complete ()
{
}
public void Fault (Exception exception)
{
}
}
readonly TaskCompletionSource<int> completion = new TaskCompletionSource<int> ();
SpinLock messageLock;
bool canAccept = true;
public ChooserBlock (
Action<T1> action1, Action<T2> action2, Action<T3> action3,
DataflowBlockOptions dataflowBlockOptions)
{
Target1 = new ChooseTarget<T1> (this, 0, action1);
Target2 = new ChooseTarget<T2> (this, 1, action2);
if (action3 != null)
Target3 = new ChooseTarget<T3> (this, 2, action3);
if (dataflowBlockOptions.CancellationToken != CancellationToken.None)
dataflowBlockOptions.CancellationToken.Register (Cancelled);
}
/// <summary>
/// Causes cancellation of <see cref="Completion"/>.
/// If a message is already being consumed (and the consumsing succeeds)
/// or if its action is being invoked, the Task is not cancelled.
/// </summary>
void Cancelled ()
{
if (!canAccept)
return;
bool lockTaken = false;
try {
messageLock.Enter (ref lockTaken);
if (!canAccept)
return;
completion.SetCanceled ();
canAccept = false;
} finally {
if (lockTaken)
messageLock.Exit ();
}
}
/// <summary>
/// Called when all sources have completed,
/// causes cancellation of <see cref="Completion"/>.
/// </summary>
public void AllSourcesCompleted ()
{
Cancelled ();
}
/// <summary>
/// Called when message has arrived (and was consumed, if necessary).
/// This method can be called only once in the lifetime of this object.
/// </summary>
void MessageArrived<TMessage> (
int index, Action<TMessage> action, TMessage value)
{
try {
action (value);
completion.SetResult (index);
} catch (Exception e) {
completion.SetException (e);
}
}
/// <summary>
/// Target block for the first source block.
/// </summary>
public ITargetBlock<T1> Target1 { get; private set; }
/// <summary>
/// Target block for the second source block.
/// </summary>
public ITargetBlock<T2> Target2 { get; private set; }
/// <summary>
/// Target block for the third source block.
/// Is <c>null</c> if there are only two actions.
/// </summary>
public ITargetBlock<T3> Target3 { get; private set; }
/// <summary>
/// Task that signifies that an item was accepted and
/// its action has been called.
/// </summary>
public Task<int> Completion {
get { return completion.Task; }
}
}
}

View File

@ -0,0 +1,199 @@
// CompletionHelper.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace System.Threading.Tasks.Dataflow {
/// <summary>
/// Used to implement Dataflow completion tracking,
/// that is the Completion property, Complete/Fault method combo
/// and the CancellationToken option.
/// </summary>
class CompletionHelper {
readonly TaskCompletionSource<object> source =
new TaskCompletionSource<object> ();
readonly AtomicBoolean canFaultOrCancelImmediatelly =
new AtomicBoolean { Value = true };
readonly AtomicBoolean requestedFaultOrCancel =
new AtomicBoolean { Value = false };
readonly ConcurrentQueue<Tuple<Exception, bool>> requestedExceptions =
new ConcurrentQueue<Tuple<Exception, bool>> ();
public CompletionHelper (DataflowBlockOptions options)
{
if (options != null && options.CancellationToken != CancellationToken.None)
options.CancellationToken.Register (RequestCancel);
}
[Obsolete ("Use ctor")]
public static CompletionHelper GetNew (DataflowBlockOptions options)
{
return new CompletionHelper (options);
}
public Task Completion {
get { return source.Task; }
}
/// <summary>
/// Whether <see cref="Completion"/> can be faulted or cancelled immediatelly.
/// It can't for example when a block is currently executing user action.
/// In that case, the fault (or cancellation) is queued,
/// and is actually acted upon when this property is set back to <c>true</c>.
/// </summary>
public bool CanFaultOrCancelImmediatelly {
get { return canFaultOrCancelImmediatelly.Value; }
set {
if (value) {
if (canFaultOrCancelImmediatelly.TrySet () && requestedFaultOrCancel.Value) {
bool canAllBeIgnored = requestedExceptions.All (t => t.Item2);
if (canAllBeIgnored) {
Tuple<Exception, bool> tuple;
requestedExceptions.TryDequeue (out tuple);
var exception = tuple.Item1;
if (exception == null)
Cancel ();
else
Fault (exception);
} else {
Tuple<Exception, bool> tuple;
bool first = true;
var exceptions = new List<Exception> (requestedExceptions.Count);
while (requestedExceptions.TryDequeue (out tuple)) {
var exception = tuple.Item1;
bool canBeIgnored = tuple.Item2;
if (first || !canBeIgnored) {
if (exception != null)
exceptions.Add (exception);
}
first = false;
}
Fault (exceptions);
}
}
} else
canFaultOrCancelImmediatelly.Value = false;
}
}
/// <summary>
/// Whether the block can act as if it's not completed
/// (accept new items, start executing user action).
/// </summary>
public bool CanRun {
get { return !Completion.IsCompleted && !requestedFaultOrCancel.Value; }
}
/// <summary>
/// Sets the block as completed.
/// Should be called only when the block is really completed
/// (e.g. the output queue is empty) and not right after
/// the user calls <see cref="IDataflowBlock.Complete"/>.
/// </summary>
public void Complete ()
{
source.TrySetResult (null);
}
/// <summary>
/// Requests faulting of the block using a given exception.
/// If the block can't be faulted immediatelly (see <see cref="CanFaultOrCancelImmediatelly"/>),
/// the exception will be queued, and the block will fault as soon as it can.
/// </summary>
/// <param name="exception">The exception that is the cause of the fault.</param>
/// <param name="canBeIgnored">Can this exception be ignored, if there are more exceptions?</param>
/// <remarks>
/// When calling <see cref="IDataflowBlock.Fault"/> repeatedly, only the first exception counts,
/// even in the cases where the block can't be faulted immediatelly.
/// But exceptions from user actions in execution blocks count always,
/// which is the reason for the <paramref name="canBeIgnored"/> parameter.
/// </remarks>
public void RequestFault (Exception exception, bool canBeIgnored = true)
{
if (exception == null)
throw new ArgumentNullException ("exception");
if (CanFaultOrCancelImmediatelly)
Fault (exception);
else {
// still need to store canBeIgnored, if we don't want to add locking here
if (!canBeIgnored || requestedExceptions.Count == 0)
requestedExceptions.Enqueue (Tuple.Create (exception, canBeIgnored));
requestedFaultOrCancel.Value = true;
}
}
/// <summary>
/// Actually faults the block with a single exception.
/// </summary>
/// <remarks>
/// Should be only called when <see cref="CanFaultOrCancelImmediatelly"/> is <c>true</c>.
/// </remarks>
void Fault (Exception exception)
{
source.TrySetException (exception);
}
/// <summary>
/// Actually faults the block with a multiple exceptions.
/// </summary>
/// <remarks>
/// Should be only called when <see cref="CanFaultOrCancelImmediatelly"/> is <c>true</c>.
/// </remarks>
void Fault (IEnumerable<Exception> exceptions)
{
source.TrySetException (exceptions);
}
/// <summary>
/// Requests cancellation of the block.
/// If the block can't be cancelled immediatelly (see <see cref="CanFaultOrCancelImmediatelly"/>),
/// the cancellation will be queued, and the block will cancel as soon as it can.
/// </summary>
void RequestCancel ()
{
if (CanFaultOrCancelImmediatelly)
Cancel ();
else {
if (requestedExceptions.Count == 0)
requestedExceptions.Enqueue (Tuple.Create<Exception, bool> (null, true));
requestedFaultOrCancel.Value = true;
}
}
/// <summary>
/// Actually cancels the block.
/// </summary>
/// <remarks>
/// Should be only called when <see cref="CanFaultOrCancelImmediatelly"/> is <c>true</c>.
/// </remarks>
void Cancel ()
{
source.TrySetCanceled ();
}
}
}

View File

@ -0,0 +1,311 @@
// DataflowBlock.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
namespace System.Threading.Tasks.Dataflow {
public static class DataflowBlock {
public static IObservable<TOutput> AsObservable<TOutput> (this ISourceBlock<TOutput> source)
{
if (source == null)
throw new ArgumentNullException ("source");
return new ObservableDataflowBlock<TOutput> (source);
}
public static IObserver<TInput> AsObserver<TInput> (this ITargetBlock<TInput> target)
{
if (target == null)
throw new ArgumentNullException ("target");
return new ObserverDataflowBlock<TInput> (target);
}
public static Task<int> Choose<T1, T2> (
ISourceBlock<T1> source1, Action<T1> action1,
ISourceBlock<T2> source2, Action<T2> action2)
{
return Choose (source1, action1, source2, action2,
DataflowBlockOptions.Default);
}
public static Task<int> Choose<T1, T2> (
ISourceBlock<T1> source1, Action<T1> action1,
ISourceBlock<T2> source2, Action<T2> action2,
DataflowBlockOptions dataflowBlockOptions)
{
if (source1 == null)
throw new ArgumentNullException ("source1");
if (source2 == null)
throw new ArgumentNullException ("source2");
if (action1 == null)
throw new ArgumentNullException ("action1");
if (action2 == null)
throw new ArgumentNullException ("action2");
if (dataflowBlockOptions == null)
throw new ArgumentNullException ("dataflowBlockOptions");
var chooser = new ChooserBlock<T1, T2, object> (action1, action2, null, dataflowBlockOptions);
source1.LinkTo (chooser.Target1);
source2.LinkTo (chooser.Target2);
Task.WhenAll (source1.Completion, source2.Completion)
.ContinueWith (_ => chooser.AllSourcesCompleted ());
return chooser.Completion;
}
public static Task<int> Choose<T1, T2, T3> (
ISourceBlock<T1> source1, Action<T1> action1,
ISourceBlock<T2> source2, Action<T2> action2,
ISourceBlock<T3> source3, Action<T3> action3)
{
return Choose (source1, action1, source2, action2, source3, action3,
DataflowBlockOptions.Default);
}
public static Task<int> Choose<T1, T2, T3> (
ISourceBlock<T1> source1, Action<T1> action1,
ISourceBlock<T2> source2, Action<T2> action2,
ISourceBlock<T3> source3, Action<T3> action3,
DataflowBlockOptions dataflowBlockOptions)
{
if (source1 == null)
throw new ArgumentNullException ("source1");
if (source2 == null)
throw new ArgumentNullException ("source2");
if (source3 == null)
throw new ArgumentNullException ("source3");
if (action1 == null)
throw new ArgumentNullException ("action1");
if (action2 == null)
throw new ArgumentNullException ("action2");
if (action3 == null)
throw new ArgumentNullException ("action3");
if (dataflowBlockOptions == null)
throw new ArgumentNullException ("dataflowBlockOptions");
var chooser = new ChooserBlock<T1, T2, T3> (action1, action2, action3, dataflowBlockOptions);
source1.LinkTo (chooser.Target1);
source2.LinkTo (chooser.Target2);
source3.LinkTo (chooser.Target3);
Task.WhenAll (source1.Completion, source2.Completion, source3.Completion)
.ContinueWith (_ => chooser.AllSourcesCompleted ());
return chooser.Completion;
}
public static IPropagatorBlock<TInput, TOutput> Encapsulate<TInput, TOutput> (
ITargetBlock<TInput> target, ISourceBlock<TOutput> source)
{
return new PropagatorWrapperBlock<TInput, TOutput> (target, source);
}
public static IDisposable LinkTo<TOutput> (this ISourceBlock<TOutput> source, ITargetBlock<TOutput> target)
{
if (source == null)
throw new ArgumentNullException ("source");
return source.LinkTo (target, DataflowLinkOptions.Default);
}
public static IDisposable LinkTo<TOutput> (
this ISourceBlock<TOutput> source, ITargetBlock<TOutput> target,
Predicate<TOutput> predicate)
{
if (source == null)
throw new ArgumentNullException ("source");
return source.LinkTo (target, DataflowLinkOptions.Default, predicate);
}
public static IDisposable LinkTo<TOutput> (
this ISourceBlock<TOutput> source, ITargetBlock<TOutput> target,
DataflowLinkOptions linkOptions, Predicate<TOutput> predicate)
{
if (source == null)
throw new ArgumentNullException ("source");
if (predicate == null)
throw new ArgumentNullException ("predicate");
if (target == null)
throw new ArgumentNullException ("target");
var predicateBlock = new PredicateBlock<TOutput> (source, target, predicate);
return source.LinkTo (predicateBlock, linkOptions);
}
public static Task<bool> OutputAvailableAsync<TOutput> (
this ISourceBlock<TOutput> source)
{
return OutputAvailableAsync (source, CancellationToken.None);
}
public static Task<bool> OutputAvailableAsync<TOutput> (
this ISourceBlock<TOutput> source, CancellationToken cancellationToken)
{
if (source == null)
throw new ArgumentNullException ("source");
cancellationToken.ThrowIfCancellationRequested ();
if (source.Completion.IsCompleted || source.Completion.IsCanceled
|| source.Completion.IsFaulted)
return Task.FromResult (false);
var block = new OutputAvailableBlock<TOutput> ();
var bridge = source.LinkTo (block,
new DataflowLinkOptions { PropagateCompletion = true });
return block.AsyncGet (bridge, cancellationToken);
}
public static bool Post<TInput> (this ITargetBlock<TInput> target, TInput item)
{
if (target == null)
throw new ArgumentNullException ("target");
return target.OfferMessage (new DataflowMessageHeader(1), item, null, false)
== DataflowMessageStatus.Accepted;
}
public static TOutput Receive<TOutput> (this ISourceBlock<TOutput> source)
{
return Receive (source, TimeSpan.FromMilliseconds (-1), CancellationToken.None);
}
public static TOutput Receive<TOutput> (this ISourceBlock<TOutput> source, CancellationToken cancellationToken)
{
return Receive (source, TimeSpan.FromMilliseconds (-1), cancellationToken);
}
public static TOutput Receive<TOutput> (this ISourceBlock<TOutput> source, TimeSpan timeout)
{
return Receive (source, timeout, CancellationToken.None);
}
public static TOutput Receive<TOutput> (
this ISourceBlock<TOutput> source, TimeSpan timeout,
CancellationToken cancellationToken)
{
if (source == null)
throw new ArgumentNullException ("source");
if (timeout.TotalMilliseconds < -1)
throw new ArgumentOutOfRangeException ("timeout");
if (timeout.TotalMilliseconds > int.MaxValue)
throw new ArgumentOutOfRangeException ("timeout");
cancellationToken.ThrowIfCancellationRequested ();
TOutput item;
var receivableSource = source as IReceivableSourceBlock<TOutput>;
if (receivableSource != null && receivableSource.TryReceive (null, out item))
return item;
if (source.Completion.IsCompleted || source.Completion.IsCanceled
|| source.Completion.IsFaulted)
throw new InvalidOperationException (
"No item could be received from the source.");
int timeoutMilliseconds = (int)timeout.TotalMilliseconds;
var block = new ReceiveBlock<TOutput> (cancellationToken, timeoutMilliseconds);
var bridge = source.LinkTo (block,
new DataflowLinkOptions { PropagateCompletion = true });
return block.WaitAndGet (bridge);
}
public static Task<TOutput> ReceiveAsync<TOutput> (this ISourceBlock<TOutput> source)
{
return ReceiveAsync (source, TimeSpan.FromMilliseconds (-1), CancellationToken.None);
}
public static Task<TOutput> ReceiveAsync<TOutput> (this ISourceBlock<TOutput> source, CancellationToken cancellationToken)
{
return ReceiveAsync (source, TimeSpan.FromMilliseconds (-1), cancellationToken);
}
public static Task<TOutput> ReceiveAsync<TOutput> (this ISourceBlock<TOutput> source, TimeSpan timeout)
{
return ReceiveAsync (source, timeout, CancellationToken.None);
}
public static Task<TOutput> ReceiveAsync<TOutput> (
this ISourceBlock<TOutput> source, TimeSpan timeout,
CancellationToken cancellationToken)
{
if (source == null)
throw new ArgumentNullException ("source");
if (timeout.TotalMilliseconds < -1)
throw new ArgumentOutOfRangeException ("timeout");
if (timeout.TotalMilliseconds > int.MaxValue)
throw new ArgumentOutOfRangeException ("timeout");
cancellationToken.ThrowIfCancellationRequested ();
int timeoutMilliseconds = (int)timeout.TotalMilliseconds;
var block = new ReceiveBlock<TOutput> (cancellationToken, timeoutMilliseconds);
var bridge = source.LinkTo (block);
return block.AsyncGet (bridge);
}
public static bool TryReceive<TOutput> (this IReceivableSourceBlock<TOutput> source, out TOutput item)
{
item = default (TOutput);
if (source == null)
throw new ArgumentNullException ("source");
return source.TryReceive (null, out item);
}
public static Task<bool> SendAsync<TInput> (
this ITargetBlock<TInput> target, TInput item)
{
return SendAsync (target, item, CancellationToken.None);
}
public static Task<bool> SendAsync<TInput> (
this ITargetBlock<TInput> target, TInput item,
CancellationToken cancellationToken)
{
if (target == null)
throw new ArgumentNullException ("target");
cancellationToken.ThrowIfCancellationRequested ();
var status = target.OfferMessage (
new DataflowMessageHeader (1), item, null, false);
if (status == DataflowMessageStatus.Accepted)
return Task.FromResult (true);
if (status != DataflowMessageStatus.Declined
&& status != DataflowMessageStatus.Postponed)
return Task.FromResult (false);
var block = new SendBlock<TInput> (target, item, cancellationToken);
return block.Send ();
}
public static ITargetBlock<TInput> NullTarget<TInput>()
{
return new NullTargetBlock<TInput> ();
}
}
}

View File

@ -0,0 +1,94 @@
// DataflowBlockOptions.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
namespace System.Threading.Tasks.Dataflow {
public class DataflowBlockOptions {
static readonly DataflowBlockOptions DefaultOptions =
new DataflowBlockOptions ();
/// <summary>
/// Cached default block options
/// </summary>
internal static DataflowBlockOptions Default {
get { return DefaultOptions; }
}
public const int Unbounded = -1;
int boundedCapacity;
int maxMessagesPerTask;
TaskScheduler taskScheduler;
string nameFormat;
public DataflowBlockOptions ()
{
BoundedCapacity = -1;
CancellationToken = CancellationToken.None;
MaxMessagesPerTask = -1;
TaskScheduler = TaskScheduler.Default;
NameFormat = "{0} Id={1}";
}
public int BoundedCapacity {
get { return boundedCapacity; }
set {
if (value < -1)
throw new ArgumentOutOfRangeException("value");
boundedCapacity = value;
}
}
public CancellationToken CancellationToken { get; set; }
public int MaxMessagesPerTask {
get { return maxMessagesPerTask; }
set {
if (value < -1)
throw new ArgumentOutOfRangeException("value");
maxMessagesPerTask = value;
}
}
public TaskScheduler TaskScheduler {
get { return taskScheduler; }
set {
if (value == null)
throw new ArgumentNullException("value");
taskScheduler = value;
}
}
public string NameFormat {
get { return nameFormat; }
set {
if (value == null)
throw new ArgumentNullException("value");
nameFormat = value;
}
}
}
}

View File

@ -0,0 +1,55 @@
// DataflowLinkOptions.cs
//
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
namespace System.Threading.Tasks.Dataflow {
public class DataflowLinkOptions {
static readonly DataflowLinkOptions DefaultOptions =
new DataflowLinkOptions ();
int maxMessages;
internal static DataflowLinkOptions Default {
get { return DefaultOptions; }
}
public DataflowLinkOptions()
{
PropagateCompletion = false;
MaxMessages = DataflowBlockOptions.Unbounded;
Append = true;
}
public bool PropagateCompletion { get; set; }
public int MaxMessages {
get { return maxMessages; }
set {
if (value < -1)
throw new ArgumentOutOfRangeException("value");
maxMessages = value;
}
}
public bool Append { get; set; }
}
}

View File

@ -0,0 +1,73 @@
// DataflowMessageHeader.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
namespace System.Threading.Tasks.Dataflow {
public struct DataflowMessageHeader : IEquatable<DataflowMessageHeader> {
readonly long id;
public DataflowMessageHeader (long id)
{
if (id == 0)
throw new ArgumentException("The value of 0 can't be used as an id for a valid header.");
this.id = id;
}
public long Id {
get {
return id;
}
}
public bool IsValid {
get {
return id != 0;
}
}
public override bool Equals (object obj)
{
return obj is DataflowMessageHeader && Equals ((DataflowMessageHeader)obj);
}
public bool Equals (DataflowMessageHeader other)
{
return other.id == id;
}
public override int GetHashCode ()
{
return id.GetHashCode ();
}
public static bool operator== (DataflowMessageHeader left, DataflowMessageHeader right)
{
return left.Equals (right);
}
public static bool operator!= (DataflowMessageHeader left, DataflowMessageHeader right)
{
return !left.Equals (right);
}
}
}

View File

@ -0,0 +1,41 @@
// DataflowMessageStatus.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace System.Threading.Tasks.Dataflow
{
public enum DataflowMessageStatus
{
Accepted,
Declined,
Postponed,
NotAvailable,
DecliningPermanently
}
}

View File

@ -0,0 +1,67 @@
// ExecutingMessageBox.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System.Collections.Concurrent;
namespace System.Threading.Tasks.Dataflow {
/// <summary>
/// Message box for executing blocks with synchrnous actions.
/// </summary>
/// <typeparam name="TInput">Type of the item the block is processing.</typeparam>
class ExecutingMessageBox<TInput> : ExecutingMessageBoxBase<TInput> {
readonly Func<bool> processItem;
public ExecutingMessageBox (
ITargetBlock<TInput> target, BlockingCollection<TInput> messageQueue,
CompletionHelper compHelper, Func<bool> externalCompleteTester,
Func<bool> processItem, Action outgoingQueueComplete,
ExecutionDataflowBlockOptions options)
: base (
target, messageQueue, compHelper, externalCompleteTester,
outgoingQueueComplete, options)
{
this.processItem = processItem;
}
/// <summary>
/// Processes the input queue of the block.
/// </summary>
protected override void ProcessQueue ()
{
StartProcessQueue ();
try {
int i = 0;
while (CanRun (i)) {
if (!processItem ())
break;
i++;
}
} catch (Exception e) {
CompHelper.RequestFault (e, false);
}
FinishProcessQueue ();
}
}
}

View File

@ -0,0 +1,163 @@
// ExecutingMessageBoxBase.cs
//
// Copyright (c) 2011 Jérémie "garuma" Laval
// Copyright (c) 2012 Petr Onderka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System.Collections.Concurrent;
namespace System.Threading.Tasks.Dataflow {
/// <summary>
/// Base message box for execution blocks (synchronous and asynchrnous).
/// </summary>
/// <typeparam name="TInput">Type of the item the block is processing.</typeparam>
abstract class ExecutingMessageBoxBase<TInput> : MessageBox<TInput> {
protected ExecutionDataflowBlockOptions Options { get; private set; }
readonly Action outgoingQueueComplete;
// even number: Task is waiting to run
// odd number: Task is not waiting to run
// invariant: dop / 2 Tasks are running or waiting
int degreeOfParallelism = 1;
protected ExecutingMessageBoxBase (
ITargetBlock<TInput> target, BlockingCollection<TInput> messageQueue,
CompletionHelper compHelper, Func<bool> externalCompleteTester,
Action outgoingQueueComplete, ExecutionDataflowBlockOptions options)
: base (
target, messageQueue, compHelper, externalCompleteTester,
options)
{
this.Options = options;
this.outgoingQueueComplete = outgoingQueueComplete;
}
/// <summary>
/// Makes sure the input queue is processed the way it needs to.
/// </summary>
/// <param name="newItem">Was new item just added?</param>
protected override void EnsureProcessing (bool newItem)
{
StartProcessing ();
}
/// <summary>
/// Starts processing queue on a task,
/// assuming <see cref="ExecutionDataflowBlockOptions.MaxDegreeOfParallelism"/>
/// was't reached yet.
/// </summary>
void StartProcessing ()
{
// atomically increase degreeOfParallelism by 1 only if it's odd
// and low enough
int startDegreeOfParallelism;
int currentDegreeOfParallelism = degreeOfParallelism;
do {
startDegreeOfParallelism = currentDegreeOfParallelism;
if (startDegreeOfParallelism % 2 == 0
|| (Options.MaxDegreeOfParallelism != DataflowBlockOptions.Unbounded
&& startDegreeOfParallelism / 2 >= Options.MaxDegreeOfParallelism))
return;
currentDegreeOfParallelism =
Interlocked.CompareExchange (ref degreeOfParallelism,
startDegreeOfParallelism + 1, startDegreeOfParallelism);
} while (startDegreeOfParallelism != currentDegreeOfParallelism);
Task.Factory.StartNew (ProcessQueue, CancellationToken.None,
TaskCreationOptions.PreferFairness, Options.TaskScheduler);
}
/// <summary>
/// Processes the input queue of the block.
/// </summary>
/// <remarks>
/// Should first call <see cref="StartProcessQueue"/>,
/// then process the queue and finally call <see cref="FinishProcessQueue"/>.
/// </remarks>
protected abstract void ProcessQueue ();
/// <summary>
/// Notifies that another processing task was started.
/// Should be called right after <see cref="ProcessQueue"/> is actually executed.
/// </summary>
protected void StartProcessQueue ()
{
CompHelper.CanFaultOrCancelImmediatelly = false;
int incrementedDegreeOfParallelism =
Interlocked.Increment (ref degreeOfParallelism);
if ((Options.MaxDegreeOfParallelism == DataflowBlockOptions.Unbounded
|| incrementedDegreeOfParallelism / 2 < Options.MaxDegreeOfParallelism)
&& MessageQueue.Count > 1 && CompHelper.CanRun)
StartProcessing ();
}
/// <summary>
/// Notifies that a processing task was finished.
/// Should be called after <see cref="ProcessQueue"/> actually finishes processing.
/// </summary>
protected void FinishProcessQueue ()
{
int decrementedDegreeOfParallelism =
Interlocked.Add (ref degreeOfParallelism, -2);
if (decrementedDegreeOfParallelism % 2 == 1) {
if (decrementedDegreeOfParallelism == 1) {
CompHelper.CanFaultOrCancelImmediatelly = true;
base.VerifyCompleteness ();
if (MessageQueue.IsCompleted)
outgoingQueueComplete ();
}
if (MessageQueue.Count > 0 && CompHelper.CanRun)
StartProcessing ();
}
}
/// <summary>
/// Notifies that outgoing queue should be completed, if possible.
/// </summary>
protected override void OutgoingQueueComplete ()
{
if (MessageQueue.IsCompleted
&& Thread.VolatileRead (ref degreeOfParallelism) == 1)
outgoingQueueComplete ();
}
/// <summary>
/// Makes sure the block is completed if it should be.
/// </summary>
protected override void VerifyCompleteness ()
{
if (Thread.VolatileRead (ref degreeOfParallelism) == 1)
base.VerifyCompleteness ();
}
/// <summary>
/// Indicates whether a processing task can continue executing.
/// </summary>
/// <param name="iteration">The number of the iteration of the task, starting from 0.</param>
protected bool CanRun (int iteration)
{
return CompHelper.CanRun
&& (Options.MaxMessagesPerTask == DataflowBlockOptions.Unbounded
|| iteration < Options.MaxMessagesPerTask);
}
}
}

Some files were not shown because too many files have changed in this diff Show More