[System.Threading.Tasks.Dataflow] Replace implementation with CoreFx version
authorAlexander Köplinger <alex.koeplinger@outlook.com>
Mon, 16 Nov 2015 15:23:09 +0000 (16:23 +0100)
committerAlexander Köplinger <alex.koeplinger@outlook.com>
Mon, 16 Nov 2015 16:17:02 +0000 (17:17 +0100)
We were seeing some random test failures on Jenkins with our Dataflow implementation.
Replacing it with Microsoft's CoreFx version fixed those and makes us more compatible.

While we'd ideally not ship this assembly at all with Mono (it doesn't ship with .NET),
we shipped it in the past and as such people might rely on it so we can't remove it.

The CoreFx commit this version was taken is 905a1940bcda0afdca2f14ceb2b0161ebc4d1d02.

87 files changed:
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowBlockOptions.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowLinkOptions.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowMessageHeader.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowMessageStatus.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/IDataflowBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/IPropagatorBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/IReceivableSourceBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/ISourceBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/ITargetBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/ActionBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/BatchBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/BatchedJoinBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/BroadcastBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/BufferBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/JoinBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/TransformBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/TransformManyBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/WriteOnceBlock.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ActionOnDispose.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/Common.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ConcurrentQueue.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/DataflowEtwProvider.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/EnumerableDebugView.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/IDebuggerDisplay.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/IProducerConsumerCollection.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ImmutableList.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/Padding.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ProducerConsumerQueues.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/QueuedMap.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ReorderingBuffer.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/SourceCore.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/SpscTargetCore.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/TargetCore.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/TargetRegistry.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/Threading.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Resources/Strings.resx [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/Makefile
mcs/class/System.Threading.Tasks.Dataflow/README.md [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/SR.cs [new file with mode: 0644]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow.dll.sources
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ActionBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/AsyncExecutingMessageBox.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BatchBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BatchedJoinBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BatchedJoinBlock`3.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BroadcastBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BroadcastOutgoingQueue.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BufferBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ChooserBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/CompletionHelper.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowBlockOptions.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowLinkOptions.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowMessageHeader.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowMessageStatus.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ExecutingMessageBox.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ExecutingMessageBoxBase.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ExecutionDataflowBlockOptions.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/GroupingDataflowBlockOptions.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/IDataflowBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/IPropagatorBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/IReceivableSourceBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ISourceBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ITargetBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/JoinBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/JoinBlock`3.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/JoinTarget.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/MessageBox.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/NameHelper.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/NullTargetBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ObservableDataflowBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ObserverDataflowBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/OutgoingQueue.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/OutgoingQueueBase.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/OutputAvailableBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/PassingMessageBox.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/PredicateBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/PropagatorWrapperBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ReceiveBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/SendBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/TargetCollection.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/TransformBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/TransformManyBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/WriteOnceBlock.cs [deleted file]
mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow_test.dll.sources
mcs/class/System.Threading.Tasks.Dataflow/Test/System.Threading.Tasks.Dataflow/CompletionHelperTest.cs [deleted file]

diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowBlock.cs
new file mode 100644 (file)
index 0000000..a083e9d
--- /dev/null
@@ -0,0 +1,2813 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// DataflowBlock.cs
+//
+//
+// Common functionality for ITargetBlock, ISourceBlock, and IPropagatorBlock.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.ExceptionServices;
+using System.Security;
+using System.Threading.Tasks.Dataflow.Internal;
+using System.Threading.Tasks.Dataflow.Internal.Threading;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>
+    /// Provides a set of static (Shared in Visual Basic) methods for working with dataflow blocks.
+    /// </summary>
+    public static class DataflowBlock
+    {
+        #region LinkTo
+        /// <summary>Links the <see cref="ISourceBlock{TOutput}"/> to the specified <see cref="ITargetBlock{TOutput}"/>.</summary>
+        /// <param name="source">The source from which to link.</param>
+        /// <param name="target">The <see cref="ITargetBlock{TOutput}"/> to which to connect the source.</param>
+        /// <returns>An IDisposable that, upon calling Dispose, will unlink the source from the target.</returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="target"/> is null (Nothing in Visual Basic).</exception>
+        public static IDisposable LinkTo<TOutput>(
+            this ISourceBlock<TOutput> source,
+            ITargetBlock<TOutput> target)
+        {
+            // Validate arguments
+            if (source == null) throw new ArgumentNullException("source");
+            if (target == null) throw new ArgumentNullException("target");
+            Contract.EndContractBlock();
+
+            // This method exists purely to pass default DataflowLinkOptions 
+            // to increase usability of the "90%" case.
+            return source.LinkTo(target, DataflowLinkOptions.Default);
+        }
+
+        /// <summary>Links the <see cref="ISourceBlock{TOutput}"/> to the specified <see cref="ITargetBlock{TOutput}"/> using the specified filter.</summary>
+        /// <param name="source">The source from which to link.</param>
+        /// <param name="target">The <see cref="ITargetBlock{TOutput}"/> to which to connect the source.</param>
+        /// <param name="predicate">The filter a message must pass in order for it to propagate from the source to the target.</param>
+        /// <returns>An IDisposable that, upon calling Dispose, will unlink the source from the target.</returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="target"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="predicate"/> is null (Nothing in Visual Basic).</exception>
+        public static IDisposable LinkTo<TOutput>(
+            this ISourceBlock<TOutput> source,
+            ITargetBlock<TOutput> target,
+            Predicate<TOutput> predicate)
+        {
+            // All argument validation handled by delegated method.
+            return LinkTo(source, target, DataflowLinkOptions.Default, predicate);
+        }
+
+        /// <summary>Links the <see cref="ISourceBlock{TOutput}"/> to the specified <see cref="ITargetBlock{TOutput}"/> using the specified filter.</summary>
+        /// <param name="source">The source from which to link.</param>
+        /// <param name="target">The <see cref="ITargetBlock{TOutput}"/> to which to connect the source.</param>
+        /// <param name="predicate">The filter a message must pass in order for it to propagate from the source to the target.</param>
+        /// <param name="linkOptions">The options to use to configure the link.</param>
+        /// <returns>An IDisposable that, upon calling Dispose, will unlink the source from the target.</returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="target"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="linkOptions"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="predicate"/> is null (Nothing in Visual Basic).</exception>
+        public static IDisposable LinkTo<TOutput>(
+            this ISourceBlock<TOutput> source,
+            ITargetBlock<TOutput> target,
+            DataflowLinkOptions linkOptions,
+            Predicate<TOutput> predicate)
+        {
+            // Validate arguments
+            if (source == null) throw new ArgumentNullException("source");
+            if (target == null) throw new ArgumentNullException("target");
+            if (linkOptions == null) throw new ArgumentNullException("linkOptions");
+            if (predicate == null) throw new ArgumentNullException("predicate");
+            Contract.EndContractBlock();
+
+            // Create the filter, which links to the real target, and then
+            // link the real source to this intermediate filter.
+            var filter = new FilteredLinkPropagator<TOutput>(source, target, predicate);
+            return source.LinkTo(filter, linkOptions);
+        }
+
+        /// <summary>Provides a synchronous filter for use in filtered LinkTos.</summary>
+        /// <typeparam name="T">Specifies the type of data being filtered.</typeparam>
+        [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+        [DebuggerTypeProxy(typeof(FilteredLinkPropagator<>.DebugView))]
+        private sealed class FilteredLinkPropagator<T> : IPropagatorBlock<T, T>, IDebuggerDisplay
+        {
+            /// <summary>The source connected with this filter.</summary>
+            private readonly ISourceBlock<T> _source;
+            /// <summary>The target with which this block is associated.</summary>
+            private readonly ITargetBlock<T> _target;
+            /// <summary>The predicate provided by the user.</summary>
+            private readonly Predicate<T> _userProvidedPredicate;
+
+            /// <summary>Initializes the filter passthrough.</summary>
+            /// <param name="source">The source connected to this filter.</param>
+            /// <param name="target">The target to which filtered messages should be passed.</param>
+            /// <param name="predicate">The predicate to run for each messsage.</param>
+            internal FilteredLinkPropagator(ISourceBlock<T> source, ITargetBlock<T> target, Predicate<T> predicate)
+            {
+                Contract.Requires(source != null, "Filtered link requires a source to filter on.");
+                Contract.Requires(target != null, "Filtered link requires a target to filter to.");
+                Contract.Requires(predicate != null, "Filtered link requires a predicate to filter with.");
+
+                // Store the arguments
+                _source = source;
+                _target = target;
+                _userProvidedPredicate = predicate;
+            }
+
+            /// <summary>Runs the user-provided predicate over an item in the correct execution context.</summary>
+            /// <param name="item">The item to evaluate.</param>
+            /// <returns>true if the item passed the filter; otherwise, false.</returns>
+            private bool RunPredicate(T item)
+            {
+                Contract.Requires(_userProvidedPredicate != null, "User-provided predicate is required.");
+
+                return _userProvidedPredicate(item); // avoid state object allocation if execution context isn't needed
+            }
+
+            /// <summary>Manually closes over state necessary in FilteredLinkPropagator.</summary>
+            private sealed class PredicateContextState
+            {
+                /// <summary>The input to be filtered.</summary>
+                internal readonly T Input;
+                /// <summary>The predicate function.</summary>
+                internal readonly Predicate<T> Predicate;
+                /// <summary>The result of the filtering operation.</summary>
+                internal bool Output;
+
+                /// <summary>Initializes the predicate state.</summary>
+                /// <param name="input">The input to be filtered.</param>
+                /// <param name="predicate">The predicate function.</param>
+                internal PredicateContextState(T input, Predicate<T> predicate)
+                {
+                    Contract.Requires(predicate != null, "A predicate with which to filter is required.");
+                    this.Input = input;
+                    this.Predicate = predicate;
+                }
+
+                /// <summary>Runs the predicate function over the input and stores the result into the output.</summary>
+                internal void Run()
+                {
+                    Contract.Requires(Predicate != null, "Non-null predicate required");
+                    Output = Predicate(Input);
+                }
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
+            DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, Boolean consumeToAccept)
+            {
+                // Validate arguments.  Some targets may have a null source, but FilteredLinkPropagator
+                // is an internal target that should only ever have source non-null.
+                if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+                if (source == null) throw new ArgumentNullException("source");
+                Contract.EndContractBlock();
+
+                // Run the filter.
+                bool passedFilter = RunPredicate(messageValue);
+
+                // If the predicate matched, pass the message along to the real target.
+                if (passedFilter)
+                {
+                    return _target.OfferMessage(messageHeader, messageValue, this, consumeToAccept);
+                }
+                // Otherwise, decline.
+                else return DataflowMessageStatus.Declined;
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+            T ISourceBlock<T>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target, out Boolean messageConsumed)
+            {
+                // This message should have only made it to the target if it passes the filter, so we shouldn't need to check again.
+                // The real source will also be doing verifications, so we don't need to validate args here.
+                Debug.Assert(messageHeader.IsValid, "Only valid messages may be consumed.");
+                return _source.ConsumeMessage(messageHeader, this, out messageConsumed);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+            bool ISourceBlock<T>.ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target)
+            {
+                // This message should have only made it to the target if it passes the filter, so we shouldn't need to check again.
+                // The real source will also be doing verifications, so we don't need to validate args here.
+                Debug.Assert(messageHeader.IsValid, "Only valid messages may be consumed.");
+                return _source.ReserveMessage(messageHeader, this);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+            void ISourceBlock<T>.ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<T> target)
+            {
+                // This message should have only made it to the target if it passes the filter, so we shouldn't need to check again.
+                // The real source will also be doing verifications, so we don't need to validate args here.
+                Debug.Assert(messageHeader.IsValid, "Only valid messages may be consumed.");
+                _source.ReleaseReservation(messageHeader, this);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+            Task IDataflowBlock.Completion { get { return _source.Completion; } }
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+            void IDataflowBlock.Complete() { _target.Complete(); }
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+            void IDataflowBlock.Fault(Exception exception) { _target.Fault(exception); }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+            IDisposable ISourceBlock<T>.LinkTo(ITargetBlock<T> target, DataflowLinkOptions linkOptions) { throw new NotSupportedException(SR.NotSupported_MemberNotNeeded); }
+
+            /// <summary>The data to display in the debugger display attribute.</summary>
+            [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+            private object DebuggerDisplayContent
+            {
+                get
+                {
+                    var displaySource = _source as IDebuggerDisplay;
+                    var displayTarget = _target as IDebuggerDisplay;
+                    return string.Format("{0} Source=\"{1}\", Target=\"{2}\"",
+                        Common.GetNameForDebugger(this),
+                        displaySource != null ? displaySource.Content : _source,
+                        displayTarget != null ? displayTarget.Content : _target);
+                }
+            }
+            /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+            object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+            /// <summary>Provides a debugger type proxy for a filter.</summary>
+            private sealed class DebugView
+            {
+                /// <summary>The filter.</summary>
+                private readonly FilteredLinkPropagator<T> _filter;
+
+                /// <summary>Initializes the debug view.</summary>
+                /// <param name="filter">The filter to view.</param>
+                public DebugView(FilteredLinkPropagator<T> filter)
+                {
+                    Contract.Requires(filter != null, "Need a filter with which to construct the debug view.");
+                    _filter = filter;
+                }
+
+                /// <summary>The linked target for this filter.</summary>
+                public ITargetBlock<T> LinkedTarget { get { return _filter._target; } }
+            }
+        }
+        #endregion
+
+        #region Post and SendAsync
+        /// <summary>Posts an item to the <see cref="T:System.Threading.Tasks.Dataflow.ITargetBlock`1"/>.</summary>
+        /// <typeparam name="TInput">Specifies the type of data accepted by the target block.</typeparam>
+        /// <param name="target">The target block.</param>
+        /// <param name="item">The item being offered to the target.</param>
+        /// <returns>true if the item was accepted by the target block; otherwise, false.</returns>
+        /// <remarks>
+        /// This method will return once the target block has decided to accept or decline the item,
+        /// but unless otherwise dictated by special semantics of the target block, it does not wait
+        /// for the item to actually be processed (for example, <see cref="T:System.Threading.Tasks.Dataflow.ActionBlock`1"/>
+        /// will return from Post as soon as it has stored the posted item into its input queue).  From the perspective
+        /// of the block's processing, Post is asynchronous. For target blocks that support postponing offered messages, 
+        /// or for blocks that may do more processing in their Post implementation, consider using
+        ///  <see cref="T:System.Threading.Tasks.Dataflow.DataflowBlock.SendAsync">SendAsync</see>, 
+        /// which will return immediately and will enable the target to postpone the posted message and later consume it 
+        /// after SendAsync returns.
+        /// </remarks>
+        public static Boolean Post<TInput>(this ITargetBlock<TInput> target, TInput item)
+        {
+            if (target == null) throw new ArgumentNullException("target");
+            return target.OfferMessage(Common.SingleMessageHeader, item, source: null, consumeToAccept: false) == DataflowMessageStatus.Accepted;
+        }
+
+        /// <summary>Asynchronously offers a message to the target message block, allowing for postponement.</summary>
+        /// <typeparam name="TInput">Specifies the type of the data to post to the target.</typeparam>
+        /// <param name="target">The target to which to post the data.</param>
+        /// <param name="item">The item being offered to the target.</param>
+        /// <returns>
+        /// A <see cref="System.Threading.Tasks.Task{Boolean}"/> that represents the asynchronous send.  If the target
+        /// accepts and consumes the offered element during the call to SendAsync, upon return
+        /// from the call the resulting <see cref="System.Threading.Tasks.Task{Boolean}"/> will be completed and its <see cref="System.Threading.Tasks.Task{Boolean}.Result">Result</see> 
+        /// property will return true.  If the target declines the offered element during the call, upon return from the call the resulting <see cref="System.Threading.Tasks.Task{Boolean}"/> will
+        /// be completed and its <see cref="System.Threading.Tasks.Task{Boolean}.Result">Result</see> property will return false. If the target
+        /// postpones the offered element, the element will be buffered until such time that the target consumes or releases it, at which
+        /// point the Task will complete, with its <see cref="System.Threading.Tasks.Task{Boolean}.Result"/> indicating whether the message was consumed.  If the target
+        /// never attempts to consume or release the message, the returned task will never complete.
+        /// </returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="target"/> is null (Nothing in Visual Basic).</exception>
+        public static Task<Boolean> SendAsync<TInput>(this ITargetBlock<TInput> target, TInput item)
+        {
+            return SendAsync<TInput>(target, item, CancellationToken.None);
+        }
+
+        /// <summary>Asynchronously offers a message to the target message block, allowing for postponement.</summary>
+        /// <typeparam name="TInput">Specifies the type of the data to post to the target.</typeparam>
+        /// <param name="target">The target to which to post the data.</param>
+        /// <param name="item">The item being offered to the target.</param>
+        /// <param name="cancellationToken">The cancellation token with which to request cancellation of the send operation.</param>
+        /// <returns>
+        /// <para>
+        /// A <see cref="System.Threading.Tasks.Task{Boolean}"/> that represents the asynchronous send.  If the target
+        /// accepts and consumes the offered element during the call to SendAsync, upon return
+        /// from the call the resulting <see cref="System.Threading.Tasks.Task{Boolean}"/> will be completed and its <see cref="System.Threading.Tasks.Task{Boolean}.Result">Result</see> 
+        /// property will return true.  If the target declines the offered element during the call, upon return from the call the resulting <see cref="System.Threading.Tasks.Task{Boolean}"/> will
+        /// be completed and its <see cref="System.Threading.Tasks.Task{Boolean}.Result">Result</see> property will return false. If the target
+        /// postpones the offered element, the element will be buffered until such time that the target consumes or releases it, at which
+        /// point the Task will complete, with its <see cref="System.Threading.Tasks.Task{Boolean}.Result"/> indicating whether the message was consumed.  If the target
+        /// never attempts to consume or release the message, the returned task will never complete.
+        /// </para>
+        /// <para>
+        /// If cancellation is requested before the target has successfully consumed the sent data, 
+        /// the returned task will complete in the Canceled state and the data will no longer be available to the target.
+        /// </para>
+        /// </returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="target"/> is null (Nothing in Visual Basic).</exception>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        public static Task<Boolean> SendAsync<TInput>(this ITargetBlock<TInput> target, TInput item, CancellationToken cancellationToken)
+        {
+            // Validate arguments.  No validation necessary for item.
+            if (target == null) throw new ArgumentNullException("target");
+            Contract.EndContractBlock();
+
+            // Fast path check for cancellation
+            if (cancellationToken.IsCancellationRequested)
+                return Common.CreateTaskFromCancellation<Boolean>(cancellationToken);
+
+            SendAsyncSource<TInput> source;
+
+            // Fast path: try to offer the item synchronously.  This first try is done
+            // without any form of cancellation, and thus consumeToAccept can be the better-performing "false".
+            try
+            {
+                switch (target.OfferMessage(Common.SingleMessageHeader, item, source: null, consumeToAccept: false))
+                {
+                    // If the message is immediately accepted, return a cached completed task with a true result
+                    case DataflowMessageStatus.Accepted:
+                        return Common.CompletedTaskWithTrueResult;
+
+                    // If the target is declining permanently, return a cached completed task with a false result
+                    case DataflowMessageStatus.DecliningPermanently:
+                        return Common.CompletedTaskWithFalseResult;
+
+#if DEBUG
+                    case DataflowMessageStatus.Postponed:
+                        Debug.Assert(false, "A message should never be postponed when no source has been provided");
+                        break;
+
+                    case DataflowMessageStatus.NotAvailable:
+                        Debug.Assert(false, "The message should never be missed, as it's offered to only this one target");
+                        break;
+#endif
+                }
+
+                // Slow path: the target did not accept the synchronous post, nor did it decline it.
+                // Create a source for the send, launch the offering, and return the representative task.
+                // This ctor attempts to register a cancellation notification which would throw if the
+                // underlying CTS has been disposed of. Therefore, keep it inside the try/catch block.
+                source = new SendAsyncSource<TInput>(target, item, cancellationToken);
+            }
+            catch (Exception exc)
+            {
+                // If the target throws from OfferMessage, return a faulted task
+                Common.StoreDataflowMessageValueIntoExceptionData(exc, item);
+                return Common.CreateTaskFromException<Boolean>(exc);
+            }
+
+            Debug.Assert(source != null, "The SendAsyncSource instance must have been constructed.");
+            source.OfferToTarget(); // synchronous to preserve message ordering
+            return source.Task;
+        }
+
+        /// <summary>
+        /// Provides a source used by SendAsync that will buffer a single message and signal when it's been accepted or declined.
+        /// </summary>
+        /// <remarks>This source must only be passed to a single target, and must only be used once.</remarks>
+        [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+        [DebuggerTypeProxy(typeof(SendAsyncSource<>.DebugView))]
+        private sealed class SendAsyncSource<TOutput> : TaskCompletionSource<Boolean>, ISourceBlock<TOutput>, IDebuggerDisplay
+        {
+            /// <summary>The target to offer to.</summary>
+            private readonly ITargetBlock<TOutput> _target;
+            /// <summary>The buffered message.</summary>
+            private readonly TOutput _messageValue;
+
+            /// <summary>CancellationToken used to cancel the send.</summary>
+            private CancellationToken _cancellationToken;
+            /// <summary>Registration with the cancellation token.</summary>
+            private CancellationTokenRegistration _cancellationRegistration;
+            /// <summary>The cancellation/completion state of the source.</summary>
+            private int _cancellationState; // one of the CANCELLATION_STATE_* constant values, defaulting to NONE
+
+            // Cancellation states:
+            // _cancellationState starts out as NONE, and will remain that way unless a CancellationToken
+            // is provided in the initial OfferToTarget call.  As such, unless a token is provided,
+            // all synchronization related to cancellation will be avoided.  Once a token is provided,
+            // the state transitions to REGISTERED.  If cancellation then is requested or if the target
+            // calls back to consume the message, the state will transition to COMPLETING prior to 
+            // actually committing the action; if it can't transition to COMPLETING, then the action doesn't
+            // take effect (e.g. if cancellation raced with the target consuming, such that the cancellation
+            // action was able to transition to COMPLETING but the consumption wasn't, then ConsumeMessage
+            // would return false indicating that the message could not be consumed).  The only additional
+            // complication here is around reservations.  If a target reserves a message, _cancellationState
+            // transitions to RESERVED.  A subsequent ConsumeMessage call can successfully transition from
+            // RESERVED to COMPLETING, but cancellation can't; cancellation can only transition from REGISTERED
+            // to COMPLETING.  If the reservation on the message is instead released, _cancellationState
+            // will transition back to REGISTERED.
+
+            /// <summary>No cancellation registration is used.</summary>
+            private const int CANCELLATION_STATE_NONE = 0;
+            /// <summary>A cancellation token has been registered.</summary>
+            private const int CANCELLATION_STATE_REGISTERED = 1;
+            /// <summary>The message has been reserved. Only used if a cancellation token is in play.</summary>
+            private const int CANCELLATION_STATE_RESERVED = 2;
+            /// <summary>Completion is now in progress. Only used if a cancellation token is in play.</summary>
+            private const int CANCELLATION_STATE_COMPLETING = 3;
+
+            /// <summary>Initializes the source.</summary>
+            /// <param name="target">The target to offer to.</param>
+            /// <param name="messageValue">The message to offer and buffer.</param>
+            /// <param name="cancellationToken">The cancellation token with which to cancel the send.</param>
+            internal SendAsyncSource(ITargetBlock<TOutput> target, TOutput messageValue, CancellationToken cancellationToken)
+            {
+                Contract.Requires(target != null, "A valid target to send to is required.");
+                _target = target;
+                _messageValue = messageValue;
+
+                // If a cancelable CancellationToken is used, update our cancellation state
+                // and register with the token.  Only if CanBeCanceled is true due we want
+                // to pay the subsequent costs around synchronization between cancellation
+                // requests and the target coming back to consume the message.
+                if (cancellationToken.CanBeCanceled)
+                {
+                    _cancellationToken = cancellationToken;
+                    _cancellationState = CANCELLATION_STATE_REGISTERED;
+
+                    try
+                    {
+                        _cancellationRegistration = cancellationToken.Register(
+                            _cancellationCallback, new WeakReference<SendAsyncSource<TOutput>>(this));
+                    }
+                    catch
+                    {
+                        // Suppress finalization.  Finalization is only required if the target drops a reference
+                        // to the source before the source has completed, and we'll never offer to the target.
+                        GC.SuppressFinalize(this);
+
+                        // Propagate the exception
+                        throw;
+                    }
+                }
+            }
+
+            /// <summary>Finalizer that completes the returned task if all references to this source are dropped.</summary>
+            ~SendAsyncSource()
+            {
+                // CompleteAsDeclined uses synchronization, which is dangerous for a finalizer 
+                // during shutdown or appdomain unload.
+                if (!Environment.HasShutdownStarted)
+                {
+                    CompleteAsDeclined(runAsync: true);
+                }
+            }
+
+            /// <summary>Completes the source in an "Accepted" state.</summary>
+            /// <param name="runAsync">true to accept asynchronously; false to accept synchronously.</param>
+            private void CompleteAsAccepted(bool runAsync)
+            {
+                RunCompletionAction(state =>
+                {
+                    try { ((SendAsyncSource<TOutput>)state).TrySetResult(true); }
+                    catch (ObjectDisposedException) { }
+                }, this, runAsync);
+            }
+
+            /// <summary>Completes the source in an "Declined" state.</summary>
+            /// <param name="runAsync">true to decline asynchronously; false to decline synchronously.</param>
+            private void CompleteAsDeclined(bool runAsync)
+            {
+                RunCompletionAction(state =>
+                {
+                    // 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); }
+                    catch (ObjectDisposedException) { }
+                }, this, runAsync);
+            }
+
+            /// <summary>Completes the source in faulted state.</summary>
+            /// <param name="exception">The exception with which to fault.</param>
+            /// <param name="runAsync">true to fault asynchronously; false to fault synchronously.</param>
+            private void CompleteAsFaulted(Exception exception, bool runAsync)
+            {
+                RunCompletionAction(state =>
+                {
+                    var tuple = (Tuple<SendAsyncSource<TOutput>, Exception>)state;
+                    try { tuple.Item1.TrySetException(tuple.Item2); }
+                    catch (ObjectDisposedException) { }
+                }, Tuple.Create<SendAsyncSource<TOutput>, Exception>(this, exception), runAsync);
+            }
+
+            /// <summary>Completes the source in canceled state.</summary>
+            /// <param name="runAsync">true to fault asynchronously; false to fault synchronously.</param>
+            private void CompleteAsCanceled(bool runAsync)
+            {
+                RunCompletionAction(state =>
+                {
+                    try { ((SendAsyncSource<TOutput>)state).TrySetCanceled(); }
+                    catch (ObjectDisposedException) { }
+                }, this, runAsync);
+            }
+
+            /// <summary>Executes a completion action.</summary>
+            /// <param name="completionAction">The action to execute, passed the state.</param>
+            /// <param name="completionActionState">The state to pass into the delegate.</param>
+            /// <param name="runAsync">true to execute the action asynchronously; false to execute it synchronously.</param>
+            /// <remarks>
+            /// async should be true if this is being called on a path that has the target on the stack, e.g.
+            /// 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>
+            [SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly")]
+            private void RunCompletionAction(Action<object> completionAction, object completionActionState, bool runAsync)
+            {
+                Contract.Requires(completionAction != null, "Completion action to run is required.");
+
+                // Suppress finalization.  Finalization is only required if the target drops a reference
+                // to the source before the source has completed, and here we're completing the source.
+                GC.SuppressFinalize(this);
+
+                // Dispose of the cancellation registration if there is one
+                if (_cancellationState != CANCELLATION_STATE_NONE)
+                {
+                    Debug.Assert(_cancellationRegistration != default(CancellationTokenRegistration),
+                        "If we're not in NONE, we must have a cancellation token we've registered with.");
+                    _cancellationRegistration.Dispose();
+                }
+
+                // If we're meant to run asynchronously, launch a task.
+                if (runAsync)
+                {
+                    System.Threading.Tasks.Task.Factory.StartNew(
+                        completionAction, completionActionState,
+                        CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+                }
+                // Otherwise, execute directly.
+                else
+                {
+                    completionAction(completionActionState);
+                }
+            }
+
+            /// <summary>Offers the message to the target asynchronously.</summary>
+            private void OfferToTargetAsync()
+            {
+                System.Threading.Tasks.Task.Factory.StartNew(
+                    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 readonly static 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)
+            {
+                SendAsyncSource<TOutput> source = Common.UnwrapWeakReference<SendAsyncSource<TOutput>>(state);
+                if (source != null)
+                {
+                    Debug.Assert(source._cancellationState != CANCELLATION_STATE_NONE,
+                        "If cancellation is in play, we must have already moved out of the NONE state.");
+
+                    // Try to reserve completion, and if we can, complete as canceled.  Note that we can only
+                    // achieve cancellation when in the REGISTERED state, and not when in the RESERVED state, 
+                    // as if a target has reserved the message, we must allow the message to be consumed successfully.
+                    if (source._cancellationState == CANCELLATION_STATE_REGISTERED && // fast check to avoid the interlocked if we can
+                        Interlocked.CompareExchange(ref source._cancellationState, CANCELLATION_STATE_COMPLETING, CANCELLATION_STATE_REGISTERED) == CANCELLATION_STATE_REGISTERED)
+                    {
+                        // We've reserved completion, so proceed to cancel the task.
+                        source.CompleteAsCanceled(true);
+                    }
+                }
+            }
+
+            /// <summary>Offers the message to the target synchronously.</summary>
+            [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+            internal void OfferToTarget()
+            {
+                try
+                {
+                    // Offer the message to the target.  If there's no cancellation in play, we can just allow the target
+                    // to accept the message directly.  But if a CancellationToken is in use, the target needs to come
+                    // back to us to get the data; that way, we can ensure we don't race between returning a canceled task but
+                    // successfully completing the send.
+                    bool consumeToAccept = _cancellationState != CANCELLATION_STATE_NONE;
+
+                    switch (_target.OfferMessage(
+                        Common.SingleMessageHeader, _messageValue, this, consumeToAccept: consumeToAccept))
+                    {
+                        // If the message is immediately accepted, complete the task as accepted
+                        case DataflowMessageStatus.Accepted:
+                            if (!consumeToAccept)
+                            {
+                                // Cancellation wasn't in use, and the target accepted the message directly,
+                                // so complete the task as accepted.
+                                CompleteAsAccepted(runAsync: false);
+                            }
+                            else
+                            {
+                                // If cancellation is in use, then since the target accepted,
+                                // our state better reflect that we're completing.
+                                Debug.Assert(_cancellationState == CANCELLATION_STATE_COMPLETING,
+                                    "The message was accepted, so we should have started completion.");
+                            }
+                            break;
+
+                        // If the message is immediately declined, complete the task as declined
+                        case DataflowMessageStatus.Declined:
+                        case DataflowMessageStatus.DecliningPermanently:
+                            CompleteAsDeclined(runAsync: false);
+                            break;
+#if DEBUG
+                        case DataflowMessageStatus.NotAvailable:
+                            Debug.Assert(false, "The message should never be missed, as it's offered to only this one target");
+                            break;
+                            // If the message was postponed, the source may or may not be complete yet.  Nothing to validate.
+                            // Treat an improper DataflowMessageStatus as postponed and do nothing.
+#endif
+                    }
+                }
+                // A faulty target might throw from OfferMessage.  If that happens,
+                // we'll try to fault the returned task.  A really faulty target might
+                // both throw from OfferMessage and call ConsumeMessage,
+                // in which case it's possible we might not be able to propagate the exception
+                // out to the caller through the task if ConsumeMessage wins the race,
+                // which is likely if the exception doesn't occur until after ConsumeMessage is
+                // called.  If that happens, we just eat the exception.
+                catch (Exception exc)
+                {
+                    Common.StoreDataflowMessageValueIntoExceptionData(exc, _messageValue);
+                    CompleteAsFaulted(exc, runAsync: false);
+                }
+            }
+
+            /// <summary>Called by the target to consume the buffered message.</summary>
+            TOutput ISourceBlock<TOutput>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out Boolean messageConsumed)
+            {
+                // Validate arguments
+                if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+                if (target == null) throw new ArgumentNullException("target");
+                Contract.EndContractBlock();
+
+                // If the task has already completed, there's nothing to consume.  This could happen if
+                // cancellation was already requested and completed the task as a result.
+                if (Task.IsCompleted)
+                {
+                    messageConsumed = false;
+                    return default(TOutput);
+                }
+
+                // If the message being asked for is not the same as the one that's buffered,
+                // something is wrong.  Complete as having failed to transfer the message.
+                bool validMessage = (messageHeader.Id == Common.SINGLE_MESSAGE_ID);
+
+                if (validMessage)
+                {
+                    int curState = _cancellationState;
+                    Debug.Assert(
+                        curState == CANCELLATION_STATE_NONE || curState == CANCELLATION_STATE_REGISTERED ||
+                        curState == CANCELLATION_STATE_RESERVED || curState == CANCELLATION_STATE_COMPLETING,
+                        "The current cancellation state is not valid.");
+
+                    // If we're not dealing with cancellation, then if we're currently registered or reserved, try to transition 
+                    // to completing. If we're able to, allow the message to be consumed, and we're done.  At this point, we 
+                    // support transitioning out of REGISTERED or RESERVED.
+                    if (curState == CANCELLATION_STATE_NONE || // no synchronization necessary if there's no cancellation
+                        (curState != CANCELLATION_STATE_COMPLETING && // fast check to avoid unnecessary synchronization
+                         Interlocked.CompareExchange(ref _cancellationState, CANCELLATION_STATE_COMPLETING, curState) == curState))
+                    {
+                        CompleteAsAccepted(runAsync: true);
+                        messageConsumed = true;
+                        return _messageValue;
+                    }
+                }
+
+                // Consumption failed
+                messageConsumed = false;
+                return default(TOutput);
+            }
+
+            /// <summary>Called by the target to reserve the buffered message.</summary>
+            bool ISourceBlock<TOutput>.ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
+            {
+                // Validate arguments
+                if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+                if (target == null) throw new ArgumentNullException("target");
+                Contract.EndContractBlock();
+
+                // If the task has already completed, such as due to cancellation, there's nothing to reserve.
+                if (Task.IsCompleted) return false;
+
+                // As long as the message is the one being requested and cancellation hasn't been requested, allow it to be reserved.
+                bool reservable = (messageHeader.Id == Common.SINGLE_MESSAGE_ID);
+                return reservable &&
+                    (_cancellationState == CANCELLATION_STATE_NONE || // avoid synchronization when cancellation is not in play
+                     Interlocked.CompareExchange(ref _cancellationState, CANCELLATION_STATE_RESERVED, CANCELLATION_STATE_REGISTERED) == CANCELLATION_STATE_REGISTERED);
+            }
+
+            /// <summary>Called by the target to release a reservation on the buffered message.</summary>
+            void ISourceBlock<TOutput>.ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
+            {
+                // Validate arguments
+                if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+                if (target == null) throw new ArgumentNullException("target");
+                Contract.EndContractBlock();
+
+                // If this is not the message we posted, bail
+                if (messageHeader.Id != Common.SINGLE_MESSAGE_ID)
+                    throw new InvalidOperationException(SR.InvalidOperation_MessageNotReservedByTarget);
+
+                // If the task has already completed, there's nothing to release.
+                if (Task.IsCompleted) return;
+
+                // If a cancellation token is being used, revert our state back to registered.  In the meantime
+                // cancellation could have been requested, so check to see now if cancellation was requested
+                // and process it if it was.
+                if (_cancellationState != CANCELLATION_STATE_NONE)
+                {
+                    if (Interlocked.CompareExchange(ref _cancellationState, CANCELLATION_STATE_REGISTERED, CANCELLATION_STATE_RESERVED) != CANCELLATION_STATE_RESERVED)
+                        throw new InvalidOperationException(SR.InvalidOperation_MessageNotReservedByTarget);
+                    if (_cancellationToken.IsCancellationRequested)
+                        CancellationHandler(new WeakReference<SendAsyncSource<TOutput>>(this)); // same code as registered with the CancellationToken
+                }
+
+                // Start the process over by reoffering the message asynchronously.
+                OfferToTargetAsync();
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+            Task IDataflowBlock.Completion { get { return Task; } }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+            IDisposable ISourceBlock<TOutput>.LinkTo(ITargetBlock<TOutput> target, DataflowLinkOptions linkOptions) { throw new NotSupportedException(SR.NotSupported_MemberNotNeeded); }
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+            void IDataflowBlock.Complete() { throw new NotSupportedException(SR.NotSupported_MemberNotNeeded); }
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+            void IDataflowBlock.Fault(Exception exception) { throw new NotSupportedException(SR.NotSupported_MemberNotNeeded); }
+
+            /// <summary>The data to display in the debugger display attribute.</summary>
+            [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+            private object DebuggerDisplayContent
+            {
+                get
+                {
+                    var displayTarget = _target as IDebuggerDisplay;
+                    return string.Format("{0} Message={1}, Target=\"{2}\"",
+                        Common.GetNameForDebugger(this),
+                        _messageValue,
+                        displayTarget != null ? displayTarget.Content : _target);
+                }
+            }
+            /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+            object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+            /// <summary>Provides a debugger type proxy for the source.</summary>
+            private sealed class DebugView
+            {
+                /// <summary>The source.</summary>
+                private readonly SendAsyncSource<TOutput> _source;
+
+                /// <summary>Initializes the debug view.</summary>
+                /// <param name="source">The source to view.</param>
+                public DebugView(SendAsyncSource<TOutput> source)
+                {
+                    Contract.Requires(source != null, "Need a source with which to construct the debug view.");
+                    _source = source;
+                }
+
+                /// <summary>The target to which we're linked.</summary>
+                public ITargetBlock<TOutput> Target { get { return _source._target; } }
+                /// <summary>The message buffered by the source.</summary>
+                public TOutput Message { get { return _source._messageValue; } }
+                /// <summary>The Task represented the posting of the message.</summary>
+                public Task<bool> Completion { get { return _source.Task; } }
+            }
+        }
+        #endregion
+
+        #region TryReceive, ReceiveAsync, and Receive
+        #region TryReceive
+        /// <summary>
+        /// Attempts to synchronously receive an item from the <see cref="T:System.Threading.Tasks.Dataflow.ISourceBlock`1"/>.
+        /// </summary>
+        /// <param name="source">The source from which to receive.</param>
+        /// <param name="item">The item received from the source.</param>
+        /// <returns>true if an item could be received; otherwise, false.</returns>
+        /// <remarks>
+        /// 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)
+        {
+            if (source == null) throw new ArgumentNullException("source");
+            Contract.EndContractBlock();
+
+            return source.TryReceive(null, out item);
+        }
+        #endregion
+
+        #region ReceiveAsync
+        /// <summary>Asynchronously receives a value from the specified source.</summary>
+        /// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
+        /// <param name="source">The source from which to asynchronously receive.</param>
+        /// <returns>
+        /// A <see cref="System.Threading.Tasks.Task{TOutput}"/> that represents the asynchronous receive operation.  When an item is successfully received from the source,
+        /// the returned task will be completed and its <see cref="System.Threading.Tasks.Task{TOutput}.Result">Result</see> will return the received item.  If an item cannot be retrieved,
+        /// because the source is empty and completed, the returned task will be canceled.
+        /// </returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source"/> is null (Nothing in Visual Basic).</exception>
+        public static Task<TOutput> ReceiveAsync<TOutput>(
+            this ISourceBlock<TOutput> source)
+        {
+            // Argument validation handled by target method
+            return ReceiveAsync(source, Common.InfiniteTimeSpan, CancellationToken.None);
+        }
+
+        /// <summary>Asynchronously receives a value from the specified source.</summary>
+        /// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
+        /// <param name="source">The source from which to asynchronously receive.</param>
+        /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> which may be used to cancel the receive operation.</param>
+        /// <returns>
+        /// A <see cref="System.Threading.Tasks.Task{TOutput}"/> that represents the asynchronous receive operation.  When an item is successfully received from the source,
+        /// the returned task will be completed and its <see cref="System.Threading.Tasks.Task{TOutput}.Result">Result</see> will return the received item.  If an item cannot be retrieved,
+        /// either because cancellation is requested or the source is empty and completed, the returned task will be canceled.
+        /// </returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source"/> is null (Nothing in Visual Basic).</exception>
+        public static Task<TOutput> ReceiveAsync<TOutput>(
+            this ISourceBlock<TOutput> source, CancellationToken cancellationToken)
+        {
+            // Argument validation handled by target method
+            return ReceiveAsync(source, Common.InfiniteTimeSpan, cancellationToken);
+        }
+
+        /// <summary>Asynchronously receives a value from the specified source.</summary>
+        /// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
+        /// <param name="source">The source from which to asynchronously receive.</param>
+        /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely.</param>
+        /// <returns>
+        /// A <see cref="System.Threading.Tasks.Task{TOutput}"/> that represents the asynchronous receive operation.  When an item is successfully received from the source,
+        /// the returned task will be completed and its <see cref="System.Threading.Tasks.Task{TOutput}.Result">Result</see> will return the received item.  If an item cannot be retrieved,
+        /// either because the timeout expires or the source is empty and completed, the returned task will be canceled.
+        /// </returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentOutOfRangeException">
+        /// timeout is a negative number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than <see cref="System.Int32.MaxValue"/>.
+        /// </exception>
+        public static Task<TOutput> ReceiveAsync<TOutput>(
+            this ISourceBlock<TOutput> source, TimeSpan timeout)
+        {
+            // Argument validation handled by target method
+            return ReceiveAsync(source, timeout, CancellationToken.None);
+        }
+
+        /// <summary>Asynchronously receives a value from the specified source.</summary>
+        /// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
+        /// <param name="source">The source from which to asynchronously receive.</param>
+        /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely.</param>
+        /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> which may be used to cancel the receive operation.</param>
+        /// <returns>
+        /// A <see cref="System.Threading.Tasks.Task{TOutput}"/> that represents the asynchronous receive operation.  When an item is successfully received from the source,
+        /// the returned task will be completed and its <see cref="System.Threading.Tasks.Task{TOutput}.Result">Result</see> will return the received item.  If an item cannot be retrieved,
+        /// either because the timeout expires, cancellation is requested, or the source is empty and completed, the returned task will be canceled.
+        /// </returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentOutOfRangeException">
+        /// timeout is a negative number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than <see cref="System.Int32.MaxValue"/>.
+        /// </exception>
+        public static Task<TOutput> ReceiveAsync<TOutput>(
+            this ISourceBlock<TOutput> source, TimeSpan timeout, CancellationToken cancellationToken)
+        {
+            // Validate arguments
+
+
+            if (source == null) throw new ArgumentNullException("source");
+            if (!Common.IsValidTimeout(timeout)) throw new ArgumentOutOfRangeException("timeout", SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
+
+            // Return the task representing the core receive operation
+            return ReceiveCore(source, true, timeout, cancellationToken);
+        }
+        #endregion
+
+        #region Receive
+        /// <summary>Synchronously receives an item from the source.</summary>
+        /// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
+        /// <param name="source">The source from which to receive.</param>
+        /// <returns>The received item.</returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.InvalidOperationException">No item could be received from the source.</exception>
+        public static TOutput Receive<TOutput>(
+            this ISourceBlock<TOutput> source)
+        {
+            // Argument validation handled by target method
+            return Receive(source, Common.InfiniteTimeSpan, CancellationToken.None);
+        }
+
+        /// <summary>Synchronously receives an item from the source.</summary>
+        /// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
+        /// <param name="source">The source from which to receive.</param>
+        /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> which may be used to cancel the receive operation.</param>
+        /// <returns>The received item.</returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.InvalidOperationException">No item could be received from the source.</exception>
+        /// <exception cref="System.OperationCanceledException">The operation was canceled before an item was received from the source.</exception>
+        /// <remarks>
+        /// If the source successfully offered an item that was received by this operation, it will be returned, even if a concurrent cancellation request occurs.
+        /// </remarks>
+        public static TOutput Receive<TOutput>(
+            this ISourceBlock<TOutput> source, CancellationToken cancellationToken)
+        {
+            // Argument validation handled by target method
+            return Receive(source, Common.InfiniteTimeSpan, cancellationToken);
+        }
+
+        /// <summary>Synchronously receives an item from the source.</summary>
+        /// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
+        /// <param name="source">The source from which to receive.</param>
+        /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely.</param>
+        /// <returns>The received item.</returns>
+        /// <exception cref="System.ArgumentOutOfRangeException">
+        /// timeout is a negative number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than <see cref="System.Int32.MaxValue"/>.
+        /// </exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.InvalidOperationException">No item could be received from the source.</exception>
+        /// <exception cref="System.TimeoutException">The specified timeout expired before an item was received from the source.</exception>
+        /// <remarks>
+        /// If the source successfully offered an item that was received by this operation, it will be returned, even if a concurrent timeout occurs.
+        /// </remarks>
+        public static TOutput Receive<TOutput>(
+            this ISourceBlock<TOutput> source, TimeSpan timeout)
+        {
+            // Argument validation handled by target method
+            return Receive(source, timeout, CancellationToken.None);
+        }
+
+        /// <summary>Synchronously receives an item from the source.</summary>
+        /// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
+        /// <param name="source">The source from which to receive.</param>
+        /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely.</param>
+        /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> which may be used to cancel the receive operation.</param>
+        /// <returns>The received item.</returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentOutOfRangeException">
+        /// timeout is a negative number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than <see cref="System.Int32.MaxValue"/>.
+        /// </exception>
+        /// <exception cref="System.InvalidOperationException">No item could be received from the source.</exception>
+        /// <exception cref="System.TimeoutException">The specified timeout expired before an item was received from the source.</exception>
+        /// <exception cref="System.OperationCanceledException">The operation was canceled before an item was received from the source.</exception>
+        /// <remarks>
+        /// If the source successfully offered an item that was received by this operation, it will be returned, even if a concurrent timeout or cancellation request occurs.
+        /// </remarks>
+        [SuppressMessage("Microsoft.Usage", "CA2200:RethrowToPreserveStackDetails")]
+        public static TOutput Receive<TOutput>(
+            this ISourceBlock<TOutput> source, TimeSpan timeout, CancellationToken cancellationToken)
+        {
+            // Validate arguments
+            if (source == null) throw new ArgumentNullException("source");
+            if (!Common.IsValidTimeout(timeout)) throw new ArgumentOutOfRangeException("timeout", SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
+
+            // Do fast path checks for both cancellation and data already existing.
+            cancellationToken.ThrowIfCancellationRequested();
+            TOutput fastCheckedItem;
+            var receivableSource = source as IReceivableSourceBlock<TOutput>;
+            if (receivableSource != null && receivableSource.TryReceive(null, out fastCheckedItem))
+            {
+                return fastCheckedItem;
+            }
+
+            // Get a TCS to represent the receive operation and wait for it to complete.
+            // If it completes successfully, return the result. Otherwise, throw the 
+            // original inner exception representing the cause.  This could be an OCE.
+            Task<TOutput> task = ReceiveCore(source, false, timeout, cancellationToken);
+            try
+            {
+                return task.GetAwaiter().GetResult(); // block until the result is available
+            }
+            catch
+            {
+                // Special case cancellation in order to ensure the exception contains the token.
+                // The public TrySetCanceled, used by ReceiveCore, is parameterless and doesn't 
+                // accept the token to use.  Thus the exception that we're catching here
+                // won't contain the cancellation token we want propagated.
+                if (task.IsCanceled) cancellationToken.ThrowIfCancellationRequested();
+
+                // If we get here, propagate the original exception.
+                throw;
+            }
+        }
+        #endregion
+
+        #region Shared by Receive and ReceiveAsync
+        /// <summary>Receives an item from the source.</summary>
+        /// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
+        /// <param name="source">The source from which to receive.</param>
+        /// <param name="attemptTryReceive">Whether to first attempt using TryReceive to get a value from the source.</param>
+        /// <param name="timeout">A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely.</param>
+        /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> which may be used to cancel the receive operation.</param>
+        /// <returns>A Task for the receive operation.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private static Task<TOutput> ReceiveCore<TOutput>(
+            this ISourceBlock<TOutput> source, bool attemptTryReceive, TimeSpan timeout, CancellationToken cancellationToken)
+        {
+            Contract.Requires(source != null, "Need a source from which to receive.");
+
+            // If cancellation has been requested, we're done before we've even started, cancel this receive.
+            if (cancellationToken.IsCancellationRequested)
+            {
+                return Common.CreateTaskFromCancellation<TOutput>(cancellationToken);
+            }
+
+            if (attemptTryReceive)
+            {
+                // If we're able to directly and immediately receive an item, use that item to complete the receive.
+                var receivableSource = source as IReceivableSourceBlock<TOutput>;
+                if (receivableSource != null)
+                {
+                    try
+                    {
+                        TOutput fastCheckedItem;
+                        if (receivableSource.TryReceive(null, out fastCheckedItem))
+                        {
+                            return Task.FromResult<TOutput>(fastCheckedItem);
+                        }
+                    }
+                    catch (Exception exc)
+                    {
+                        return Common.CreateTaskFromException<TOutput>(exc);
+                    }
+                }
+            }
+
+            int millisecondsTimeout = (int)timeout.TotalMilliseconds;
+            if (millisecondsTimeout == 0)
+            {
+                return Common.CreateTaskFromException<TOutput>(ReceiveTarget<TOutput>.CreateExceptionForTimeout());
+            }
+
+            return ReceiveCoreByLinking<TOutput>(source, millisecondsTimeout, cancellationToken);
+        }
+
+        /// <summary>The reason for a ReceiveCoreByLinking call failing.</summary>
+        private enum ReceiveCoreByLinkingCleanupReason
+        {
+            /// <summary>The Receive operation completed successfully, obtaining a value from the source.</summary>
+            Success = 0,
+            /// <summary>The timer expired before a value could be received.</summary>
+            Timer = 1,
+            /// <summary>The cancellation token had cancellation requested before a value could be received.</summary>
+            Cancellation = 2,
+            /// <summary>The source completed before a value could be received.</summary>
+            SourceCompletion = 3,
+            /// <summary>An error occurred while linking up the target.</summary>
+            SourceProtocolError = 4,
+            /// <summary>An error during cleanup after completion for another reason.</summary>
+            ErrorDuringCleanup = 5
+        }
+
+        /// <summary>Cancels a CancellationTokenSource passed as the object state argument.</summary>
+        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>
+        /// <param name="source">The source from which to receive.</param>
+        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or -1 to wait indefinitely.</param>
+        /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> which may be used to cancel the receive operation.</param>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private static Task<TOutput> ReceiveCoreByLinking<TOutput>(ISourceBlock<TOutput> source, int millisecondsTimeout, CancellationToken cancellationToken)
+        {
+            // Create a target to link from the source
+            var target = new ReceiveTarget<TOutput>();
+
+            // Keep cancellation registrations inside the try/catch in case the underlying CTS is disposed in which case an exception is thrown
+            try
+            {
+                // Create a cancellation token that will be canceled when either the provided token 
+                // is canceled or the source block completes.
+                if (cancellationToken.CanBeCanceled)
+                {
+                    target._externalCancellationToken = cancellationToken;
+                    target._regFromExternalCancellationToken = cancellationToken.Register(_cancelCts, target._cts);
+                }
+
+                // We need to cleanup if one of a few things happens:
+                // - The target completes successfully due to receiving data.
+                // - The user-specified timeout occurs, such that we should bail on the receive.
+                // - The cancellation token has cancellation requested, such that we should bail on the receive.
+                // - The source completes, since it won't send any more data.
+                // Note that there's a potential race here, in that the cleanup delegate could be executed
+                // from the timer before the timer variable is set, but that's ok, because then timer variable
+                // will just show up as null in the cleanup and there will be nothing to dispose (nor will anything
+                // need to be disposed, since it's the timer that fired.  Timer.Dispose is also thread-safe to be 
+                // called multiple times concurrently.)
+                if (millisecondsTimeout > 0)
+                {
+                    target._timer = new Timer(
+                        ReceiveTarget<TOutput>.CachedLinkingTimerCallback, target,
+                        millisecondsTimeout, Timeout.Infinite);
+                }
+
+                if (target._cts.Token.CanBeCanceled)
+                {
+                    target._cts.Token.Register(
+                        ReceiveTarget<TOutput>.CachedLinkingCancellationCallback, target); // we don't have to cleanup this registration, as this cts is short-lived
+                }
+
+                // Link the target to the source
+                IDisposable unlink = source.LinkTo(target, DataflowLinkOptions.UnlinkAfterOneAndPropagateCompletion);
+                target._unlink = unlink;
+
+                // If completion has started, there is a chance it started after we linked.
+                // In that case, we must dispose of the unlinker.
+                // If completion started before we linked, the cleanup code will try to unlink.
+                // So we are racing to dispose of the unlinker.
+                if (Volatile.Read(ref target._cleanupReserved))
+                {
+                    IDisposable disposableUnlink = Interlocked.CompareExchange(ref target._unlink, null, unlink);
+                    if (disposableUnlink != null) disposableUnlink.Dispose();
+                }
+            }
+            catch (Exception exception)
+            {
+                target._receivedException = exception;
+                target.TryCleanupAndComplete(ReceiveCoreByLinkingCleanupReason.SourceProtocolError);
+                // If we lose the race here, we may end up eating this exception.
+            }
+
+            return target.Task;
+        }
+
+        /// <summary>Provides a TaskCompletionSource that is also a dataflow target for use in ReceiveCore.</summary>
+        /// <typeparam name="T">Specifies the type of data offered to the target.</typeparam>
+        [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
+        [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+        private sealed class ReceiveTarget<T> : TaskCompletionSource<T>, ITargetBlock<T>, IDebuggerDisplay
+        {
+            /// <summary>Cached delegate used in ReceiveCoreByLinking on the created timer.  Passed the ReceiveTarget as the 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 readonly static TimerCallback CachedLinkingTimerCallback = 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 readonly static Action<object> CachedLinkingCancellationCallback = 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;
+
+            /// <summary>The cancellation token source representing both external and internal cancellation.</summary>
+            internal readonly CancellationTokenSource _cts = new CancellationTokenSource();
+            /// <summary>Indicates a code path is already on route to complete the target. 0 is false, 1 is true.</summary>
+            internal bool _cleanupReserved; // must only be accessed under IncomingLock
+            /// <summary>The external token that cancels the internal token.</summary>
+            internal CancellationToken _externalCancellationToken;
+            /// <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;
+            /// <summary>The unlinker from removing this target from the source from which we're receiving.</summary>
+            internal IDisposable _unlink;
+            /// <summary>The received exception if an error occurred.</summary>
+            internal Exception _receivedException;
+
+            /// <summary>Gets the sync obj used to synchronize all activity on this target.</summary>
+            internal object IncomingLock { get { return _cts; } }
+
+            /// <summary>Initializes the target.</summary>
+            internal ReceiveTarget() { }
+
+            /// <summary>Offers a message to be used to complete the TaskCompletionSource.</summary>
+            [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+            DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, Boolean consumeToAccept)
+            {
+                // Validate arguments
+                if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+                if (source == null && consumeToAccept) throw new ArgumentException(SR.Argument_CantConsumeFromANullSource, "consumeToAccept");
+                Contract.EndContractBlock();
+
+                DataflowMessageStatus status = DataflowMessageStatus.NotAvailable;
+
+                // If we're already one our way to being done, don't accept anything.
+                // This is a fast-path check prior to taking the incoming lock;
+                // _cleanupReserved only ever goes from false to true.
+                if (Volatile.Read(ref _cleanupReserved)) return DataflowMessageStatus.DecliningPermanently;
+
+                lock (IncomingLock)
+                {
+                    // Check again now that we've taken the lock
+                    if (_cleanupReserved) return DataflowMessageStatus.DecliningPermanently;
+
+                    try
+                    {
+                        // 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;
+                        if (consumed)
+                        {
+                            status = DataflowMessageStatus.Accepted;
+                            _receivedValue = acceptedValue;
+                            _cleanupReserved = true;
+                        }
+                    }
+                    catch (Exception exc)
+                    {
+                        // An error occurred.  Take ourselves out of the game.
+                        status = DataflowMessageStatus.DecliningPermanently;
+                        Common.StoreDataflowMessageValueIntoExceptionData(exc, messageValue);
+                        _receivedException = exc;
+                        _cleanupReserved = true;
+                    }
+                }
+
+                // Do any cleanup outside of the lock.  The right to cleanup was reserved above for these cases.
+                if (status == DataflowMessageStatus.Accepted)
+                {
+                    CleanupAndComplete(ReceiveCoreByLinkingCleanupReason.Success);
+                }
+                else if (status == DataflowMessageStatus.DecliningPermanently) // should only be the case if an error occurred
+                {
+                    CleanupAndComplete(ReceiveCoreByLinkingCleanupReason.SourceProtocolError);
+                }
+
+                return status;
+            }
+
+            /// <summary>
+            /// Attempts to reserve the right to cleanup and complete, and if successfully, 
+            /// continues to cleanup and complete.
+            /// </summary>
+            /// <param name="reason">The reason we're completing and cleaning up.</param>
+            /// <returns>true if successful in completing; otherwise, false.</returns>
+            internal bool TryCleanupAndComplete(ReceiveCoreByLinkingCleanupReason reason)
+            {
+                // If cleanup was already reserved, bail.
+                if (Volatile.Read(ref _cleanupReserved)) return false;
+
+                // Atomically using IncomingLock try to reserve the completion routine.
+                lock (IncomingLock)
+                {
+                    if (_cleanupReserved) return false;
+                    _cleanupReserved = true;
+                }
+
+                // We've reserved cleanup and completion, so do it.
+                CleanupAndComplete(reason);
+                return true;
+            }
+
+            /// <summary>Cleans up the target for completion.</summary>
+            /// <param name="reason">The reason we're completing and cleaning up.</param>
+            /// <remarks>This method must only be called once on this instance.</remarks>
+            [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+            [SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
+            private void CleanupAndComplete(ReceiveCoreByLinkingCleanupReason reason)
+            {
+                Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+                Debug.Assert(Volatile.Read(ref _cleanupReserved), "Should only be called once by whomever reserved the right.");
+
+                // Unlink from the source.  If we're cleaning up because the source
+                // 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;
+                if (reason != ReceiveCoreByLinkingCleanupReason.SourceCompletion && unlink != null)
+                {
+                    IDisposable disposableUnlink = Interlocked.CompareExchange(ref _unlink, null, unlink);
+                    if (disposableUnlink != null)
+                    {
+                        // If an error occurs, fault the target and override the reason to
+                        // continue executing, i.e. do the remaining cleanup without completing
+                        // the target the way we originally intended to.
+                        try
+                        {
+                            disposableUnlink.Dispose(); // must not be holding IncomingLock, or could deadlock
+                        }
+                        catch (Exception exc)
+                        {
+                            _receivedException = exc;
+                            reason = ReceiveCoreByLinkingCleanupReason.SourceProtocolError;
+                        }
+                    }
+                }
+
+                // Cleanup the timer.  (Even if we're here because of the timer firing, we still
+                // want to aggressively dispose of the timer.)
+                if (_timer != null) _timer.Dispose();
+
+                // Cancel the token everyone is listening to.  We also want to unlink
+                // from the user-provided cancellation token to prevent a leak.
+                // We do *not* dispose of the cts itself here, as there could be a race
+                // with the code registering this cleanup delegate with cts; not disposing
+                // is ok, though, because there's no resources created by the CTS
+                // that needs to be cleaned up since we're not using the wait handle.
+                // This is also why we don't use CreateLinkedTokenSource, as that combines
+                // both disposing of the token source and disposal of the connection link
+                // into a single dispose operation.
+                // if we're here because of cancellation, no need to cancel again
+                if (reason != ReceiveCoreByLinkingCleanupReason.Cancellation)
+                {
+                    // if the source complete without receiving a value, we check the cancellation one more time
+                    if (reason == ReceiveCoreByLinkingCleanupReason.SourceCompletion &&
+                        (_externalCancellationToken.IsCancellationRequested || _cts.IsCancellationRequested))
+                    {
+                        reason = ReceiveCoreByLinkingCleanupReason.Cancellation;
+                    }
+                    _cts.Cancel();
+                }
+                _regFromExternalCancellationToken.Dispose();
+
+                // No need to dispose of the cts, either, as we're not accessing its WaitHandle
+                // nor was it created as a linked token source.  Disposing it could also be dangerous
+                // if other code tries to access it after we dispose of it... best to leave it available.
+
+                // Complete the task based on the reason
+                switch (reason)
+                {
+                    // Task final state: RanToCompletion
+                    case ReceiveCoreByLinkingCleanupReason.Success:
+                        System.Threading.Tasks.Task.Factory.StartNew(state =>
+                        {
+                            // Complete with the received value
+                            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;
+
+                    // Task final state: Canceled
+                    case ReceiveCoreByLinkingCleanupReason.Cancellation:
+                        System.Threading.Tasks.Task.Factory.StartNew(state =>
+                        {
+                            // Complete as canceled
+                            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);
+                        break;
+                    default:
+                        Debug.Assert(false, "Invalid linking cleanup reason specified.");
+                        goto case ReceiveCoreByLinkingCleanupReason.Cancellation;
+
+                    // Task final state: Faulted
+                    case ReceiveCoreByLinkingCleanupReason.SourceCompletion:
+                        if (_receivedException == null) _receivedException = CreateExceptionForSourceCompletion();
+                        goto case ReceiveCoreByLinkingCleanupReason.SourceProtocolError;
+                    case ReceiveCoreByLinkingCleanupReason.Timer:
+                        if (_receivedException == null) _receivedException = CreateExceptionForTimeout();
+                        goto case ReceiveCoreByLinkingCleanupReason.SourceProtocolError;
+                    case ReceiveCoreByLinkingCleanupReason.SourceProtocolError:
+                    case ReceiveCoreByLinkingCleanupReason.ErrorDuringCleanup:
+                        Debug.Assert(_receivedException != null, "We need an exception with which to fault the task.");
+                        System.Threading.Tasks.Task.Factory.StartNew(state =>
+                        {
+                            // Complete with the received exception
+                            var target = (ReceiveTarget<T>)state;
+                            try { target.TrySetException(target._receivedException ?? new Exception()); }
+                            catch (ObjectDisposedException) { /* benign race if returned task is already disposed */ }
+                        }, this, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
+                        break;
+                }
+            }
+
+            /// <summary>Creates an exception to use when a source completed before receiving a value.</summary>
+            /// <returns>The initialized exception.</returns>
+            internal static Exception CreateExceptionForSourceCompletion()
+            {
+                return Common.InitializeStackTrace(new InvalidOperationException(SR.InvalidOperation_DataNotAvailableForReceive));
+            }
+
+            /// <summary>Creates an exception to use when a timeout occurs before receiving a value.</summary>
+            /// <returns>The initialized exception.</returns>
+            internal static Exception CreateExceptionForTimeout()
+            {
+                return Common.InitializeStackTrace(new TimeoutException());
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+            void IDataflowBlock.Complete()
+            {
+                TryCleanupAndComplete(ReceiveCoreByLinkingCleanupReason.SourceCompletion);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+            void IDataflowBlock.Fault(Exception exception) { ((IDataflowBlock)this).Complete(); }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+            Task IDataflowBlock.Completion { get { throw new NotSupportedException(SR.NotSupported_MemberNotNeeded); } }
+
+            /// <summary>The data to display in the debugger display attribute.</summary>
+            [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+            private object DebuggerDisplayContent
+            {
+                get
+                {
+                    return string.Format("{0} IsCompleted={1}",
+                        Common.GetNameForDebugger(this), base.Task.IsCompleted);
+                }
+            }
+            /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+            object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+        }
+        #endregion
+        #endregion
+
+        #region OutputAvailableAsync
+        /// <summary>
+        /// Provides a <see cref="System.Threading.Tasks.Task{TResult}"/> 
+        /// that asynchronously monitors the source for available output.
+        /// </summary>
+        /// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
+        /// <param name="source">The source to monitor.</param>
+        /// <returns>
+        /// A <see cref="System.Threading.Tasks.Task{Boolean}"/> that informs of whether and when
+        /// more output is available.  When the task completes, if its <see cref="System.Threading.Tasks.Task{Boolean}.Result"/> is true, more output 
+        /// is available in the source (though another consumer of the source may retrieve the data).  
+        /// If it returns false, more output is not and will never be available, due to the source 
+        /// completing prior to output being available.
+        /// </returns>
+        public static Task<bool> OutputAvailableAsync<TOutput>(this ISourceBlock<TOutput> source)
+        {
+            return OutputAvailableAsync<TOutput>(source, CancellationToken.None);
+        }
+
+        /// <summary>
+        /// Provides a <see cref="System.Threading.Tasks.Task{TResult}"/> 
+        /// that asynchronously monitors the source for available output.
+        /// </summary>
+        /// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
+        /// <param name="source">The source to monitor.</param>
+        /// <param name="cancellationToken">The cancellation token with which to cancel the asynchronous operation.</param>
+        /// <returns>
+        /// A <see cref="System.Threading.Tasks.Task{Boolean}"/> that informs of whether and when
+        /// more output is available.  When the task completes, if its <see cref="System.Threading.Tasks.Task{Boolean}.Result"/> is true, more output 
+        /// is available in the source (though another consumer of the source may retrieve the data).  
+        /// If it returns false, more output is not and will never be available, due to the source 
+        /// completing prior to output being available.
+        /// </returns>
+        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
+        public static Task<bool> OutputAvailableAsync<TOutput>(
+            this ISourceBlock<TOutput> source, CancellationToken cancellationToken)
+        {
+            // Validate arguments
+            if (source == null) throw new ArgumentNullException("source");
+            Contract.EndContractBlock();
+
+            // Fast path for cancellation
+            if (cancellationToken.IsCancellationRequested)
+                return Common.CreateTaskFromCancellation<bool>(cancellationToken);
+
+            // In a method like this, normally we would want to check source.Completion.IsCompleted
+            // and avoid linking completely by simply returning a completed task.  However,
+            // some blocks that are completed still have data available, like WriteOnceBlock,
+            // which completes as soon as it gets a value and stores that value forever.
+            // As such, OutputAvailableAsync must link from the source so that the source
+            // can push data to us if it has it, at which point we can immediately unlink.
+
+            // Create a target task that will complete when it's offered a message (but it won't accept the message)
+            var target = new OutputAvailableAsyncTarget<TOutput>();
+            try
+            {
+                // Link from the source.  If the source propagates a message during or immediately after linking
+                // such that our target is already completed, just return its task.
+                target._unlinker = source.LinkTo(target, DataflowLinkOptions.UnlinkAfterOneAndPropagateCompletion);
+
+                // If the task is already completed (an exception may have occurred, or the source may have propagated
+                // a message to the target during LinkTo or soon thereafter), just return the task directly.
+                if (target.Task.IsCompleted)
+                {
+                    return target.Task;
+                }
+
+                // If cancellation could be requested, hook everything up to be notified of cancellation requests.
+                if (cancellationToken.CanBeCanceled)
+                {
+                    // When cancellation is requested, unlink the target from the source and cancel the target.
+                    target._ctr = cancellationToken.Register(OutputAvailableAsyncTarget<TOutput>.s_cancelAndUnlink, target);
+                }
+
+                // We can't return the task directly, as the source block will be completing the task synchronously,
+                // and thus any synchronous continuations would run as part of the source block's call.  We don't have to worry
+                // about cancellation, as we've coded cancellation to complete the task asynchronously, and with the continuation
+                // set as NotOnCanceled, so the continuation will be canceled immediately when the antecedent is canceled, which
+                // will thus be asynchronously from the cancellation token source's cancellation call.
+                return target.Task.ContinueWith(
+                    OutputAvailableAsyncTarget<TOutput>.s_handleCompletion, target,
+                    CancellationToken.None, Common.GetContinuationOptions() | TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default);
+            }
+            catch (Exception exc)
+            {
+                // Source.LinkTo could throw, as could cancellationToken.Register if cancellation was already requested
+                // such that it synchronously invokes the source's unlinker IDisposable, which could throw.
+                target.TrySetException(exc);
+
+                // Undo the link from the source to the target
+                target.AttemptThreadSafeUnlink();
+
+                // Return the now faulted task
+                return target.Task;
+            }
+        }
+
+        /// <summary>Provides a target used in OutputAvailableAsync operations.</summary>
+        /// <typeparam name="T">Specifies the type of data in the data source being checked.</typeparam>
+        [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+        private sealed class OutputAvailableAsyncTarget<T> : TaskCompletionSource<bool>, ITargetBlock<T>, IDebuggerDisplay
+        {
+            /// <summary>
+            /// Cached continuation delegate that unregisters from cancellation and
+            /// marshals the antecedent's result to the return value.
+            /// </summary>
+            internal readonly static Func<Task<bool>, object, bool> s_handleCompletion = (antecedent, state) =>
+            {
+                var target = state as OutputAvailableAsyncTarget<T>;
+                Debug.Assert(target != null, "Expected non-null target");
+                target._ctr.Dispose();
+                return antecedent.GetAwaiter().GetResult();
+            };
+
+            /// <summary>
+            /// Cached delegate that cancels the target and unlinks the target from the source.
+            /// Expects an OutputAvailableAsyncTarget as the state argument. 
+            /// </summary>
+            internal readonly static 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)
+            {
+                var target = state as OutputAvailableAsyncTarget<T>;
+                Debug.Assert(target != null, "Expected a non-null target");
+
+                // Cancel asynchronously so that we're not completing the task as part of the cts.Cancel() call,
+                // since synchronous continuations off that task would then run as part of Cancel.
+                // 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;
+                                                                thisTarget.TrySetCanceled();
+                                                                thisTarget.AttemptThreadSafeUnlink();
+                                                            },
+                    target, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+            }
+
+            /// <summary>Disposes of _unlinker if the target has been linked.</summary>
+            internal void AttemptThreadSafeUnlink()
+            {
+                // A race is possible. Therefore use an interlocked operation.
+                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;
+            /// <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, Boolean consumeToAccept)
+            {
+                if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+                if (source == null) throw new ArgumentNullException("source");
+                Contract.EndContractBlock();
+
+                TrySetResult(true);
+                return DataflowMessageStatus.DecliningPermanently;
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+            void IDataflowBlock.Complete()
+            {
+                TrySetResult(false);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+            void IDataflowBlock.Fault(Exception exception)
+            {
+                if (exception == null) throw new ArgumentNullException("exception");
+                Contract.EndContractBlock();
+                TrySetResult(false);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+            Task IDataflowBlock.Completion { get { throw new NotSupportedException(SR.NotSupported_MemberNotNeeded); } }
+
+            /// <summary>The data to display in the debugger display attribute.</summary>
+            [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+            private object DebuggerDisplayContent
+            {
+                get
+                {
+                    return string.Format("{0} IsCompleted={1}",
+                        Common.GetNameForDebugger(this), base.Task.IsCompleted);
+                }
+            }
+            /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+            object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+        }
+        #endregion
+
+        #region Encapsulate
+        /// <summary>Encapsulates a target and a source into a single propagator.</summary>
+        /// <typeparam name="TInput">Specifies the type of input expected by the target.</typeparam>
+        /// <typeparam name="TOutput">Specifies the type of output produced by the source.</typeparam>
+        /// <param name="target">The target to encapsulate.</param>
+        /// <param name="source">The source to encapsulate.</param>
+        /// <returns>The encapsulated target and source.</returns>
+        /// <remarks>
+        /// This method does not in any way connect the target to the source. It creates a
+        /// propagator block whose target methods delegate to the specified target and whose
+        /// source methods delegate to the specified source.  Any connection between the target
+        /// and the source is left for the developer to explicitly provide.  The propagator's
+        /// <see cref="IDataflowBlock"/> implementation delegates to the specified source.
+        /// </remarks>
+        public static IPropagatorBlock<TInput, TOutput> Encapsulate<TInput, TOutput>(
+            ITargetBlock<TInput> target, ISourceBlock<TOutput> source)
+        {
+            if (target == null) throw new ArgumentNullException("target");
+            if (source == null) throw new ArgumentNullException("source");
+            Contract.EndContractBlock();
+            return new EncapsulatingPropagator<TInput, TOutput>(target, source);
+        }
+
+        /// <summary>Provides a dataflow block that encapsulates a target and a source to form a single propagator.</summary>
+        [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+        [DebuggerTypeProxy(typeof(EncapsulatingPropagator<,>.DebugView))]
+        private sealed class EncapsulatingPropagator<TInput, TOutput> : IPropagatorBlock<TInput, TOutput>, IReceivableSourceBlock<TOutput>, IDebuggerDisplay
+        {
+            /// <summary>The target half.</summary>
+            private ITargetBlock<TInput> _target;
+            /// <summary>The source half.</summary>
+            private ISourceBlock<TOutput> _source;
+
+            public EncapsulatingPropagator(ITargetBlock<TInput> target, ISourceBlock<TOutput> source)
+            {
+                Contract.Requires(target != null, "The target should never be null; this should be checked by all internal usage.");
+                Contract.Requires(source != null, "The source should never be null; this should be checked by all internal usage.");
+                _target = target;
+                _source = source;
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+            public void Complete()
+            {
+                _target.Complete();
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+            void IDataflowBlock.Fault(Exception exception)
+            {
+                if (exception == null) throw new ArgumentNullException("exception");
+                Contract.EndContractBlock();
+
+                _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)
+            {
+                return _target.OfferMessage(messageHeader, messageValue, source, consumeToAccept);
+            }
+
+            /// <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/Sources/Member[@name="LinkTo"]/*' />
+            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)
+            {
+                var receivableSource = _source as IReceivableSourceBlock<TOutput>;
+                if (receivableSource != null) return receivableSource.TryReceive(filter, out item);
+
+                item = default(TOutput);
+                return false;
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
+            public bool TryReceiveAll(out IList<TOutput> items)
+            {
+                var receivableSource = _source as IReceivableSourceBlock<TOutput>;
+                if (receivableSource != null) return receivableSource.TryReceiveAll(out items);
+
+                items = default(IList<TOutput>);
+                return false;
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+            public TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out Boolean messageConsumed)
+            {
+                return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+            public bool ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
+            {
+                return _source.ReserveMessage(messageHeader, target);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+            public void ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
+            {
+                _source.ReleaseReservation(messageHeader, target);
+            }
+
+            /// <summary>The data to display in the debugger display attribute.</summary>
+            [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+            private object DebuggerDisplayContent
+            {
+                get
+                {
+                    var displayTarget = _target as IDebuggerDisplay;
+                    var displaySource = _source as IDebuggerDisplay;
+                    return string.Format("{0} Target=\"{1}\", Source=\"{2}\"",
+                        Common.GetNameForDebugger(this),
+                        displayTarget != null ? displayTarget.Content : _target,
+                        displaySource != null ? displaySource.Content : _source);
+                }
+            }
+            /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+            object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+            /// <summary>A debug view for the propagator.</summary>
+            private sealed class DebugView
+            {
+                /// <summary>The propagator being debugged.</summary>
+                private readonly EncapsulatingPropagator<TInput, TOutput> _propagator;
+
+                /// <summary>Initializes the debug view.</summary>
+                /// <param name="propagator">The propagator being debugged.</param>
+                public DebugView(EncapsulatingPropagator<TInput, TOutput> propagator)
+                {
+                    Contract.Requires(propagator != null, "Need a block with which to construct the debug view.");
+                    _propagator = propagator;
+                }
+
+                /// <summary>The target.</summary>
+                public ITargetBlock<TInput> Target { get { return _propagator._target; } }
+                /// <summary>The source.</summary>
+                public ISourceBlock<TOutput> Source { get { return _propagator._source; } }
+            }
+        }
+        #endregion
+
+        #region Choose
+        #region Choose<T1,T2>
+        /// <summary>Monitors two dataflow sources, invoking the provided handler for whichever source makes data available first.</summary>
+        /// <typeparam name="T1">Specifies type of data contained in the first source.</typeparam>
+        /// <typeparam name="T2">Specifies type of data contained in the second source.</typeparam>
+        /// <param name="source1">The first source.</param>
+        /// <param name="action1">The handler to execute on data from the first source.</param>
+        /// <param name="source2">The second source.</param>
+        /// <param name="action2">The handler to execute on data from the second source.</param>
+        /// <returns>
+        /// <para>
+        /// A <see cref="System.Threading.Tasks.Task{Int32}"/> that represents the asynchronous choice.
+        /// If both sources are completed prior to the choice completing, 
+        /// the resulting task will be canceled. When one of the sources has data available and successfully propagates 
+        /// it to the choice, the resulting task will complete when the handler completes: if the handler throws an exception,
+        /// the task will end in the <see cref="System.Threading.Tasks.TaskStatus.Faulted"/> state containing the unhandled exception, otherwise the task
+        /// will end with its <see cref="System.Threading.Tasks.Task{Int32}.Result"/> set to either 0 or 1 to
+        /// represent the first or second source, respectively.
+        /// </para>
+        /// <para>
+        /// This method will only consume an element from one of the two data sources, never both.
+        /// </para>
+        /// </returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source1"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action1"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source2"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action2"/> is null (Nothing in Visual Basic).</exception>
+        public static Task<Int32> Choose<T1, T2>(
+            ISourceBlock<T1> source1, Action<T1> action1,
+            ISourceBlock<T2> source2, Action<T2> action2)
+        {
+            // All argument validation is handled by the delegated method
+            return Choose(source1, action1, source2, action2, DataflowBlockOptions.Default);
+        }
+
+        /// <summary>Monitors two dataflow sources, invoking the provided handler for whichever source makes data available first.</summary>
+        /// <typeparam name="T1">Specifies type of data contained in the first source.</typeparam>
+        /// <typeparam name="T2">Specifies type of data contained in the second source.</typeparam>
+        /// <param name="source1">The first source.</param>
+        /// <param name="action1">The handler to execute on data from the first source.</param>
+        /// <param name="source2">The second source.</param>
+        /// <param name="action2">The handler to execute on data from the second source.</param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this choice.</param>
+        /// <returns>
+        /// <para>
+        /// A <see cref="System.Threading.Tasks.Task{Int32}"/> that represents the asynchronous choice.
+        /// If both sources are completed prior to the choice completing, or if the CancellationToken
+        /// provided as part of <paramref name="dataflowBlockOptions"/> is canceled prior to the choice completing,
+        /// the resulting task will be canceled. When one of the sources has data available and successfully propagates 
+        /// it to the choice, the resulting task will complete when the handler completes: if the handler throws an exception,
+        /// the task will end in the <see cref="System.Threading.Tasks.TaskStatus.Faulted"/> state containing the unhandled exception, otherwise the task
+        /// will end with its <see cref="System.Threading.Tasks.Task{Int32}.Result"/> set to either 0 or 1 to
+        /// represent the first or second source, respectively.
+        /// </para>
+        /// <para>
+        /// This method will only consume an element from one of the two data sources, never both.
+        /// If cancellation is requested after an element has been received, the cancellation request will be ignored,
+        /// and the relevant handler will be allowed to execute. 
+        /// </para>
+        /// </returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source1"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action1"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source2"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action2"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
+        public static Task<Int32> Choose<T1, T2>(
+            ISourceBlock<T1> source1, Action<T1> action1,
+            ISourceBlock<T2> source2, Action<T2> action2,
+            DataflowBlockOptions dataflowBlockOptions)
+        {
+            // Validate arguments
+            if (source1 == null) throw new ArgumentNullException("source1");
+            if (action1 == null) throw new ArgumentNullException("action1");
+            if (source2 == null) throw new ArgumentNullException("source2");
+            if (action2 == null) throw new ArgumentNullException("action2");
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+
+            // Delegate to the shared implementation
+            return ChooseCore<T1, T2, VoidResult>(source1, action1, source2, action2, null, null, dataflowBlockOptions);
+        }
+        #endregion
+
+        #region Choose<T1,T2,T3>
+        /// <summary>Monitors three dataflow sources, invoking the provided handler for whichever source makes data available first.</summary>
+        /// <typeparam name="T1">Specifies type of data contained in the first source.</typeparam>
+        /// <typeparam name="T2">Specifies type of data contained in the second source.</typeparam>
+        /// <typeparam name="T3">Specifies type of data contained in the third source.</typeparam>
+        /// <param name="source1">The first source.</param>
+        /// <param name="action1">The handler to execute on data from the first source.</param>
+        /// <param name="source2">The second source.</param>
+        /// <param name="action2">The handler to execute on data from the second source.</param>
+        /// <param name="source3">The third source.</param>
+        /// <param name="action3">The handler to execute on data from the third source.</param>
+        /// <returns>
+        /// <para>
+        /// A <see cref="System.Threading.Tasks.Task{Int32}"/> that represents the asynchronous choice.
+        /// If all sources are completed prior to the choice completing, 
+        /// the resulting task will be canceled. When one of the sources has data available and successfully propagates 
+        /// it to the choice, the resulting task will complete when the handler completes: if the handler throws an exception,
+        /// the task will end in the <see cref="System.Threading.Tasks.TaskStatus.Faulted"/> state containing the unhandled exception, otherwise the task
+        /// will end with its <see cref="System.Threading.Tasks.Task{Int32}.Result"/> set to the 0-based index of the source.
+        /// </para>
+        /// <para>
+        /// This method will only consume an element from one of the data sources, never more than one.
+        /// </para>
+        /// </returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source1"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action1"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source2"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action2"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source3"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action3"/> is null (Nothing in Visual Basic).</exception>
+        public static Task<Int32> Choose<T1, T2, T3>(
+            ISourceBlock<T1> source1, Action<T1> action1,
+            ISourceBlock<T2> source2, Action<T2> action2,
+            ISourceBlock<T3> source3, Action<T3> action3)
+        {
+            // All argument validation is handled by the delegated method
+            return Choose(source1, action1, source2, action2, source3, action3, DataflowBlockOptions.Default);
+        }
+
+        /// <summary>Monitors three dataflow sources, invoking the provided handler for whichever source makes data available first.</summary>
+        /// <typeparam name="T1">Specifies type of data contained in the first source.</typeparam>
+        /// <typeparam name="T2">Specifies type of data contained in the second source.</typeparam>
+        /// <typeparam name="T3">Specifies type of data contained in the third source.</typeparam>
+        /// <param name="source1">The first source.</param>
+        /// <param name="action1">The handler to execute on data from the first source.</param>
+        /// <param name="source2">The second source.</param>
+        /// <param name="action2">The handler to execute on data from the second source.</param>
+        /// <param name="source3">The third source.</param>
+        /// <param name="action3">The handler to execute on data from the third source.</param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this choice.</param>
+        /// <returns>
+        /// <para>
+        /// A <see cref="System.Threading.Tasks.Task{Int32}"/> that represents the asynchronous choice.
+        /// If all sources are completed prior to the choice completing, or if the CancellationToken
+        /// provided as part of <paramref name="dataflowBlockOptions"/> is canceled prior to the choice completing,
+        /// the resulting task will be canceled. When one of the sources has data available and successfully propagates 
+        /// it to the choice, the resulting task will complete when the handler completes: if the handler throws an exception,
+        /// the task will end in the <see cref="System.Threading.Tasks.TaskStatus.Faulted"/> state containing the unhandled exception, otherwise the task
+        /// will end with its <see cref="System.Threading.Tasks.Task{Int32}.Result"/> set to the 0-based index of the source.
+        /// </para>
+        /// <para>
+        /// This method will only consume an element from one of the data sources, never more than one.
+        /// If cancellation is requested after an element has been received, the cancellation request will be ignored,
+        /// and the relevant handler will be allowed to execute. 
+        /// </para>
+        /// </returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source1"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action1"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source2"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action2"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source3"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action3"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
+        public static Task<Int32> Choose<T1, T2, T3>(
+            ISourceBlock<T1> source1, Action<T1> action1,
+            ISourceBlock<T2> source2, Action<T2> action2,
+            ISourceBlock<T3> source3, Action<T3> action3,
+            DataflowBlockOptions dataflowBlockOptions)
+        {
+            // Validate arguments
+            if (source1 == null) throw new ArgumentNullException("source1");
+            if (action1 == null) throw new ArgumentNullException("action1");
+            if (source2 == null) throw new ArgumentNullException("source2");
+            if (action2 == null) throw new ArgumentNullException("action2");
+            if (source3 == null) throw new ArgumentNullException("source3");
+            if (action3 == null) throw new ArgumentNullException("action3");
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+
+            // Delegate to the shared implementation
+            return ChooseCore<T1, T2, T3>(source1, action1, source2, action2, source3, action3, dataflowBlockOptions);
+        }
+        #endregion
+
+        #region Choose Shared
+        /// <summary>Monitors dataflow sources, invoking the provided handler for whichever source makes data available first.</summary>
+        /// <typeparam name="T1">Specifies type of data contained in the first source.</typeparam>
+        /// <typeparam name="T2">Specifies type of data contained in the second source.</typeparam>
+        /// <typeparam name="T3">Specifies type of data contained in the third source.</typeparam>
+        /// <param name="source1">The first source.</param>
+        /// <param name="action1">The handler to execute on data from the first source.</param>
+        /// <param name="source2">The second source.</param>
+        /// <param name="action2">The handler to execute on data from the second source.</param>
+        /// <param name="source3">The third source.</param>
+        /// <param name="action3">The handler to execute on data from the third source.</param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this choice.</param>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
+        private static Task<Int32> ChooseCore<T1, T2, T3>(
+            ISourceBlock<T1> source1, Action<T1> action1,
+            ISourceBlock<T2> source2, Action<T2> action2,
+            ISourceBlock<T3> source3, Action<T3> action3,
+            DataflowBlockOptions dataflowBlockOptions)
+        {
+            Contract.Requires(source1 != null && action1 != null, "The first source and action should not be null.");
+            Contract.Requires(source2 != null && action2 != null, "The second source and action should not be null.");
+            Contract.Requires((source3 == null) == (action3 == null), "The third action should be null iff the third source is null.");
+            Contract.Requires(dataflowBlockOptions != null, "Options are required.");
+            bool hasThirdSource = source3 != null; // In the future, if we want higher arities on Choose, we can simply add more such checks on additional arguments
+
+            // Early cancellation check and bail out
+            if (dataflowBlockOptions.CancellationToken.IsCancellationRequested)
+                return Common.CreateTaskFromCancellation<Int32>(dataflowBlockOptions.CancellationToken);
+
+            // Fast path: if any of the sources already has data available that can be received immediately.
+            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)))
+                {
+                    return resultTask;
+                }
+            }
+            catch (Exception exc)
+            {
+                // In case TryReceive in TryChooseFromSource erroneously throws
+                return Common.CreateTaskFromException<int>(exc);
+            }
+
+            // Slow path: link up to all of the sources.  Separated out to avoid a closure on the fast path.
+            return ChooseCoreByLinking(source1, action1, source2, action2, source3, action3, dataflowBlockOptions);
+        }
+
+        /// <summary>
+        /// Tries to remove data from a receivable source and schedule an action to process that received item.
+        /// </summary>
+        /// <typeparam name="T">Specifies the type of data to process.</typeparam>
+        /// <param name="source">The source from which to receive the data.</param>
+        /// <param name="action">The action to run for the received data.</param>
+        /// <param name="branchId">The branch ID associated with this source/action pair.</param>
+        /// <param name="scheduler">The scheduler to use to process the action.</param>
+        /// <param name="task">The task created for processing the received item.</param>
+        /// <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)
+        {
+            // Validate arguments
+            Contract.Requires(source != null, "Expected a non-null source");
+            Contract.Requires(action != null, "Expected a non-null action");
+            Contract.Requires(branchId >= 0, "Expected a valid branch ID (> 0)");
+            Contract.Requires(scheduler != null, "Expected a non-null scheduler");
+
+            // Try to receive from the source.  If we can't, bail.
+            T result;
+            var receivableSource = source as IReceivableSourceBlock<T>;
+            if (receivableSource == null || !receivableSource.TryReceive(out result))
+            {
+                task = null;
+                return false;
+            }
+
+            // We successfully received an item.  Launch a task to process it.
+            task = Task.Factory.StartNew(ChooseTarget<T>.s_processBranchFunction,
+                Tuple.Create<Action<T>, T, int>(action, result, branchId),
+                CancellationToken.None, Common.GetCreationOptionsForTask(), scheduler);
+            return true;
+        }
+
+        /// <summary>Monitors dataflow sources, invoking the provided handler for whichever source makes data available first.</summary>
+        /// <typeparam name="T1">Specifies type of data contained in the first source.</typeparam>
+        /// <typeparam name="T2">Specifies type of data contained in the second source.</typeparam>
+        /// <typeparam name="T3">Specifies type of data contained in the third source.</typeparam>
+        /// <param name="source1">The first source.</param>
+        /// <param name="action1">The handler to execute on data from the first source.</param>
+        /// <param name="source2">The second source.</param>
+        /// <param name="action2">The handler to execute on data from the second source.</param>
+        /// <param name="source3">The third source.</param>
+        /// <param name="action3">The handler to execute on data from the third source.</param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this choice.</param>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
+        private static Task<Int32> ChooseCoreByLinking<T1, T2, T3>(
+            ISourceBlock<T1> source1, Action<T1> action1,
+            ISourceBlock<T2> source2, Action<T2> action2,
+            ISourceBlock<T3> source3, Action<T3> action3,
+            DataflowBlockOptions dataflowBlockOptions)
+        {
+            Contract.Requires(source1 != null && action1 != null, "The first source and action should not be null.");
+            Contract.Requires(source2 != null && action2 != null, "The second source and action should not be null.");
+            Contract.Requires((source3 == null) == (action3 == null), "The third action should be null iff the third source is null.");
+            Contract.Requires(dataflowBlockOptions != null, "Options are required.");
+
+            bool hasThirdSource = source3 != null; // In the future, if we want higher arities on Choose, we can simply add more such checks on additional arguments
+
+            // Create object to act as both completion marker and sync obj for targets.
+            var boxedCompleted = new StrongBox<Task>();
+
+            // Set up teardown cancellation.  We will request cancellation when a) the supplied options token
+            // has cancellation requested or b) when we actually complete somewhere in order to tear down
+            // the rest of our configured set up.
+            CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(dataflowBlockOptions.CancellationToken, CancellationToken.None);
+
+            // Set up the branches.
+            TaskScheduler scheduler = dataflowBlockOptions.TaskScheduler;
+            var branchTasks = new Task<int>[hasThirdSource ? 3 : 2];
+            branchTasks[0] = CreateChooseBranch(boxedCompleted, cts, scheduler, 0, source1, action1);
+            branchTasks[1] = CreateChooseBranch(boxedCompleted, cts, scheduler, 1, source2, action2);
+            if (hasThirdSource)
+            {
+                branchTasks[2] = CreateChooseBranch(boxedCompleted, cts, scheduler, 2, source3, action3);
+            }
+
+            // Asynchronously wait for all branches to complete, then complete
+            // a task to be returned to the caller.
+            var result = new TaskCompletionSource<int>();
+            Task.Factory.ContinueWhenAll(branchTasks, tasks =>
+            {
+                // Process the outcome of all branches.  At most one will have completed
+                // successfully, returning its branch ID.  Others may have faulted,
+                // in which case we need to propagate their exceptions, regardless
+                // 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;
+                int successfulBranchId = -1;
+                foreach (Task<int> task in tasks)
+                {
+                    switch (task.Status)
+                    {
+                        case TaskStatus.Faulted:
+                            Common.AddException(ref exceptions, task.Exception, unwrapInnerExceptions: true);
+                            break;
+                        case TaskStatus.RanToCompletion:
+                            int resultBranchId = task.Result;
+                            if (resultBranchId >= 0)
+                            {
+                                Debug.Assert(resultBranchId < tasks.Length, "Expected a valid branch ID");
+                                Debug.Assert(successfulBranchId == -1, "There should be at most one successful branch.");
+                                successfulBranchId = resultBranchId;
+                            }
+                            else Debug.Assert(resultBranchId == -1, "Expected -1 as a signal of a non-successful branch");
+                            break;
+                    }
+                }
+
+                // If we found any exceptions, fault the Choose task.  Otherwise, if any branch completed
+                // successfully, store its result, or if cancellation was request
+                if (exceptions != null)
+                {
+                    result.TrySetException(exceptions);
+                }
+                else if (successfulBranchId >= 0)
+                {
+                    result.TrySetResult(successfulBranchId);
+                }
+                else
+                {
+                    result.TrySetCanceled();
+                }
+
+                // By now we know that all of the tasks have completed, so there
+                // can't be any more use of the CancellationTokenSource.
+                cts.Dispose();
+            }, CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default);
+            return result.Task;
+        }
+
+        /// <summary>Creates a target for a branch of a Choose.</summary>
+        /// <typeparam name="T">Specifies the type of data coming through this branch.</typeparam>
+        /// <param name="boxedCompleted">A strong box around the completed Task from any target. Also sync obj for access to the targets.</param>
+        /// <param name="cts">The CancellationTokenSource used to issue tear down / cancellation requests.</param>
+        /// <param name="scheduler">The TaskScheduler on which to scheduler work.</param>
+        /// <param name="branchId">The ID of this branch, used to complete the resultTask.</param>
+        /// <param name="source">The source with which this branch is associated.</param>
+        /// <param name="action">The action to run for a single element received from the source.</param>
+        /// <returns>A task representing the branch.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private static Task<int> CreateChooseBranch<T>(
+            StrongBox<Task> boxedCompleted, CancellationTokenSource cts,
+            TaskScheduler scheduler,
+            int branchId, ISourceBlock<T> source, Action<T> action)
+        {
+            // If the cancellation token is already canceled, there is no need to create and link a target.
+            // Instead, directly return a canceled task.
+            if (cts.IsCancellationRequested)
+                return Common.CreateTaskFromCancellation<int>(cts.Token);
+
+            // Proceed with creating and linking a hidden target. Also get the source's completion task, 
+            // as we need it to know when the source completes.  Both of these operations
+            // could throw an exception if the block is faulty.
+            var target = new ChooseTarget<T>(boxedCompleted, cts.Token);
+            IDisposable unlink;
+            try
+            {
+                unlink = source.LinkTo(target, DataflowLinkOptions.UnlinkAfterOneAndPropagateCompletion);
+            }
+            catch (Exception exc)
+            {
+                cts.Cancel();
+                return Common.CreateTaskFromException<int>(exc);
+            }
+
+            // The continuation task below is implicitly capturing the right execution context,
+            // as CreateChooseBranch is called synchronously from Choose, so we
+            // don't need to additionally capture and marshal an ExecutionContext.
+
+            return target.Task.ContinueWith(completed =>
+            {
+                try
+                {
+                    // If the target ran to completion, i.e. it got a message, 
+                    // cancel the other branch(es) and proceed with the user callback.
+                    if (completed.Status == TaskStatus.RanToCompletion)
+                    {
+                        // Cancel the cts to trigger completion of the other branches.
+                        cts.Cancel();
+
+                        // Proceed with the user callback.
+                        action(completed.Result);
+
+                        // Return the ID of our branch to indicate.
+                        return branchId;
+                    }
+                    return -1;
+                }
+                finally
+                {
+                    // Unlink from the source.  This could throw if the block is faulty,
+                    // in which case our branch's task will fault.  If this
+                    // does throw, it'll end up propagating instead of the
+                    // original action's exception if there was one.
+                    unlink.Dispose();
+                }
+            }, CancellationToken.None, Common.GetContinuationOptions(), scheduler);
+        }
+
+        /// <summary>Provides a dataflow target used by Choose to receive data from a single source.</summary>
+        /// <typeparam name="T">Specifies the type of data offered to this target.</typeparam>
+        [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+        private sealed class ChooseTarget<T> : TaskCompletionSource<T>, ITargetBlock<T>, IDebuggerDisplay
+        {
+            /// <summary>
+            /// 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 =>
+            {
+                Tuple<Action<T>, T, int> actionResultBranch = (Tuple<Action<T>, T, int>)state;
+                actionResultBranch.Item1(actionResultBranch.Item2);
+                return actionResultBranch.Item3;
+            };
+
+            /// <summary>
+            /// A wrapper for the task that represents the completed branch of this choice.
+            /// The wrapper is also the sync object used to protect all choice branch's access to shared state.
+            /// </summary>
+            private StrongBox<Task> _completed;
+
+            /// <summary>Initializes the target.</summary>
+            /// <param name="completed">The completed wrapper shared between all choice branches.</param>
+            /// <param name="cancellationToken">The cancellation token used to cancel this target.</param>
+            internal ChooseTarget(StrongBox<Task> completed, CancellationToken cancellationToken)
+            {
+                Contract.Requires(completed != null, "Requires a shared target to complete.");
+                _completed = completed;
+
+                // Handle async cancellation by canceling the target without storing it into _completed.
+                // _completed must only be set to a RanToCompletion task for a successful branch.
+                Common.WireCancellationToComplete(cancellationToken, base.Task,
+                    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, Boolean consumeToAccept)
+            {
+                // Validate arguments
+                if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+                if (source == null && consumeToAccept) throw new ArgumentException(SR.Argument_CantConsumeFromANullSource, "consumeToAccept");
+                Contract.EndContractBlock();
+
+                lock (_completed)
+                {
+                    // If we or another participating choice has already completed, we're done.
+                    if (_completed.Value != null || base.Task.IsCompleted) return DataflowMessageStatus.DecliningPermanently;
+
+                    // Consume the message from the source if necessary
+                    if (consumeToAccept)
+                    {
+                        bool consumed;
+                        messageValue = source.ConsumeMessage(messageHeader, this, out consumed);
+                        if (!consumed) return DataflowMessageStatus.NotAvailable;
+                    }
+
+                    // Store the result and signal our success
+                    TrySetResult(messageValue);
+                    _completed.Value = Task;
+                    return DataflowMessageStatus.Accepted;
+                }
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+            void IDataflowBlock.Complete()
+            {
+                lock (_completed) TrySetCanceled();
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+            void IDataflowBlock.Fault(Exception exception) { ((IDataflowBlock)this).Complete(); }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+            Task IDataflowBlock.Completion { get { throw new NotSupportedException(SR.NotSupported_MemberNotNeeded); } }
+
+            /// <summary>The data to display in the debugger display attribute.</summary>
+            [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+            private object DebuggerDisplayContent
+            {
+                get
+                {
+                    return string.Format("{0} IsCompleted={1}",
+                        Common.GetNameForDebugger(this), base.Task.IsCompleted);
+                }
+            }
+            /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+            object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+        }
+        #endregion
+        #endregion
+
+        #region AsObservable
+        /// <summary>Creates a new <see cref="System.IObservable{TOutput}"/> abstraction over the <see cref="ISourceBlock{TOutput}"/>.</summary>
+        /// <typeparam name="TOutput">Specifies the type of data contained in the source.</typeparam>
+        /// <param name="source">The source to wrap.</param>
+        /// <returns>An IObservable{TOutput} that enables observers to be subscribed to the source.</returns>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="source"/> is null (Nothing in Visual Basic).</exception>
+        public static IObservable<TOutput> AsObservable<TOutput>(this ISourceBlock<TOutput> source)
+        {
+            if (source == null) throw new ArgumentNullException("source");
+            Contract.EndContractBlock();
+            return SourceObservable<TOutput>.From(source);
+        }
+
+        /// <summary>Cached options for non-greedy processing.</summary>
+        private static readonly ExecutionDataflowBlockOptions _nonGreedyExecutionOptions = new ExecutionDataflowBlockOptions { BoundedCapacity = 1 };
+
+        /// <summary>Provides an IObservable veneer over a source block.</summary>
+        [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+        [DebuggerTypeProxy(typeof(SourceObservable<>.DebugView))]
+        private sealed class SourceObservable<TOutput> : IObservable<TOutput>, IDebuggerDisplay
+        {
+            /// <summary>The table that maps source to cached observable.</summary>
+            /// <remarks>
+            /// ConditionalWeakTable doesn't do the initialization under a lock, just the publication.
+            /// This means that if there's a race to create two observables off the same source, we could end
+            /// up instantiating multiple SourceObservable instances, of which only one will be published.
+            /// Worst case, we end up with a few additional continuations off of the source's completion task.
+            /// </remarks>
+            private static readonly ConditionalWeakTable<ISourceBlock<TOutput>, SourceObservable<TOutput>> _table =
+                new ConditionalWeakTable<ISourceBlock<TOutput>, SourceObservable<TOutput>>();
+
+            /// <summary>Gets an observable to represent the source block.</summary>
+            /// <param name="source">The source.</param>
+            /// <returns>The observable.</returns>
+            internal static IObservable<TOutput> From(ISourceBlock<TOutput> source)
+            {
+                Contract.Requires(source != null, "Requires a source for which to retrieve the observable.");
+                return _table.GetValue(source, s => new SourceObservable<TOutput>(s));
+            }
+
+            /// <summary>Object used to synchronize all subscriptions, unsubscriptions, and propagations.</summary>
+            private readonly object _SubscriptionLock = new object();
+            /// <summary>The wrapped source.</summary>
+            private readonly ISourceBlock<TOutput> _source;
+            /// <summary>
+            /// The current target.  We use the same target until the number of subscribers
+            /// drops to 0, at which point we substitute in a new target.
+            /// </summary>
+            private ObserversState _observersState;
+
+            /// <summary>Initializes the SourceObservable.</summary>
+            /// <param name="source">The source to wrap.</param>
+            internal SourceObservable(ISourceBlock<TOutput> source)
+            {
+                Contract.Requires(source != null, "The observable requires a source to wrap.");
+                _source = source;
+                _observersState = new ObserversState(this);
+            }
+
+            /// <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()
+            {
+                Task sourceCompletionTask = Common.GetPotentiallyNotSupportedCompletionTask(_source);
+                return sourceCompletionTask != null && sourceCompletionTask.IsFaulted ?
+                    sourceCompletionTask.Exception : null;
+            }
+
+            /// <summary>Subscribes the observer to the source.</summary>
+            /// <param name="observer">the observer to subscribe.</param>
+            /// <returns>An IDisposable that may be used to unsubscribe the source.</returns>
+            [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
+            IDisposable IObservable<TOutput>.Subscribe(IObserver<TOutput> observer)
+            {
+                // Validate arguments
+                if (observer == null) throw new ArgumentNullException("observer");
+                Contract.EndContractBlock();
+                Common.ContractAssertMonitorStatus(_SubscriptionLock, held: false);
+
+                Task sourceCompletionTask = Common.GetPotentiallyNotSupportedCompletionTask(_source);
+
+                // Synchronize all observers for this source.
+                Exception error = null;
+                lock (_SubscriptionLock)
+                {
+                    // Fast path for if everything is already done.  We need to ensure that both
+                    // the source is complete and that the target has finished propagating data to all observers.
+                    // If there  was an error, we grab it here and then we'll complete the observer
+                    // outside of the lock.
+                    if (sourceCompletionTask != null && sourceCompletionTask.IsCompleted &&
+                        _observersState.Target.Completion.IsCompleted)
+                    {
+                        error = GetCompletionError();
+                    }
+                    // Otherwise, we need to subscribe this observer.
+                    else
+                    {
+                        // Hook up the observer.  If this is the first observer, link the source to the target.
+                        _observersState.Observers = _observersState.Observers.Add(observer);
+                        if (_observersState.Observers.Count == 1)
+                        {
+                            Debug.Assert(_observersState.Unlinker == null, "The source should not be linked to the target.");
+                            _observersState.Unlinker = _source.LinkTo(_observersState.Target);
+                            if (_observersState.Unlinker == null)
+                            {
+                                _observersState.Observers = ImmutableList<IObserver<TOutput>>.Empty;
+                                return null;
+                            }
+                        }
+
+                        // Return a disposable that will unlink this observer, and if it's the last
+                        // observer for the source, shut off the pipe to observers.
+                        return Disposables.Create((s, o) => s.Unsubscribe(o), this, observer);
+                    }
+                }
+
+                // Complete the observer.
+                if (error != null) observer.OnError(error);
+                else observer.OnCompleted();
+                return Disposables.Nop;
+            }
+
+            /// <summary>Unsubscribes the observer.</summary>
+            /// <param name="observer">The observer being unsubscribed.</param>
+            private void Unsubscribe(IObserver<TOutput> observer)
+            {
+                Contract.Requires(observer != null, "Expected an observer.");
+                Common.ContractAssertMonitorStatus(_SubscriptionLock, held: false);
+
+                lock (_SubscriptionLock)
+                {
+                    ObserversState currentState = _observersState;
+                    Debug.Assert(currentState != null, "Observer state should never be null.");
+
+                    // If the observer was already unsubscribed (or is otherwise no longer present in our list), bail.
+                    if (!currentState.Observers.Contains(observer)) return;
+
+                    // If this is the last observer being removed, reset to be ready for future subscribers.
+                    if (currentState.Observers.Count == 1)
+                    {
+                        ResetObserverState();
+                    }
+                    // Otherwise, just remove the observer.  Note that we don't remove the observer
+                    // from the current target if this is the last observer. This is done in case the target
+                    // has already taken data from the source: we want that data to end up somewhere,
+                    // and we can't put it back in the source, so we ensure we send it along to the observer.
+                    else
+                    {
+                        currentState.Observers = currentState.Observers.Remove(observer);
+                    }
+                }
+            }
+
+            /// <summary>Resets the observer state to the original, inactive state.</summary>
+            /// <returns>The list of active observers prior to the reset.</returns>
+            private ImmutableList<IObserver<TOutput>> ResetObserverState()
+            {
+                Common.ContractAssertMonitorStatus(_SubscriptionLock, held: true);
+
+                ObserversState currentState = _observersState;
+                Debug.Assert(currentState != null, "Observer state should never be null.");
+                Debug.Assert(currentState.Unlinker != null, "The target should be linked.");
+                Debug.Assert(currentState.Canceler != null, "The target should have set up continuations.");
+
+                // Replace the target with a clean one, unlink and cancel, and return the previous set of observers
+                ImmutableList<IObserver<TOutput>> currentObservers = currentState.Observers;
+                _observersState = new ObserversState(this);
+                currentState.Unlinker.Dispose();
+                currentState.Canceler.Cancel();
+                return currentObservers;
+            }
+
+            /// <summary>The data to display in the debugger display attribute.</summary>
+            [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+            private object DebuggerDisplayContent
+            {
+                get
+                {
+                    var displaySource = _source as IDebuggerDisplay;
+                    return string.Format("Observers={0}, Block=\"{1}\"",
+                        _observersState.Observers.Count,
+                        displaySource != null ? displaySource.Content : _source);
+                }
+            }
+            /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+            object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+            /// <summary>Provides a debugger type proxy for the observable.</summary>
+            private sealed class DebugView
+            {
+                /// <summary>The observable being debugged.</summary>
+                private readonly SourceObservable<TOutput> _observable;
+
+                /// <summary>Initializes the debug view.</summary>
+                /// <param name="observable">The target being debugged.</param>
+                public DebugView(SourceObservable<TOutput> observable)
+                {
+                    Contract.Requires(observable != null, "Need a block with which to construct the debug view.");
+                    _observable = observable;
+                }
+
+                /// <summary>Gets an enumerable of the observers.</summary>
+                [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+                public IObserver<TOutput>[] Observers { get { return _observable._observersState.Observers.ToArray(); } }
+            }
+
+            /// <summary>State associated with the current target for propagating data to observers.</summary>
+            [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
+            private sealed class ObserversState
+            {
+                /// <summary>The owning SourceObservable.</summary>
+                internal readonly SourceObservable<TOutput> Observable;
+                /// <summary>The ActionBlock that consumes data from a source and offers it to targets.</summary>
+                internal readonly ActionBlock<TOutput> Target;
+                /// <summary>Used to cancel continuations when they're no longer necessary.</summary>
+                internal readonly CancellationTokenSource Canceler = new CancellationTokenSource();
+                /// <summary>
+                /// A list of the observers currently registered with this target.  The list is immutable
+                /// to enable iteration through the list while the set of observers may be changing.
+                /// </summary>
+                internal ImmutableList<IObserver<TOutput>> Observers = ImmutableList<IObserver<TOutput>>.Empty;
+                /// <summary>Used to unlink the source from this target when the last observer is unsubscribed.</summary>
+                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;
+
+                /// <summary>Initializes the target instance.</summary>
+                /// <param name="observable">The owning observable.</param>
+                internal ObserversState(SourceObservable<TOutput> observable)
+                {
+                    Contract.Requires(observable != null, "Observe state must be mapped to a source observable.");
+
+                    // Set up the target block
+                    Observable = observable;
+                    Target = new ActionBlock<TOutput>((Func<TOutput, Task>)ProcessItemAsync, DataflowBlock._nonGreedyExecutionOptions);
+
+                    // 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,
+                        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);
+                    if (sourceCompletionTask != null)
+                    {
+                        sourceCompletionTask.ContinueWith((_1, state1) =>
+                        {
+                            var ti = (ObserversState)state1;
+                            ti.Target.Complete();
+                            ti.Target.Completion.ContinueWith(
+                                (_2, state2) => ((ObserversState)state2).NotifyObserversOfCompletion(), state1,
+                                CancellationToken.None,
+                                Common.GetContinuationOptions(TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.ExecuteSynchronously),
+                                TaskScheduler.Default);
+                        }, this, Canceler.Token, Common.GetContinuationOptions(TaskContinuationOptions.ExecuteSynchronously), TaskScheduler.Default);
+                    }
+                }
+
+                /// <summary>Forwards an item to all currently subscribed observers.</summary>
+                /// <param name="item">The item to forward.</param>
+                [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+                private Task ProcessItemAsync(TOutput item)
+                {
+                    Common.ContractAssertMonitorStatus(Observable._SubscriptionLock, held: false);
+
+                    ImmutableList<IObserver<TOutput>> currentObservers;
+                    lock (Observable._SubscriptionLock) currentObservers = Observers;
+                    try
+                    {
+                        foreach (IObserver<TOutput> observer in currentObservers)
+                        {
+                            // If the observer is our own TargetObserver, we SendAsync() to it
+                            // rather than going through IObserver.OnNext() which allows us to
+                            // continue offering to the remaining observers without blocking.
+                            var targetObserver = observer as TargetObserver<TOutput>;
+                            if (targetObserver != null)
+                            {
+                                Task<bool> sendAsyncTask = targetObserver.SendAsyncToTarget(item);
+                                if (sendAsyncTask.Status != TaskStatus.RanToCompletion)
+                                {
+                                    // Ensure the SendAsyncTaskList is instantiated
+                                    if (_tempSendAsyncTaskList == null) _tempSendAsyncTaskList = new List<Task<bool>>();
+
+                                    // Add the task to the list
+                                    _tempSendAsyncTaskList.Add(sendAsyncTask);
+                                }
+                            }
+                            else
+                            {
+                                observer.OnNext(item);
+                            }
+                        }
+
+                        // If there are SendAsync tasks to wait on...
+                        if (_tempSendAsyncTaskList != null && _tempSendAsyncTaskList.Count > 0)
+                        {
+                            // Consolidate all SendAsync tasks into one
+                            Task<bool[]> allSendAsyncTasksConsolidated = Task.WhenAll(_tempSendAsyncTaskList);
+
+                            // Clear the temp SendAsync task list
+                            _tempSendAsyncTaskList.Clear();
+
+                            // Return the consolidated task
+                            return allSendAsyncTasksConsolidated;
+                        }
+                    }
+                    catch (Exception exc)
+                    {
+                        // Return a faulted task
+                        return Common.CreateTaskFromException<VoidResult>(exc);
+                    }
+
+                    // All observers accepted normally. 
+                    // Return a completed task.
+                    return Common.CompletedTaskWithTrueResult;
+                }
+
+                /// <summary>Notifies all currently registered observers that they should complete.</summary>
+                /// <param name="targetException">
+                /// Non-null when an unexpected exception occurs during processing.  Faults
+                /// all subscribed observers and resets the observable back to its original condition.
+                /// </param>
+                [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+                private void NotifyObserversOfCompletion(Exception targetException = null)
+                {
+                    Contract.Requires(Target.Completion.IsCompleted, "The target must have already completed in order to notify of completion.");
+                    Common.ContractAssertMonitorStatus(Observable._SubscriptionLock, held: false);
+
+                    // Send completion notification to all observers.
+                    ImmutableList<IObserver<TOutput>> currentObservers;
+                    lock (Observable._SubscriptionLock)
+                    {
+                        // Get the currently registered set of observers. Then, if we're being called due to the target 
+                        // block failing from an unexpected exception, reset the observer state so that subsequent 
+                        // subscribed observers will get a new target block.  Finally clear out our observer list.
+                        currentObservers = Observers;
+                        if (targetException != null) Observable.ResetObserverState();
+                        Observers = ImmutableList<IObserver<TOutput>>.Empty;
+                    }
+
+                    // If there are any observers to complete...
+                    if (currentObservers.Count > 0)
+                    {
+                        // Determine if we should fault or complete the observers
+                        Exception error = targetException ?? Observable.GetCompletionError();
+                        try
+                        {
+                            // Do it.
+                            if (error != null)
+                            {
+                                foreach (IObserver<TOutput> observer in currentObservers) observer.OnError(error);
+                            }
+                            else
+                            {
+                                foreach (IObserver<TOutput> observer in currentObservers) observer.OnCompleted();
+                            }
+                        }
+                        catch (Exception exc)
+                        {
+                            // If an observer throws an exception at this point (which it shouldn't do),
+                            // we have little recourse but to let that exception propagate.  Since allowing it to
+                            // propagate here would just result in it getting eaten by the owning task,
+                            // we instead have it propagate on the thread pool.
+                            Common.ThrowAsync(exc);
+                        }
+                    }
+                }
+            }
+        }
+        #endregion
+
+        #region AsObserver
+        /// <summary>Creates a new <see cref="System.IObserver{TInput}"/> abstraction over the <see cref="ITargetBlock{TInput}"/>.</summary>
+        /// <typeparam name="TInput">Specifies the type of input accepted by the target block.</typeparam>
+        /// <param name="target">The target to wrap.</param>
+        /// <returns>An observer that wraps the target block.</returns>
+        public static IObserver<TInput> AsObserver<TInput>(this ITargetBlock<TInput> target)
+        {
+            if (target == null) throw new ArgumentNullException("target");
+            Contract.EndContractBlock();
+            return new TargetObserver<TInput>(target);
+        }
+
+        /// <summary>Provides an observer wrapper for a target block.</summary>
+        [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+        private sealed class TargetObserver<TInput> : IObserver<TInput>, IDebuggerDisplay
+        {
+            /// <summary>The wrapped target.</summary>
+            private readonly ITargetBlock<TInput> _target;
+
+            /// <summary>Initializes the observer.</summary>
+            /// <param name="target">The target to wrap.</param>
+            internal TargetObserver(ITargetBlock<TInput> target)
+            {
+                Contract.Requires(target != null, "A target to observe is required.");
+                _target = target;
+            }
+
+            /// <summary>Sends the value to the observer.</summary>
+            /// <param name="value">The value to send.</param>
+            void IObserver<TInput>.OnNext(TInput value)
+            {
+                // Send the value asynchronously...
+                Task<bool> task = SendAsyncToTarget(value);
+
+                // And block until it's received.
+                task.GetAwaiter().GetResult(); // propagate original (non-aggregated) exception
+            }
+
+            /// <summary>Completes the target.</summary>
+            void IObserver<TInput>.OnCompleted()
+            {
+                _target.Complete();
+            }
+
+            /// <summary>Forwards the error to the target.</summary>
+            /// <param name="error">The exception to forward.</param>
+            void IObserver<TInput>.OnError(Exception error)
+            {
+                _target.Fault(error);
+            }
+
+            /// <summary>Sends a value to the underlying target asynchronously.</summary>
+            /// <param name="value">The value to send.</param>
+            /// <returns>A Task{bool} to wait on.</returns>
+            internal Task<bool> SendAsyncToTarget(TInput value)
+            {
+                return _target.SendAsync(value);
+            }
+
+            /// <summary>The data to display in the debugger display attribute.</summary>
+            [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+            private object DebuggerDisplayContent
+            {
+                get
+                {
+                    var displayTarget = _target as IDebuggerDisplay;
+                    return string.Format("Block=\"{0}\"",
+                        displayTarget != null ? displayTarget.Content : _target);
+                }
+            }
+            /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+            object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+        }
+        #endregion
+
+        #region NullTarget
+        /// <summary>
+        /// Gets a target block that synchronously accepts all messages offered to it and drops them.
+        /// </summary>
+        /// <typeparam name="TInput">The type of the messages this block can accept.</typeparam>
+        /// <returns>A <see cref="T:System.Threading.Tasks.Dataflow.ITargetBlock`1"/> that accepts and subsequently drops all offered messages.</returns>
+        public static ITargetBlock<TInput> NullTarget<TInput>()
+        {
+            return new NullTargetBlock<TInput>();
+        }
+
+        /// <summary>
+        /// Target block that synchronously accepts all messages offered to it and drops them.
+        /// </summary>
+        /// <typeparam name="TInput">The type of the messages this block can accept.</typeparam>
+        private class NullTargetBlock<TInput> : ITargetBlock<TInput>
+        {
+            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, Boolean consumeToAccept)
+            {
+                if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+                Contract.EndContractBlock();
+
+                // If the source requires an explicit synchronous consumption, do it
+                if (consumeToAccept)
+                {
+                    if (source == null) throw new ArgumentException(SR.Argument_CantConsumeFromANullSource, "consumeToAccept");
+                    bool messageConsumed;
+
+                    // If the source throws during this call, let the exception propagate back to the source
+                    source.ConsumeMessage(messageHeader, this, out messageConsumed);
+                    if (!messageConsumed) return DataflowMessageStatus.NotAvailable;
+                }
+
+                // Always tell the source the message has been accepted
+                return DataflowMessageStatus.Accepted;
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+            void IDataflowBlock.Complete() { } // No-op
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+            void IDataflowBlock.Fault(Exception exception) { } // No-op
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+            Task IDataflowBlock.Completion
+            {
+                get { return LazyInitializer.EnsureInitialized(ref _completion, () => new TaskCompletionSource<VoidResult>().Task); }
+            }
+        }
+        #endregion
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowBlockOptions.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowBlockOptions.cs
new file mode 100644 (file)
index 0000000..44532da
--- /dev/null
@@ -0,0 +1,414 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// DataflowBlockOptions.cs
+//
+//
+// DataflowBlockOptions types for configuring dataflow blocks
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Diagnostics;
+using System.Threading.Tasks;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>
+    /// Provides options used to configure the processing performed by dataflow blocks.
+    /// </summary>
+    /// <remarks>
+    /// <see cref="DataflowBlockOptions"/> is mutable and can be configured through its properties.  
+    /// When specific configuration options are not set, the following defaults are used:
+    /// <list type="table">
+    ///     <listheader>
+    ///         <term>Options</term>
+    ///         <description>Default</description>
+    ///     </listheader>
+    ///     <item>
+    ///         <term>TaskScheduler</term>
+    ///         <description><see cref="System.Threading.Tasks.TaskScheduler.Default"/></description>
+    ///     </item>
+    ///     <item>
+    ///         <term>MaxMessagesPerTask</term>
+    ///         <description>DataflowBlockOptions.Unbounded (-1)</description>
+    ///     </item>
+    ///     <item>
+    ///         <term>CancellationToken</term>
+    ///         <description><see cref="System.Threading.CancellationToken.None"/></description>
+    ///     </item>
+    ///     <item>
+    ///         <term>BoundedCapacity</term>
+    ///         <description>DataflowBlockOptions.Unbounded (-1)</description>
+    ///     </item>
+    ///     <item>
+    ///         <term>NameFormat</term>
+    ///         <description>"{0} Id={1}"</description>
+    ///     </item>
+    /// </list>
+    /// Dataflow blocks capture the state of the options at their construction.  Subsequent changes
+    /// to the provided <see cref="DataflowBlockOptions"/> instance should not affect the behavior
+    /// of a dataflow block.
+    /// </remarks>
+    [DebuggerDisplay("TaskScheduler = {TaskScheduler}, MaxMessagesPerTask = {MaxMessagesPerTask}, BoundedCapacity = {BoundedCapacity}")]
+    public class DataflowBlockOptions
+    {
+        /// <summary>
+        /// A constant used to specify an unlimited quantity for <see cref="DataflowBlockOptions"/> members 
+        /// that provide an upper bound. This field is constant.
+        /// </summary>
+        public const Int32 Unbounded = -1;
+
+        /// <summary>The scheduler to use for scheduling tasks to process messages.</summary>
+        private TaskScheduler _taskScheduler = TaskScheduler.Default;
+        /// <summary>The cancellation token to monitor for cancellation requests.</summary>
+        private CancellationToken _cancellationToken = CancellationToken.None;
+        /// <summary>The maximum number of messages that may be processed per task.</summary>
+        private Int32 _maxMessagesPerTask = Unbounded;
+        /// <summary>The maximum number of messages that may be buffered by the block.</summary>
+        private Int32 _boundedCapacity = Unbounded;
+        /// <summary>The name format to use for creating a name for a block.</summary>
+        private string _nameFormat = "{0} Id={1}"; // see NameFormat property for a description of format items
+
+        /// <summary>A default instance of <see cref="DataflowBlockOptions"/>.</summary>
+        /// <remarks>
+        /// Do not change the values of this instance.  It is shared by all of our blocks when no options are provided by the user.
+        /// </remarks>
+        internal static readonly DataflowBlockOptions Default = new DataflowBlockOptions();
+
+        /// <summary>Returns this <see cref="DataflowBlockOptions"/> instance if it's the default instance or else a cloned instance.</summary>
+        /// <returns>An instance of the options that may be cached by the block.</returns>
+        internal DataflowBlockOptions DefaultOrClone()
+        {
+            return (this == Default) ?
+                this :
+                new DataflowBlockOptions
+                {
+                    TaskScheduler = this.TaskScheduler,
+                    CancellationToken = this.CancellationToken,
+                    MaxMessagesPerTask = this.MaxMessagesPerTask,
+                    BoundedCapacity = this.BoundedCapacity,
+                    NameFormat = this.NameFormat
+                };
+        }
+
+        /// <summary>Initializes the <see cref="DataflowBlockOptions"/>.</summary>
+        public DataflowBlockOptions() { }
+
+        /// <summary>Gets or sets the <see cref="System.Threading.Tasks.TaskScheduler"/> to use for scheduling tasks.</summary>
+        public TaskScheduler TaskScheduler
+        {
+            get { return _taskScheduler; }
+            set
+            {
+                Debug.Assert(this != Default, "Default instance is supposed to be immutable.");
+                if (value == null) throw new ArgumentNullException("value");
+                _taskScheduler = value;
+            }
+        }
+
+        /// <summary>Gets or sets the <see cref="System.Threading.CancellationToken"/> to monitor for cancellation requests.</summary>
+        public CancellationToken CancellationToken
+        {
+            get { return _cancellationToken; }
+            set
+            {
+                Debug.Assert(this != Default, "Default instance is supposed to be immutable.");
+                _cancellationToken = value;
+            }
+        }
+
+        /// <summary>Gets or sets the maximum number of messages that may be processed per task.</summary>
+        public Int32 MaxMessagesPerTask
+        {
+            get { return _maxMessagesPerTask; }
+            set
+            {
+                Debug.Assert(this != Default, "Default instance is supposed to be immutable.");
+                if (value < 1 && value != Unbounded) throw new ArgumentOutOfRangeException("value");
+                _maxMessagesPerTask = value;
+            }
+        }
+
+        /// <summary>Gets a MaxMessagesPerTask value that may be used for comparison purposes.</summary>
+        /// <returns>The maximum value, usable for comparison purposes.</returns>
+        /// <remarks>Unlike MaxMessagesPerTask, this property will always return a positive value.</remarks>
+        internal Int32 ActualMaxMessagesPerTask
+        {
+            get { return (_maxMessagesPerTask == Unbounded) ? Int32.MaxValue : _maxMessagesPerTask; }
+        }
+
+        /// <summary>Gets or sets the maximum number of messages that may be buffered by the block.</summary>
+        public Int32 BoundedCapacity
+        {
+            get { return _boundedCapacity; }
+            set
+            {
+                Debug.Assert(this != Default, "Default instance is supposed to be immutable.");
+                if (value < 1 && value != Unbounded) throw new ArgumentOutOfRangeException("value");
+                _boundedCapacity = value;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the format string to use when a block is queried for its name.
+        /// </summary>
+        /// <remarks>
+        /// The name format may contain up to two format items. {0} will be substituted 
+        /// with the block's name. {1} will be substituted with the block's Id, as is 
+        /// returned from the block's Completion.Id property.
+        /// </remarks>
+        public string NameFormat
+        {
+            get { return _nameFormat; }
+            set
+            {
+                Debug.Assert(this != Default, "Default instance is supposed to be immutable.");
+                if (value == null) throw new ArgumentNullException("value");
+                _nameFormat = value;
+            }
+        }
+    }
+
+    /// <summary>
+    /// Provides options used to configure the processing performed by dataflow blocks that
+    /// process each message through the invocation of a user-provided delegate, blocks such
+    /// as <see cref="ActionBlock{T}"/> and <see cref="TransformBlock{TInput,TOutput}"/>.
+    /// </summary>
+    /// <remarks>
+    /// <see cref="ExecutionDataflowBlockOptions"/> is mutable and can be configured through its properties.  
+    /// When specific configuration options are not set, the following defaults are used:
+    /// <list type="table">
+    ///     <listheader>
+    ///         <term>Options</term>
+    ///         <description>Default</description>
+    ///     </listheader>
+    ///     <item>
+    ///         <term>TaskScheduler</term>
+    ///         <description><see cref="System.Threading.Tasks.TaskScheduler.Default"/></description>
+    ///     </item>
+    ///     <item>
+    ///         <term>CancellationToken</term>
+    ///         <description><see cref="System.Threading.CancellationToken.None"/></description>
+    ///     </item>
+    ///     <item>
+    ///         <term>MaxMessagesPerTask</term>
+    ///         <description>DataflowBlockOptions.Unbounded (-1)</description>
+    ///     </item>
+    ///     <item>
+    ///         <term>BoundedCapacity</term>
+    ///         <description>DataflowBlockOptions.Unbounded (-1)</description>
+    ///     </item>
+    ///     <item>
+    ///         <term>NameFormat</term>
+    ///         <description>"{0} Id={1}"</description>
+    ///     </item>
+    ///     <item>
+    ///         <term>MaxDegreeOfParallelism</term>
+    ///         <description>1</description>
+    ///     </item>
+    ///     <item>
+    ///         <term>SingleProducerConstrained</term>
+    ///         <description>false</description>
+    ///     </item>
+    /// </list>
+    /// Dataflow block captures the state of the options at their construction.  Subsequent changes
+    /// to the provided <see cref="ExecutionDataflowBlockOptions"/> instance should not affect the behavior
+    /// of a dataflow block.
+    /// </remarks>
+    [DebuggerDisplay("TaskScheduler = {TaskScheduler}, MaxMessagesPerTask = {MaxMessagesPerTask}, BoundedCapacity = {BoundedCapacity}, MaxDegreeOfParallelism = {MaxDegreeOfParallelism}")]
+    public class ExecutionDataflowBlockOptions : DataflowBlockOptions
+    {
+        /// <summary>A default instance of <see cref="DataflowBlockOptions"/>.</summary>
+        /// <remarks>
+        /// Do not change the values of this instance.  It is shared by all of our blocks when no options are provided by the user.
+        /// </remarks>
+        internal new static readonly ExecutionDataflowBlockOptions Default = new ExecutionDataflowBlockOptions();
+
+        /// <summary>Returns this <see cref="ExecutionDataflowBlockOptions"/> instance if it's the default instance or else a cloned instance.</summary>
+        /// <returns>An instance of the options that may be cached by the block.</returns>
+        internal new ExecutionDataflowBlockOptions DefaultOrClone()
+        {
+            return (this == Default) ?
+                this :
+                new ExecutionDataflowBlockOptions
+                {
+                    TaskScheduler = this.TaskScheduler,
+                    CancellationToken = this.CancellationToken,
+                    MaxMessagesPerTask = this.MaxMessagesPerTask,
+                    BoundedCapacity = this.BoundedCapacity,
+                    NameFormat = this.NameFormat,
+                    MaxDegreeOfParallelism = this.MaxDegreeOfParallelism,
+                    SingleProducerConstrained = this.SingleProducerConstrained
+                };
+        }
+
+        /// <summary>The maximum number of tasks that may be used concurrently to process messages.</summary>
+        private Int32 _maxDegreeOfParallelism = 1;
+        /// <summary>Whether the code using this block will only ever have a single producer accessing the block at any given time.</summary>
+        private Boolean _singleProducerConstrained = false;
+
+        /// <summary>Initializes the <see cref="ExecutionDataflowBlockOptions"/>.</summary>
+        public ExecutionDataflowBlockOptions() { }
+
+        /// <summary>Gets the maximum number of messages that may be processed by the block concurrently.</summary>
+        public Int32 MaxDegreeOfParallelism
+        {
+            get { return _maxDegreeOfParallelism; }
+            set
+            {
+                Debug.Assert(this != Default, "Default instance is supposed to be immutable.");
+                if (value < 1 && value != Unbounded) throw new ArgumentOutOfRangeException("value");
+                _maxDegreeOfParallelism = value;
+            }
+        }
+
+        /// <summary>
+        /// Gets whether code using the dataflow block is constrained to one producer at a time.
+        /// </summary>
+        /// <remarks>
+        /// This property defaults to false, such that the block may be used by multiple
+        /// producers concurrently.  This property should only be set to true if the code
+        /// using the block can guarantee that it will only ever be used by one producer
+        /// (e.g. a source linked to the block) at a time, meaning that methods like Post, 
+        /// Complete, Fault, and OfferMessage will never be called concurrently.  Some blocks 
+        /// may choose to capitalize on the knowledge that there will only be one producer at a time
+        /// in order to provide better performance.
+        /// </remarks>
+        public Boolean SingleProducerConstrained
+        {
+            get { return _singleProducerConstrained; }
+            set
+            {
+                Debug.Assert(this != Default, "Default instance is supposed to be immutable.");
+                _singleProducerConstrained = value;
+            }
+        }
+
+        /// <summary>Gets a MaxDegreeOfParallelism value that may be used for comparison purposes.</summary>
+        /// <returns>The maximum value, usable for comparison purposes.</returns>
+        /// <remarks>Unlike MaxDegreeOfParallelism, this property will always return a positive value.</remarks>
+        internal Int32 ActualMaxDegreeOfParallelism
+        {
+            get { return (_maxDegreeOfParallelism == Unbounded) ? Int32.MaxValue : _maxDegreeOfParallelism; }
+        }
+
+        /// <summary>Gets whether these dataflow block options allow for parallel execution.</summary>
+        internal Boolean SupportsParallelExecution { get { return _maxDegreeOfParallelism == Unbounded || _maxDegreeOfParallelism > 1; } }
+    }
+
+    /// <summary>
+    /// Provides options used to configure the processing performed by dataflow blocks that
+    /// group together multiple messages, blocks such as <see cref="JoinBlock{T1,T2}"/> and 
+    /// <see cref="BatchBlock{T}"/>.
+    /// </summary>
+    /// <remarks>
+    /// <see cref="GroupingDataflowBlockOptions"/> is mutable and can be configured through its properties.  
+    /// When specific configuration options are not set, the following defaults are used:
+    /// <list type="table">
+    ///     <listheader>
+    ///         <term>Options</term>
+    ///         <description>Default</description>
+    ///     </listheader>
+    ///     <item>
+    ///         <term>TaskScheduler</term>
+    ///         <description><see cref="System.Threading.Tasks.TaskScheduler.Default"/></description>
+    ///     </item>
+    ///     <item>
+    ///         <term>CancellationToken</term>
+    ///         <description><see cref="System.Threading.CancellationToken.None"/></description>
+    ///     </item>
+    ///     <item>
+    ///         <term>MaxMessagesPerTask</term>
+    ///         <description>DataflowBlockOptions.Unbounded (-1)</description>
+    ///     </item>
+    ///     <item>
+    ///         <term>BoundedCapacity</term>
+    ///         <description>DataflowBlockOptions.Unbounded (-1)</description>
+    ///     </item>
+    ///     <item>
+    ///         <term>NameFormat</term>
+    ///         <description>"{0} Id={1}"</description>
+    ///     </item>
+    ///     <item>
+    ///         <term>MaxNumberOfGroups</term>
+    ///         <description>GroupingDataflowBlockOptions.Unbounded (-1)</description>
+    ///     </item>
+    ///     <item>
+    ///         <term>Greedy</term>
+    ///         <description>true</description>
+    ///     </item>
+    /// </list>
+    /// Dataflow block capture the state of the options at their construction.  Subsequent changes
+    /// to the provided <see cref="GroupingDataflowBlockOptions"/> instance should not affect the behavior
+    /// of a dataflow block.
+    /// </remarks>
+    [DebuggerDisplay("TaskScheduler = {TaskScheduler}, MaxMessagesPerTask = {MaxMessagesPerTask}, BoundedCapacity = {BoundedCapacity}, Greedy = {Greedy}, MaxNumberOfGroups = {MaxNumberOfGroups}")]
+    public class GroupingDataflowBlockOptions : DataflowBlockOptions
+    {
+        /// <summary>A default instance of <see cref="DataflowBlockOptions"/>.</summary>
+        /// <remarks>
+        /// Do not change the values of this instance.  It is shared by all of our blocks when no options are provided by the user.
+        /// </remarks>
+        internal new static readonly GroupingDataflowBlockOptions Default = new GroupingDataflowBlockOptions();
+
+        /// <summary>Returns this <see cref="GroupingDataflowBlockOptions"/> instance if it's the default instance or else a cloned instance.</summary>
+        /// <returns>An instance of the options that may be cached by the block.</returns>
+        internal new GroupingDataflowBlockOptions DefaultOrClone()
+        {
+            return (this == Default) ?
+                this :
+                new GroupingDataflowBlockOptions
+                {
+                    TaskScheduler = this.TaskScheduler,
+                    CancellationToken = this.CancellationToken,
+                    MaxMessagesPerTask = this.MaxMessagesPerTask,
+                    BoundedCapacity = this.BoundedCapacity,
+                    NameFormat = this.NameFormat,
+                    Greedy = this.Greedy,
+                    MaxNumberOfGroups = this.MaxNumberOfGroups
+                };
+        }
+
+        /// <summary>Whether the block should greedily consume offered messages.</summary>
+        private Boolean _greedy = true;
+        /// <summary>The maximum number of groups that should be generated by the block.</summary>
+        private Int64 _maxNumberOfGroups = Unbounded;
+
+        /// <summary>Initializes the <see cref="GroupingDataflowBlockOptions"/>.</summary>
+        public GroupingDataflowBlockOptions() { }
+
+        /// <summary>Gets or sets the Boolean value to use to determine whether to greedily consume offered messages.</summary>
+        public Boolean Greedy
+        {
+            get { return _greedy; }
+            set
+            {
+                Debug.Assert(this != Default, "Default instance is supposed to be immutable.");
+                _greedy = value;
+            }
+        }
+
+        /// <summary>Gets or sets the maximum number of groups that should be generated by the block.</summary>
+        public Int64 MaxNumberOfGroups
+        {
+            get { return _maxNumberOfGroups; }
+            set
+            {
+                Debug.Assert(this != Default, "Default instance is supposed to be immutable.");
+                if (value <= 0 && value != Unbounded) throw new ArgumentOutOfRangeException("value");
+                _maxNumberOfGroups = value;
+            }
+        }
+
+        /// <summary>Gets a MaxNumberOfGroups value that may be used for comparison purposes.</summary>
+        /// <returns>The maximum value, usable for comparison purposes.</returns>
+        /// <remarks>Unlike MaxNumberOfGroups, this property will always return a positive value.</remarks>
+        internal Int64 ActualMaxNumberOfGroups
+        {
+            get { return (_maxNumberOfGroups == Unbounded) ? Int64.MaxValue : _maxNumberOfGroups; }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowLinkOptions.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowLinkOptions.cs
new file mode 100644 (file)
index 0000000..079da23
--- /dev/null
@@ -0,0 +1,113 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// DataflowLinkOptions.cs
+//
+//
+// DataflowLinkOptions type for configuring links between dataflow blocks
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Diagnostics;
+using System.Threading.Tasks;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>
+    /// Provides options used to configure a link between dataflow blocks.
+    /// </summary>
+    /// <remarks>
+    /// <see cref="DataflowLinkOptions"/> is mutable and can be configured through its properties.  
+    /// When specific configuration options are not set, the following defaults are used:
+    /// <list type="table">
+    ///     <listheader>
+    ///         <term>Options</term>
+    ///         <description>Default</description>
+    ///     </listheader>
+    ///     <item>
+    ///         <term>PropagateCompletion</term>
+    ///         <description>False</description>
+    ///     </item>
+    ///     <item>
+    ///         <term>MaxMessages</term>
+    ///         <description>DataflowBlockOptions.Unbounded (-1)</description>
+    ///     </item>
+    ///     <item>
+    ///         <term>Append</term>
+    ///         <description>True</description>
+    ///     </item>
+    /// </list>
+    /// Dataflow blocks capture the state of the options at linking. Subsequent changes to the provided
+    /// <see cref="DataflowLinkOptions"/> instance should not affect the behavior of a link.
+    /// </remarks>
+    [DebuggerDisplay("PropagateCompletion = {PropagateCompletion}, MaxMessages = {MaxMessages}, Append = {Append}")]
+    public class DataflowLinkOptions
+    {
+        /// <summary>
+        /// A constant used to specify an unlimited quantity for <see cref="DataflowLinkOptions"/> members 
+        /// that provide an upper bound. This field is a constant tied to <see cref="DataflowLinkOptions.Unbounded"/>.
+        /// </summary>
+        internal const Int32 Unbounded = DataflowBlockOptions.Unbounded;
+
+        /// <summary>Whether the linked target will have completion and faulting notification propagated to it automatically.</summary>
+        private Boolean _propagateCompletion = false;
+        /// <summary>The maximum number of messages that may be consumed across the link.</summary>
+        private Int32 _maxNumberOfMessages = Unbounded;
+        /// <summary>Whether the link should be appended to the source’s list of links, or whether it should be prepended.</summary>
+        private Boolean _append = true;
+
+        /// <summary>A default instance of <see cref="DataflowLinkOptions"/>.</summary>
+        /// <remarks>
+        /// Do not change the values of this instance.  It is shared by all of our blocks when no options are provided by the user.
+        /// </remarks>
+        internal static readonly DataflowLinkOptions Default = new DataflowLinkOptions();
+
+        /// <summary>A cached instance of <see cref="DataflowLinkOptions"/>.</summary>
+        /// <remarks>
+        /// Do not change the values of this instance.  It is shared by all of our blocks that need to unlink after one message has been consumed.
+        /// </remarks>
+        internal static readonly DataflowLinkOptions UnlinkAfterOneAndPropagateCompletion = new DataflowLinkOptions() { MaxMessages = 1, PropagateCompletion = true };
+
+        /// <summary>Initializes the <see cref="DataflowLinkOptions"/>.</summary>
+        public DataflowLinkOptions()
+        {
+        }
+
+        /// <summary>Gets or sets whether the linked target will have completion and faulting notification propagated to it automatically.</summary>
+        public Boolean PropagateCompletion
+        {
+            get { return _propagateCompletion; }
+            set
+            {
+                Debug.Assert(this != Default && this != UnlinkAfterOneAndPropagateCompletion, "Default and UnlinkAfterOneAndPropagateCompletion instances are supposed to be immutable.");
+                _propagateCompletion = value;
+            }
+        }
+
+        /// <summary>Gets or sets the maximum number of messages that may be consumed across the link.</summary>
+        public Int32 MaxMessages
+        {
+            get { return _maxNumberOfMessages; }
+            set
+            {
+                Debug.Assert(this != Default && this != UnlinkAfterOneAndPropagateCompletion, "Default and UnlinkAfterOneAndPropagateCompletion instances are supposed to be immutable.");
+                if (value < 1 && value != Unbounded) throw new ArgumentOutOfRangeException("value");
+                _maxNumberOfMessages = value;
+            }
+        }
+
+        /// <summary>Gets or sets whether the link should be appended to the source’s list of links, or whether it should be prepended.</summary>
+        public Boolean Append
+        {
+            get { return _append; }
+            set
+            {
+                Debug.Assert(this != Default && this != UnlinkAfterOneAndPropagateCompletion, "Default and UnlinkAfterOneAndPropagateCompletion instances are supposed to be immutable.");
+                _append = value;
+            }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowMessageHeader.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowMessageHeader.cs
new file mode 100644 (file)
index 0000000..a09fe50
--- /dev/null
@@ -0,0 +1,92 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// DataflowMessageHeader.cs
+//
+//
+// A container of data attributes passed between dataflow blocks.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Threading.Tasks.Dataflow.Internal;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Provides a container of data attributes for passing between dataflow blocks.</summary>
+    [DebuggerDisplay("Id = {Id}")]
+    public struct DataflowMessageHeader : IEquatable<DataflowMessageHeader>
+    {
+        /// <summary>The message ID. Needs to be unique within the source.</summary>
+        private readonly long _id;
+
+        /// <summary>Initializes the <see cref="DataflowMessageHeader"/> with the specified attributes.</summary>
+        /// <param name="id">The ID of the message. Must be unique within the originating source block. Need not be globally unique.</param>
+        public DataflowMessageHeader(Int64 id)
+        {
+            if (id == default(long)) throw new ArgumentException(SR.Argument_InvalidMessageId, "id");
+            Contract.EndContractBlock();
+
+            _id = id;
+        }
+
+        /// <summary>Gets the validity of the message.</summary>
+        /// <returns>True if the ID of the message is different from 0. False if the ID of the message is 0</returns>
+        public Boolean IsValid { get { return _id != default(long); } }
+
+        /// <summary>Gets the ID of the message within the source.</summary>
+        /// <returns>The ID contained in the <see cref="DataflowMessageHeader"/> instance.</returns>
+        public Int64 Id { get { return _id; } }
+
+        // These overrides are required by the FX API Guidelines.
+        // NOTE: When these overrides are present, the compiler doesn't complain about statements 
+        // like 'if (struct == null) ...' which will result in incorrect behavior at runtime.
+        // The product code should not use them. Instead, it should compare the Id properties.
+        // To verify that, every once in a while, comment out this region and build the product. 
+        #region Comparison Operators
+        /// <summary>Checks two <see cref="DataflowMessageHeader"/> instances for equality by ID without boxing.</summary>
+        /// <param name="other">Another <see cref="DataflowMessageHeader"/> instance.</param>
+        /// <returns>True if the instances are equal. False otherwise.</returns>
+        public bool Equals(DataflowMessageHeader other)
+        {
+            return this == other;
+        }
+
+        /// <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)
+        {
+            return obj is DataflowMessageHeader && this == (DataflowMessageHeader)obj;
+        }
+
+        /// <summary>Generates a hash code for the <see cref="DataflowMessageHeader"/> instance.</summary>
+        /// <returns>Hash code.</returns>
+        public override int GetHashCode()
+        {
+            return (int)Id;
+        }
+
+        /// <summary>Checks two <see cref="DataflowMessageHeader"/> instances for equality by ID.</summary>
+        /// <param name="left">A <see cref="DataflowMessageHeader"/> instance.</param>
+        /// <param name="right">A <see cref="DataflowMessageHeader"/> instance.</param>
+        /// <returns>True if the instances are equal. False otherwise.</returns>
+        public static bool operator ==(DataflowMessageHeader left, DataflowMessageHeader right)
+        {
+            return left.Id == right.Id;
+        }
+
+        /// <summary>Checks two <see cref="DataflowMessageHeader"/> instances for non-equality by ID.</summary>
+        /// <param name="left">A <see cref="DataflowMessageHeader"/> instance.</param>
+        /// <param name="right">A <see cref="DataflowMessageHeader"/> instance.</param>
+        /// <returns>True if the instances are not equal. False otherwise.</returns>
+        public static bool operator !=(DataflowMessageHeader left, DataflowMessageHeader right)
+        {
+            return left.Id != right.Id;
+        }
+        #endregion
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowMessageStatus.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/DataflowMessageStatus.cs
new file mode 100644 (file)
index 0000000..17a71c2
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// DataflowMessageStatus.cs
+//
+//
+// Status about the propagation of a message.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Represents the status of a <see cref="DataflowMessageHeader"/> when passed between dataflow blocks.</summary>
+    public enum DataflowMessageStatus
+    {
+        /// <summary>
+        /// Indicates that the <see cref="ITargetBlock{TInput}"/> accepted the message.  Once a target has accepted a message, 
+        /// it is wholly owned by the target.
+        /// </summary>
+        Accepted = 0x0,
+
+        /// <summary>
+        /// Indicates that the <see cref="ITargetBlock{TInput}"/> declined the message.  The <see cref="ISourceBlock{TOutput}"/> still owns the message.
+        /// </summary>
+        Declined = 0x1,
+
+        /// <summary>
+        /// Indicates that the <see cref="ITargetBlock{TInput}"/> postponed the message for potential consumption at a later time.  
+        /// The <see cref="ISourceBlock{TOutput}"/> still owns the message.
+        /// </summary>
+        Postponed = 0x2,
+
+        /// <summary>
+        /// Indicates that the <see cref="ITargetBlock{TInput}"/> tried to accept the message from the <see cref="ISourceBlock{TOutput}"/>, but the 
+        /// message was no longer available.
+        /// </summary>
+        NotAvailable = 0x3,
+
+        /// <summary>
+        /// Indicates that the <see cref="ITargetBlock{TInput}"/> declined the message.  The <see cref="ISourceBlock{TOutput}"/> still owns the message.  
+        /// Additionally, the <see cref="ITargetBlock{TInput}"/> will decline all future messages sent by the source.
+        /// </summary>
+        DecliningPermanently = 0x4
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/IDataflowBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/IDataflowBlock.cs
new file mode 100644 (file)
index 0000000..d917770
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// IDataflowBlock.cs
+//
+//
+// The base interface for all dataflow blocks.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Represents a dataflow block.</summary>
+    public interface IDataflowBlock
+    {
+        // IMPLEMENT IMPLICITLY
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+        Task Completion { get; }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+        void Complete();
+
+        // IMPLEMENT EXPLICITLY
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void Fault(Exception exception);
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/IPropagatorBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/IPropagatorBlock.cs
new file mode 100644 (file)
index 0000000..d8a8335
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// IPropagatorBlock.cs
+//
+//
+// The base interface for all propagator blocks.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Represents a dataflow block that is both a target for data and a source of data.</summary>
+    /// <typeparam name="TInput">Specifies the type of data accepted by the <see cref="IPropagatorBlock{TInput,TOutput}"/>.</typeparam>
+    /// <typeparam name="TOutput">Specifies the type of data supplied by the <see cref="IPropagatorBlock{TInput,TOutput}"/>.</typeparam>
+    public interface IPropagatorBlock<in TInput, out TOutput> : ITargetBlock<TInput>, ISourceBlock<TOutput>
+    {
+        // No additional members beyond those inherited from ITargetBlock<TInput> and ISourceBlock<TOutput>
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/IReceivableSourceBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/IReceivableSourceBlock.cs
new file mode 100644 (file)
index 0000000..15688cb
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// IReceivableSourceBlock.cs
+//
+//
+// The base interface for all source blocks.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Represents a dataflow block that supports receiving of messages without linking.</summary>
+    /// <typeparam name="TOutput">Specifies the type of data supplied by the <see cref="IReceivableSourceBlock{TOutput}"/>.</typeparam>
+    public interface IReceivableSourceBlock<TOutput> : ISourceBlock<TOutput>
+    {
+        // IMPLEMENT IMPLICITLY
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
+        bool TryReceive(Predicate<TOutput> filter, 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);
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/ISourceBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/ISourceBlock.cs
new file mode 100644 (file)
index 0000000..34f904c
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// ISourceBlock.cs
+//
+//
+// The base interface for all source blocks.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Represents a dataflow block that is a source of data.</summary>
+    /// <typeparam name="TOutput">Specifies the type of data supplied by the <see cref="ISourceBlock{TOutput}"/>.</typeparam>
+    public interface ISourceBlock<out TOutput> : IDataflowBlock
+    {
+        // IMPLEMENT IMPLICITLY
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+        IDisposable LinkTo(ITargetBlock<TOutput> target, DataflowLinkOptions linkOptions);
+
+        // IMPLEMENT EXPLICITLY
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+        [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#")]
+        TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out Boolean messageConsumed);
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+        Boolean ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target);
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+        void ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target);
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/ITargetBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Base/ITargetBlock.cs
new file mode 100644 (file)
index 0000000..4d2614c
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// ITargetBlock.cs
+//
+//
+// The base interface for all target blocks.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Represents a dataflow block that is a target for data.</summary>
+    /// <typeparam name="TInput">Specifies the type of data accepted by the <see cref="ITargetBlock{TInput}"/>.</typeparam>
+    public interface ITargetBlock<in TInput> : IDataflowBlock
+    {
+        // IMPLEMENT EXPLICITLY
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
+        DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, Boolean consumeToAccept);
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/ActionBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/ActionBlock.cs
new file mode 100644 (file)
index 0000000..67824a9
--- /dev/null
@@ -0,0 +1,383 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// ActionBlock.cs
+//
+//
+// A target block that executes an action for each message.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks.Dataflow.Internal;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Provides a dataflow block that invokes a provided <see cref="System.Action{T}"/> delegate for every data element received.</summary>
+    /// <typeparam name="TInput">Specifies the type of data operated on by this <see cref="ActionBlock{T}"/>.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(ActionBlock<>.DebugView))]
+    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;
+        /// <summary>The core implementation of this message block when in SPSC mode.</summary>
+        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>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action"/> is null (Nothing in Visual Basic).</exception>
+        public ActionBlock(Action<TInput> action) :
+            this((Delegate)action, ExecutionDataflowBlockOptions.Default)
+        { }
+
+        /// <summary>Initializes the <see cref="ActionBlock{T}"/> with the specified <see cref="System.Action{T}"/> and <see cref="ExecutionDataflowBlockOptions"/>.</summary>
+        /// <param name="action">The action to invoke with each data element received.</param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="ActionBlock{T}"/>.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        public ActionBlock(Action<TInput> action, ExecutionDataflowBlockOptions dataflowBlockOptions) :
+            this((Delegate)action, dataflowBlockOptions)
+        { }
+
+        /// <summary>Initializes the <see cref="ActionBlock{T}"/> with the specified <see cref="System.Func{T,Task}"/>.</summary>
+        /// <param name="action">The action to invoke with each data element received.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action"/> is null (Nothing in Visual Basic).</exception>
+        public ActionBlock(Func<TInput, Task> action) :
+            this((Delegate)action, ExecutionDataflowBlockOptions.Default)
+        { }
+
+        /// <summary>Initializes the <see cref="ActionBlock{T}"/> with the specified <see cref="System.Func{T,Task}"/> and <see cref="ExecutionDataflowBlockOptions"/>.</summary>
+        /// <param name="action">The action to invoke with each data element received.</param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="ActionBlock{T}"/>.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        public ActionBlock(Func<TInput, Task> action, ExecutionDataflowBlockOptions dataflowBlockOptions) :
+            this((Delegate)action, dataflowBlockOptions)
+        { }
+
+        /// <summary>Initializes the <see cref="ActionBlock{T}"/> with the specified delegate and options.</summary>
+        /// <param name="action">The action to invoke with each data element received.</param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="ActionBlock{T}"/>.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="action"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        private ActionBlock(Delegate action, ExecutionDataflowBlockOptions dataflowBlockOptions)
+        {
+            // Validate arguments
+            if (action == null) throw new ArgumentNullException("action");
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+            Contract.Ensures((_spscTarget != null) ^ (_defaultTarget != null), "One and only one of the two targets must be non-null after construction");
+            Contract.EndContractBlock();
+
+            // Ensure we have options that can't be changed by the caller
+            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
+
+            // Based on the mode, initialize the target.  If the user specifies SingleProducerConstrained,
+            // we'll try to employ an optimized mode under a limited set of circumstances.
+            var syncAction = action as Action<TInput>;
+            if (syncAction != null &&
+                dataflowBlockOptions.SingleProducerConstrained &&
+                dataflowBlockOptions.MaxDegreeOfParallelism == 1 &&
+                !dataflowBlockOptions.CancellationToken.CanBeCanceled &&
+                dataflowBlockOptions.BoundedCapacity == DataflowBlockOptions.Unbounded)
+            {
+                // Initialize the SPSC fast target to handle the bulk of the processing.
+                // The SpscTargetCore is only supported when BoundedCapacity, CancellationToken,
+                // and MaxDOP are all their default values.  It's also only supported for sync
+                // delegates and not for async delegates.
+                _spscTarget = new SpscTargetCore<TInput>(this, syncAction, dataflowBlockOptions);
+            }
+            else
+            {
+                // Initialize the TargetCore which handles the bulk of the processing.
+                // The default target core can handle all options and delegate flavors.
+
+                if (syncAction != null) // sync
+                {
+                    _defaultTarget = new TargetCore<TInput>(this,
+                        messageWithId => ProcessMessage(syncAction, messageWithId),
+                        null, dataflowBlockOptions, TargetCoreOptions.RepresentsBlockCompletion);
+                }
+                else // async
+                {
+                    var asyncAction = action as Func<TInput, Task>;
+                    Debug.Assert(asyncAction != null, "action is of incorrect delegate type");
+                    _defaultTarget = new TargetCore<TInput>(this,
+                        messageWithId => ProcessMessageWithTask(asyncAction, messageWithId),
+                        null, dataflowBlockOptions, TargetCoreOptions.RepresentsBlockCompletion | TargetCoreOptions.UsesAsyncCompletion);
+                }
+
+                // Handle async cancellation requests by declining on the target
+                Common.WireCancellationToComplete(
+                    dataflowBlockOptions.CancellationToken, Completion, state => ((TargetCore<TInput>)state).Complete(exception: null, dropPendingMessages: true), _defaultTarget);
+            }
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
+            }
+#endif
+        }
+
+        /// <summary>Processes the message with a user-provided action.</summary>
+        /// <param name="action">The action to use to process the message.</param>
+        /// <param name="messageWithId">The message to be processed.</param>
+        private void ProcessMessage(Action<TInput> action, KeyValuePair<TInput, long> messageWithId)
+        {
+            try
+            {
+                action(messageWithId.Key);
+            }
+            catch (Exception exc)
+            {
+                // If this exception represents cancellation, swallow it rather than shutting down the block.
+                if (!Common.IsCooperativeCancellation(exc)) throw;
+            }
+            finally
+            {
+                // We're done synchronously processing an element, so reduce the bounding count
+                // that was incrementing when this element was enqueued.
+                if (_defaultTarget.IsBounded) _defaultTarget.ChangeBoundingCount(-1);
+            }
+        }
+
+        /// <summary>Processes the message with a user-provided action that returns a task.</summary>
+        /// <param name="action">The action to use to process the message.</param>
+        /// <param name="messageWithId">The message to be processed.</param>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private void ProcessMessageWithTask(Func<TInput, Task> action, KeyValuePair<TInput, long> messageWithId)
+        {
+            Contract.Requires(action != null, "action needed for processing");
+
+            // Run the action to get the task that represents the operation's completion
+            Task task = null;
+            Exception caughtException = null;
+            try
+            {
+                task = action(messageWithId.Key);
+            }
+            catch (Exception exc) { caughtException = exc; }
+
+            // If no task is available, we're done.
+            if (task == null)
+            {
+                // If we didn't get a task because an exception occurred,
+                // store it (if the exception was cancellation, just ignore it).
+                if (caughtException != null && !Common.IsCooperativeCancellation(caughtException))
+                {
+                    Common.StoreDataflowMessageValueIntoExceptionData(caughtException, messageWithId.Key);
+                    _defaultTarget.Complete(caughtException, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: false);
+                }
+
+                // Signal that we're done this async operation.
+                _defaultTarget.SignalOneAsyncMessageCompleted(boundingCountChange: -1);
+                return;
+            }
+            else if (task.IsCompleted)
+            {
+                AsyncCompleteProcessMessageWithTask(task);
+            }
+            else
+            {
+                // Otherwise, join with the asynchronous operation when it completes.
+                task.ContinueWith((completed, state) =>
+                {
+                    ((ActionBlock<TInput>)state).AsyncCompleteProcessMessageWithTask(completed);
+                }, this, CancellationToken.None, Common.GetContinuationOptions(TaskContinuationOptions.ExecuteSynchronously), TaskScheduler.Default);
+            }
+        }
+
+        /// <summary>Completes the processing of an asynchronous message.</summary>
+        /// <param name="completed">The completed task.</param>
+        private void AsyncCompleteProcessMessageWithTask(Task completed)
+        {
+            Contract.Requires(completed != null, "Need completed task for processing");
+            Contract.Requires(completed.IsCompleted, "The task to be processed must be completed by now.");
+
+            // 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
+            // depend on this.
+            if (completed.IsFaulted)
+            {
+                _defaultTarget.Complete(completed.Exception, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: true);
+            }
+
+            // Regardless of faults, note that we're done processing.  There are
+            // no outputs to keep track of for action block, so we always decrement 
+            // the bounding count here (the callee will handle checking whether
+            // we're actually in a bounded mode).
+            _defaultTarget.SignalOneAsyncMessageCompleted(boundingCountChange: -1);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+        public void Complete()
+        {
+            if (_defaultTarget != null)
+            {
+                _defaultTarget.Complete(exception: null, dropPendingMessages: false);
+            }
+            else
+            {
+                _spscTarget.Complete(exception: null);
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            if (_defaultTarget != null)
+            {
+                _defaultTarget.Complete(exception, dropPendingMessages: true);
+            }
+            else
+            {
+                _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; }
+        }
+
+        /// <summary>Posts an item to the <see cref="T:System.Threading.Tasks.Dataflow.ITargetBlock`1"/>.</summary>
+        /// <param name="item">The item being offered to the target.</param>
+        /// <returns>true if the item was accepted by the target block; otherwise, false.</returns>
+        /// <remarks>
+        /// This method will return once the target block has decided to accept or decline the item,
+        /// but unless otherwise dictated by special semantics of the target block, it does not wait
+        /// for the item to actually be processed (for example, <see cref="T:System.Threading.Tasks.Dataflow.ActionBlock`1"/>
+        /// will return from Post as soon as it has stored the posted item into its input queue).  From the perspective
+        /// of the block's processing, Post is asynchronous. For target blocks that support postponing offered messages, 
+        /// or for blocks that may do more processing in their Post implementation, consider using
+        ///  <see cref="T:System.Threading.Tasks.Dataflow.DataflowBlock.SendAsync">SendAsync</see>, 
+        /// which will return immediately and will enable the target to postpone the posted message and later consume it 
+        /// after SendAsync returns.
+        /// </remarks>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool Post(TInput item)
+        {
+            // Even though this method is available with the exact same functionality as an extension method
+            // on ITargetBlock, using that extension method goes through an interface call on ITargetBlock,
+            // which for very high-throughput scenarios shows up as noticeable overhead on certain architectures.  
+            // We can eliminate that call for direct ActionBlock usage by providing the same method as an instance method.
+
+            return _defaultTarget != null ?
+                _defaultTarget.OfferMessage(Common.SingleMessageHeader, item, null, false) == DataflowMessageStatus.Accepted :
+                _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, Boolean consumeToAccept)
+        {
+            return _defaultTarget != null ?
+                _defaultTarget.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; }
+        }
+
+        /// <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; }
+        }
+
+        /// <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);
+        }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                return string.Format("{0}, InputCount={1}",
+                    Common.GetNameForDebugger(this, _defaultTarget != null ? _defaultTarget.DataflowBlockOptions : _spscTarget.DataflowBlockOptions),
+                    InputCountForDebugger);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for the Call.</summary>
+        private sealed class DebugView
+        {
+            /// <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;
+            /// <summary>The action block's SPSC target being viewed.</summary>
+            private readonly SpscTargetCore<TInput>.DebuggingInformation _spscDebugInfo;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="actionBlock">The target being debugged.</param>
+            public DebugView(ActionBlock<TInput> actionBlock)
+            {
+                Contract.Requires(actionBlock != null, "Need a block with which to construct the debug view.");
+                _actionBlock = actionBlock;
+                if (_actionBlock._defaultTarget != null)
+                {
+                    _defaultDebugInfo = actionBlock._defaultTarget.GetDebuggingInformation();
+                }
+                else
+                {
+                    _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; }
+            }
+            /// <summary>Gets any postponed messages.</summary>
+            public QueuedMap<ISourceBlock<TInput>, DataflowMessageHeader> PostponedMessages
+            {
+                get { return _defaultDebugInfo != null ? _defaultDebugInfo.PostponedMessages : null; }
+            }
+
+            /// <summary>Gets the number of outstanding input operations.</summary>
+            public Int32 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; }
+            }
+            /// <summary>Gets whether the block is declining further messages.</summary>
+            public bool 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; }
+            }
+            /// <summary>Gets the block's Id.</summary>
+            public int Id { get { return Common.GetBlockId(_actionBlock); } }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/BatchBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/BatchBlock.cs
new file mode 100644 (file)
index 0000000..8a12745
--- /dev/null
@@ -0,0 +1,1206 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// BatchBlock.cs
+//
+//
+// A propagator block that groups individual messages into arrays of messages.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Security;
+using System.Threading.Tasks.Dataflow.Internal;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Provides a dataflow block that batches inputs into arrays.</summary>
+    /// <typeparam name="T">Specifies the type of data put into batches.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(BatchBlock<>.DebugView))]
+    [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
+    public sealed class BatchBlock<T> : IPropagatorBlock<T, T[]>, IReceivableSourceBlock<T[]>, IDebuggerDisplay
+    {
+        /// <summary>The target half of this batch.</summary>
+        private readonly BatchBlockTargetCore _target;
+        /// <summary>The source half of this batch.</summary>
+        private readonly SourceCore<T[]> _source;
+
+        /// <summary>Initializes this <see cref="BatchBlock{T}"/> with the specified batch size.</summary>
+        /// <param name="batchSize">The number of items to group into a batch.</param>
+        /// <exception cref="System.ArgumentOutOfRangeException">The <paramref name="batchSize"/> must be positive.</exception>
+        public BatchBlock(Int32 batchSize) :
+            this(batchSize, GroupingDataflowBlockOptions.Default)
+        { }
+
+        /// <summary>Initializes this <see cref="BatchBlock{T}"/> with the  specified batch size, declining option, and block options.</summary>
+        /// <param name="batchSize">The number of items to group into a batch.</param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="BatchBlock{T}"/>.</param>
+        /// <exception cref="System.ArgumentOutOfRangeException">The <paramref name="batchSize"/> must be positive.</exception>
+        /// <exception cref="System.ArgumentOutOfRangeException">The <paramref name="batchSize"/> must be no greater than the value of the BoundedCapacity option if a non-default value has been set.</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        public BatchBlock(Int32 batchSize, GroupingDataflowBlockOptions dataflowBlockOptions)
+        {
+            // Validate arguments
+            if (batchSize < 1) throw new ArgumentOutOfRangeException("batchSize", SR.ArgumentOutOfRange_GenericPositive);
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+            if (dataflowBlockOptions.BoundedCapacity > 0 && dataflowBlockOptions.BoundedCapacity < batchSize) throw new ArgumentOutOfRangeException("batchSize", SR.ArgumentOutOfRange_BatchSizeMustBeNoGreaterThanBoundedCapacity);
+            Contract.EndContractBlock();
+
+            // Ensure we have options that can't be changed by the caller
+            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
+
+            // Initialize bounding actions
+            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);
+                itemCountingFunc = (owningSource, singleOutputItem, multipleOutputItems) => BatchBlockTargetCore.CountItems(singleOutputItem, multipleOutputItems);
+            }
+
+            // Initialize source
+            _source = new SourceCore<T[]>(this, dataflowBlockOptions,
+                owningSource => ((BatchBlock<T>)owningSource)._target.Complete(exception: null, dropPendingMessages: true, releaseReservedMessages: false),
+                onItemsRemoved, itemCountingFunc);
+
+            // Initialize target
+            _target = new BatchBlockTargetCore(this, batchSize, batch => _source.AddMessage(batch), dataflowBlockOptions);
+
+            // When the target is done, let the source know it won't be getting any more data
+            _target.Completion.ContinueWith(delegate { _source.Complete(); },
+                CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default);
+
+            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
+            // In those cases we need to fault the target half to drop its buffered messages and to release its 
+            // reservations. This should not create an infinite loop, because all our implementations are designed
+            // to handle multiple completion requests and to carry over only one.
+            _source.Completion.ContinueWith((completed, state) =>
+            {
+                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);
+            }, 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);
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
+            }
+#endif
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+        public void Complete() { _target.Complete(exception: null, dropPendingMessages: false, releaseReservedMessages: false); }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            _target.Complete(exception, dropPendingMessages: true, releaseReservedMessages: false);
+        }
+
+        /// <summary>
+        /// Triggers the <see cref="BatchBlock{T}"/> to initiate a batching operation even if the number
+        /// of currently queued or postponed items is less than the <see cref="BatchSize"/>.
+        /// </summary>
+        /// <remarks>
+        /// In greedy mode, a batch will be generated from queued items even if fewer exist than the batch size.  
+        /// In non-greedy mode, a batch will be generated asynchronously from postponed items even if
+        /// fewer than the batch size can be consumed.
+        /// </remarks>
+        public void TriggerBatch() { _target.TriggerBatch(); }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+        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 Boolean TryReceive(Predicate<T[]> filter, 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); }
+
+        /// <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/Blocks/Member[@name="Completion"]/*' />
+        public Task Completion { get { return _source.Completion; } }
+
+        /// <summary>Gets the size of the batches generated by this <see cref="BatchBlock{T}"/>.</summary>
+        /// <remarks>
+        /// If the number of items provided to the block is not evenly divisible by the batch size provided
+        /// to the block's constructor, the block's final batch may contain fewer than the requested number of items.
+        /// </remarks>
+        public Int32 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, Boolean consumeToAccept)
+        {
+            return _target.OfferMessage(messageHeader, messageValue, source, consumeToAccept);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+        T[] ISourceBlock<T[]>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<T[]> target, out Boolean messageConsumed)
+        {
+            return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+        bool ISourceBlock<T[]>.ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<T[]> target)
+        {
+            return _source.ReserveMessage(messageHeader, target);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+        void ISourceBlock<T[]>.ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<T[]> target)
+        {
+            _source.ReleaseReservation(messageHeader, target);
+        }
+
+        /// <summary>Gets the number of messages waiting to be offered.  This must only be used from the debugger as it avoids taking necessary locks.</summary>
+        private int OutputCountForDebugger { get { return _source.GetDebuggingInformation().OutputCount; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="ToString"]/*' />
+        public override string ToString() { return Common.GetNameForDebugger(this, _source.DataflowBlockOptions); }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                return string.Format("{0}, BatchSize={1}, OutputCount={2}",
+                    Common.GetNameForDebugger(this, _source.DataflowBlockOptions),
+                    BatchSize,
+                    OutputCountForDebugger);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for the Batch.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The batch block being viewed.</summary>
+            private BatchBlock<T> _batchBlock;
+            /// <summary>The target half being viewed.</summary>
+            private readonly BatchBlockTargetCore.DebuggingInformation _targetDebuggingInformation;
+            /// <summary>The source half of the block being viewed.</summary>
+            private readonly SourceCore<T[]>.DebuggingInformation _sourceDebuggingInformation;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="batchBlock">The batch being viewed.</param>
+            public DebugView(BatchBlock<T> batchBlock)
+            {
+                Contract.Requires(batchBlock != null, "Need a block with which to construct the debug view");
+                _batchBlock = batchBlock;
+                _targetDebuggingInformation = batchBlock._target.GetDebuggingInformation();
+                _sourceDebuggingInformation = batchBlock._source.GetDebuggingInformation();
+            }
+
+            /// <summary>Gets the messages waiting to be processed.</summary>
+            public IEnumerable<T> InputQueue { get { return _targetDebuggingInformation.InputQueue; } }
+            /// <summary>Gets the messages waiting to be received.</summary>
+            public IEnumerable<T[]> OutputQueue { get { return _sourceDebuggingInformation.OutputQueue; } }
+            /// <summary>Gets the number of batches that have been completed.</summary>
+            public long BatchesCompleted { get { return _targetDebuggingInformation.NumberOfBatchesCompleted; } }
+
+            /// <summary>Gets the task being used for input processing.</summary>
+            public Task TaskForInputProcessing { get { return _targetDebuggingInformation.TaskForInputProcessing; } }
+            /// <summary>Gets the task being used for output processing.</summary>
+            public Task TaskForOutputProcessing { get { return _sourceDebuggingInformation.TaskForOutputProcessing; } }
+
+            /// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
+            public GroupingDataflowBlockOptions DataflowBlockOptions { get { return _targetDebuggingInformation.DataflowBlockOptions; } }
+            /// <summary>Gets the size of batches generated by the block.</summary>
+            public int BatchSize { get { return _batchBlock.BatchSize; } }
+            /// <summary>Gets whether the block is declining further messages.</summary>
+            public bool IsDecliningPermanently { get { return _targetDebuggingInformation.IsDecliningPermanently; } }
+            /// <summary>Gets whether the block is completed.</summary>
+            public bool IsCompleted { get { return _sourceDebuggingInformation.IsCompleted; } }
+            /// <summary>Gets the block's Id.</summary>
+            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; } }
+            /// <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; } }
+        }
+
+        /// <summary>Provides the core target implementation for a Batch.</summary>
+        [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
+        [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+        private sealed class BatchBlockTargetCore
+        {
+            /// <summary>The messages in this target.</summary>
+            private readonly Queue<T> _messages = new Queue<T>();
+            /// <summary>A task representing the completion of the block.</summary>
+            private readonly TaskCompletionSource<VoidResult> _completionTask = new TaskCompletionSource<VoidResult>();
+
+            /// <summary>Gets the object used as the incoming lock.</summary>
+            private object IncomingLock { get { return _completionTask; } }
+
+            /// <summary>The target that owns this target core.</summary>
+            private readonly BatchBlock<T> _owningBatch;
+            /// <summary>The batch size.</summary>
+            private readonly int _batchSize;
+            /// <summary>State used when in non-greedy mode.</summary>
+            private readonly NonGreedyState _nonGreedyState;
+            /// <summary>Bounding state for when the block is executing in bounded mode.</summary>
+            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>
+            private readonly Action<T[]> _batchCompletedAction;
+
+            /// <summary>Whether to stop accepting new messages.</summary>
+            private bool _decliningPermanently;
+            /// <summary>Whether we've completed at least one batch.</summary>
+            private long _batchesCompleted;
+            /// <summary>Whether someone has reserved the right to call CompleteBlockOncePossible.</summary>
+            private bool _completionReserved;
+
+            /// <summary>State used only when in non-greedy mode.</summary>
+            private sealed class NonGreedyState
+            {
+                /// <summary>Collection of postponed messages.</summary>
+                internal readonly QueuedMap<ISourceBlock<T>, DataflowMessageHeader> PostponedMessages;
+                /// <summary>A temporary array used to store data retrieved from PostponedMessages.</summary>
+                internal readonly KeyValuePair<ISourceBlock<T>, DataflowMessageHeader>[] PostponedMessagesTemp;
+                /// <summary>A temporary list used in non-greedy mode when consuming postponed messages to store successfully reserved messages.</summary>
+                internal readonly List<KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>> ReservedSourcesTemp;
+                /// <summary>Whether the next batching operation should accept fewer than BatchSize items.</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;
+
+                /// <summary>Initializes the NonGreedyState.</summary>
+                /// <param name="batchSize">The batch size used by the BatchBlock.</param>
+                internal NonGreedyState(int batchSize)
+                {
+                    // A non-greedy batch requires at least batchSize sources to be successful.
+                    // Thus, we initialize our collections to be able to store at least that many elements
+                    // in order to avoid unnecessary allocations below that point.
+                    Contract.Requires(batchSize > 0, "A positive batch size is required");
+                    PostponedMessages = new QueuedMap<ISourceBlock<T>, DataflowMessageHeader>(batchSize);
+                    PostponedMessagesTemp = new KeyValuePair<ISourceBlock<T>, DataflowMessageHeader>[batchSize];
+                    ReservedSourcesTemp = new List<KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>>(batchSize);
+                }
+            }
+
+            /// <summary>Initializes this target core with the specified configuration.</summary>
+            /// <param name="owningBatch">The owning batch target.</param>
+            /// <param name="batchSize">The number of items to group into a batch.</param>
+            /// <param name="batchCompletedAction">The delegate to invoke when a batch is completed.</param>
+            /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="BatchBlock{T}"/>.  Assumed to be immutable.</param>
+            /// <exception cref="System.ArgumentOutOfRangeException">The <paramref name="batchSize"/> must be positive.</exception>
+            /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+            internal BatchBlockTargetCore(BatchBlock<T> owningBatch, Int32 batchSize, Action<T[]> batchCompletedAction, GroupingDataflowBlockOptions dataflowBlockOptions)
+            {
+                Contract.Requires(owningBatch != null, "This batch target core must be associated with a batch block.");
+                Contract.Requires(batchSize >= 1, "Batch sizes must be positive.");
+                Contract.Requires(batchCompletedAction != null, "Completion action must be specified.");
+                Contract.Requires(dataflowBlockOptions != null, "Options required to configure the block.");
+
+                // Store arguments
+                _owningBatch = owningBatch;
+                _batchSize = batchSize;
+                _batchCompletedAction = batchCompletedAction;
+                _dataflowBlockOptions = dataflowBlockOptions;
+
+                // We'll be using _nonGreedyState even if we are greedy with bounding
+                bool boundingEnabled = dataflowBlockOptions.BoundedCapacity > 0;
+                if (!_dataflowBlockOptions.Greedy || boundingEnabled) _nonGreedyState = new NonGreedyState(batchSize);
+                if (boundingEnabled) _boundingState = new BoundingState(dataflowBlockOptions.BoundedCapacity);
+            }
+
+            /// <summary>
+            /// Triggers a batching operation even if the number of currently queued or postponed items is less than the <see cref="BatchSize"/>.
+            /// </summary>
+            internal void TriggerBatch()
+            {
+                lock (IncomingLock)
+                {
+                    // If we shouldn't be doing any more work, bail.  Otherwise, note that we're willing to 
+                    // accept fewer items in the next batching operation, and ensure processing is kicked off.
+                    if (!_decliningPermanently && !_dataflowBlockOptions.CancellationToken.IsCancellationRequested)
+                    {
+                        if (_nonGreedyState == null)
+                        {
+                            MakeBatchIfPossible(evenIfFewerThanBatchSize: true);
+                        }
+                        else
+                        {
+                            _nonGreedyState.AcceptFewerThanBatchSize = true;
+                            ProcessAsyncIfNecessary();
+                        }
+                    }
+                    CompleteBlockIfPossible();
+                }
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
+            internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, Boolean consumeToAccept)
+            {
+                // Validate arguments
+                if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+                if (source == null && consumeToAccept) throw new ArgumentException(SR.Argument_CantConsumeFromANullSource, "consumeToAccept");
+                Contract.EndContractBlock();
+
+                lock (IncomingLock)
+                {
+                    // If we shouldn't be accepting more messages, don't.
+                    if (_decliningPermanently)
+                    {
+                        CompleteBlockIfPossible();
+                        return DataflowMessageStatus.DecliningPermanently;
+                    }
+
+                    // We can directly accept the message if:
+                    //      1) we are being greedy AND we are not bounding, OR 
+                    //      2) we are being greedy AND we are bounding AND there is room available AND there are no postponed messages AND we are not currently processing. 
+                    // (If there were any postponed messages, we would need to postpone so that ordering would be maintained.)
+                    // (We should also postpone if we are currently processing, because there may be a race between consuming postponed messages and
+                    // accepting new ones directly into the queue.)
+                    if (_dataflowBlockOptions.Greedy &&
+                            (_boundingState == null
+                                ||
+                             (_boundingState.CountIsLessThanBound && _nonGreedyState.PostponedMessages.Count == 0 && _nonGreedyState.TaskForInputProcessing == null)))
+                    {
+                        // Consume the message from the source if necessary
+                        if (consumeToAccept)
+                        {
+                            Debug.Assert(source != null, "We must have thrown if source == null && consumeToAccept == true.");
+
+                            bool consumed;
+                            messageValue = source.ConsumeMessage(messageHeader, _owningBatch, out consumed);
+                            if (!consumed) return DataflowMessageStatus.NotAvailable;
+                        }
+
+                        // Once consumed, enqueue it.
+                        _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 
+                        // the number we can make from data already enqueued meets our quota.
+                        if (!_decliningPermanently &&
+                            (_batchesCompleted + (_messages.Count / _batchSize)) >= _dataflowBlockOptions.ActualMaxNumberOfGroups)
+                        {
+                            _decliningPermanently = true;
+                        }
+
+                        // Now that we have a message, see if we can make forward progress.
+                        MakeBatchIfPossible(evenIfFewerThanBatchSize: false);
+
+                        CompleteBlockIfPossible();
+                        return DataflowMessageStatus.Accepted;
+                    }
+                    // Otherwise, we try to postpone if a source was provided
+                    else if (source != null)
+                    {
+                        Debug.Assert(_nonGreedyState != null, "_nonGreedyState must have been initialized during construction in non-greedy mode.");
+
+                        // We always postpone using _nonGreedyState even if we are being greedy with bounding
+                        _nonGreedyState.PostponedMessages.Push(source, messageHeader);
+
+                        // In non-greedy mode, we need to see if batch could be completed
+                        if (!_dataflowBlockOptions.Greedy) ProcessAsyncIfNecessary();
+
+                        return DataflowMessageStatus.Postponed;
+                    }
+                    // We can't do anything else about this message
+                    return DataflowMessageStatus.Declined;
+                }
+            }
+
+            /// <summary>Completes/faults the block.
+            /// 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>
+            [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+            internal void Complete(Exception exception, bool dropPendingMessages, bool releaseReservedMessages, bool revertProcessingState = false)
+            {
+                // Ensure that no new messages may be added
+                lock (IncomingLock)
+                {
+                    // Faulting from outside is allowed until we start declining permanently.
+                    // Faulting from inside is allowed at any time.
+                    if (exception != null && (!_decliningPermanently || releaseReservedMessages))
+                    {
+                        // Record the exception in the source.
+                        // The source, which exposes its Completion to the public will take this
+                        // into account and will complete in Faulted state.
+                        _owningBatch._source.AddException(exception);
+                    }
+
+                    // Drop pending messages if requested
+                    if (dropPendingMessages) _messages.Clear();
+                }
+
+                // Release reserved messages if requested.
+                // This must be done from outside the lock.
+                if (releaseReservedMessages)
+                {
+                    try { ReleaseReservedMessages(throwOnFirstException: false); }
+                    catch (Exception e) { _owningBatch._source.AddException(e); }
+                }
+
+                // Triggering completion requires the lock
+                lock (IncomingLock)
+                {
+                    // Revert the dirty processing state if requested
+                    if (revertProcessingState)
+                    {
+                        Debug.Assert(_nonGreedyState != null && _nonGreedyState.TaskForInputProcessing != null,
+                                        "The processing state must be dirty when revertProcessingState==true.");
+                        _nonGreedyState.TaskForInputProcessing = null;
+                    }
+
+                    // Trigger completion
+                    _decliningPermanently = true;
+                    CompleteBlockIfPossible();
+                }
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+            internal Task Completion { get { return _completionTask.Task; } }
+
+            /// <summary>Gets the size of the batches generated by this <see cref="BatchBlock{T}"/>.</summary>
+            internal Int32 BatchSize { get { return _batchSize; } }
+
+            /// <summary>Gets whether the target has had cancellation requested or an exception has occurred.</summary>
+            private bool CanceledOrFaulted
+            {
+                get
+                {
+                    return _dataflowBlockOptions.CancellationToken.IsCancellationRequested || _owningBatch._source.HasExceptions;
+                }
+            }
+
+            /// <summary>Returns the available capacity to bring in postponed items. The exact values above _batchSize don't matter.</summary>
+            private int BoundedCapacityAvailable
+            {
+                get
+                {
+                    Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+                    return _boundingState != null ?
+                                _dataflowBlockOptions.BoundedCapacity - _boundingState.CurrentCount :
+                                _batchSize;
+                }
+            }
+
+            /// <summary>Completes the block once all completion conditions are met.</summary>
+            private void CompleteBlockIfPossible()
+            {
+                Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+                if (!_completionReserved)
+                {
+                    bool currentlyProcessing = _nonGreedyState != null && _nonGreedyState.TaskForInputProcessing != null;
+                    bool completedAllDesiredBatches = _batchesCompleted >= _dataflowBlockOptions.ActualMaxNumberOfGroups;
+                    bool noMoreMessages = _decliningPermanently && _messages.Count < _batchSize;
+
+                    bool complete = !currentlyProcessing && (completedAllDesiredBatches || noMoreMessages || CanceledOrFaulted);
+                    if (complete)
+                    {
+                        _completionReserved = true;
+
+                        // Make sure the target is declining
+                        _decliningPermanently = true;
+
+                        // If we still have straggling items remaining, make them into their own batch even though there are fewer than batchSize
+                        if (_messages.Count > 0) MakeBatchIfPossible(evenIfFewerThanBatchSize: true);
+
+                        // We need to complete the block, but we may have arrived here from an external
+                        // call to the block.  To avoid running arbitrary code in the form of 
+                        // completion task continuations in that case, do it in a separate task.
+                        Task.Factory.StartNew(thisTargetCore =>
+                        {
+                            var targetCore = (BatchBlockTargetCore)thisTargetCore;
+
+                            // Release any postponed messages
+                            List<Exception> exceptions = null;
+                            if (targetCore._nonGreedyState != null)
+                            {
+                                // Note: No locks should be held at this point
+                                Common.ReleaseAllPostponedMessages(targetCore._owningBatch,
+                                                                   targetCore._nonGreedyState.PostponedMessages,
+                                                                   ref exceptions);
+                            }
+
+                            if (exceptions != null)
+                            {
+                                // It is important to migrate these exceptions to the source part of the owning batch,
+                                // because that is the completion task that is publically exposed.
+                                targetCore._owningBatch._source.AddExceptions(exceptions);
+                            }
+
+                            // Target's completion task is only available internally with the sole purpose
+                            // of releasing the task that completes the parent. Hence the actual reason
+                            // for completing this task doesn't matter.
+                            targetCore._completionTask.TrySetResult(default(VoidResult));
+                        }, this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+                    }
+                }
+            }
+
+            /// <summary>
+            /// Gets whether we should launch further synchronous or asynchronous processing
+            /// to create batches.
+            /// </summary>
+            private bool BatchesNeedProcessing
+            {
+                get
+                {
+                    Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+                    // If we're currently processing asynchronously, let that async task
+                    // handle all work; nothing more to do here.  If we're not currently processing
+                    // but cancellation has been requested, don't do more work either.
+                    bool completedAllDesiredBatches = _batchesCompleted >= _dataflowBlockOptions.ActualMaxNumberOfGroups;
+                    bool currentlyProcessing = _nonGreedyState != null && _nonGreedyState.TaskForInputProcessing != null;
+                    if (completedAllDesiredBatches || currentlyProcessing || CanceledOrFaulted) return false;
+
+                    // Now, if it's possible to create a batch from queued items or if there are enough
+                    // postponed items to attempt a batch, batches need processing.
+                    int neededMessageCountToCompleteBatch = _batchSize - _messages.Count;
+                    int boundedCapacityAvailable = BoundedCapacityAvailable;
+
+                    // We have items queued up sufficient to make up a batch
+                    if (neededMessageCountToCompleteBatch <= 0) return true;
+
+                    if (_nonGreedyState != null)
+                    {
+                        // We can make a triggered batch using postponed messages
+                        if (_nonGreedyState.AcceptFewerThanBatchSize &&
+                            (_messages.Count > 0 || (_nonGreedyState.PostponedMessages.Count > 0 && boundedCapacityAvailable > 0)))
+                            return true;
+
+                        if (_dataflowBlockOptions.Greedy)
+                        {
+                            // We are in greedy mode and we have postponed messages. 
+                            // (In greedy mode we only postpone due to lack of bounding capacity.) 
+                            // And now we have capacity to consume some postponed messages. 
+                            // (In greedy mode we can/should consume as many postponed messages as we can even  
+                            // if those messages are insufficient to make up a batch.)
+                            if (_nonGreedyState.PostponedMessages.Count > 0 && boundedCapacityAvailable > 0) return true;
+                        }
+                        else
+                        {
+                            // We are in non-greedy mode and we have enough postponed messages and bounding capacity to make a full batch
+                            if (_nonGreedyState.PostponedMessages.Count >= neededMessageCountToCompleteBatch &&
+                                boundedCapacityAvailable >= neededMessageCountToCompleteBatch)
+                                return true;
+                        }
+                    }
+
+                    // There is no other reason to kick off a processing task
+                    return false;
+                }
+            }
+
+            /// <summary>Called when new messages are available to be processed.</summary>
+            /// <param name="isReplacementReplica">Whether this call is the continuation of a previous message loop.</param>
+            private void ProcessAsyncIfNecessary(bool isReplacementReplica = false)
+            {
+                Contract.Requires(_nonGreedyState != null, "Non-greedy state is required for non-greedy mode.");
+                Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+                if (BatchesNeedProcessing)
+                {
+                    ProcessAsyncIfNecessary_Slow(isReplacementReplica);
+                }
+            }
+
+            /// <summary>
+            /// Slow path for ProcessAsyncIfNecessary. 
+            /// Separating out the slow path into its own method makes it more likely that the fast path method will get inlined.
+            /// </summary>
+            private void ProcessAsyncIfNecessary_Slow(bool isReplacementReplica)
+            {
+                Contract.Requires(BatchesNeedProcessing, "There must be a batch that needs processing.");
+
+                // 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,
+                                                    Common.GetCreationOptionsForTask(isReplacementReplica));
+
+#if FEATURE_TRACING
+                DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+                if (etwLog.IsEnabled())
+                {
+                    etwLog.TaskLaunchedForMessageHandling(
+                        _owningBatch, _nonGreedyState.TaskForInputProcessing, DataflowEtwProvider.TaskLaunchedReason.ProcessingInputMessages,
+                        _messages.Count + (_nonGreedyState != null ? _nonGreedyState.PostponedMessages.Count : 0));
+                }
+#endif
+
+                // Start the task handling scheduling exceptions
+                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),
+                                        exception, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+                }
+            }
+
+
+            /// <summary>Task body used to process messages.</summary>
+            [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+            private void ProcessMessagesLoopCore()
+            {
+                Contract.Requires(_nonGreedyState != null, "Non-greedy state is required for non-greedy mode.");
+                Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+                try
+                {
+                    int maxMessagesPerTask = _dataflowBlockOptions.ActualMaxMessagesPerTask;
+                    int timesThroughLoop = 0;
+                    bool madeProgress;
+                    do
+                    {
+                        // Determine whether a batch has been forced/triggered.
+                        // (If the value is read as false and is set to true immediately afterwards,
+                        // we'll simply force the next time around.  The only code that can
+                        // set the value to false is this function, after reading a true value.)
+                        bool triggered = Volatile.Read(ref _nonGreedyState.AcceptFewerThanBatchSize);
+
+                        // Retrieve postponed items:
+                        //      In non-greedy mode: Reserve + Consume
+                        //      In greedy bounded mode: Consume (without a prior reservation)
+                        if (!_dataflowBlockOptions.Greedy) RetrievePostponedItemsNonGreedy(allowFewerThanBatchSize: triggered);
+                        else RetrievePostponedItemsGreedyBounded(allowFewerThanBatchSize: triggered);
+
+                        // Try to make a batch if there are enough buffered messages
+                        lock (IncomingLock)
+                        {
+                            madeProgress = MakeBatchIfPossible(evenIfFewerThanBatchSize: triggered);
+
+                            // Reset the trigger flag if:
+                            // - We made a batch, regardless of whether it came due to a trigger or not.
+                            // - We tried to make a batch due to a trigger, but were unable to, which
+                            //   could happen if we're unable to consume any of the postponed messages.
+                            if (madeProgress || triggered) _nonGreedyState.AcceptFewerThanBatchSize = false;
+                        }
+
+                        timesThroughLoop++;
+                    } while (madeProgress && timesThroughLoop < maxMessagesPerTask);
+                }
+                catch (Exception exc)
+                {
+                    Complete(exc, dropPendingMessages: false, releaseReservedMessages: true);
+                }
+                finally
+                {
+                    lock (IncomingLock)
+                    {
+                        // We're no longer processing, so null out the processing task
+                        _nonGreedyState.TaskForInputProcessing = null;
+
+                        // However, we may have given up early because we hit our own configured
+                        // processing limits rather than because we ran out of work to do.  If that's
+                        // the case, make sure we spin up another task to keep going.
+                        ProcessAsyncIfNecessary(isReplacementReplica: true);
+
+                        // If, however, we stopped because we ran out of work to do and we
+                        // know we'll never get more, then complete.
+                        CompleteBlockIfPossible();
+                    }
+                }
+            }
+
+            /// <summary>Create a batch from the available items.</summary>
+            /// <param name="evenIfFewerThanBatchSize">
+            /// Whether to make a batch even if there are fewer than BatchSize items available.
+            /// </param>
+            /// <returns>true if a batch was created and published; otherwise, false.</returns>
+            private bool MakeBatchIfPossible(bool evenIfFewerThanBatchSize)
+            {
+                Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+                // Is a full batch available?
+                bool fullBatch = _messages.Count >= _batchSize;
+
+                // If so, or if it's ok to make a batch with fewer than batchSize, make one.
+                if (fullBatch || (evenIfFewerThanBatchSize && _messages.Count > 0))
+                {
+                    var newBatch = new T[fullBatch ? _batchSize : _messages.Count];
+                    for (int i = 0; i < newBatch.Length; i++) newBatch[i] = _messages.Dequeue();
+                    _batchCompletedAction(newBatch);
+                    _batchesCompleted++;
+                    if (_batchesCompleted >= _dataflowBlockOptions.ActualMaxNumberOfGroups) _decliningPermanently = true;
+                    return true;
+                }
+                // No batch could be created
+                else return false;
+            }
+
+            /// <summary>Retrieves postponed items in non-greedy mode if we have enough to make a batch.</summary>
+            /// <remarks>Whether we'll accept consuming fewer elements than the defined batch size.</remarks>
+            private void RetrievePostponedItemsNonGreedy(bool allowFewerThanBatchSize)
+            {
+                Contract.Requires(!_dataflowBlockOptions.Greedy, "This method may only be used in non-greedy mode.");
+                Contract.Requires(_nonGreedyState != null, "Non-greedy state is required for non-greedy mode.");
+                Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+                // Shortcuts just to keep the code cleaner
+                QueuedMap<ISourceBlock<T>, DataflowMessageHeader> postponed = _nonGreedyState.PostponedMessages;
+                KeyValuePair<ISourceBlock<T>, DataflowMessageHeader>[] postponedTemp = _nonGreedyState.PostponedMessagesTemp;
+                List<KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader,T>>> reserved = _nonGreedyState.ReservedSourcesTemp;
+
+                // Clear the temporary buffer.  This is safe to do without a lock because
+                // it is only accessed by the serial message loop.
+                reserved.Clear();
+
+                int poppedInitially;
+                int boundedCapacityAvailable;
+                lock (IncomingLock)
+                {
+                    // The queue must be empty between batches in non-greedy mode
+                    Debug.Assert(_messages.Count == 0, "The queue must be empty between batches in non-greedy mode");
+
+                    // If there are not enough postponed items (or if we're not allowing consumption), there's nothing more to be done
+                    boundedCapacityAvailable = BoundedCapacityAvailable;
+                    if (_decliningPermanently ||
+                        postponed.Count == 0 ||
+                        boundedCapacityAvailable <= 0 ||
+                        (!allowFewerThanBatchSize && (postponed.Count < _batchSize || boundedCapacityAvailable < _batchSize)))
+                        return;
+
+                    // Grab an initial batch of postponed messages.
+                    poppedInitially = postponed.PopRange(postponedTemp, 0, _batchSize);
+                    Debug.Assert(allowFewerThanBatchSize ? poppedInitially > 0 : poppedInitially == _batchSize,
+                                    "We received fewer than we expected based on the previous check.");
+                } // Release the lock.  We must not hold it while calling Reserve/Consume/Release.
+
+                // Try to reserve the initial batch of messages.
+                for (int i = 0; i < poppedInitially; i++)
+                {
+                    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 reservedSourceAndMessage = new KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>(sourceAndMessage.Key, reservedMessage);
+                        reserved.Add(reservedSourceAndMessage);
+                    }
+                }
+                Array.Clear(postponedTemp, 0, postponedTemp.Length); // clear out the temp array so as not to hold onto messages too long
+
+                // If we didn't reserve enough to make a batch, start picking off postponed messages
+                // one by one until we either have enough reserved or we run out of messages
+                while (reserved.Count < _batchSize)
+                {
+                    KeyValuePair<ISourceBlock<T>, DataflowMessageHeader> sourceAndMessage;
+                    lock (IncomingLock)
+                    {
+                        if (!postponed.TryPop(out sourceAndMessage)) break;
+                    } // 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 reservedSourceAndMessage = new KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>(sourceAndMessage.Key, reservedMessage);
+                        reserved.Add(reservedSourceAndMessage);
+                    }
+                }
+
+                Debug.Assert(reserved.Count <= _batchSize, "Expected the number of reserved sources to be <= the number needed for a batch.");
+
+                // We've now reserved what we can.  Either consume them all or release them all.
+                if (reserved.Count > 0)
+                {
+                    // TriggerBatch adds a complication here.  It's possible that while we've been reserving
+                    // messages, Post has been used to queue up a bunch of messages to the batch,
+                    // and that if the batch has a max group count and enough messages were posted,
+                    // we could now be declining.  In that case, if we don't specially handle the situation,
+                    // we could consume messages that we won't be able to turn into a batch, since MaxNumberOfGroups
+                    // implies the block will only ever output a maximum number of batches.  To handle this,
+                    // we start declining before consuming, now that we know we'll have enough to form a batch.
+                    // (If an exception occurs after we do this, we'll be shutting down the block anyway.)
+                    // This is also why we still reserve/consume rather than just consume in forced mode, 
+                    // so that we only consume if we're able to turn what we consume into a batch.
+                    bool shouldProceedToConsume = true;
+                    if (allowFewerThanBatchSize)
+                    {
+                        lock (IncomingLock)
+                        {
+                            if (!_decliningPermanently &&
+                                (_batchesCompleted + 1) >= _dataflowBlockOptions.ActualMaxNumberOfGroups)
+                            // Note that this logic differs from the other location where we do a similar check.
+                            // Here we want to know whether we're one shy of meeting our quota, because we'll accept
+                            // any size batch.  Elsewhere, we need to know whether we have the right number of messages
+                            // queued up.
+                            {
+                                shouldProceedToConsume = !_decliningPermanently;
+                                _decliningPermanently = true;
+                            }
+                        }
+                    }
+
+                    if (shouldProceedToConsume && (allowFewerThanBatchSize || reserved.Count == _batchSize))
+                    {
+                        ConsumeReservedMessagesNonGreedy();
+                    }
+                    else
+                    {
+                        ReleaseReservedMessages(throwOnFirstException: true);
+                    }
+                }
+
+                // Clear out the reserved list, so as not to hold onto values longer than necessary.
+                // We don't do this in case of failure, because the higher-level exception handler
+                // accesses the list to try to release reservations.
+                reserved.Clear();
+            }
+
+            /// <summary>Retrieves postponed items in greedy bounded mode.</summary>
+            /// <remarks>Whether we'll accept consuming fewer elements than the defined batch size.</remarks>
+            private void RetrievePostponedItemsGreedyBounded(bool allowFewerThanBatchSize)
+            {
+                Contract.Requires(_dataflowBlockOptions.Greedy, "This method may only be used in greedy mode.");
+                Contract.Requires(_nonGreedyState != null, "Non-greedy state is required for non-greedy mode.");
+                Contract.Requires(_boundingState != null, "Bounding state is required when in bounded mode.");
+                Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+                // Shortcuts just to keep the code cleaner
+                QueuedMap<ISourceBlock<T>, DataflowMessageHeader> postponed = _nonGreedyState.PostponedMessages;
+                KeyValuePair<ISourceBlock<T>, DataflowMessageHeader>[] postponedTemp = _nonGreedyState.PostponedMessagesTemp;
+                List<KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>> reserved = _nonGreedyState.ReservedSourcesTemp;
+
+                // Clear the temporary buffer.  This is safe to do without a lock because
+                // it is only accessed by the serial message loop.
+                reserved.Clear();
+
+                int poppedInitially;
+                int boundedCapacityAvailable;
+                int itemCountNeededToCompleteBatch;
+                lock (IncomingLock)
+                {
+                    // If there are not enough postponed items (or if we're not allowing consumption), there's nothing more to be done
+                    boundedCapacityAvailable = BoundedCapacityAvailable;
+                    itemCountNeededToCompleteBatch = _batchSize - _messages.Count;
+                    if (_decliningPermanently ||
+                        postponed.Count == 0 ||
+                        boundedCapacityAvailable <= 0)
+                        return;
+
+                    // Grab an initial batch of postponed messages.
+                    if (boundedCapacityAvailable < itemCountNeededToCompleteBatch) itemCountNeededToCompleteBatch = boundedCapacityAvailable;
+                    poppedInitially = postponed.PopRange(postponedTemp, 0, itemCountNeededToCompleteBatch);
+                    Debug.Assert(poppedInitially > 0, "We received fewer than we expected based on the previous check.");
+                } // Release the lock.  We must not hold it while calling Reserve/Consume/Release.
+
+                // Treat popped messages as reserved. 
+                // We don't have to formally reserve because we are in greedy mode.
+                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 reservedSourceAndMessage = new KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>(sourceAndMessage.Key, reservedMessage);
+                    reserved.Add(reservedSourceAndMessage);
+                }
+                Array.Clear(postponedTemp, 0, postponedTemp.Length); // clear out the temp array so as not to hold onto messages too long
+
+                // If we didn't reserve enough to make a batch, start picking off postponed messages
+                // one by one until we either have enough reserved or we run out of messages
+                while (reserved.Count < itemCountNeededToCompleteBatch)
+                {
+                    KeyValuePair<ISourceBlock<T>, DataflowMessageHeader> sourceAndMessage;
+                    lock (IncomingLock)
+                    {
+                        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 reservedSourceAndMessage = new KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>(sourceAndMessage.Key, reservedMessage);
+                    reserved.Add(reservedSourceAndMessage);
+                }
+
+                Debug.Assert(reserved.Count <= itemCountNeededToCompleteBatch, "Expected the number of reserved sources to be <= the number needed for a batch.");
+
+                // We've gotten as many postponed messages as we can. Try to consume them.
+                if (reserved.Count > 0)
+                {
+                    // TriggerBatch adds a complication here.  It's possible that while we've been reserving
+                    // messages, Post has been used to queue up a bunch of messages to the batch,
+                    // and that if the batch has a max group count and enough messages were posted,
+                    // we could now be declining.  In that case, if we don't specially handle the situation,
+                    // we could consume messages that we won't be able to turn into a batch, since MaxNumberOfGroups
+                    // implies the block will only ever output a maximum number of batches.  To handle this,
+                    // we start declining before consuming, now that we know we'll have enough to form a batch.
+                    // (If an exception occurs after we do this, we'll be shutting down the block anyway.)
+                    // This is also why we still reserve/consume rather than just consume in forced mode, 
+                    // so that we only consume if we're able to turn what we consume into a batch.
+                    bool shouldProceedToConsume = true;
+                    if (allowFewerThanBatchSize)
+                    {
+                        lock (IncomingLock)
+                        {
+                            if (!_decliningPermanently &&
+                                (_batchesCompleted + 1) >= _dataflowBlockOptions.ActualMaxNumberOfGroups)
+                            // Note that this logic differs from the other location where we do a similar check.
+                            // Here we want to know whether we're one shy of meeting our quota, because we'll accept
+                            // any size batch.  Elsewhere, we need to know whether we have the right number of messages
+                            // queued up.
+                            {
+                                shouldProceedToConsume = !_decliningPermanently;
+                                _decliningPermanently = true;
+                            }
+                        }
+                    }
+
+                    if (shouldProceedToConsume)
+                    {
+                        ConsumeReservedMessagesGreedyBounded();
+                    }
+                }
+
+                // Clear out the reserved list, so as not to hold onto values longer than necessary.
+                // We don't do this in case of failure, because the higher-level exception handler
+                // accesses the list to try to release reservations.
+                reserved.Clear();
+            }
+
+            /// <summary>
+            /// Consumes all of the reserved messages stored in the non-greedy state's temporary reserved source list.
+            /// </summary>
+            private void ConsumeReservedMessagesNonGreedy()
+            {
+                Contract.Requires(!_dataflowBlockOptions.Greedy, "This method may only be used in non-greedy mode.");
+                Contract.Requires(_nonGreedyState != null, "Non-greedy state is required for non-greedy mode.");
+                Contract.Requires(_nonGreedyState.ReservedSourcesTemp != null, "ReservedSourcesTemp should have been initialized.");
+                Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+                // Consume the reserved items and store the data.
+                List<KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>> reserved = _nonGreedyState.ReservedSourcesTemp;
+                for (int i = 0; i < reserved.Count; i++)
+                {
+                    // We can only store the data into _messages while holding the IncomingLock, we 
+                    // don't want to allocate extra objects for each batch, and we don't want to 
+                    // take and release the lock for each individual item... but we do need to use
+                    // the consumed message rather than the initial one.  To handle this, because KeyValuePair is immutable,
+                    // we store a new KVP with the newly consumed message back into the temp list, so that we can
+                    // then enumerate the temp list en mass while taking the lock once afterwards.
+                    KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>> sourceAndMessage = reserved[i];
+                    reserved[i] = default(KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>); // in case of exception from ConsumeMessage
+                    bool consumed;
+                    T consumedValue = sourceAndMessage.Key.ConsumeMessage(sourceAndMessage.Value.Key, _owningBatch, out consumed);
+                    if (!consumed)
+                    {
+                        // The protocol broke down, so throw an exception, as this is fatal.  Before doing so, though,
+                        // null out all of the messages we've already consumed, as a higher-level event handler
+                        // should try to release everything in the reserved list.
+                        for (int prev = 0; prev < i; prev++) reserved[prev] = default(KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>);
+                        throw new InvalidOperationException(SR.InvalidOperation_FailedToConsumeReservedMessage);
+                    }
+
+                    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;
+                }
+                lock (IncomingLock)
+                {
+                    // Increment the bounding count with the number of consumed messages 
+                    if (_boundingState != null) _boundingState.CurrentCount += reserved.Count;
+
+                    // Enqueue the consumed mesasages
+                    foreach (KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>> sourceAndMessage in reserved)
+                    {
+                        _messages.Enqueue(sourceAndMessage.Value.Value);
+                    }
+                }
+            }
+
+            /// <summary>
+            /// Consumes all of the reserved messages stored in the non-greedy state's temporary reserved source list.
+            /// </summary>
+            private void ConsumeReservedMessagesGreedyBounded()
+            {
+                Contract.Requires(_dataflowBlockOptions.Greedy, "This method may only be used in greedy mode.");
+                Contract.Requires(_nonGreedyState != null, "Non-greedy state is required for non-greedy mode.");
+                Contract.Requires(_nonGreedyState.ReservedSourcesTemp != null, "ReservedSourcesTemp should have been initialized.");
+                Contract.Requires(_boundingState != null, "Bounded state is required for bounded mode.");
+                Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+                // Consume the reserved items and store the data.
+                int consumedCount = 0;
+                List<KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>> reserved = _nonGreedyState.ReservedSourcesTemp;
+                for (int i = 0; i < reserved.Count; i++)
+                {
+                    // We can only store the data into _messages while holding the IncomingLock, we 
+                    // don't want to allocate extra objects for each batch, and we don't want to 
+                    // take and release the lock for each individual item... but we do need to use
+                    // the consumed message rather than the initial one.  To handle this, because KeyValuePair is immutable,
+                    // we store a new KVP with the newly consumed message back into the temp list, so that we can
+                    // then enumerate the temp list en mass while taking the lock once afterwards.
+                    KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>> sourceAndMessage = reserved[i];
+                    reserved[i] = default(KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>); // in case of exception from ConsumeMessage
+                    bool consumed;
+                    T consumedValue = sourceAndMessage.Key.ConsumeMessage(sourceAndMessage.Value.Key, _owningBatch, out consumed);
+                    if (consumed)
+                    {
+                        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;
+
+                        // Keep track of the actually consumed messages
+                        consumedCount++;
+                    }
+                }
+                lock (IncomingLock)
+                {
+                    // Increment the bounding count with the number of consumed messages 
+                    if (_boundingState != null) _boundingState.CurrentCount += consumedCount;
+
+                    // Enqueue the consumed mesasages
+                    foreach (KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>> sourceAndMessage in reserved)
+                    {
+                        // If we didn't consume this message, the KeyValuePai will be default, i.e. the source will be null
+                        if (sourceAndMessage.Key != null) _messages.Enqueue(sourceAndMessage.Value.Value);
+                    }
+                }
+            }
+
+            /// <summary>
+            /// Releases all of the reserved messages stored in the non-greedy state's temporary reserved source list.
+            /// </summary>
+            /// <param name="throwOnFirstException">
+            /// Whether to allow an exception from a release to propagate immediately,
+            /// or to delay propagation until all releases have been attempted.
+            /// </param>
+            [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+            internal void ReleaseReservedMessages(bool throwOnFirstException)
+            {
+                Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+                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<KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>> reserved = _nonGreedyState.ReservedSourcesTemp;
+                for (int i = 0; i < reserved.Count; i++)
+                {
+                    KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>> sourceAndMessage = reserved[i];
+                    reserved[i] = default(KeyValuePair<ISourceBlock<T>, KeyValuePair<DataflowMessageHeader, T>>);
+                    ISourceBlock<T> source = sourceAndMessage.Key;
+                    KeyValuePair<DataflowMessageHeader, T> message = sourceAndMessage.Value;
+                    if (source != null && message.Key.IsValid)
+                    {
+                        try { source.ReleaseReservation(message.Key, _owningBatch); }
+                        catch (Exception e)
+                        {
+                            if (throwOnFirstException) throw;
+                            if (exceptions == null) exceptions = new List<Exception>(1);
+                            exceptions.Add(e);
+                        }
+                    }
+                }
+
+                if (exceptions != null) throw new AggregateException(exceptions);
+            }
+
+            /// <summary>Notifies the block that one or more items was removed from the queue.</summary>
+            /// <param name="numItemsRemoved">The number of items removed.</param>
+            internal void OnItemsRemoved(int numItemsRemoved)
+            {
+                Contract.Requires(numItemsRemoved > 0, "Should only be called for a positive number of items removed.");
+                Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+                // If we're bounding, we need to know when an item is removed so that we
+                // can update the count that's mirroring the actual count in the source's queue,
+                // and potentially kick off processing to start consuming postponed messages.
+                if (_boundingState != null)
+                {
+                    lock (IncomingLock)
+                    {
+                        // Decrement the count, which mirrors the count in the source half
+                        Debug.Assert(_boundingState.CurrentCount - numItemsRemoved >= 0,
+                            "It should be impossible to have a negative number of items.");
+                        _boundingState.CurrentCount -= numItemsRemoved;
+
+                        ProcessAsyncIfNecessary();
+                        CompleteBlockIfPossible();
+                    }
+                }
+            }
+
+            /// <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)
+            {
+                // If multipleOutputItems == null, then singleOutputItem is the subject of counting
+                if (multipleOutputItems == null) return singleOutputItem.Length;
+
+                // multipleOutputItems != null. Count the elements in each item.
+                int count = 0;
+                foreach (T[] item in multipleOutputItems) count += item.Length;
+                return count;
+            }
+
+            /// <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.Count; } }
+
+            /// <summary>Gets information about this helper to be used for display in a debugger.</summary>
+            /// <returns>Debugging information about this target.</returns>
+            internal DebuggingInformation GetDebuggingInformation() { return new DebuggingInformation(this); }
+
+            /// <summary>Gets the object to display in the debugger display attribute.</summary>
+            [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+            private object DebuggerDisplayContent
+            {
+                get
+                {
+                    var displayBatch = _owningBatch as IDebuggerDisplay;
+                    return string.Format("Block=\"{0}\"",
+                        displayBatch != null ? displayBatch.Content : _owningBatch);
+                }
+            }
+
+            /// <summary>Provides a wrapper for commonly needed debugging information.</summary>
+            internal sealed class DebuggingInformation
+            {
+                /// <summary>The target being viewed.</summary>
+                private BatchBlockTargetCore _target;
+
+                /// <summary>Initializes the debugging helper.</summary>
+                /// <param name="target">The target being viewed.</param>
+                public DebuggingInformation(BatchBlockTargetCore target) { _target = target; }
+
+                /// <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; } }
+                /// <summary>Gets the collection of postponed messages.</summary>
+                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>
+                public GroupingDataflowBlockOptions DataflowBlockOptions { get { return _target._dataflowBlockOptions; } }
+                /// <summary>Gets the number of batches that have been completed.</summary>
+                public long NumberOfBatchesCompleted { get { return _target._batchesCompleted; } }
+            }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/BatchedJoinBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/BatchedJoinBlock.cs
new file mode 100644 (file)
index 0000000..f1cf990
--- /dev/null
@@ -0,0 +1,783 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// BatchedJoinBlock.cs
+//
+//
+// A propagator block that groups individual messages of multiple types
+// into tuples of arrays of those messages.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Threading.Tasks.Dataflow.Internal;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>
+    /// Provides a dataflow block that batches a specified number of inputs of potentially differing types
+    /// provided to one or more of its targets.
+    /// </summary>
+    /// <typeparam name="T1">Specifies the type of data accepted by the block's first target.</typeparam>
+    /// <typeparam name="T2">Specifies the type of data accepted by the block's second target.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(BatchedJoinBlock<,>.DebugView))]
+    public sealed class BatchedJoinBlock<T1, T2> : IReceivableSourceBlock<Tuple<IList<T1>, IList<T2>>>, IDebuggerDisplay
+    {
+        /// <summary>The size of the batches generated by this BatchedJoin.</summary>
+        private readonly int _batchSize;
+        /// <summary>State shared among the targets.</summary>
+        private readonly BatchedJoinBlockTargetSharedResources _sharedResources;
+        /// <summary>The target providing inputs of type T1.</summary>
+        private readonly BatchedJoinBlockTarget<T1> _target1;
+        /// <summary>The target providing inputs of type T2.</summary>
+        private readonly BatchedJoinBlockTarget<T2> _target2;
+        /// <summary>The source side.</summary>
+        private readonly SourceCore<Tuple<IList<T1>, IList<T2>>> _source;
+
+        /// <summary>Initializes this <see cref="BatchedJoinBlock{T1,T2}"/> with the specified configuration.</summary>
+        /// <param name="batchSize">The number of items to group into a batch.</param>
+        /// <exception cref="System.ArgumentOutOfRangeException">The <paramref name="batchSize"/> must be positive.</exception>
+        public BatchedJoinBlock(Int32 batchSize) :
+            this(batchSize, GroupingDataflowBlockOptions.Default)
+        { }
+
+        /// <summary>Initializes this <see cref="BatchedJoinBlock{T1,T2}"/> with the specified configuration.</summary>
+        /// <param name="batchSize">The number of items to group into a batch.</param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="BatchedJoinBlock{T1,T2}"/>.</param>
+        /// <exception cref="System.ArgumentOutOfRangeException">The <paramref name="batchSize"/> must be positive.</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        public BatchedJoinBlock(Int32 batchSize, GroupingDataflowBlockOptions dataflowBlockOptions)
+        {
+            // Validate arguments
+            if (batchSize < 1) throw new ArgumentOutOfRangeException("batchSize", SR.ArgumentOutOfRange_GenericPositive);
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+            if (!dataflowBlockOptions.Greedy) throw new ArgumentException(SR.Argument_NonGreedyNotSupported, "dataflowBlockOptions");
+            if (dataflowBlockOptions.BoundedCapacity != DataflowBlockOptions.Unbounded) throw new ArgumentException(SR.Argument_BoundedCapacityNotSupported, "dataflowBlockOptions");
+            Contract.EndContractBlock();
+
+            // Store arguments
+            _batchSize = batchSize;
+            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
+
+            // Configure the source
+            _source = new SourceCore<Tuple<IList<T1>, IList<T2>>>(
+                this, dataflowBlockOptions, owningSource => ((BatchedJoinBlock<T1, T2>)owningSource).CompleteEachTarget());
+
+            // The action to run when a batch should be created.  This is typically called
+            // when we have a full batch, but it will also be called when we're done receiving
+            // messages, and thus when there may be a few stragglers we need to make a batch out of.
+            Action createBatchAction = () =>
+            {
+                if (_target1.Count > 0 || _target2.Count > 0)
+                {
+                    _source.AddMessage(Tuple.Create(_target1.GetAndEmptyMessages(), _target2.GetAndEmptyMessages()));
+                }
+            };
+
+            // Configure the targets
+            _sharedResources = new BatchedJoinBlockTargetSharedResources(
+                batchSize, dataflowBlockOptions,
+                createBatchAction,
+                () =>
+                {
+                    createBatchAction();
+                    _source.Complete();
+                },
+                _source.AddException,
+                Complete);
+            _target1 = new BatchedJoinBlockTarget<T1>(_sharedResources);
+            _target2 = new BatchedJoinBlockTarget<T2>(_sharedResources);
+
+            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
+            // In those cases we need to fault the target half to drop its buffered messages and to release its 
+            // reservations. This should not create an infinite loop, because all our implementations are designed
+            // to handle multiple completion requests and to carry over only one.
+            _source.Completion.ContinueWith((completed, state) =>
+            {
+                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);
+            }, 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);
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
+            }
+#endif
+        }
+
+        /// <summary>Gets the size of the batches generated by this <see cref="BatchedJoinBlock{T1,T2}"/>.</summary>
+        public Int32 BatchSize { get { return _batchSize; } }
+
+        /// <summary>Gets a target that may be used to offer messages of the first type.</summary>
+        public ITargetBlock<T1> Target1 { get { return _target1; } }
+
+        /// <summary>Gets a target that may be used to offer messages of the second type.</summary>
+        public ITargetBlock<T2> Target2 { get { return _target2; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public IDisposable LinkTo(ITargetBlock<Tuple<IList<T1>, IList<T2>>> target, DataflowLinkOptions linkOptions)
+        {
+            return _source.LinkTo(target, linkOptions);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public Boolean TryReceive(Predicate<Tuple<IList<T1>, IList<T2>>> filter, out Tuple<IList<T1>, IList<T2>> item)
+        {
+            return _source.TryReceive(filter, out item);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public bool TryReceiveAll(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/Blocks/Member[@name="Completion"]/*' />
+        public Task Completion { get { return _source.Completion; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+        public void Complete()
+        {
+            Debug.Assert(_target1 != null, "_target1 not initialized");
+            Debug.Assert(_target2 != null, "_target2 not initialized");
+
+            _target1.Complete();
+            _target2.Complete();
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            Debug.Assert(_sharedResources != null, "_sharedResources not initialized");
+            Debug.Assert(_sharedResources._incomingLock != null, "_sharedResources._incomingLock not initialized");
+            Debug.Assert(_source != null, "_source not initialized");
+
+            lock (_sharedResources._incomingLock)
+            {
+                if (!_sharedResources._decliningPermanently) _source.AddException(exception);
+            }
+            Complete();
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+        Tuple<IList<T1>, IList<T2>> ISourceBlock<Tuple<IList<T1>, IList<T2>>>.ConsumeMessage(
+            DataflowMessageHeader messageHeader, ITargetBlock<Tuple<IList<T1>, IList<T2>>> target, out Boolean messageConsumed)
+        {
+            return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+        bool ISourceBlock<Tuple<IList<T1>, IList<T2>>>.ReserveMessage(
+            DataflowMessageHeader messageHeader, ITargetBlock<Tuple<IList<T1>, IList<T2>>> target)
+        {
+            return _source.ReserveMessage(messageHeader, target);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+        void ISourceBlock<Tuple<IList<T1>, IList<T2>>>.ReleaseReservation(
+            DataflowMessageHeader messageHeader, ITargetBlock<Tuple<IList<T1>, IList<T2>>> target)
+        {
+            _source.ReleaseReservation(messageHeader, target);
+        }
+
+        /// <summary>
+        /// Invokes Complete on each target
+        /// </summary>
+        private void CompleteEachTarget()
+        {
+            _target1.Complete();
+            _target2.Complete();
+        }
+
+        /// <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 OutputCountForDebugger { get { return _source.GetDebuggingInformation().OutputCount; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="ToString"]/*' />
+        public override string ToString() { return Common.GetNameForDebugger(this, _source.DataflowBlockOptions); }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                return string.Format("{0}, BatchSize={1}, OutputCount={2}",
+                    Common.GetNameForDebugger(this, _source.DataflowBlockOptions),
+                    BatchSize,
+                    OutputCountForDebugger);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for the Transform.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The block being viewed.</summary>
+            private readonly BatchedJoinBlock<T1, T2> _batchedJoinBlock;
+            /// <summary>The source half of the block being viewed.</summary>
+            private readonly SourceCore<Tuple<IList<T1>, IList<T2>>>.DebuggingInformation _sourceDebuggingInformation;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="batchedJoinBlock">The batched join being viewed.</param>
+            public DebugView(BatchedJoinBlock<T1, T2> batchedJoinBlock)
+            {
+                Contract.Requires(batchedJoinBlock != null, "Need a block with which to construct the debug view.");
+                _batchedJoinBlock = batchedJoinBlock;
+                _sourceDebuggingInformation = batchedJoinBlock._source.GetDebuggingInformation();
+            }
+
+            /// <summary>Gets the messages waiting to be received.</summary>
+            public IEnumerable<Tuple<IList<T1>, IList<T2>>> OutputQueue { get { return _sourceDebuggingInformation.OutputQueue; } }
+            /// <summary>Gets the number of batches created.</summary>
+            public long BatchesCreated { get { return _batchedJoinBlock._sharedResources._batchesCreated; } }
+            /// <summary>Gets the number of items remaining to form a batch.</summary>
+            public int RemainingItemsForBatch { get { return _batchedJoinBlock._sharedResources._remainingItemsInBatch; } }
+
+            /// <summary>Gets the size of the batches generated by this BatchedJoin.</summary>
+            public Int32 BatchSize { get { return _batchedJoinBlock._batchSize; } }
+            /// <summary>Gets the first target.</summary>
+            public ITargetBlock<T1> Target1 { get { return _batchedJoinBlock._target1; } }
+            /// <summary>Gets the second target.</summary>
+            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; } }
+
+            /// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
+            public GroupingDataflowBlockOptions DataflowBlockOptions { get { return (GroupingDataflowBlockOptions)_sourceDebuggingInformation.DataflowBlockOptions; } }
+            /// <summary>Gets whether the block is completed.</summary>
+            public bool IsCompleted { get { return _sourceDebuggingInformation.IsCompleted; } }
+            /// <summary>Gets the block's Id.</summary>
+            public int Id { get { return Common.GetBlockId(_batchedJoinBlock); } }
+
+            /// <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; } }
+        }
+    }
+
+    /// <summary>
+    /// Provides a dataflow block that batches a specified number of inputs of potentially differing types
+    /// provided to one or more of its targets.
+    /// </summary>
+    /// <typeparam name="T1">Specifies the type of data accepted by the block's first target.</typeparam>
+    /// <typeparam name="T2">Specifies the type of data accepted by the block's second target.</typeparam>
+    /// <typeparam name="T3">Specifies the type of data accepted by the block's third target.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(BatchedJoinBlock<,,>.DebugView))]
+    [SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")]
+    public sealed class BatchedJoinBlock<T1, T2, T3> : IReceivableSourceBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>>, IDebuggerDisplay
+    {
+        /// <summary>The size of the batches generated by this BatchedJoin.</summary>
+        private readonly int _batchSize;
+        /// <summary>State shared among the targets.</summary>
+        private readonly BatchedJoinBlockTargetSharedResources _sharedResources;
+        /// <summary>The target providing inputs of type T1.</summary>
+        private readonly BatchedJoinBlockTarget<T1> _target1;
+        /// <summary>The target providing inputs of type T2.</summary>
+        private readonly BatchedJoinBlockTarget<T2> _target2;
+        /// <summary>The target providing inputs of type T3.</summary>
+        private readonly BatchedJoinBlockTarget<T3> _target3;
+        /// <summary>The source side.</summary>
+        private readonly SourceCore<Tuple<IList<T1>, IList<T2>, IList<T3>>> _source;
+
+        /// <summary>Initializes this <see cref="BatchedJoinBlock{T1,T2,T3}"/> with the specified configuration.</summary>
+        /// <param name="batchSize">The number of items to group into a batch.</param>
+        /// <exception cref="System.ArgumentOutOfRangeException">The <paramref name="batchSize"/> must be positive.</exception>
+        public BatchedJoinBlock(Int32 batchSize) :
+            this(batchSize, GroupingDataflowBlockOptions.Default)
+        { }
+
+        /// <summary>Initializes this <see cref="BatchedJoinBlock{T1,T2,T3}"/> with the specified configuration.</summary>
+        /// <param name="batchSize">The number of items to group into a batch.</param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="BatchedJoinBlock{T1,T2}"/>.</param>
+        /// <exception cref="System.ArgumentOutOfRangeException">The <paramref name="batchSize"/> must be positive.</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        public BatchedJoinBlock(Int32 batchSize, GroupingDataflowBlockOptions dataflowBlockOptions)
+        {
+            // Validate arguments
+            if (batchSize < 1) throw new ArgumentOutOfRangeException("batchSize", SR.ArgumentOutOfRange_GenericPositive);
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+            if (!dataflowBlockOptions.Greedy ||
+                dataflowBlockOptions.BoundedCapacity != DataflowBlockOptions.Unbounded)
+            {
+                throw new ArgumentException(SR.Argument_NonGreedyNotSupported, "dataflowBlockOptions");
+            }
+            Contract.EndContractBlock();
+
+            // Store arguments
+            _batchSize = batchSize;
+            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
+
+            // Configure the source
+            _source = new SourceCore<Tuple<IList<T1>, IList<T2>, IList<T3>>>(
+                this, dataflowBlockOptions, owningSource => ((BatchedJoinBlock<T1, T2, T3>)owningSource).CompleteEachTarget());
+
+            // The action to run when a batch should be created.  This is typically called
+            // when we have a full batch, but it will also be called when we're done receiving
+            // messages, and thus when there may be a few stragglers we need to make a batch out of.
+            Action createBatchAction = () =>
+            {
+                if (_target1.Count > 0 || _target2.Count > 0 || _target3.Count > 0)
+                {
+                    _source.AddMessage(Tuple.Create(_target1.GetAndEmptyMessages(), _target2.GetAndEmptyMessages(), _target3.GetAndEmptyMessages()));
+                }
+            };
+
+            // Configure the targets
+            _sharedResources = new BatchedJoinBlockTargetSharedResources(
+                batchSize, dataflowBlockOptions,
+                createBatchAction,
+                () =>
+                {
+                    createBatchAction();
+                    _source.Complete();
+                },
+                _source.AddException,
+                Complete);
+            _target1 = new BatchedJoinBlockTarget<T1>(_sharedResources);
+            _target2 = new BatchedJoinBlockTarget<T2>(_sharedResources);
+            _target3 = new BatchedJoinBlockTarget<T3>(_sharedResources);
+
+            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
+            // In those cases we need to fault the target half to drop its buffered messages and to release its 
+            // reservations. This should not create an infinite loop, because all our implementations are designed
+            // 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;
+                Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion.");
+                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);
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
+            }
+#endif
+        }
+
+        /// <summary>Gets the size of the batches generated by this <see cref="BatchedJoinBlock{T1,T2,T3}"/>.</summary>
+        public Int32 BatchSize { get { return _batchSize; } }
+
+        /// <summary>Gets a target that may be used to offer messages of the first type.</summary>
+        public ITargetBlock<T1> Target1 { get { return _target1; } }
+
+        /// <summary>Gets a target that may be used to offer messages of the second type.</summary>
+        public ITargetBlock<T2> Target2 { get { return _target2; } }
+
+        /// <summary>Gets a target that may be used to offer messages of the third type.</summary>
+        public ITargetBlock<T3> Target3 { get { return _target3; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+        public IDisposable LinkTo(ITargetBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>> target, DataflowLinkOptions linkOptions)
+        {
+            return _source.LinkTo(target, linkOptions);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public Boolean TryReceive(Predicate<Tuple<IList<T1>, IList<T2>, IList<T3>>> filter, out Tuple<IList<T1>, IList<T2>, IList<T3>> item)
+        {
+            return _source.TryReceive(filter, out item);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public bool TryReceiveAll(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/Blocks/Member[@name="Completion"]/*' />
+        public Task Completion { get { return _source.Completion; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+        public void Complete()
+        {
+            Debug.Assert(_target1 != null, "_target1 not initialized");
+            Debug.Assert(_target2 != null, "_target2 not initialized");
+            Debug.Assert(_target3 != null, "_target3 not initialized");
+
+            _target1.Complete();
+            _target2.Complete();
+            _target3.Complete();
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            Debug.Assert(_sharedResources != null, "_sharedResources not initialized");
+            Debug.Assert(_sharedResources._incomingLock != null, "_sharedResources._incomingLock not initialized");
+            Debug.Assert(_source != null, "_source not initialized");
+
+            lock (_sharedResources._incomingLock)
+            {
+                if (!_sharedResources._decliningPermanently) _source.AddException(exception);
+            }
+            Complete();
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+        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 Boolean messageConsumed)
+        {
+            return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+        bool ISourceBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>>.ReserveMessage(
+            DataflowMessageHeader messageHeader, ITargetBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>> target)
+        {
+            return _source.ReserveMessage(messageHeader, target);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+        void ISourceBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>>.ReleaseReservation(
+            DataflowMessageHeader messageHeader, ITargetBlock<Tuple<IList<T1>, IList<T2>, IList<T3>>> target)
+        {
+            _source.ReleaseReservation(messageHeader, target);
+        }
+
+        /// <summary>
+        /// Invokes Complete on each target
+        /// </summary>
+        private void CompleteEachTarget()
+        {
+            _target1.Complete();
+            _target2.Complete();
+            _target3.Complete();
+        }
+
+        /// <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 OutputCountForDebugger { get { return _source.GetDebuggingInformation().OutputCount; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="ToString"]/*' />
+        public override string ToString() { return Common.GetNameForDebugger(this, _source.DataflowBlockOptions); }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                return string.Format("{0}, BatchSize={1}, OutputCount={2}",
+                    Common.GetNameForDebugger(this, _source.DataflowBlockOptions),
+                    BatchSize,
+                    OutputCountForDebugger);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for the Transform.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The block being viewed.</summary>
+            private readonly BatchedJoinBlock<T1, T2, T3> _batchedJoinBlock;
+            /// <summary>The source half of the block being viewed.</summary>
+            private readonly SourceCore<Tuple<IList<T1>, IList<T2>, IList<T3>>>.DebuggingInformation _sourceDebuggingInformation;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="batchedJoinBlock">The batched join being viewed.</param>
+            public DebugView(BatchedJoinBlock<T1, T2, T3> batchedJoinBlock)
+            {
+                Contract.Requires(batchedJoinBlock != null, "Need a block with which to construct the debug view.");
+                _sourceDebuggingInformation = batchedJoinBlock._source.GetDebuggingInformation();
+                _batchedJoinBlock = batchedJoinBlock;
+            }
+
+            /// <summary>Gets the messages waiting to be received.</summary>
+            public IEnumerable<Tuple<IList<T1>, IList<T2>, IList<T3>>> OutputQueue { get { return _sourceDebuggingInformation.OutputQueue; } }
+            /// <summary>Gets the number of batches created.</summary>
+            public long BatchesCreated { get { return _batchedJoinBlock._sharedResources._batchesCreated; } }
+            /// <summary>Gets the number of items remaining to form a batch.</summary>
+            public int RemainingItemsForBatch { get { return _batchedJoinBlock._sharedResources._remainingItemsInBatch; } }
+
+            /// <summary>Gets the size of the batches generated by this BatchedJoin.</summary>
+            public Int32 BatchSize { get { return _batchedJoinBlock._batchSize; } }
+            /// <summary>Gets the first target.</summary>
+            public ITargetBlock<T1> Target1 { get { return _batchedJoinBlock._target1; } }
+            /// <summary>Gets the second target.</summary>
+            public ITargetBlock<T2> Target2 { get { return _batchedJoinBlock._target2; } }
+            /// <summary>Gets the second target.</summary>
+            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; } }
+
+            /// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
+            public GroupingDataflowBlockOptions DataflowBlockOptions { get { return (GroupingDataflowBlockOptions)_sourceDebuggingInformation.DataflowBlockOptions; } }
+            /// <summary>Gets whether the block is completed.</summary>
+            public bool IsCompleted { get { return _sourceDebuggingInformation.IsCompleted; } }
+            /// <summary>Gets the block's Id.</summary>
+            public int Id { get { return Common.GetBlockId(_batchedJoinBlock); } }
+
+            /// <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; } }
+        }
+    }
+}
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    /// <summary>Provides the target used in a BatchedJoin.</summary>
+    /// <typeparam name="T">Specifies the type of data accepted by this target.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(BatchedJoinBlockTarget<>.DebugView))]
+    internal sealed class BatchedJoinBlockTarget<T> : ITargetBlock<T>, IDebuggerDisplay
+    {
+        /// <summary>The shared resources used by all targets associated with the same batched join instance.</summary>
+        private readonly BatchedJoinBlockTargetSharedResources _sharedResources;
+        /// <summary>Whether this target is declining future messages.</summary>
+        private bool _decliningPermanently;
+        /// <summary>Input messages for the next batch.</summary>
+        private IList<T> _messages = new List<T>();
+
+        /// <summary>Initializes the target.</summary>
+        /// <param name="sharedResources">The shared resources used by all targets associated with this batched join.</param>
+        internal BatchedJoinBlockTarget(BatchedJoinBlockTargetSharedResources sharedResources)
+        {
+            Contract.Requires(sharedResources != null, "Targets require a shared resources through which to communicate.");
+
+            // Store the shared resources, and register with it to let it know there's 
+            // another target. This is done in a non-thread-safe manner and must be done 
+            // during construction of the batched join instance.
+            _sharedResources = sharedResources;
+            sharedResources._remainingAliveTargets++;
+        }
+
+        /// <summary>Gets the number of messages buffered in this target.</summary>
+        internal int Count { get { return _messages.Count; } }
+
+        /// <summary>Gets the messages buffered by this target and then empties the collection.</summary>
+        /// <returns>The messages from the target.</returns>
+        internal IList<T> GetAndEmptyMessages()
+        {
+            Common.ContractAssertMonitorStatus(_sharedResources._incomingLock, held: true);
+
+            IList<T> toReturn = _messages;
+            _messages = new List<T>();
+            return toReturn;
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
+        public DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, Boolean consumeToAccept)
+        {
+            // Validate arguments
+            if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            if (source == null && consumeToAccept) throw new ArgumentException(SR.Argument_CantConsumeFromANullSource, "consumeToAccept");
+            Contract.EndContractBlock();
+
+            lock (_sharedResources._incomingLock)
+            {
+                // If we've already stopped accepting messages, decline permanently
+                if (_decliningPermanently ||
+                    _sharedResources._decliningPermanently)
+                    return DataflowMessageStatus.DecliningPermanently;
+
+                // Consume the message from the source if necessary, and store the message
+                if (consumeToAccept)
+                {
+                    Debug.Assert(source != null, "We must have thrown if source == null && consumeToAccept == true.");
+
+                    bool consumed;
+                    messageValue = source.ConsumeMessage(messageHeader, this, out consumed);
+                    if (!consumed) return DataflowMessageStatus.NotAvailable;
+                }
+                _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();
+
+                return DataflowMessageStatus.Accepted;
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+        public void Complete()
+        {
+            lock (_sharedResources._incomingLock)
+            {
+                // If this is the first time Complete is being called,
+                // note that there's now one fewer targets receiving messages for the batched join.
+                if (!_decliningPermanently)
+                {
+                    _decliningPermanently = true;
+                    if (--_sharedResources._remainingAliveTargets == 0) _sharedResources._allTargetsDecliningPermanentlyAction();
+                }
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            lock (_sharedResources._incomingLock)
+            {
+                if (!_decliningPermanently && !_sharedResources._decliningPermanently) _sharedResources._exceptionAction(exception);
+            }
+
+            _sharedResources._completionAction();
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+        Task IDataflowBlock.Completion { get { throw new NotSupportedException(SR.NotSupported_MemberNotNeeded); } }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                return string.Format("{0} InputCount={1}",
+                    Common.GetNameForDebugger(this),
+                    _messages.Count);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for the Transform.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The batched join block target being viewed.</summary>
+            private readonly BatchedJoinBlockTarget<T> _batchedJoinBlockTarget;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="batchedJoinBlockTarget">The batched join target being viewed.</param>
+            public DebugView(BatchedJoinBlockTarget<T> batchedJoinBlockTarget)
+            {
+                Contract.Requires(batchedJoinBlockTarget != null, "Need a block with which to construct the debug view.");
+                _batchedJoinBlockTarget = batchedJoinBlockTarget;
+            }
+
+            /// <summary>Gets the messages waiting to be processed.</summary>
+            public IEnumerable<T> InputQueue { get { return _batchedJoinBlockTarget._messages; } }
+            /// <summary>Gets whether the block is declining further messages.</summary>
+            public bool IsDecliningPermanently
+            {
+                get
+                {
+                    return _batchedJoinBlockTarget._decliningPermanently ||
+                        _batchedJoinBlockTarget._sharedResources._decliningPermanently;
+                }
+            }
+        }
+    }
+
+    /// <summary>Provides a container for resources shared across all targets used by the same BatchedJoinBlock instance.</summary>
+    internal sealed class BatchedJoinBlockTargetSharedResources
+    {
+        /// <summary>Initializes the shared resources.</summary>
+        /// <param name="batchSize">The size of a batch to create.</param>
+        /// <param name="dataflowBlockOptions">The options used to configure the shared resources.  Assumed to be immutable.</param>
+        /// <param name="batchSizeReachedAction">The action to invoke when a batch is completed.</param>
+        /// <param name="allTargetsDecliningAction">The action to invoke when no more targets are accepting input.</param>
+        /// <param name="exceptionAction">The action to invoke when an exception needs to be logged.</param>
+        /// <param name="completionAction">The action to invoke when completing, typically invoked due to a call to Fault.</param>
+        internal BatchedJoinBlockTargetSharedResources(
+            int batchSize, GroupingDataflowBlockOptions dataflowBlockOptions,
+            Action batchSizeReachedAction, Action allTargetsDecliningAction,
+            Action<Exception> exceptionAction, Action completionAction)
+        {
+            Debug.Assert(batchSize >= 1, "A positive batch size is required.");
+            Debug.Assert(batchSizeReachedAction != null, "Need an action to invoke for each batch.");
+            Debug.Assert(allTargetsDecliningAction != null, "Need an action to invoke when all targets have declined.");
+
+            _incomingLock = new object();
+            _batchSize = batchSize;
+
+            // _remainingAliveTargets will be incremented when targets are added.
+            // They must be added during construction of the BatchedJoin<...>.
+            _remainingAliveTargets = 0;
+            _remainingItemsInBatch = batchSize;
+
+            // Configure what to do when batches are completed and/or all targets start declining
+            _allTargetsDecliningPermanentlyAction = () =>
+            {
+                // Invoke the caller's action
+                allTargetsDecliningAction();
+
+                // Don't accept any more messages.  We should already
+                // be doing this anyway through each individual target's declining flag, 
+                // so setting it to true is just a precaution and is also helpful
+                // when onceOnly is true.
+                _decliningPermanently = true;
+            };
+            _batchSizeReachedAction = () =>
+            {
+                // Invoke the caller's action
+                batchSizeReachedAction();
+                _batchesCreated++;
+
+                // If this batched join is meant to be used for only a single
+                // batch, invoke the completion logic.
+                if (_batchesCreated >= dataflowBlockOptions.ActualMaxNumberOfGroups) _allTargetsDecliningPermanentlyAction();
+
+                // Otherwise, get ready for the next batch.
+                else _remainingItemsInBatch = _batchSize;
+            };
+            _exceptionAction = exceptionAction;
+            _completionAction = completionAction;
+        }
+
+        /// <summary>
+        /// A lock used to synchronize all incoming messages on all targets. It protects all of the rest 
+        /// of the shared Resources's state and will be held while invoking the delegates.
+        /// </summary>
+        internal readonly object _incomingLock;
+        /// <summary>The size of the batches to generate.</summary>
+        internal readonly int _batchSize;
+
+        /// <summary>The action to invoke when enough elements have been accumulated to make a batch.</summary>
+        internal readonly Action _batchSizeReachedAction;
+        /// <summary>The action to invoke when all targets are declining further messages.</summary>
+        internal readonly Action _allTargetsDecliningPermanentlyAction;
+        /// <summary>The action to invoke when an exception has to be logged.</summary>
+        internal readonly Action<Exception> _exceptionAction;
+        /// <summary>The action to invoke when the owning block has to be completed.</summary>
+        internal readonly Action _completionAction;
+
+        /// <summary>The number of items remaining to form a batch.</summary>
+        internal int _remainingItemsInBatch;
+        /// <summary>The number of targets still alive (i.e. not declining all further messages).</summary>
+        internal int _remainingAliveTargets;
+        /// <summary>Whether all targets should decline all further messages.</summary>
+        internal bool _decliningPermanently;
+        /// <summary>The number of batches created.</summary>
+        internal long _batchesCreated;
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/BroadcastBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/BroadcastBlock.cs
new file mode 100644 (file)
index 0000000..43edb41
--- /dev/null
@@ -0,0 +1,1262 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// BroadcastBlock.cs
+//
+//
+// A propagator that broadcasts incoming messages to all targets, overwriting the current
+// message in the process.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Security;
+using System.Threading.Tasks.Dataflow.Internal;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>
+    /// Provides a buffer for storing at most one element at time, overwriting each message with the next as it arrives.  
+    /// Messages are broadcast to all linked targets, all of which may consume a clone of the message.
+    /// </summary>
+    /// <typeparam name="T">Specifies the type of the data buffered by this dataflow block.</typeparam>
+    /// <remarks>
+    /// <see cref="BroadcastBlock{T}"/> exposes at most one element at a time.  However, unlike
+    /// <see cref="WriteOnceBlock{T}"/>, that element will be overwritten as new elements are provided
+    /// to the block.  <see cref="BroadcastBlock{T}"/> ensures that the current element is broadcast to any
+    /// linked targets before allowing the element to be overwritten.
+    /// </remarks>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(BroadcastBlock<>.DebugView))]
+    public sealed class BroadcastBlock<T> : IPropagatorBlock<T, T>, IReceivableSourceBlock<T>, IDebuggerDisplay
+    {
+        /// <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;
+        /// <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>
+        private bool _completionReserved;
+        /// <summary>Gets the lock used to synchronize incoming requests.</summary>
+        private object IncomingLock { get { return _source; } }
+
+        /// <summary>Initializes the <see cref="BroadcastBlock{T}"/> with the specified cloning function.</summary>
+        /// <param name="cloningFunction">
+        /// 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) :
+            this(cloningFunction, DataflowBlockOptions.Default)
+        { }
+
+        /// <summary>Initializes the <see cref="BroadcastBlock{T}"/>  with the specified cloning function and <see cref="DataflowBlockOptions"/>.</summary>
+        /// <param name="cloningFunction">
+        /// 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>
+        /// <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)
+        {
+            // Validate arguments
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+            Contract.EndContractBlock();
+
+            // Ensure we have options that can't be changed by the caller
+            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
+
+            // Initialize bounding state if necessary
+            Action<int> onItemsRemoved = null;
+            if (dataflowBlockOptions.BoundedCapacity > 0)
+            {
+                Debug.Assert(dataflowBlockOptions.BoundedCapacity > 0, "Positive bounding count expected; should have been verified by options ctor");
+                onItemsRemoved = OnItemsRemoved;
+                _boundingState = new BoundingStateWithPostponedAndTask<T>(dataflowBlockOptions.BoundedCapacity);
+            }
+
+            // Initialize the source side
+            _source = new BroadcastingSourceCore<T>(this, cloningFunction, dataflowBlockOptions, onItemsRemoved);
+
+            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
+            // In those cases we need to fault the target half to drop its buffered messages and to release its 
+            // reservations. This should not create an infinite loop, because all our implementations are designed
+            // to handle multiple completion requests and to carry over only one.
+            _source.Completion.ContinueWith((completed, state) =>
+            {
+                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);
+            }, 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);
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
+            }
+#endif
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+        public void Complete()
+        {
+            CompleteCore(exception: null, storeExceptionEvenIfAlreadyCompleting: false);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            CompleteCore(exception, storeExceptionEvenIfAlreadyCompleting: false);
+        }
+
+        internal void CompleteCore(Exception exception, bool storeExceptionEvenIfAlreadyCompleting, bool revertProcessingState = false)
+        {
+            Contract.Requires(storeExceptionEvenIfAlreadyCompleting || !revertProcessingState,
+                            "Indicating dirty processing state may only come with storeExceptionEvenIfAlreadyCompleting==true.");
+            Contract.EndContractBlock();
+
+            lock (IncomingLock)
+            {
+                // Faulting from outside is allowed until we start declining permanently.
+                // Faulting from inside is allowed at any time.
+                if (exception != null && (!_decliningPermanently || storeExceptionEvenIfAlreadyCompleting))
+                {
+                    _source.AddException(exception);
+                }
+
+                // Revert the dirty processing state if requested
+                if (revertProcessingState)
+                {
+                    Debug.Assert(_boundingState != null && _boundingState.TaskForInputProcessing != null,
+                                    "The processing state must be dirty when revertProcessingState==true.");
+                    _boundingState.TaskForInputProcessing = null;
+                }
+
+                // Trigger completion if possible
+                _decliningPermanently = true;
+                CompleteTargetIfPossible();
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+        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 Boolean TryReceive(Predicate<T> filter, out T item) { return _source.TryReceive(filter, out item); }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
+        Boolean IReceivableSourceBlock<T>.TryReceiveAll(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, Boolean consumeToAccept)
+        {
+            // Validate arguments
+            if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            if (source == null && consumeToAccept) throw new ArgumentException(SR.Argument_CantConsumeFromANullSource, "consumeToAccept");
+            Contract.EndContractBlock();
+
+            lock (IncomingLock)
+            {
+                // If we've already stopped accepting messages, decline permanently
+                if (_decliningPermanently)
+                {
+                    CompleteTargetIfPossible();
+                    return DataflowMessageStatus.DecliningPermanently;
+                }
+
+                // We can directly accept the message if:
+                //      1) we are not bounding, OR 
+                //      2) we are bounding AND there is room available AND there are no postponed messages AND we are not currently processing. 
+                // (If there were any postponed messages, we would need to postpone so that ordering would be maintained.)
+                // (We should also postpone if we are currently processing, because there may be a race between consuming postponed messages and
+                // accepting new ones directly into the queue.)
+                if (_boundingState == null
+                        ||
+                    (_boundingState.CountIsLessThanBound && _boundingState.PostponedMessages.Count == 0 && _boundingState.TaskForInputProcessing == null))
+                {
+                    // Consume the message from the source if necessary
+                    if (consumeToAccept)
+                    {
+                        Debug.Assert(source != null, "We must have thrown if source == null && consumeToAccept == true.");
+
+                        bool consumed;
+                        messageValue = source.ConsumeMessage(messageHeader, this, out consumed);
+                        if (!consumed) return DataflowMessageStatus.NotAvailable;
+                    }
+
+                    // Once consumed, pass it to the delegate
+                    _source.AddMessage(messageValue);
+                    if (_boundingState != null) _boundingState.CurrentCount += 1; // track this new item against our bound
+                    return DataflowMessageStatus.Accepted;
+                }
+                // Otherwise, we try to postpone if a source was provided
+                else if (source != null)
+                {
+                    Debug.Assert(_boundingState != null && _boundingState.PostponedMessages != null,
+                        "PostponedMessages must have been initialized during construction in bounding mode.");
+
+                    _boundingState.PostponedMessages.Push(source, messageHeader);
+                    return DataflowMessageStatus.Postponed;
+                }
+                // We can't do anything else about this message
+                return DataflowMessageStatus.Declined;
+            }
+        }
+
+        /// <summary>Notifies the block that one or more items was removed from the queue.</summary>
+        /// <param name="numItemsRemoved">The number of items removed.</param>
+        private void OnItemsRemoved(int numItemsRemoved)
+        {
+            Contract.Requires(numItemsRemoved > 0, "Should only be called for a positive number of items removed.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            // If we're bounding, we need to know when an item is removed so that we
+            // can update the count that's mirroring the actual count in the source's queue,
+            // and potentially kick off processing to start consuming postponed messages.
+            if (_boundingState != null)
+            {
+                lock (IncomingLock)
+                {
+                    // Decrement the count, which mirrors the count in the source half
+                    Debug.Assert(_boundingState.CurrentCount - numItemsRemoved >= 0,
+                        "It should be impossible to have a negative number of items.");
+                    _boundingState.CurrentCount -= numItemsRemoved;
+
+                    ConsumeAsyncIfNecessary();
+                    CompleteTargetIfPossible();
+                }
+            }
+        }
+
+        /// <summary>Called when postponed messages may need to be consumed.</summary>
+        /// <param name="isReplacementReplica">Whether this call is the continuation of a previous message loop.</param>
+        internal void ConsumeAsyncIfNecessary(bool isReplacementReplica = false)
+        {
+            Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+            Debug.Assert(_boundingState != null, "Must be in bounded mode.");
+
+            if (!_decliningPermanently &&
+                _boundingState.TaskForInputProcessing == null &&
+                _boundingState.PostponedMessages.Count > 0 &&
+                _boundingState.CountIsLessThanBound)
+            {
+                // 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,
+                        Common.GetCreationOptionsForTask(isReplacementReplica));
+
+#if FEATURE_TRACING
+                DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+                if (etwLog.IsEnabled())
+                {
+                    etwLog.TaskLaunchedForMessageHandling(
+                        this, _boundingState.TaskForInputProcessing, DataflowEtwProvider.TaskLaunchedReason.ProcessingInputMessages,
+                        _boundingState.PostponedMessages.Count);
+                }
+#endif
+
+                // Start the task handling scheduling exceptions
+                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),
+                                        exception, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+                }
+            }
+        }
+
+        /// <summary>Task body used to consume postponed messages.</summary>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private void ConsumeMessagesLoopCore()
+        {
+            Contract.Requires(_boundingState != null && _boundingState.TaskForInputProcessing != null,
+                "May only be called in bounded mode and when a task is in flight.");
+            Debug.Assert(_boundingState.TaskForInputProcessing.Id == Task.CurrentId,
+                "This must only be called from the in-flight processing task.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            try
+            {
+                int maxMessagesPerTask = _source.DataflowBlockOptions.ActualMaxMessagesPerTask;
+                for (int i = 0;
+                    i < maxMessagesPerTask && ConsumeAndStoreOneMessageIfAvailable();
+                    i++)
+                    ;
+            }
+            catch (Exception exception)
+            {
+                // Prevent the creation of new processing tasks
+                CompleteCore(exception, storeExceptionEvenIfAlreadyCompleting: true);
+            }
+            finally
+            {
+                lock (IncomingLock)
+                {
+                    // We're no longer processing, so null out the processing task
+                    _boundingState.TaskForInputProcessing = null;
+
+                    // However, we may have given up early because we hit our own configured
+                    // processing limits rather than because we ran out of work to do.  If that's
+                    // the case, make sure we spin up another task to keep going.
+                    ConsumeAsyncIfNecessary(isReplacementReplica: true);
+
+                    // If, however, we stopped because we ran out of work to do and we
+                    // know we'll never get more, then complete.
+                    CompleteTargetIfPossible();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Retrieves one postponed message if there's room and if we can consume a postponed message.
+        /// Stores any consumed message into the source half.
+        /// </summary>
+        /// <returns>true if a message could be consumed and stored; otherwise, false.</returns>
+        /// <remarks>This must only be called from the asynchronous processing loop.</remarks>
+        private bool ConsumeAndStoreOneMessageIfAvailable()
+        {
+            Contract.Requires(_boundingState != null && _boundingState.TaskForInputProcessing != null,
+                "May only be called in bounded mode and when a task is in flight.");
+            Debug.Assert(_boundingState.TaskForInputProcessing.Id == Task.CurrentId,
+                "This must only be called from the in-flight processing task.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            // Loop through the postponed messages until we get one.
+            while (true)
+            {
+                // Get the next item to retrieve.  If there are no more, bail.
+                KeyValuePair<ISourceBlock<T>, DataflowMessageHeader> sourceAndMessage;
+                lock (IncomingLock)
+                {
+                    if (!_boundingState.CountIsLessThanBound) return false;
+                    if (!_boundingState.PostponedMessages.TryPop(out sourceAndMessage)) return false;
+
+                    // Optimistically assume we're going to get the item. This avoids taking the lock
+                    // again if we're right.  If we're wrong, we decrement it later under lock.
+                    _boundingState.CurrentCount++;
+                }
+
+                // Consume the item
+                bool consumed = false;
+                try
+                {
+                    T consumedValue = sourceAndMessage.Key.ConsumeMessage(sourceAndMessage.Value, this, out consumed);
+                    if (consumed)
+                    {
+                        _source.AddMessage(consumedValue);
+                        return true;
+                    }
+                }
+                finally
+                {
+                    // We didn't get the item, so decrement the count to counteract our optimistic assumption.
+                    if (!consumed)
+                    {
+                        lock (IncomingLock) _boundingState.CurrentCount--;
+                    }
+                }
+            }
+        }
+
+        /// <summary>Completes the target, notifying the source, once all completion conditions are met.</summary>
+        private void CompleteTargetIfPossible()
+        {
+            Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+            if (_decliningPermanently &&
+                !_completionReserved &&
+                (_boundingState == null || _boundingState.TaskForInputProcessing == null))
+            {
+                _completionReserved = true;
+
+                // If we're in bounding mode and we have any postponed messages, we need to clear them,
+                // which means calling back to the source, which means we need to escape the incoming lock.
+                if (_boundingState != null && _boundingState.PostponedMessages.Count > 0)
+                {
+                    Task.Factory.StartNew(state =>
+                    {
+                        var thisBroadcastBlock = (BroadcastBlock<T>)state;
+
+                        // Release any postponed messages
+                        List<Exception> exceptions = null;
+                        if (thisBroadcastBlock._boundingState != null)
+                        {
+                            // Note: No locks should be held at this point
+                            Common.ReleaseAllPostponedMessages(thisBroadcastBlock,
+                                                               thisBroadcastBlock._boundingState.PostponedMessages,
+                                                               ref exceptions);
+                        }
+
+                        if (exceptions != null)
+                        {
+                            // It is important to migrate these exceptions to the source part of the owning batch,
+                            // because that is the completion task that is publically exposed.
+                            thisBroadcastBlock._source.AddExceptions(exceptions);
+                        }
+
+                        thisBroadcastBlock._source.Complete();
+                    }, this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+                }
+                // Otherwise, we can just decline the source directly.
+                else
+                {
+                    _source.Complete();
+                }
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+        T ISourceBlock<T>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target, out Boolean messageConsumed)
+        {
+            return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+        bool ISourceBlock<T>.ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target)
+        {
+            return _source.ReserveMessage(messageHeader, target);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+        void ISourceBlock<T>.ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<T> target)
+        {
+            _source.ReleaseReservation(messageHeader, target);
+        }
+
+        /// <summary>Gets a value to be used for the DebuggerDisplayAttribute.  This must not throw even if HasValue is false.</summary>
+        private bool HasValueForDebugger { get { return _source.GetDebuggingInformation().HasValue; } }
+        /// <summary>Gets a value to be used for the DebuggerDisplayAttribute.  This must not throw even if HasValue is false.</summary>
+        private T ValueForDebugger { get { return _source.GetDebuggingInformation().Value; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="ToString"]/*' />
+        public override string ToString() { return Common.GetNameForDebugger(this, _source.DataflowBlockOptions); }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                return string.Format("{0}, HasValue={1}, Value={2}",
+                    Common.GetNameForDebugger(this, _source.DataflowBlockOptions),
+                    HasValueForDebugger,
+                    ValueForDebugger);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for the BroadcastBlock.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The BroadcastBlock being debugged.</summary>
+            private readonly BroadcastBlock<T> _broadcastBlock;
+            /// <summary>Debug info about the source side of the broadcast.</summary>
+            private readonly BroadcastingSourceCore<T>.DebuggingInformation _sourceDebuggingInformation;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="broadcastBlock">The BroadcastBlock being debugged.</param>
+            public DebugView(BroadcastBlock<T> broadcastBlock)
+            {
+                Contract.Requires(broadcastBlock != null, "Need a block with which to construct the debug view.");
+                _broadcastBlock = broadcastBlock;
+                _sourceDebuggingInformation = broadcastBlock._source.GetDebuggingInformation();
+            }
+
+            /// <summary>Gets the messages waiting to be processed.</summary>
+            public IEnumerable<T> InputQueue { get { return _sourceDebuggingInformation.InputQueue; } }
+            /// <summary>Gets whether the broadcast has a current value.</summary>
+            public bool HasValue { get { return _broadcastBlock.HasValueForDebugger; } }
+            /// <summary>Gets the broadcast's current value.</summary>
+            public T Value { get { return _broadcastBlock.ValueForDebugger; } }
+
+            /// <summary>Gets the task being used for output processing.</summary>
+            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 whether the block is declining further messages.</summary>
+            public bool IsDecliningPermanently { get { return _broadcastBlock._decliningPermanently; } }
+            /// <summary>Gets whether the block is completed.</summary>
+            public bool IsCompleted { get { return _sourceDebuggingInformation.IsCompleted; } }
+            /// <summary>Gets the block's Id.</summary>
+            public int Id { get { return Common.GetBlockId(_broadcastBlock); } }
+
+            /// <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; } }
+        }
+
+        /// <summary>Provides a core implementation for blocks that implement <see cref="ISourceBlock{TOutput}"/>.</summary>
+        /// <typeparam name="TOutput">Specifies the type of data supplied by the <see cref="SourceCore{TOutput}"/>.</typeparam>
+        [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
+        [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+        private sealed class BroadcastingSourceCore<TOutput>
+        {
+            /// <summary>A registry used to store all linked targets and information about them.</summary>
+            private readonly TargetRegistry<TOutput> _targetRegistry;
+            /// <summary>All of the output messages queued up to be received by consumers/targets.</summary>
+            private readonly Queue<TOutput> _messages = new Queue<TOutput>();
+            /// <summary>A TaskCompletionSource that represents the completion of this block.</summary>
+            private readonly TaskCompletionSource<VoidResult> _completionTask = new TaskCompletionSource<VoidResult>();
+            /// <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;
+
+            /// <summary>Gets the object to use as the outgoing lock.</summary>
+            private object OutgoingLock { get { return _completionTask; } }
+            /// <summary>Gets the object to use as the value lock.</summary>
+            private object ValueLock { get { return _targetRegistry; } }
+
+            /// <summary>The source utilize this helper.</summary>
+            private readonly BroadcastBlock<TOutput> _owningSource;
+            /// <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;
+
+            /// <summary>An indicator whether _currentMessage has a value.</summary>
+            private bool _currentMessageIsValid;
+            /// <summary>The message currently being broadcast.</summary>
+            private TOutput _currentMessage;
+            /// <summary>The target that the next message is reserved for, or null if nothing is reserved.</summary>
+            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;
+            /// <summary>Exceptions that may have occurred and gone unhandled during processing.</summary>
+            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>
+            private bool _completionReserved;
+
+            /// <summary>Initializes the source core.</summary>
+            /// <param name="owningSource">The source utilizing this core.</param>
+            /// <param name="cloningFunction">The function to use to clone the data when offered to other blocks.  May be null.</param>
+            /// <param name="dataflowBlockOptions">The options to use to configure the block.</param>
+            /// <param name="itemsRemovedAction">Action to invoke when an item is removed.</param>
+            internal BroadcastingSourceCore(
+                BroadcastBlock<TOutput> owningSource,
+                Func<TOutput, TOutput> cloningFunction,
+                DataflowBlockOptions dataflowBlockOptions,
+                Action<int> itemsRemovedAction)
+            {
+                Contract.Requires(owningSource != null, "Must be associated with a broadcast block.");
+                Contract.Requires(dataflowBlockOptions != null, "Options are required to configure this block.");
+
+                // Store the arguments
+                _owningSource = owningSource;
+                _cloningFunction = cloningFunction;
+                _dataflowBlockOptions = dataflowBlockOptions;
+                _itemsRemovedAction = itemsRemovedAction;
+
+                // Construct members that depend on the arguments
+                _targetRegistry = new TargetRegistry<TOutput>(_owningSource);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
+            internal Boolean TryReceive(Predicate<TOutput> filter, out TOutput item)
+            {
+                // Take the lock only long enough to get the message,
+                // synchronizing with other activities on the block.
+                // We don't want to execute the user-provided cloning delegate
+                // while holding the lock.
+                TOutput message;
+                bool isValid;
+                lock (OutgoingLock)
+                {
+                    lock (ValueLock)
+                    {
+                        message = _currentMessage;
+                        isValid = _currentMessageIsValid;
+                    }
+                }
+
+                // 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)))
+                {
+                    item = CloneItem(message);
+                    return true;
+                }
+                else
+                {
+                    item = default(TOutput);
+                    return false;
+                }
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
+            internal Boolean TryReceiveAll(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.
+                TOutput item;
+                if (TryReceive(null, out item))
+                {
+                    items = new TOutput[] { item };
+                    return true;
+                }
+                else
+                {
+                    items = null;
+                    return false;
+                }
+            }
+
+            /// <summary>Adds a message to the source block for propagation.</summary>
+            /// <param name="item">The item to be wrapped in a message to be added.</param>
+            internal void AddMessage(TOutput item)
+            {
+                // This method must not take the outgoing lock, as it will be called in situations
+                // where a derived type's incoming lock is held.  The lock leveling structure
+                // we're employing is such that outgoing may be held while acquiring incoming, but
+                // of course not the other way around.  This is the reason why DataflowSourceBlock
+                // needs ValueLock as well.  Otherwise, it would be pure overhead.
+                lock (ValueLock)
+                {
+                    if (_decliningPermanently) return;
+                    _messages.Enqueue(item);
+                    if (_messages.Count == 1) _enableOffering = true;
+                    OfferAsyncIfNecessary();
+                }
+            }
+
+            /// <summary>Informs the block that it will not be receiving additional messages.</summary>
+            internal void Complete()
+            {
+                lock (ValueLock)
+                {
+                    _decliningPermanently = true;
+
+                    // Complete may be called in a context where an incoming lock is held.  We need to 
+                    // call CompleteBlockIfPossible, but we can't do so if the incoming lock is held.
+                    // However, now that _decliningPermanently has been set, the timing of
+                    // CompleteBlockIfPossible doesn't matter, so we schedule it to run asynchronously
+                    // 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;
+                        lock (thisSourceCore.OutgoingLock)
+                        {
+                            lock (thisSourceCore.ValueLock)
+                            {
+                                thisSourceCore.CompleteBlockIfPossible();
+                            }
+                        }
+                    }, this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+                }
+            }
+
+            /// <summary>Clones the item.</summary>
+            /// <param name="item">The item to clone.</param>
+            /// <returns>The cloned item.</returns>
+            private TOutput CloneItem(TOutput item)
+            {
+                return _cloningFunction != null ?
+                    _cloningFunction(item) :
+                    item;
+            }
+
+            /// <summary>Offers the current message to a specific target.</summary>
+            /// <param name="target">The target to which to offer the current message.</param>
+            private void OfferCurrentMessageToNewTarget(ITargetBlock<TOutput> target)
+            {
+                Contract.Requires(target != null, "Target required to offer messages to.");
+                Common.ContractAssertMonitorStatus(OutgoingLock, held: true);
+                Common.ContractAssertMonitorStatus(ValueLock, held: false);
+
+                // Get the current message if there is one
+                TOutput currentMessage;
+                bool isValid;
+                lock (ValueLock)
+                {
+                    currentMessage = _currentMessage;
+                    isValid = _currentMessageIsValid;
+                }
+
+                // If there is no valid message yet, there is nothing to offer
+                if (!isValid) return;
+
+                // 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);
+
+                // If accepted and the target was linked as "unlinkAfterOne", remove it
+                if (result == DataflowMessageStatus.Accepted)
+                {
+                    if (!useCloning)
+                    {
+                        // If accepted and the target was linked as "once", mark it for removal.
+                        // If we were forcing consumption, this removal would have already
+                        // happened in ConsumeMessage.
+                        _targetRegistry.Remove(target, onlyIfReachedMaxMessages: true);
+                    }
+                }
+                // If declined permanently, remove it
+                else if (result == DataflowMessageStatus.DecliningPermanently)
+                {
+                    _targetRegistry.Remove(target);
+                }
+                else Debug.Assert(result != DataflowMessageStatus.NotAvailable, "Messages from a Broadcast should never be missed.");
+            }
+
+            /// <summary>Offers messages to targets.</summary>
+            private bool OfferToTargets()
+            {
+                Common.ContractAssertMonitorStatus(OutgoingLock, held: true);
+                Common.ContractAssertMonitorStatus(ValueLock, held: false);
+
+                DataflowMessageHeader header = default(DataflowMessageHeader);
+                TOutput message = default(TOutput);
+                int numDequeuedMessages = 0;
+                lock (ValueLock)
+                {
+                    // If there's a reservation or there aren't any more messages,
+                    // there's nothing for us to do.  If there's no reservation
+                    // and a message is available, dequeue the next one and store it
+                    // as the new current.  If we're now at 0 message, disable further
+                    // propagation until more messages arrive.
+                    if (_nextMessageReservedFor == null && _messages.Count > 0)
+                    {
+                        // If there  are no targets registered, we might as well empty out the broadcast,
+                        // keeping just the last.  Otherwise, it'll happen anyway, but much more expensively.
+                        if (_targetRegistry.FirstTargetNode == null)
+                        {
+                            while (_messages.Count > 1)
+                            {
+                                _messages.Dequeue();
+                                numDequeuedMessages++;
+                            }
+                        }
+
+                        // Get the next message to offer
+                        Debug.Assert(_messages.Count > 0, "There must be at least one message to dequeue.");
+                        _currentMessage = message = _messages.Dequeue();
+                        numDequeuedMessages++;
+                        _currentMessageIsValid = true;
+                        header = new DataflowMessageHeader(++_nextMessageId);
+                        if (_messages.Count == 0) _enableOffering = false;
+                    }
+                    else
+                    {
+                        _enableOffering = false;
+                        return false;
+                    }
+                } // must not hold ValueLock when calling out to targets
+
+                // Offer the message
+                if (header.IsValid)
+                {
+                    // Notify the owner block that our count has decreased
+                    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;
+                    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;
+                        ITargetBlock<TOutput> target = cur.Target;
+                        OfferMessageToTarget(header, message, target);
+                        cur = next;
+                    }
+                }
+                return true;
+            }
+
+            /// <summary>Offers the specified message to the specified target.</summary>
+            /// <param name="header">The header of the message to offer.</param>
+            /// <param name="message">The message to offer.</param>
+            /// <param name="target">The target to which the message should be offered.</param>
+            /// <remarks>
+            /// This will remove the target from the target registry if the result of the propagation demands it.
+            /// </remarks>
+            private void OfferMessageToTarget(DataflowMessageHeader header, TOutput message, ITargetBlock<TOutput> target)
+            {
+                Common.ContractAssertMonitorStatus(OutgoingLock, held: true);
+                Common.ContractAssertMonitorStatus(ValueLock, held: false);
+
+                // Offer the message.  If there's a cloning function, we force the target to
+                // come back to us to consume the message, allowing us the opportunity to run
+                // 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;
+                switch (target.OfferMessage(header, message, _owningSource, consumeToAccept: useCloning))
+                {
+                    case DataflowMessageStatus.Accepted:
+                        if (!useCloning)
+                        {
+                            // If accepted and the target was linked as "once", mark it for removal.
+                            // If we were forcing consumption, this removal would have already
+                            // happened in ConsumeMessage.
+                            _targetRegistry.Remove(target, onlyIfReachedMaxMessages: true);
+                        }
+                        break;
+
+                    case DataflowMessageStatus.DecliningPermanently:
+                        // If declined permanently, mark the target for removal
+                        _targetRegistry.Remove(target);
+                        break;
+
+                    case DataflowMessageStatus.NotAvailable:
+                        Debug.Assert(false, "Messages from a Broadcast should never be missed.");
+                        break;
+                        // No action required for Postponed or Declined
+                }
+            }
+
+            /// <summary>Called when we want to enable asynchronously offering message to targets.</summary>
+            /// <param name="isReplacementReplica">Whether this call is the continuation of a previous message loop.</param>
+            private void OfferAsyncIfNecessary(bool isReplacementReplica = false)
+            {
+                Common.ContractAssertMonitorStatus(ValueLock, held: true);
+                // This method must not take the OutgoingLock.
+
+                bool currentlyProcessing = _taskForOutputProcessing != null;
+                bool processingToDo = _enableOffering && _messages.Count > 0;
+
+                // If there's any work to be done...
+                if (!currentlyProcessing && processingToDo && !CanceledOrFaulted)
+                {
+                    // 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,
+                                                        Common.GetCreationOptionsForTask(isReplacementReplica));
+
+#if FEATURE_TRACING
+                    DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+                    if (etwLog.IsEnabled())
+                    {
+                        etwLog.TaskLaunchedForMessageHandling(
+                            _owningSource, _taskForOutputProcessing, DataflowEtwProvider.TaskLaunchedReason.OfferingOutputMessages, _messages.Count);
+                    }
+#endif
+
+                    // Start the task handling scheduling exceptions
+                    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.
+                        // Then revert the proactive processing state changes.
+                        // And last, try to complete the block.
+                        AddException(exception);
+                        _decliningPermanently = true;
+                        _taskForOutputProcessing = null;
+
+                        // Get out from under currently held locks - ValueLock is taken, but OutgoingLock may not be.
+                        // Re-take the locks on a separate thread.
+                        Task.Factory.StartNew(state =>
+                        {
+                            var thisSourceCore = (BroadcastingSourceCore<TOutput>)state;
+                            lock (thisSourceCore.OutgoingLock)
+                            {
+                                lock (thisSourceCore.ValueLock)
+                                {
+                                    thisSourceCore.CompleteBlockIfPossible();
+                                }
+                            }
+                        }, this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+                    }
+                }
+            }
+
+            /// <summary>Task body used to process messages.</summary>
+            [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+            private void OfferMessagesLoopCore()
+            {
+                try
+                {
+                    int maxMessagesPerTask = _dataflowBlockOptions.ActualMaxMessagesPerTask;
+                    lock (OutgoingLock)
+                    {
+                        // Offer as many messages as we can
+                        for (int counter = 0;
+                            counter < maxMessagesPerTask && !CanceledOrFaulted;
+                            counter++)
+                        {
+                            if (!OfferToTargets()) break;
+                        }
+                    }
+                }
+                catch (Exception exception)
+                {
+                    _owningSource.CompleteCore(exception, storeExceptionEvenIfAlreadyCompleting: true);
+                }
+                finally
+                {
+                    lock (OutgoingLock)
+                    {
+                        lock (ValueLock)
+                        {
+                            // We're no longer processing, so null out the processing task
+                            _taskForOutputProcessing = null;
+
+                            // However, we may have given up early because we hit our own configured
+                            // processing limits rather than because we ran out of work to do.  If that's
+                            // the case, make sure we spin up another task to keep going.
+                            OfferAsyncIfNecessary(isReplacementReplica: true);
+
+                            // If, however, we stopped because we ran out of work to do and we
+                            // know we'll never get more, then complete.
+                            CompleteBlockIfPossible();
+                        }
+                    }
+                }
+            }
+
+            /// <summary>Completes the block's processing if there's nothing left to do and never will be.</summary>
+            private void CompleteBlockIfPossible()
+            {
+                Common.ContractAssertMonitorStatus(OutgoingLock, held: true);
+                Common.ContractAssertMonitorStatus(ValueLock, held: true);
+
+                if (!_completionReserved)
+                {
+                    bool currentlyProcessing = _taskForOutputProcessing != null;
+                    bool noMoreMessages = _decliningPermanently && _messages.Count == 0;
+
+                    // Are we done forever?
+                    bool complete = !currentlyProcessing && (noMoreMessages || CanceledOrFaulted);
+                    if (complete)
+                    {
+                        CompleteBlockIfPossible_Slow();
+                    }
+                }
+            }
+
+            /// <summary>
+            /// Slow path for CompleteBlockIfPossible. 
+            /// Separating out the slow path into its own method makes it more likely that the fast path method will get inlined.
+            /// </summary>
+            private void CompleteBlockIfPossible_Slow()
+            {
+                Contract.Requires(_taskForOutputProcessing == null, "There must be no processing tasks.");
+                Contract.Requires(
+                    (_decliningPermanently && _messages.Count == 0) || CanceledOrFaulted,
+                    "There must be no more messages or the block must be canceled or faulted.");
+
+                _completionReserved = true;
+
+                // Run asynchronously to get out of the currently held locks
+                Task.Factory.StartNew(thisSourceCore => ((BroadcastingSourceCore<TOutput>)thisSourceCore).CompleteBlockOncePossible(),
+                    this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+            }
+
+            /// <summary>
+            /// Completes the block.  This must only be called once, and only once all of the completion conditions are met.
+            /// As such, it must only be called from CompleteBlockIfPossible.
+            /// </summary>
+            private void CompleteBlockOncePossible()
+            {
+                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.
+                lock (OutgoingLock)
+                {
+                    // Save the linked list of targets so that it could be traversed later to propagate completion
+                    linkedTargets = _targetRegistry.ClearEntryPoints();
+                    lock (ValueLock)
+                    {
+                        _messages.Clear();
+
+                        // Save a local reference to the exceptions list and null out the field,
+                        // so that if the target side tries to add an exception this late,
+                        // it will go to a separate list (that will be ignored.)
+                        exceptions = _exceptions;
+                        _exceptions = null;
+                    }
+                }
+
+                // If it's due to an exception, finish in a faulted state
+                if (exceptions != null)
+                {
+                    _completionTask.TrySetException(exceptions);
+                }
+                // It's due to cancellation, finish in a canceled state
+                else if (_dataflowBlockOptions.CancellationToken.IsCancellationRequested)
+                {
+                    _completionTask.TrySetCanceled();
+                }
+                // Otherwise, finish in a successful state.
+                else
+                {
+                    _completionTask.TrySetResult(default(VoidResult));
+                }
+
+                // Now that the completion task is completed, we may propagate completion to the linked targets
+                _targetRegistry.PropagateCompletion(linkedTargets);
+#if FEATURE_TRACING
+                DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+                if (etwLog.IsEnabled())
+                {
+                    etwLog.DataflowBlockCompleted(_owningSource);
+                }
+#endif
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+            [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
+            internal IDisposable LinkTo(ITargetBlock<TOutput> target, DataflowLinkOptions linkOptions)
+            {
+                // Validate arguments
+                if (target == null) throw new ArgumentNullException("target");
+                if (linkOptions == null) throw new ArgumentNullException("linkOptions");
+                Contract.EndContractBlock();
+
+                lock (OutgoingLock)
+                {
+                    // If we've completed or completion has at least started, offer the message to this target,
+                    // and propagate completion if that was requested.
+                    // Then there's nothing more to be done.
+                    if (_completionReserved)
+                    {
+                        OfferCurrentMessageToNewTarget(target);
+                        if (linkOptions.PropagateCompletion) Common.PropagateCompletionOnceCompleted(_completionTask.Task, target);
+                        return Disposables.Nop;
+                    }
+
+                    // Otherwise, add the target and then offer it the current
+                    // message.  We do this in this order because offering may
+                    // cause the target to be removed if it's unlinkAfterOne,
+                    // and in the reverse order we would end up adding the target
+                    // after it was "removed".
+                    _targetRegistry.Add(ref target, linkOptions);
+                    OfferCurrentMessageToNewTarget(target);
+                    return Common.CreateUnlinker(OutgoingLock, _targetRegistry, target);
+                }
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+            internal TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out Boolean messageConsumed)
+            {
+                // Validate arguments
+                if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+                if (target == null) throw new ArgumentNullException("target");
+                Contract.EndContractBlock();
+
+                TOutput valueToClone;
+                lock (OutgoingLock) // We may currently be calling out under this lock to the target; requires it to be reentrant
+                {
+                    lock (ValueLock)
+                    {
+                        // If this isn't the next message to be served up, bail
+                        if (messageHeader.Id != _nextMessageId)
+                        {
+                            messageConsumed = false;
+                            return default(TOutput);
+                        }
+
+                        // If the caller has the reservation, release the reservation.
+                        // We still allow others to take the message if there's a reservation.
+                        if (_nextMessageReservedFor == target)
+                        {
+                            _nextMessageReservedFor = null;
+                            _enableOffering = true;
+                        }
+                        _targetRegistry.Remove(target, onlyIfReachedMaxMessages: true);
+
+                        OfferAsyncIfNecessary();
+                        CompleteBlockIfPossible();
+
+                        // Return a clone of the consumed message.
+                        valueToClone = _currentMessage;
+                    }
+                }
+
+                messageConsumed = true;
+                return CloneItem(valueToClone);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+            internal Boolean ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
+            {
+                // Validate arguments
+                if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+                if (target == null) throw new ArgumentNullException("target");
+                Contract.EndContractBlock();
+
+                lock (OutgoingLock)
+                {
+                    // If no one currently holds a reservation...
+                    if (_nextMessageReservedFor == null)
+                    {
+                        lock (ValueLock)
+                        {
+                            // ...and the requested message is next in line, allow it
+                            if (messageHeader.Id == _nextMessageId)
+                            {
+                                _nextMessageReservedFor = target;
+                                _enableOffering = false;
+                                return true;
+                            }
+                        }
+                    }
+                }
+                return false;
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+            internal void ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
+            {
+                // Validate arguments
+                if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+                if (target == null) throw new ArgumentNullException("target");
+                Contract.EndContractBlock();
+
+                lock (OutgoingLock)
+                {
+                    // If someone else holds the reservation, bail.
+                    if (_nextMessageReservedFor != target) throw new InvalidOperationException(SR.InvalidOperation_MessageNotReservedByTarget);
+
+                    TOutput messageToReoffer;
+                    lock (ValueLock)
+                    {
+                        // If this is not the message at the head of the queue, bail
+                        if (messageHeader.Id != _nextMessageId) throw new InvalidOperationException(SR.InvalidOperation_MessageNotReservedByTarget);
+
+                        // Otherwise, release the reservation, and reoffer the message to all targets.
+                        _nextMessageReservedFor = null;
+                        _enableOffering = true;
+                        messageToReoffer = _currentMessage;
+                        OfferAsyncIfNecessary();
+                    }
+
+                    // We need to explicitly reoffer this message to the releaser,
+                    // as otherwise if the target has join behavior it could end up waiting for an offer from
+                    // this broadcast forever, even though data is in fact available.  We could only
+                    // 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);
+                }
+            }
+
+            /// <summary>Gets whether the source has had cancellation requested or an exception has occurred.</summary>
+            private bool CanceledOrFaulted
+            {
+                get
+                {
+                    // Cancellation is honored as soon as the CancellationToken has been signaled.
+                    // Faulting is honored after an exception has been encountered and the owning block
+                    // has invoked Complete on us.
+                    return _dataflowBlockOptions.CancellationToken.IsCancellationRequested ||
+                        (Volatile.Read(ref _exceptions) != null && _decliningPermanently);
+                }
+            }
+
+            /// <summary>Adds an individual exceptionto this source.</summary>
+            /// <param name="exception">The exception to add</param>
+            internal void AddException(Exception exception)
+            {
+                Contract.Requires(exception != null, "An exception to add is required.");
+                Contract.Requires(!Completion.IsCompleted || Completion.IsFaulted, "The block must either not be completed or be faulted if we're still storing exceptions.");
+                lock (ValueLock)
+                {
+                    Common.AddException(ref _exceptions, exception);
+                }
+            }
+
+            /// <summary>Adds exceptions to this source.</summary>
+            /// <param name="exceptions">The exceptions to add</param>
+            internal void AddExceptions(List<Exception> exceptions)
+            {
+                Contract.Requires(exceptions != null, "A list of exceptions to add is required.");
+                Contract.Requires(!Completion.IsCompleted || Completion.IsFaulted, "The block must either not be completed or be faulted if we're still storing exceptions.");
+                lock (ValueLock)
+                {
+                    foreach (Exception exception in exceptions)
+                    {
+                        Common.AddException(ref _exceptions, exception);
+                    }
+                }
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+            internal Task Completion { get { return _completionTask.Task; } }
+
+            /// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
+            internal DataflowBlockOptions DataflowBlockOptions { get { return _dataflowBlockOptions; } }
+
+            /// <summary>Gets the object to display in the debugger display attribute.</summary>
+            [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+            private object DebuggerDisplayContent
+            {
+                get
+                {
+                    var displaySource = _owningSource as IDebuggerDisplay;
+                    return string.Format("Block=\"{0}\"",
+                        displaySource != null ? displaySource.Content : _owningSource);
+                }
+            }
+
+            /// <summary>Gets information about this helper to be used for display in a debugger.</summary>
+            /// <returns>Debugging information about this source core.</returns>
+            internal DebuggingInformation GetDebuggingInformation() { return new DebuggingInformation(this); }
+
+            /// <summary>Provides debugging information about the source core.</summary>
+            internal sealed class DebuggingInformation
+            {
+                /// <summary>The source being viewed.</summary>
+                private BroadcastingSourceCore<TOutput> _source;
+
+                /// <summary>Initializes the type proxy.</summary>
+                /// <param name="source">The source being viewed.</param>
+                public DebuggingInformation(BroadcastingSourceCore<TOutput> source) { _source = source; }
+
+                /// <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; } }
+                /// <summary>Gets the number of messages waiting to be made current.</summary>
+                public int InputCount { get { return _source._messages.Count; } }
+                /// <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; } }
+
+                /// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
+                public DataflowBlockOptions DataflowBlockOptions { get { return _source._dataflowBlockOptions; } }
+                /// <summary>Gets whether the block is declining further messages.</summary>
+                public bool IsDecliningPermanently { get { return _source._decliningPermanently; } }
+                /// <summary>Gets whether the block is completed.</summary>
+                public bool IsCompleted { get { return _source.Completion.IsCompleted; } }
+
+                /// <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; } }
+            }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/BufferBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/BufferBlock.cs
new file mode 100644 (file)
index 0000000..a2a544b
--- /dev/null
@@ -0,0 +1,492 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// BufferBlock.cs
+//
+//
+// A propagator block that provides support for unbounded and bounded FIFO buffers.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Security;
+using System.Threading.Tasks.Dataflow.Internal;
+using System.Diagnostics.CodeAnalysis;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Provides a buffer for storing data.</summary>
+    /// <typeparam name="T">Specifies the type of the data buffered by this dataflow block.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(BufferBlock<>.DebugView))]
+    public sealed class BufferBlock<T> : IPropagatorBlock<T, T>, IReceivableSourceBlock<T>, IDebuggerDisplay
+    {
+        /// <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;
+        /// <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>
+        private bool _targetCompletionReserved;
+        /// <summary>Gets the lock object used to synchronize incoming requests.</summary>
+        private object IncomingLock { get { return _source; } }
+
+        /// <summary>Initializes the <see cref="BufferBlock{T}"/>.</summary>
+        public BufferBlock() :
+            this(DataflowBlockOptions.Default)
+        { }
+
+        /// <summary>Initializes the <see cref="BufferBlock{T}"/> with the specified <see cref="DataflowBlockOptions"/>.</summary>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="BufferBlock{T}"/>.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        public BufferBlock(DataflowBlockOptions dataflowBlockOptions)
+        {
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+            Contract.EndContractBlock();
+
+            // Ensure we have options that can't be changed by the caller
+            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
+
+            // Initialize bounding state if necessary
+            Action<ISourceBlock<T>, int> onItemsRemoved = null;
+            if (dataflowBlockOptions.BoundedCapacity > 0)
+            {
+                onItemsRemoved = (owningSource, count) => ((BufferBlock<T>)owningSource).OnItemsRemoved(count);
+                _boundingState = new BoundingStateWithPostponedAndTask<T>(dataflowBlockOptions.BoundedCapacity);
+            }
+
+            // Initialize the source state
+            _source = new SourceCore<T>(this, dataflowBlockOptions,
+                owningSource => ((BufferBlock<T>)owningSource).Complete(),
+                onItemsRemoved);
+
+            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
+            // In those cases we need to fault the target half to drop its buffered messages and to release its 
+            // reservations. This should not create an infinite loop, because all our implementations are designed
+            // to handle multiple completion requests and to carry over only one.
+            _source.Completion.ContinueWith((completed, state) =>
+            {
+                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);
+            }, 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);
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
+            }
+#endif
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
+        DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, Boolean consumeToAccept)
+        {
+            // Validate arguments
+            if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            if (source == null && consumeToAccept) throw new ArgumentException(SR.Argument_CantConsumeFromANullSource, "consumeToAccept");
+            Contract.EndContractBlock();
+
+            lock (IncomingLock)
+            {
+                // If we've already stopped accepting messages, decline permanently
+                if (_targetDecliningPermanently)
+                {
+                    CompleteTargetIfPossible();
+                    return DataflowMessageStatus.DecliningPermanently;
+                }
+
+                // We can directly accept the message if:
+                //      1) we are not bounding, OR 
+                //      2) we are bounding AND there is room available AND there are no postponed messages AND we are not currently processing. 
+                // (If there were any postponed messages, we would need to postpone so that ordering would be maintained.)
+                // (We should also postpone if we are currently processing, because there may be a race between consuming postponed messages and
+                // accepting new ones directly into the queue.)
+                if (_boundingState == null
+                        ||
+                    (_boundingState.CountIsLessThanBound && _boundingState.PostponedMessages.Count == 0 && _boundingState.TaskForInputProcessing == null))
+                {
+                    // Consume the message from the source if necessary
+                    if (consumeToAccept)
+                    {
+                        Debug.Assert(source != null, "We must have thrown if source == null && consumeToAccept == true.");
+
+                        bool consumed;
+                        messageValue = source.ConsumeMessage(messageHeader, this, out consumed);
+                        if (!consumed) return DataflowMessageStatus.NotAvailable;
+                    }
+
+                    // Once consumed, pass it to the source
+                    _source.AddMessage(messageValue);
+                    if (_boundingState != null) _boundingState.CurrentCount++;
+
+                    return DataflowMessageStatus.Accepted;
+                }
+                // Otherwise, we try to postpone if a source was provided
+                else if (source != null)
+                {
+                    Debug.Assert(_boundingState != null && _boundingState.PostponedMessages != null,
+                        "PostponedMessages must have been initialized during construction in bounding mode.");
+
+                    _boundingState.PostponedMessages.Push(source, messageHeader);
+                    return DataflowMessageStatus.Postponed;
+                }
+                // We can't do anything else about this message
+                return DataflowMessageStatus.Declined;
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+        public void Complete() { CompleteCore(exception: null, storeExceptionEvenIfAlreadyCompleting: false); }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            CompleteCore(exception, storeExceptionEvenIfAlreadyCompleting: false);
+        }
+
+        private void CompleteCore(Exception exception, bool storeExceptionEvenIfAlreadyCompleting, bool revertProcessingState = false)
+        {
+            Contract.Requires(storeExceptionEvenIfAlreadyCompleting || !revertProcessingState,
+                            "Indicating dirty processing state may only come with storeExceptionEvenIfAlreadyCompleting==true.");
+            Contract.EndContractBlock();
+
+            lock (IncomingLock)
+            {
+                // Faulting from outside is allowed until we start declining permanently.
+                // Faulting from inside is allowed at any time.
+                if (exception != null && (!_targetDecliningPermanently || storeExceptionEvenIfAlreadyCompleting))
+                {
+                    _source.AddException(exception);
+                }
+
+                // Revert the dirty processing state if requested
+                if (revertProcessingState)
+                {
+                    Debug.Assert(_boundingState != null && _boundingState.TaskForInputProcessing != null,
+                                    "The processing state must be dirty when revertProcessingState==true.");
+                    _boundingState.TaskForInputProcessing = null;
+                }
+
+                // Trigger completion
+                _targetDecliningPermanently = true;
+                CompleteTargetIfPossible();
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+        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 Boolean TryReceive(Predicate<T> filter, out T item) { return _source.TryReceive(filter, out item); }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
+        public Boolean TryReceiveAll(out IList<T> items) { return _source.TryReceiveAll(out items); }
+
+        /// <summary>Gets the number of items currently stored in the buffer.</summary>
+        public Int32 Count { get { return _source.OutputCount; } }
+
+        /// <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/Sources/Member[@name="ConsumeMessage"]/*' />
+        T ISourceBlock<T>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target, out Boolean messageConsumed)
+        {
+            return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+        bool ISourceBlock<T>.ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target)
+        {
+            return _source.ReserveMessage(messageHeader, target);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+        void ISourceBlock<T>.ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<T> target)
+        {
+            _source.ReleaseReservation(messageHeader, target);
+        }
+
+        /// <summary>Notifies the block that one or more items was removed from the queue.</summary>
+        /// <param name="numItemsRemoved">The number of items removed.</param>
+        private void OnItemsRemoved(int numItemsRemoved)
+        {
+            Contract.Requires(numItemsRemoved > 0, "A positive number of items to remove is required.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            // If we're bounding, we need to know when an item is removed so that we
+            // can update the count that's mirroring the actual count in the source's queue,
+            // and potentially kick off processing to start consuming postponed messages.
+            if (_boundingState != null)
+            {
+                lock (IncomingLock)
+                {
+                    // Decrement the count, which mirrors the count in the source half
+                    Debug.Assert(_boundingState.CurrentCount - numItemsRemoved >= 0,
+                        "It should be impossible to have a negative number of items.");
+                    _boundingState.CurrentCount -= numItemsRemoved;
+
+                    ConsumeAsyncIfNecessary();
+                    CompleteTargetIfPossible();
+                }
+            }
+        }
+
+        /// <summary>Called when postponed messages may need to be consumed.</summary>
+        /// <param name="isReplacementReplica">Whether this call is the continuation of a previous message loop.</param>
+        internal void ConsumeAsyncIfNecessary(bool isReplacementReplica = false)
+        {
+            Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+            Debug.Assert(_boundingState != null, "Must be in bounded mode.");
+
+            if (!_targetDecliningPermanently &&
+                _boundingState.TaskForInputProcessing == null &&
+                _boundingState.PostponedMessages.Count > 0 &&
+                _boundingState.CountIsLessThanBound)
+            {
+                // 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,
+                        Common.GetCreationOptionsForTask(isReplacementReplica));
+
+#if FEATURE_TRACING
+                DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+                if (etwLog.IsEnabled())
+                {
+                    etwLog.TaskLaunchedForMessageHandling(
+                        this, _boundingState.TaskForInputProcessing, DataflowEtwProvider.TaskLaunchedReason.ProcessingInputMessages,
+                        _boundingState.PostponedMessages.Count);
+                }
+#endif
+
+                // Start the task handling scheduling exceptions
+                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),
+                                        exception, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+                }
+            }
+        }
+
+
+        /// <summary>Task body used to consume postponed messages.</summary>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private void ConsumeMessagesLoopCore()
+        {
+            Contract.Requires(_boundingState != null && _boundingState.TaskForInputProcessing != null,
+                "May only be called in bounded mode and when a task is in flight.");
+            Debug.Assert(_boundingState.TaskForInputProcessing.Id == Task.CurrentId,
+                "This must only be called from the in-flight processing task.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            try
+            {
+                int maxMessagesPerTask = _source.DataflowBlockOptions.ActualMaxMessagesPerTask;
+                for (int i = 0;
+                    i < maxMessagesPerTask && ConsumeAndStoreOneMessageIfAvailable();
+                    i++)
+                    ;
+            }
+            catch (Exception exc)
+            {
+                // Prevent the creation of new processing tasks
+                CompleteCore(exc, storeExceptionEvenIfAlreadyCompleting: true);
+            }
+            finally
+            {
+                lock (IncomingLock)
+                {
+                    // We're no longer processing, so null out the processing task
+                    _boundingState.TaskForInputProcessing = null;
+
+                    // However, we may have given up early because we hit our own configured
+                    // processing limits rather than because we ran out of work to do.  If that's
+                    // the case, make sure we spin up another task to keep going.
+                    ConsumeAsyncIfNecessary(isReplacementReplica: true);
+
+                    // If, however, we stopped because we ran out of work to do and we
+                    // know we'll never get more, then complete.
+                    CompleteTargetIfPossible();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Retrieves one postponed message if there's room and if we can consume a postponed message.
+        /// Stores any consumed message into the source half.
+        /// </summary>
+        /// <returns>true if a message could be consumed and stored; otherwise, false.</returns>
+        /// <remarks>This must only be called from the asynchronous processing loop.</remarks>
+        private bool ConsumeAndStoreOneMessageIfAvailable()
+        {
+            Contract.Requires(_boundingState != null && _boundingState.TaskForInputProcessing != null,
+                "May only be called in bounded mode and when a task is in flight.");
+            Debug.Assert(_boundingState.TaskForInputProcessing.Id == Task.CurrentId,
+                "This must only be called from the in-flight processing task.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            // Loop through the postponed messages until we get one.
+            while (true)
+            {
+                // Get the next item to retrieve.  If there are no more, bail.
+                KeyValuePair<ISourceBlock<T>, DataflowMessageHeader> sourceAndMessage;
+                lock (IncomingLock)
+                {
+                    if (!_boundingState.CountIsLessThanBound) return false;
+                    if (!_boundingState.PostponedMessages.TryPop(out sourceAndMessage)) return false;
+
+                    // Optimistically assume we're going to get the item. This avoids taking the lock
+                    // again if we're right.  If we're wrong, we decrement it later under lock.
+                    _boundingState.CurrentCount++;
+                }
+
+                // Consume the item
+                bool consumed = false;
+                try
+                {
+                    T consumedValue = sourceAndMessage.Key.ConsumeMessage(sourceAndMessage.Value, this, out consumed);
+                    if (consumed)
+                    {
+                        _source.AddMessage(consumedValue);
+                        return true;
+                    }
+                }
+                finally
+                {
+                    // We didn't get the item, so decrement the count to counteract our optimistic assumption.
+                    if (!consumed)
+                    {
+                        lock (IncomingLock) _boundingState.CurrentCount--;
+                    }
+                }
+            }
+        }
+
+        /// <summary>Completes the target, notifying the source, once all completion conditions are met.</summary>
+        private void CompleteTargetIfPossible()
+        {
+            Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+            if (_targetDecliningPermanently &&
+                !_targetCompletionReserved &&
+                (_boundingState == null || _boundingState.TaskForInputProcessing == null))
+            {
+                _targetCompletionReserved = true;
+
+                // If we're in bounding mode and we have any postponed messages, we need to clear them,
+                // which means calling back to the source, which means we need to escape the incoming lock.
+                if (_boundingState != null && _boundingState.PostponedMessages.Count > 0)
+                {
+                    Task.Factory.StartNew(state =>
+                    {
+                        var thisBufferBlock = (BufferBlock<T>)state;
+
+                        // Release any postponed messages
+                        List<Exception> exceptions = null;
+                        if (thisBufferBlock._boundingState != null)
+                        {
+                            // Note: No locks should be held at this point
+                            Common.ReleaseAllPostponedMessages(thisBufferBlock,
+                                                               thisBufferBlock._boundingState.PostponedMessages,
+                                                               ref exceptions);
+                        }
+
+                        if (exceptions != null)
+                        {
+                            // It is important to migrate these exceptions to the source part of the owning batch,
+                            // because that is the completion task that is publically exposed.
+                            thisBufferBlock._source.AddExceptions(exceptions);
+                        }
+
+                        thisBufferBlock._source.Complete();
+                    }, this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+                }
+                // Otherwise, we can just decline the source directly.
+                else
+                {
+                    _source.Complete();
+                }
+            }
+        }
+
+        /// <summary>Gets the number of messages in the buffer.  This must only be used from the debugger as it avoids taking necessary locks.</summary>
+        private int CountForDebugger { get { return _source.GetDebuggingInformation().OutputCount; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="ToString"]/*' />
+        public override string ToString() { return Common.GetNameForDebugger(this, _source.DataflowBlockOptions); }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                return string.Format("{0}, Count={1}",
+                    Common.GetNameForDebugger(this, _source.DataflowBlockOptions),
+                    CountForDebugger);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for the BufferBlock.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The buffer block.</summary>
+            private readonly BufferBlock<T> _bufferBlock;
+            /// <summary>The buffer's source half.</summary>
+            private readonly SourceCore<T>.DebuggingInformation _sourceDebuggingInformation;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="bufferBlock">The BufferBlock being viewed.</param>
+            public DebugView(BufferBlock<T> bufferBlock)
+            {
+                Contract.Requires(bufferBlock != null, "Need a block with which to construct the debug view.");
+                _bufferBlock = bufferBlock;
+                _sourceDebuggingInformation = bufferBlock._source.GetDebuggingInformation();
+            }
+
+            /// <summary>Gets the collection of postponed message headers.</summary>
+            public QueuedMap<ISourceBlock<T>, DataflowMessageHeader> PostponedMessages
+            {
+                get { return _bufferBlock._boundingState != null ? _bufferBlock._boundingState.PostponedMessages : null; }
+            }
+            /// <summary>Gets the messages in the buffer.</summary>
+            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; } }
+            /// <summary>Gets the task being used for output processing.</summary>
+            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 whether the block is declining further messages.</summary>
+            public bool IsDecliningPermanently { get { return _bufferBlock._targetDecliningPermanently; } }
+            /// <summary>Gets whether the block is completed.</summary>
+            public bool IsCompleted { get { return _sourceDebuggingInformation.IsCompleted; } }
+            /// <summary>Gets the block's Id.</summary>
+            public int Id { get { return Common.GetBlockId(_bufferBlock); } }
+
+            /// <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; } }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/JoinBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/JoinBlock.cs
new file mode 100644 (file)
index 0000000..cbfa49d
--- /dev/null
@@ -0,0 +1,1482 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// JoinBlock.cs
+//
+//
+// Blocks that join multiple messages of different types together into a tuple,
+// with one item per type.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Security;
+using System.Threading.Tasks.Dataflow.Internal;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>
+    /// Provides a dataflow block that joins across multiple dataflow sources, not necessarily of the same type, 
+    /// waiting for one item to arrive for each type before they’re all released together as a tuple of one item per type.
+    /// </summary>
+    /// <typeparam name="T1">Specifies the type of data accepted by the block's first target.</typeparam>
+    /// <typeparam name="T2">Specifies the type of data accepted by the block's second target.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(JoinBlock<,>.DebugView))]
+    public sealed class JoinBlock<T1, T2> : IReceivableSourceBlock<Tuple<T1, T2>>, IDebuggerDisplay
+    {
+        /// <summary>Resources shared by all targets for this join block.</summary>
+        private readonly JoinBlockTargetSharedResources _sharedResources;
+        /// <summary>The source half of this join.</summary>
+        private readonly SourceCore<Tuple<T1, T2>> _source;
+        /// <summary>The first target.</summary>
+        private readonly JoinBlockTarget<T1> _target1;
+        /// <summary>The second target.</summary>
+        private readonly JoinBlockTarget<T2> _target2;
+
+        /// <summary>Initializes the <see cref="JoinBlock{T1,T2}"/>.</summary>
+        public JoinBlock() :
+            this(GroupingDataflowBlockOptions.Default)
+        { }
+
+        /// <summary>Initializes the <see cref="JoinBlock{T1,T2}"/>.</summary>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="JoinBlock{T1,T2}"/>.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        public JoinBlock(GroupingDataflowBlockOptions dataflowBlockOptions)
+        {
+            // Validate arguments
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+            Contract.EndContractBlock();
+
+            // Ensure we have options that can't be changed by the caller
+            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
+
+            // Initialize bounding state if necessary
+            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
+            _source = new SourceCore<Tuple<T1, T2>>(this, dataflowBlockOptions,
+                owningSource => ((JoinBlock<T1, T2>)owningSource)._sharedResources.CompleteEachTarget(),
+                onItemsRemoved);
+
+            // Configure targets
+            var targets = new JoinBlockTargetBase[2];
+            _sharedResources = new JoinBlockTargetSharedResources(this, targets,
+                () =>
+                {
+                    _source.AddMessage(Tuple.Create(_target1.GetOneMessage(), _target2.GetOneMessage()));
+                },
+                exception =>
+                {
+                    Volatile.Write(ref _sharedResources._hasExceptions, true);
+                    _source.AddException(exception);
+                },
+                dataflowBlockOptions);
+            targets[0] = _target1 = new JoinBlockTarget<T1>(_sharedResources);
+            targets[1] = _target2 = new JoinBlockTarget<T2>(_sharedResources);
+
+            // Let the source know when all targets have completed
+            Task.Factory.ContinueWhenAll(
+                new[] { _target1.CompletionTaskInternal, _target2.CompletionTaskInternal },
+                _ => _source.Complete(),
+                CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default);
+
+            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
+            // In those cases we need to fault the target half to drop its buffered messages and to release its 
+            // reservations. This should not create an infinite loop, because all our implementations are designed
+            // to handle multiple completion requests and to carry over only one.
+            _source.Completion.ContinueWith((completed, state) =>
+            {
+                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);
+            }, 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);
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
+            }
+#endif
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+        public IDisposable LinkTo(ITargetBlock<Tuple<T1, T2>> target, DataflowLinkOptions linkOptions)
+        {
+            return _source.LinkTo(target, linkOptions);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
+        public Boolean TryReceive(Predicate<Tuple<T1, T2>> filter, out Tuple<T1, T2> item)
+        {
+            return _source.TryReceive(filter, out item);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public bool TryReceiveAll(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; } }
+
+        /// <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/Blocks/Member[@name="Complete"]/*' />
+        public void Complete()
+        {
+            Debug.Assert(_target1 != null, "_target1 not initialized");
+            Debug.Assert(_target2 != null, "_target2 not initialized");
+
+            _target1.CompleteCore(exception: null, dropPendingMessages: false, releaseReservedMessages: false);
+            _target2.CompleteCore(exception: null, dropPendingMessages: false, releaseReservedMessages: false);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            Debug.Assert(_sharedResources != null, "_sharedResources not initialized");
+            Debug.Assert(_sharedResources._exceptionAction != null, "_sharedResources._exceptionAction not initialized");
+
+            lock (_sharedResources.IncomingLock)
+            {
+                if (!_sharedResources._decliningPermanently) _sharedResources._exceptionAction(exception);
+            }
+
+            Complete();
+        }
+
+        /// <summary>Gets a target that may be used to offer messages of the first type.</summary>
+        public ITargetBlock<T1> Target1 { get { return _target1; } }
+
+        /// <summary>Gets a target that may be used to offer messages of the second type.</summary>
+        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 Boolean messageConsumed)
+        {
+            return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+        bool ISourceBlock<Tuple<T1, T2>>.ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target)
+        {
+            return _source.ReserveMessage(messageHeader, target);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+        void ISourceBlock<Tuple<T1, T2>>.ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target)
+        {
+            _source.ReleaseReservation(messageHeader, target);
+        }
+
+        /// <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 OutputCountForDebugger { get { return _source.GetDebuggingInformation().OutputCount; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="ToString"]/*' />
+        public override string ToString() { return Common.GetNameForDebugger(this, _source.DataflowBlockOptions); }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                return string.Format("{0}, OutputCount={1}",
+                    Common.GetNameForDebugger(this, _source.DataflowBlockOptions),
+                    OutputCountForDebugger);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for the JoinBlock.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The JoinBlock being viewed.</summary>
+            private readonly JoinBlock<T1, T2> _joinBlock;
+            /// <summary>The source half of the block being viewed.</summary>
+            private readonly SourceCore<Tuple<T1, T2>>.DebuggingInformation _sourceDebuggingInformation;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="joinBlock">The JoinBlock being viewed.</param>
+            public DebugView(JoinBlock<T1, T2> joinBlock)
+            {
+                Contract.Requires(joinBlock != null, "Need a block with which to construct the debug view.");
+                _joinBlock = joinBlock;
+                _sourceDebuggingInformation = joinBlock._source.GetDebuggingInformation();
+            }
+
+            /// <summary>Gets the messages waiting to be received.</summary>
+            public IEnumerable<Tuple<T1, T2>> OutputQueue { get { return _sourceDebuggingInformation.OutputQueue; } }
+            /// <summary>Gets the number of joins created thus far.</summary>
+            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; } }
+            /// <summary>Gets the task being used for output processing.</summary>
+            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 whether the block is declining further messages.</summary>
+            public bool IsDecliningPermanently { get { return _joinBlock._sharedResources._decliningPermanently; } }
+            /// <summary>Gets whether the block is completed.</summary>
+            public bool IsCompleted { get { return _sourceDebuggingInformation.IsCompleted; } }
+            /// <summary>Gets the block's Id.</summary>
+            public int Id { get { return Common.GetBlockId(_joinBlock); } }
+
+            /// <summary>Gets the first target.</summary>
+            public ITargetBlock<T1> Target1 { get { return _joinBlock._target1; } }
+            /// <summary>Gets the second target.</summary>
+            public ITargetBlock<T2> Target2 { get { return _joinBlock._target2; } }
+
+            /// <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; } }
+        }
+    }
+
+    /// <summary>
+    /// Provides a dataflow block that joins across multiple dataflow sources, not necessarily of the same type, 
+    /// waiting for one item to arrive for each type before they’re all released together as a tuple of one item per type.
+    /// </summary>
+    /// <typeparam name="T1">Specifies the type of data accepted by the block's first target.</typeparam>
+    /// <typeparam name="T2">Specifies the type of data accepted by the block's second target.</typeparam>
+    /// <typeparam name="T3">Specifies the type of data accepted by the block's third target.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(JoinBlock<,,>.DebugView))]
+    [SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")]
+    public sealed class JoinBlock<T1, T2, T3> : IReceivableSourceBlock<Tuple<T1, T2, T3>>, IDebuggerDisplay
+    {
+        /// <summary>Resources shared by all targets for this join block.</summary>
+        private readonly JoinBlockTargetSharedResources _sharedResources;
+        /// <summary>The source half of this join.</summary>
+        private readonly SourceCore<Tuple<T1, T2, T3>> _source;
+        /// <summary>The first target.</summary>
+        private readonly JoinBlockTarget<T1> _target1;
+        /// <summary>The second target.</summary>
+        private readonly JoinBlockTarget<T2> _target2;
+        /// <summary>The third target.</summary>
+        private readonly JoinBlockTarget<T3> _target3;
+
+        /// <summary>Initializes the <see cref="JoinBlock{T1,T2,T3}"/>.</summary>
+        public JoinBlock() :
+            this(GroupingDataflowBlockOptions.Default)
+        { }
+
+        /// <summary>Initializes the <see cref="JoinBlock{T1,T2,T3}"/>.</summary>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="JoinBlock{T1,T2}"/>.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        public JoinBlock(GroupingDataflowBlockOptions dataflowBlockOptions)
+        {
+            // Validate arguments
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+            Contract.EndContractBlock();
+
+            // Ensure we have options that can't be changed by the caller
+            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
+
+            // Initialize bounding state if necessary
+            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
+            _source = new SourceCore<Tuple<T1, T2, T3>>(this, dataflowBlockOptions,
+                owningSource => ((JoinBlock<T1, T2, T3>)owningSource)._sharedResources.CompleteEachTarget(),
+                onItemsRemoved);
+
+            // Configure the targets
+            var targets = new JoinBlockTargetBase[3];
+            _sharedResources = new JoinBlockTargetSharedResources(this, targets,
+                () => _source.AddMessage(Tuple.Create(_target1.GetOneMessage(), _target2.GetOneMessage(), _target3.GetOneMessage())),
+                exception =>
+                {
+                    Volatile.Write(ref _sharedResources._hasExceptions, true);
+                    _source.AddException(exception);
+                },
+                dataflowBlockOptions);
+            targets[0] = _target1 = new JoinBlockTarget<T1>(_sharedResources);
+            targets[1] = _target2 = new JoinBlockTarget<T2>(_sharedResources);
+            targets[2] = _target3 = new JoinBlockTarget<T3>(_sharedResources);
+
+            // Let the source know when all targets have completed
+            Task.Factory.ContinueWhenAll(
+                new[] { _target1.CompletionTaskInternal, _target2.CompletionTaskInternal, _target3.CompletionTaskInternal },
+                _ => _source.Complete(),
+                CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default);
+
+            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
+            // In those cases we need to fault the target half to drop its buffered messages and to release its 
+            // reservations. This should not create an infinite loop, because all our implementations are designed
+            // 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;
+                Debug.Assert(completed.IsFaulted, "The source must be faulted in order to trigger a target completion.");
+                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);
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
+            }
+#endif
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+        public IDisposable LinkTo(ITargetBlock<Tuple<T1, T2, T3>> target, DataflowLinkOptions linkOptions)
+        {
+            return _source.LinkTo(target, linkOptions);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
+        public Boolean TryReceive(Predicate<Tuple<T1, T2, T3>> filter, out Tuple<T1, T2, T3> item)
+        {
+            return _source.TryReceive(filter, out item);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public bool TryReceiveAll(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; } }
+
+        /// <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/Blocks/Member[@name="Complete"]/*' />
+        public void Complete()
+        {
+            Debug.Assert(_target1 != null, "_target1 not initialized");
+            Debug.Assert(_target2 != null, "_target2 not initialized");
+            Debug.Assert(_target3 != null, "_target3 not initialized");
+
+            _target1.CompleteCore(exception: null, dropPendingMessages: false, releaseReservedMessages: false);
+            _target2.CompleteCore(exception: null, dropPendingMessages: false, releaseReservedMessages: false);
+            _target3.CompleteCore(exception: null, dropPendingMessages: false, releaseReservedMessages: false);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            Debug.Assert(_sharedResources != null, "_sharedResources not initialized");
+            Debug.Assert(_sharedResources._exceptionAction != null, "_sharedResources._exceptionAction not initialized");
+
+            lock (_sharedResources.IncomingLock)
+            {
+                if (!_sharedResources._decliningPermanently) _sharedResources._exceptionAction(exception);
+            }
+
+            Complete();
+        }
+
+        /// <summary>Gets a target that may be used to offer messages of the first type.</summary>
+        public ITargetBlock<T1> Target1 { get { return _target1; } }
+
+        /// <summary>Gets a target that may be used to offer messages of the second type.</summary>
+        public ITargetBlock<T2> Target2 { get { return _target2; } }
+
+        /// <summary>Gets a target that may be used to offer messages of the third type.</summary>
+        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 Boolean messageConsumed)
+        {
+            return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+        bool ISourceBlock<Tuple<T1, T2, T3>>.ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2, T3>> target)
+        {
+            return _source.ReserveMessage(messageHeader, target);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+        void ISourceBlock<Tuple<T1, T2, T3>>.ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2, T3>> target)
+        {
+            _source.ReleaseReservation(messageHeader, target);
+        }
+
+        /// <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 OutputCountForDebugger { get { return _source.GetDebuggingInformation().OutputCount; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="ToString"]/*' />
+        public override string ToString() { return Common.GetNameForDebugger(this, _source.DataflowBlockOptions); }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                return string.Format("{0} OutputCount={1}",
+                    Common.GetNameForDebugger(this, _source.DataflowBlockOptions),
+                    OutputCountForDebugger);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for the Batch.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The JoinBlock being viewed.</summary>
+            private readonly JoinBlock<T1, T2, T3> _joinBlock;
+            /// <summary>The source half of the block being viewed.</summary>
+            private readonly SourceCore<Tuple<T1, T2, T3>>.DebuggingInformation _sourceDebuggingInformation;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="joinBlock">The JoinBlock being viewed.</param>
+            public DebugView(JoinBlock<T1, T2, T3> joinBlock)
+            {
+                Contract.Requires(joinBlock != null, "Need a block with which to construct the debug view.");
+                _joinBlock = joinBlock;
+                _sourceDebuggingInformation = joinBlock._source.GetDebuggingInformation();
+            }
+
+            /// <summary>Gets the messages waiting to be received.</summary>
+            public IEnumerable<Tuple<T1, T2, T3>> OutputQueue { get { return _sourceDebuggingInformation.OutputQueue; } }
+            /// <summary>Gets the number of joins created thus far.</summary>
+            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; } }
+            /// <summary>Gets the task being used for output processing.</summary>
+            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 whether the block is declining further messages.</summary>
+            public bool IsDecliningPermanently { get { return _joinBlock._sharedResources._decliningPermanently; } }
+            /// <summary>Gets whether the block is completed.</summary>
+            public bool IsCompleted { get { return _sourceDebuggingInformation.IsCompleted; } }
+            /// <summary>Gets the block's Id.</summary>
+            public int Id { get { return Common.GetBlockId(_joinBlock); } }
+
+            /// <summary>Gets the first target.</summary>
+            public ITargetBlock<T1> Target1 { get { return _joinBlock._target1; } }
+            /// <summary>Gets the second target.</summary>
+            public ITargetBlock<T2> Target2 { get { return _joinBlock._target2; } }
+            /// <summary>Gets the third target.</summary>
+            public ITargetBlock<T3> Target3 { get { return _joinBlock._target3; } }
+
+            /// <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; } }
+        }
+    }
+}
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    /// <summary>Provides the target used in a Join.</summary>
+    /// <typeparam name="T">Specifies the type of data accepted by this target.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(JoinBlockTarget<>.DebugView))]
+    internal sealed class JoinBlockTarget<T> : JoinBlockTargetBase, ITargetBlock<T>, IDebuggerDisplay
+    {
+        /// <summary>The shared resources used by all targets associated with the same join instance.</summary>
+        private readonly JoinBlockTargetSharedResources _sharedResources;
+        /// <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;
+        /// <summary>State used when in non-greedy mode.</summary>
+        private readonly NonGreedyState _nonGreedy;
+        /// <summary>Whether this target is declining future messages.</summary>
+        private bool _decliningPermanently;
+
+        /// <summary>State used only when in non-greedy mode.</summary>
+        private sealed class NonGreedyState
+        {
+            /// <summary>Collection of the last postponed message per source.</summary>
+            internal readonly QueuedMap<ISourceBlock<T>, DataflowMessageHeader> PostponedMessages = new QueuedMap<ISourceBlock<T>, DataflowMessageHeader>();
+            /// <summary>The currently reserved message.</summary>
+            internal KeyValuePair<ISourceBlock<T>, DataflowMessageHeader> ReservedMessage;
+            /// <summary>The currently consumed message.</summary>
+            internal KeyValuePair<bool, T> ConsumedMessage;
+        }
+
+        /// <summary>Initializes the target.</summary>
+        /// <param name="sharedResources">The shared resources used by all targets associated with this join.</param>
+        internal JoinBlockTarget(JoinBlockTargetSharedResources sharedResources)
+        {
+            Contract.Requires(sharedResources != null, "Targets need shared resources through which to communicate.");
+
+            // Store arguments and initialize configuration
+            GroupingDataflowBlockOptions dbo = sharedResources._dataflowBlockOptions;
+            _sharedResources = sharedResources;
+            if (!dbo.Greedy || dbo.BoundedCapacity > 0) _nonGreedy = new NonGreedyState();
+            if (dbo.Greedy) _messages = new Queue<T>();
+        }
+
+        /// <summary>Gets a message buffered by this target.</summary>
+        /// <remarks>This must be called while holding the shared Resources's incoming lock.</remarks>
+        internal T GetOneMessage()
+        {
+            Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: true);
+            if (_sharedResources._dataflowBlockOptions.Greedy)
+            {
+                Debug.Assert(_messages != null, "_messages must have been initialized in greedy mode");
+                Debug.Assert(_messages.Count >= 0, "A message must have been consumed by this point.");
+                return _messages.Dequeue();
+            }
+            else
+            {
+                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));
+                return value;
+            }
+        }
+
+        /// <summary>Gets whether the target is declining messages.</summary>
+        internal override bool IsDecliningPermanently
+        {
+            get
+            {
+                Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: true);
+                return _decliningPermanently;
+            }
+        }
+
+        /// <summary>Gets whether the target has at least one message available.</summary>
+        internal override bool HasAtLeastOneMessageAvailable
+        {
+            get
+            {
+                Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: true);
+                if (_sharedResources._dataflowBlockOptions.Greedy)
+                {
+                    Debug.Assert(_messages != null, "_messages must have been initialized in greedy mode");
+                    return _messages.Count > 0;
+                }
+                else
+                {
+                    return _nonGreedy.ConsumedMessage.Key;
+                }
+            }
+        }
+
+        /// <summary>Gets whether the target has at least one postponed message.</summary>
+        internal override bool HasAtLeastOnePostponedMessage
+        {
+            get
+            {
+                Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: true);
+                return _nonGreedy != null && _nonGreedy.PostponedMessages.Count > 0;
+            }
+        }
+
+        /// <summary>Gets the number of messages available or postponed.</summary>
+        internal override int NumberOfMessagesAvailableOrPostponed
+        {
+            get
+            {
+                Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: true);
+                return !_sharedResources._dataflowBlockOptions.Greedy ? _nonGreedy.PostponedMessages.Count : _messages.Count;
+            }
+        }
+
+        /// <summary>Gets whether this target has the highest number of available/buffered messages. This is only valid in greedy mode.</summary>
+        internal override bool HasTheHighestNumberOfMessagesAvailable
+        {
+            get
+            {
+                Debug.Assert(_sharedResources._dataflowBlockOptions.Greedy, "This is only valid in greedy mode");
+                Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: true);
+
+                // Note: If there is a tie, we must return true
+                int count = _messages.Count;
+                foreach (JoinBlockTargetBase target in _sharedResources._targets)
+                    if (target != this && target.NumberOfMessagesAvailableOrPostponed > count) 
+                        return false; // Strictly bigger!
+                return true;
+            }
+        }
+
+        /// <summary>Reserves one of the postponed messages.</summary>
+        /// <returns>true if a message was reserved; otherwise, false.</returns>
+        internal override bool ReserveOneMessage()
+        {
+            Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: false);
+            Debug.Assert(!_sharedResources._dataflowBlockOptions.Greedy, "This is only used in non-greedy mode");
+
+            KeyValuePair<ISourceBlock<T>, DataflowMessageHeader> next;
+
+            lock (_sharedResources.IncomingLock)
+            {
+                // The queue must be empty between joins in non-greedy mode
+                Debug.Assert(!HasAtLeastOneMessageAvailable, "The queue must be empty between joins in non-greedy mode");
+
+                // 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;
+            }
+
+            // We'll bail out of this loop either when we have reserved a message (true)
+            // or when we have exhausted the list of postponed messages (false)
+            for (; ;)
+            {
+                // Try to reserve the popped message
+                if (next.Key.ReserveMessage(next.Value, this))
+                {
+                    _nonGreedy.ReservedMessage = next;
+                    return true;
+                }
+
+                // We could not reserve that message.
+                // Try to pop another postponed message and continue looping.
+                lock (_sharedResources.IncomingLock)
+                {
+                    // If there are no postponed messages, we can't do anything
+                    if (!_nonGreedy.PostponedMessages.TryPop(out next)) return false;
+                }
+            }
+        }
+
+        /// <summary>Consumes a reserved message.</summary>
+        /// <returns>true if a message was consumed; otherwise, false.</returns>
+        internal override bool ConsumeReservedMessage()
+        {
+            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");
+
+            bool consumed;
+            T consumedValue = _nonGreedy.ReservedMessage.Key.ConsumeMessage(_nonGreedy.ReservedMessage.Value, this, out consumed);
+
+            // Null out our reservation
+            _nonGreedy.ReservedMessage = default(KeyValuePair<ISourceBlock<T>, DataflowMessageHeader>);
+
+            // The protocol requires that a reserved message must be consumable,
+            // but it is possible that the source may misbehave. 
+            // In that case complete the target and signal to the owning block to shut down gracefully.
+            if (!consumed)
+            {
+                _sharedResources._exceptionAction(new InvalidOperationException(SR.InvalidOperation_FailedToConsumeReservedMessage));
+
+                // Complete this target, which will trigger completion of the owning join block.
+                CompleteOncePossible();
+
+                // We need to signal to the caller to stop consuming immediately
+                return false;
+            }
+            else
+            {
+                lock (_sharedResources.IncomingLock)
+                {
+                    // 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);
+                    // We don't account bounding per target in non-greedy mode. We do it once per batch (in the loop).
+
+                    CompleteIfLastJoinIsFeasible();
+                }
+            }
+
+            return true;
+        }
+
+        /// <summary>Consumes up to one postponed message in greedy bounded mode.</summary>
+        internal override bool ConsumeOnePostponedMessage()
+        {
+            Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: false);
+            Debug.Assert(_sharedResources._dataflowBlockOptions.Greedy, "This is only used in greedy mode");
+            Debug.Assert(_sharedResources._boundingState != null, "This is only used in bounding mode");
+
+            // We'll bail out of this loop either when we have consumed a message (true)
+            // or when we have exhausted the list of postponed messages (false)
+            while (true)
+            {
+                KeyValuePair<ISourceBlock<T>, DataflowMessageHeader> next;
+                bool hasTheHighestNumberOfMessagesAvailable;
+
+                lock (_sharedResources.IncomingLock)
+                {
+                    // While we are holding the lock, check bounding capacity and try to pop a postponed message.
+                    // If anything fails, we can't do anything.
+                    hasTheHighestNumberOfMessagesAvailable = HasTheHighestNumberOfMessagesAvailable;
+                    bool boundingCapacityAvailable = _sharedResources._boundingState.CountIsLessThanBound || !hasTheHighestNumberOfMessagesAvailable;
+                    if (_decliningPermanently || _sharedResources._decliningPermanently ||
+                        !boundingCapacityAvailable || !_nonGreedy.PostponedMessages.TryPop(out next))
+                        return false;
+                }
+
+                // Try to consume the popped message
+                bool consumed;
+                T consumedValue = next.Key.ConsumeMessage(next.Value, this, out consumed);
+                if (consumed)
+                {
+                    lock (_sharedResources.IncomingLock)
+                    {
+                        // 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);
+
+                        CompleteIfLastJoinIsFeasible();
+                        return true;
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Start declining if the number of joins we've already made plus the number we can 
+        /// make from data already enqueued meets our quota.
+        /// </summary>
+        private void CompleteIfLastJoinIsFeasible()
+        {
+            Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: true);
+            int messageCount = _sharedResources._dataflowBlockOptions.Greedy ?
+                                    _messages.Count :
+                                    _nonGreedy.ConsumedMessage.Key ? 1 : 0;
+            if ((_sharedResources._joinsCreated + messageCount) >= _sharedResources._dataflowBlockOptions.ActualMaxNumberOfGroups)
+            {
+                _decliningPermanently = true;
+
+                bool allAreDecliningPermanently = true;
+                foreach (JoinBlockTargetBase target in _sharedResources._targets)
+                {
+                    if (!target.IsDecliningPermanently)
+                    {
+                        allAreDecliningPermanently = false;
+                        break;
+                    }
+                }
+                if (allAreDecliningPermanently) _sharedResources._decliningPermanently = true;
+            }
+        }
+
+        /// <summary>Releases the reservation on a reserved message.</summary>
+        internal override void ReleaseReservedMessage()
+        {
+            Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: false);
+
+            // Release only if we have a reserved message.
+            // Otherwise do nothing.
+            if (_nonGreedy != null && _nonGreedy.ReservedMessage.Key != null)
+            {
+                // Release the reservation and null out our reservation flag even if an exception occurs
+                try { _nonGreedy.ReservedMessage.Key.ReleaseReservation(_nonGreedy.ReservedMessage.Value, this); }
+                finally { ClearReservation(); }
+            }
+        }
+
+        /// <summary>Unconditionally clears a reserved message.</summary>
+        internal override void ClearReservation()
+        {
+            Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: false);
+            Debug.Assert(_nonGreedy != null, "Only valid in non-greedy mode.");
+
+            _nonGreedy.ReservedMessage = default(KeyValuePair<ISourceBlock<T>, DataflowMessageHeader>);
+        }
+
+        /// <summary>Completes the target.</summary>
+        internal override void CompleteOncePossible()
+        {
+            Common.ContractAssertMonitorStatus(_sharedResources.IncomingLock, held: false);
+
+            // This target must not have an outstanding reservation
+            Debug.Assert(_nonGreedy == null || _nonGreedy.ReservedMessage.Key == null,
+                "Must be in greedy mode, or in non-greedy mode but without any reserved messages.");
+
+            // Clean up any messages that may be stragglers left behind
+            lock (_sharedResources.IncomingLock)
+            {
+                _decliningPermanently = true;
+                if (_messages != null) _messages.Clear();
+            }
+
+            // Release any postponed messages
+            List<Exception> exceptions = null;
+            if (_nonGreedy != null)
+            {
+                // Note: No locks should be held at this point
+                Common.ReleaseAllPostponedMessages(this, _nonGreedy.PostponedMessages, ref exceptions);
+            }
+
+            if (exceptions != null)
+            {
+                // It is important to migrate these exceptions to the source part of the owning join,
+                // because that is the completion task that is publically exposed.
+                foreach (Exception exc in exceptions)
+                {
+                    _sharedResources._exceptionAction(exc);
+                }
+            }
+
+            // Targets' completion tasks are only available internally with the sole purpose
+            // of releasing the task that completes the parent. Hence the actual reason
+            // for completing this task doesn't matter.
+            _completionTask.TrySetResult(default(VoidResult));
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
+        DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, Boolean consumeToAccept)
+        {
+            // Validate arguments
+            if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            if (source == null && consumeToAccept) throw new ArgumentException(SR.Argument_CantConsumeFromANullSource, "consumeToAccept");
+            Contract.EndContractBlock();
+
+            lock (_sharedResources.IncomingLock)
+            {
+                // If we shouldn't be accepting more messages, don't.
+                if (_decliningPermanently || _sharedResources._decliningPermanently)
+                {
+                    _sharedResources.CompleteBlockIfPossible();
+                    return DataflowMessageStatus.DecliningPermanently;
+                }
+
+                // We can directly accept the message if:
+                //      1) we are being greedy AND we are not bounding, OR 
+                //      2) we are being greedy AND we are bounding AND there is room available AND there are no postponed messages AND we are not currently processing. 
+                // (If there were any postponed messages, we would need to postpone so that ordering would be maintained.)
+                // (We should also postpone if we are currently processing, because there may be a race between consuming postponed messages and
+                // accepting new ones directly into the queue.)
+                if (_sharedResources._dataflowBlockOptions.Greedy &&
+                        (_sharedResources._boundingState == null
+                            ||
+                         ((_sharedResources._boundingState.CountIsLessThanBound || !HasTheHighestNumberOfMessagesAvailable) &&
+                          _nonGreedy.PostponedMessages.Count == 0 && _sharedResources._taskForInputProcessing == null)))
+                {
+                    if (consumeToAccept)
+                    {
+                        Debug.Assert(source != null, "We must have thrown if source == null && consumeToAccept == true.");
+
+                        bool consumed;
+                        messageValue = source.ConsumeMessage(messageHeader, this, out consumed);
+                        if (!consumed) return DataflowMessageStatus.NotAvailable;
+                    }
+                    if (_sharedResources._boundingState != null && HasTheHighestNumberOfMessagesAvailable) _sharedResources._boundingState.CurrentCount += 1; // track this new item against our bound
+                    _messages.Enqueue(messageValue);
+                    CompleteIfLastJoinIsFeasible();
+
+                    // Since we're in greedy mode, we can skip asynchronous processing and 
+                    // make joins aggressively based on enqueued data. 
+                    if (_sharedResources.AllTargetsHaveAtLeastOneMessage)
+                    {
+                        _sharedResources._joinFilledAction();
+                        _sharedResources._joinsCreated++;
+                    }
+
+                    _sharedResources.CompleteBlockIfPossible();
+                    return DataflowMessageStatus.Accepted;
+                }
+                // Otherwise, we try to postpone if a source was provided
+                else if (source != null)
+                {
+                    Debug.Assert(_nonGreedy != null, "_nonGreedy must have been initialized during construction in non-greedy mode.");
+
+                    // Postpone the message now and kick off an async two-phase consumption.
+                    _nonGreedy.PostponedMessages.Push(source, messageHeader);
+                    _sharedResources.ProcessAsyncIfNecessary();
+                    return DataflowMessageStatus.Postponed;
+                }
+                // We can't do anything else about this message
+                return DataflowMessageStatus.Declined;
+            }
+        }
+
+        /// <summary>Completes/faults the block.
+        /// 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>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        internal override void CompleteCore(Exception exception, bool dropPendingMessages, bool releaseReservedMessages)
+        {
+            bool greedy = _sharedResources._dataflowBlockOptions.Greedy;
+            lock (_sharedResources.IncomingLock)
+            {
+                // Faulting from outside is allowed until we start declining permanently.
+                // Faulting from inside is allowed at any time.
+                if (exception != null && ((!_decliningPermanently && !_sharedResources._decliningPermanently) || releaseReservedMessages))
+                {
+                    _sharedResources._exceptionAction(exception);
+                }
+
+                // Drop pending messages if requested
+                if (dropPendingMessages && greedy)
+                {
+                    Debug.Assert(_messages != null, "_messages must be initialized in greedy mode.");
+                    _messages.Clear();
+                }
+            }
+
+            // Release reserved messages if requested.
+            // This must be done from outside the lock.
+            if (releaseReservedMessages && !greedy)
+            {
+                // Do this on all targets
+                foreach (JoinBlockTargetBase target in _sharedResources._targets)
+                {
+                    try { target.ReleaseReservedMessage(); }
+                    catch (Exception e) { _sharedResources._exceptionAction(e); }
+                }
+            }
+
+            // Triggering completion requires the lock
+            lock (_sharedResources.IncomingLock)
+            {
+                // Trigger completion
+                _decliningPermanently = true;
+                _sharedResources.CompleteBlockIfPossible();
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            CompleteCore(exception, dropPendingMessages: true, releaseReservedMessages: false);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+        public Task Completion { get { throw new NotSupportedException(SR.NotSupported_MemberNotNeeded); } }
+        /// <summary>The completion task on Join targets is only hidden from the public. It still exists for internal purposes.</summary>
+        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; } }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                var displayJoin = _sharedResources._ownerJoin as IDebuggerDisplay;
+                return string.Format("{0} InputCount={1}, Join=\"{2}\"",
+                    Common.GetNameForDebugger(this),
+                    InputCountForDebugger,
+                    displayJoin != null ? displayJoin.Content : _sharedResources._ownerJoin);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for the Transform.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The join block target being viewed.</summary>
+            private readonly JoinBlockTarget<T> _joinBlockTarget;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="joinBlockTarget">The join being viewed.</param>
+            public DebugView(JoinBlockTarget<T> joinBlockTarget)
+            {
+                Contract.Requires(joinBlockTarget != null, "Need a target with which to construct the debug view.");
+                _joinBlockTarget = joinBlockTarget;
+            }
+
+            /// <summary>Gets the messages waiting to be processed.</summary>
+            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>Provides a non-generic base type for all join targets.</summary>
+    internal abstract class JoinBlockTargetBase
+    {
+        /// <summary>Whether the target is postponing messages.</summary>
+        internal abstract bool IsDecliningPermanently { get; }
+        /// <summary>Whether the target has at least one message available.</summary>
+        internal abstract bool HasAtLeastOneMessageAvailable { get; }
+        /// <summary>Whether the target has at least one message postponed.</summary>
+        internal abstract bool HasAtLeastOnePostponedMessage { get; }
+        /// <summary>Gets the number of messages available or postponed.</summary>
+        internal abstract int NumberOfMessagesAvailableOrPostponed { get; }
+        /// <summary>Gets whether the target has the highest number of messages available. (A tie yields true.)</summary>
+        internal abstract bool HasTheHighestNumberOfMessagesAvailable { get; }
+
+        /// <summary>Reserves a single message.</summary>
+        /// <returns>Whether a message was reserved.</returns>
+        internal abstract bool ReserveOneMessage();
+        /// <summary>Consumes any previously reserved message.</summary>
+        /// <returns>Whether a message was consumed.</returns>
+        internal abstract bool ConsumeReservedMessage();
+        /// <summary>Consumes up to one postponed message in greedy bounded mode.</summary>
+        /// <returns>Whether a message was consumed.</returns>
+        internal abstract bool ConsumeOnePostponedMessage();
+        /// <summary>Releases any previously reserved message.</summary>
+        internal abstract void ReleaseReservedMessage();
+        /// <summary>Unconditionally clears a reserved message. This is only invoked in case of an exception.</summary>
+        internal abstract void ClearReservation();
+
+        /// <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);
+        /// <summary>Completes the target.</summary>
+        internal abstract void CompleteOncePossible();
+    }
+
+    /// <summary>Provides a container for resources shared across all targets used by the same BatchedJoin instance.</summary>
+    [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    internal sealed class JoinBlockTargetSharedResources
+    {
+        /// <summary>Initializes the shared resources.</summary>
+        /// <param name="ownerJoin">
+        /// The join block that owns these shared resources.
+        /// </param>
+        /// <param name="targets">
+        /// The array of targets associated with the join. Also doubles as the sync object used to synchronize targets.
+        /// </param>
+        /// <param name="joinFilledAction">The delegate to invoke when enough messages have been consumed to fulfill the join.</param>
+        /// <param name="exceptionAction">The delegate to invoke when a target encounters an error.</param>
+        /// <param name="dataflowBlockOptions">The options for the join.</param>
+        internal JoinBlockTargetSharedResources(
+            IDataflowBlock ownerJoin, JoinBlockTargetBase[] targets,
+            Action joinFilledAction, Action<Exception> exceptionAction,
+            GroupingDataflowBlockOptions dataflowBlockOptions)
+        {
+            Contract.Requires(ownerJoin != null, "Resources must be associated with a join.");
+            Contract.Requires(targets != null, "Resources must be shared between multiple targets.");
+            Contract.Requires(joinFilledAction != null, "An action to invoke when a join is created must be provided.");
+            Contract.Requires(exceptionAction != null, "An action to invoke for faults must be provided.");
+            Contract.Requires(dataflowBlockOptions != null, "Options must be provided to configure the resources.");
+
+            // Store arguments
+            _ownerJoin = ownerJoin;
+            _targets = targets;
+            _joinFilledAction = joinFilledAction;
+            _exceptionAction = exceptionAction;
+            _dataflowBlockOptions = dataflowBlockOptions;
+
+            // Initialize bounding state if necessary
+            if (dataflowBlockOptions.BoundedCapacity > 0) _boundingState = new BoundingState(dataflowBlockOptions.BoundedCapacity);
+        }
+
+        // *** Accessible fields and properties
+        internal readonly IDataflowBlock _ownerJoin;
+        /// <summary>All of the targets associated with the join.</summary>
+        internal readonly JoinBlockTargetBase[] _targets;
+        /// <summary>The delegate to invoke when a target encounters an error.</summary>
+        internal readonly Action<Exception> _exceptionAction;
+        /// <summary>The delegate to invoke when enough messages have been consumed to fulfill the join.</summary>
+        internal readonly Action _joinFilledAction;
+        /// <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;
+        /// <summary>Whether all targets should decline all further messages.</summary>
+        internal bool _decliningPermanently;
+        /// <summary>The task used to process messages.</summary>
+        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>
+        internal long _joinsCreated;
+
+        // *** Private fields and properties - mutatable
+        /// <summary>A task has reserved the right to run the completion routine.</summary>
+        private bool _completionReserved;
+
+        /// <summary>Gets the lock used to synchronize all incoming messages on all targets.</summary>
+        internal object IncomingLock { get { return _targets; } }
+
+        /// <summary>Invokes Complete on each target with dropping buffered messages.</summary>
+        internal void CompleteEachTarget()
+        {
+            foreach (JoinBlockTargetBase target in _targets)
+            {
+                target.CompleteCore(exception: null, dropPendingMessages: true, releaseReservedMessages: false);
+            }
+        }
+
+        /// <summary>Gets whether all of the targets have at least one message in their queues.</summary>
+        internal bool AllTargetsHaveAtLeastOneMessage
+        {
+            get
+            {
+                Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+                foreach (JoinBlockTargetBase target in _targets)
+                {
+                    if (!target.HasAtLeastOneMessageAvailable) return false;
+                }
+                return true;
+            }
+        }
+
+        /// <summary>Gets whether all of the targets have at least one message in their queues or have at least one postponed message.</summary>
+        private bool TargetsHaveAtLeastOneMessageQueuedOrPostponed
+        {
+            get
+            {
+                Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+                if (_boundingState == null)
+                {
+                    foreach (JoinBlockTargetBase target in _targets)
+                    {
+                        if (!target.HasAtLeastOneMessageAvailable &&
+                            (_decliningPermanently || target.IsDecliningPermanently || !target.HasAtLeastOnePostponedMessage))
+                            return false;
+                    }
+                    return true;
+                }
+                else
+                {
+                    // Cache the availability state so we don't evaluate it multiple times
+                    bool boundingCapacityAvailable = _boundingState.CountIsLessThanBound;
+
+                    // In bounding mode, we have more complex rules whether we should process input messages:
+                    //      1) In greedy mode if a target has postponed messages and there is bounding capacity
+                    //         available, then we should greedily consume messages up to the bounding capacity
+                    //         even if that doesn't lead to an output join.
+                    //      2) The ability to make join depends on: 
+                    //          2a) message availability for each target, AND
+                    //          2b) availability of bounding space
+
+                    bool joinIsPossible = true;
+                    bool joinWillNotAffectBoundingCount = false;
+                    foreach (JoinBlockTargetBase target in _targets)
+                    {
+                        bool targetCanConsumePostponedMessages = !_decliningPermanently && !target.IsDecliningPermanently && target.HasAtLeastOnePostponedMessage;
+
+                        // Rule #1
+                        if (_dataflowBlockOptions.Greedy && targetCanConsumePostponedMessages && (boundingCapacityAvailable || !target.HasTheHighestNumberOfMessagesAvailable)) return true;
+
+                        // Rule #2a
+                        bool targetHasMessagesAvailable = target.HasAtLeastOneMessageAvailable;
+                        joinIsPossible &= targetHasMessagesAvailable || targetCanConsumePostponedMessages;
+
+                        // Rule #2b
+                        // If there is a target that has at least one queued message, bounding space availability
+                        // is no longer an issue, because 1 item from the input side will be replaced with 1
+                        // item on the output side.
+                        if (targetHasMessagesAvailable) joinWillNotAffectBoundingCount = true;
+                    }
+
+                    // Rule #2
+                    return joinIsPossible && (joinWillNotAffectBoundingCount || boundingCapacityAvailable);
+                }
+            }
+        }
+
+        /// <summary>Retrieves postponed items if we have enough to make a batch.</summary>
+        /// <returns>true if input messages for a batch were consumed (all or none); false otherwise.</returns>
+        private bool RetrievePostponedItemsNonGreedy()
+        {
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            // If there are not enough postponed items, we have nothing to do.
+            lock (IncomingLock)
+            {
+                if (!TargetsHaveAtLeastOneMessageQueuedOrPostponed) return false;
+            } // Release the lock.  We must not hold it while calling Reserve/Consume/Release.
+
+            // Try to reserve a postponed message on every target that doesn't already have messages available
+            bool reservedAll = true;
+            foreach (JoinBlockTargetBase target in _targets)
+            {
+                if (!target.ReserveOneMessage())
+                {
+                    reservedAll = false;
+                    break;
+                }
+            }
+
+            // If we were able to, consume them all and place the consumed messages into each's queue
+            if (reservedAll)
+            {
+                foreach (JoinBlockTargetBase target in _targets)
+                {
+                    // If we couldn't consume a message, release reservations wherever possible 
+                    if (!target.ConsumeReservedMessage())
+                    {
+                        reservedAll = false;
+                        break;
+                    }
+                }
+            }
+
+            // If we were unable to reserve all messages, release the reservations
+            if (!reservedAll)
+            {
+                foreach (JoinBlockTargetBase target in _targets)
+                {
+                    target.ReleaseReservedMessage();
+                }
+            }
+
+            return reservedAll;
+        }
+
+        /// <summary>Retrieves up to one postponed item through each target.</summary>
+        /// <returns>true if at least one input message was consumed (through any target); false otherwise.</returns>
+        private bool RetrievePostponedItemsGreedyBounded()
+        {
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            // Try to consume a postponed message through each target as possible
+            bool consumed = false;
+            foreach (JoinBlockTargetBase target in _targets)
+            {
+                // It is sufficient to consume through one target to consider we've made progress
+                consumed |= target.ConsumeOnePostponedMessage();
+            }
+
+            return consumed;
+        }
+
+        /// <summary>Gets whether the target has had cancellation requested or an exception has occurred.</summary>
+        private bool CanceledOrFaulted
+        {
+            get
+            {
+                Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+                return _dataflowBlockOptions.CancellationToken.IsCancellationRequested || _hasExceptions;
+            }
+        }
+
+        /// <summary>
+        /// Gets whether the join is in a state where processing can be done, meaning there's data
+        /// to be processed and the block is in a state where such processing is allowed.
+        /// </summary>
+        internal bool JoinNeedsProcessing
+        {
+            get
+            {
+                Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+                return
+                    _taskForInputProcessing == null && // not currently processing asynchronously
+                    !CanceledOrFaulted && // not canceled or faulted
+                    TargetsHaveAtLeastOneMessageQueuedOrPostponed; // all targets have work queued or postponed
+            }
+        }
+
+        /// <summary>Called when new messages are available to be processed.</summary>
+        /// <param name="isReplacementReplica">Whether this call is the continuation of a previous message loop.</param>
+        internal void ProcessAsyncIfNecessary(bool isReplacementReplica = false)
+        {
+            Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+            if (JoinNeedsProcessing)
+            {
+                ProcessAsyncIfNecessary_Slow(isReplacementReplica);
+            }
+        }
+
+        /// <summary>
+        /// Slow path for ProcessAsyncIfNecessary. 
+        /// Separating out the slow path into its own method makes it more likely that the fast path method will get inlined.
+        /// </summary>
+        private void ProcessAsyncIfNecessary_Slow(bool isReplacementReplica)
+        {
+            Contract.Requires(JoinNeedsProcessing, "There must be a join that needs processing.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+            // 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,
+                                                Common.GetCreationOptionsForTask(isReplacementReplica));
+
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.TaskLaunchedForMessageHandling(
+                    _ownerJoin, _taskForInputProcessing, DataflowEtwProvider.TaskLaunchedReason.ProcessingInputMessages,
+                    _targets.Max(t => t.NumberOfMessagesAvailableOrPostponed));
+            }
+#endif
+
+            // Start the task handling scheduling exceptions
+            Exception exception = Common.StartTaskSafe(_taskForInputProcessing, _dataflowBlockOptions.TaskScheduler);
+            if (exception != null)
+            {
+                // All of the following actions must be performed under the lock. 
+                // So do them now while the lock is being held.
+
+                // First, log the exception while the processing state is dirty which is preventing the block from completing.
+                // Then revert the proactive processing state changes.
+                // And last, try to complete the block.
+                _exceptionAction(exception);
+                _taskForInputProcessing = null;
+                CompleteBlockIfPossible();
+            }
+        }
+
+        /// <summary>Completes the join block if possible.</summary>
+        internal void CompleteBlockIfPossible()
+        {
+            Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+            if (!_completionReserved)
+            {
+                // Check whether we're sure we'll never be able to fill another join.
+                // That could happen if we're not accepting more messages and not all targets have a message...
+                bool impossibleToCompleteAnotherJoin = _decliningPermanently && !AllTargetsHaveAtLeastOneMessage;
+                if (!impossibleToCompleteAnotherJoin)
+                {
+                    //...or that could happen if an individual target isn't accepting messages and doesn't have any messages available
+                    foreach (JoinBlockTargetBase target in _targets)
+                    {
+                        if (target.IsDecliningPermanently && !target.HasAtLeastOneMessageAvailable)
+                        {
+                            impossibleToCompleteAnotherJoin = true;
+                            break;
+                        }
+                    }
+                }
+
+                // We're done forever if there's no task currently processing and 
+                // either it's impossible we'll have another join or we're canceled.
+                bool currentlyProcessing = _taskForInputProcessing != null;
+                bool shouldComplete = !currentlyProcessing && (impossibleToCompleteAnotherJoin || CanceledOrFaulted);
+
+                if (shouldComplete)
+                {
+                    // Make sure no one else tries to call CompleteBlockOncePossible
+                    _completionReserved = true;
+
+                    // Make sure all targets are declining
+                    _decliningPermanently = true;
+
+                    // Complete each target asynchronously so as not to invoke synchronous continuations under a lock
+                    Task.Factory.StartNew(state =>
+                    {
+                        var sharedResources = (JoinBlockTargetSharedResources)state;
+                        foreach (JoinBlockTargetBase target in sharedResources._targets) target.CompleteOncePossible();
+                    }, this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+                }
+            }
+        }
+
+        /// <summary>Task body used to process messages.</summary>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private void ProcessMessagesLoopCore()
+        {
+            Contract.Requires(!_dataflowBlockOptions.Greedy || _boundingState != null, "This only makes sense in non-greedy or bounding mode");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+            try
+            {
+                int timesThroughLoop = 0;
+                int maxMessagesPerTask = _dataflowBlockOptions.ActualMaxMessagesPerTask;
+                bool madeProgress;
+                do
+                {
+                    // Retrieve postponed messages.
+                    // In greedy bounded mode, consuming a message through a target is sufficient 
+                    // to consider we've made progress, i.e. to stay in the loop. 
+                    madeProgress = !_dataflowBlockOptions.Greedy ?
+                                        RetrievePostponedItemsNonGreedy() :
+                                        RetrievePostponedItemsGreedyBounded();
+
+                    if (madeProgress)
+                    {
+                        // Convert buffered messages into a filled join if each target has at least one buffered message
+                        lock (IncomingLock)
+                        {
+                            if (AllTargetsHaveAtLeastOneMessage)
+                            {
+                                _joinFilledAction(); // Pluck a message from each target
+                                _joinsCreated++;
+
+                                // If we are in non-greedy mode, do this once per join
+                                if (!_dataflowBlockOptions.Greedy && _boundingState != null) _boundingState.CurrentCount += 1;
+                            }
+                        }
+                    }
+
+                    timesThroughLoop++;
+                } while (madeProgress && timesThroughLoop < maxMessagesPerTask);
+            }
+            catch (Exception exception)
+            {
+                // We can trigger completion of the JoinBlock by completing one target.
+                // It doesn't matter which one. So we always complete the first one.
+                Debug.Assert(_targets.Length > 0, "A join must have targets.");
+                _targets[0].CompleteCore(exception, dropPendingMessages: true, releaseReservedMessages: true);
+                // The finally section will do the block completion.
+            }
+            finally
+            {
+                lock (IncomingLock)
+                {
+                    // We're no longer processing, so null out the processing task
+                    _taskForInputProcessing = null;
+
+                    // However, we may have given up early because we hit our own configured
+                    // processing limits rather than because we ran out of work to do.  If that's
+                    // the case, make sure we spin up another task to keep going.
+                    ProcessAsyncIfNecessary(isReplacementReplica: true);
+
+                    // If, however, we stopped because we ran out of work to do and we
+                    // know we'll never get more, then complete.
+                    CompleteBlockIfPossible();
+                }
+            }
+        }
+
+        /// <summary>Notifies the block that one or more items was removed from the queue.</summary>
+        /// <param name="numItemsRemoved">The number of items removed.</param>
+        internal void OnItemsRemoved(int numItemsRemoved)
+        {
+            Contract.Requires(numItemsRemoved > 0, "Number of items removed needs to be positive.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            // If we're bounding, we need to know when an item is removed so that we
+            // can update the count that's mirroring the actual count in the source's queue,
+            // and potentially kick off processing to start consuming postponed messages.
+            if (_boundingState != null)
+            {
+                lock (IncomingLock)
+                {
+                    // Decrement the count, which mirrors the count in the source half
+                    Debug.Assert(_boundingState.CurrentCount - numItemsRemoved >= 0,
+                        "It should be impossible to have a negative number of items.");
+                    _boundingState.CurrentCount -= numItemsRemoved;
+
+                    ProcessAsyncIfNecessary();
+                    CompleteBlockIfPossible();
+                }
+            }
+        }
+
+        /// <summary>Gets the object to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                var displayJoin = _ownerJoin as IDebuggerDisplay;
+                return string.Format("Block=\"{0}\"",
+                    displayJoin != null ? displayJoin.Content : _ownerJoin);
+            }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/TransformBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/TransformBlock.cs
new file mode 100644 (file)
index 0000000..97ba13d
--- /dev/null
@@ -0,0 +1,427 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// TransformBlock.cs
+//
+//
+// A propagator block that runs a function on each input to produce a single output.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Threading.Tasks.Dataflow.Internal;
+using System.Diagnostics.CodeAnalysis;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Provides a dataflow block that invokes a provided <see cref="System.Func{TInput,TOutput}"/> delegate for every data element received.</summary>
+    /// <typeparam name="TInput">Specifies the type of data received and operated on by this <see cref="TransformBlock{TInput,TOutput}"/>.</typeparam>
+    /// <typeparam name="TOutput">Specifies the type of data output by this <see cref="TransformBlock{TInput,TOutput}"/>.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(TransformBlock<,>.DebugView))]
+    public sealed class TransformBlock<TInput, TOutput> : IPropagatorBlock<TInput, TOutput>, IReceivableSourceBlock<TOutput>, IDebuggerDisplay
+    {
+        /// <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;
+        /// <summary>The source side.</summary>
+        private readonly SourceCore<TOutput> _source;
+
+        /// <summary>Initializes the <see cref="TransformBlock{TInput,TOutput}"/> with the specified <see cref="System.Func{TInput,TOutput}"/>.</summary>
+        /// <param name="transform">The function to invoke with each data element received.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="transform"/> is null (Nothing in Visual Basic).</exception>
+        public TransformBlock(Func<TInput, TOutput> transform) :
+            this(transform, null, ExecutionDataflowBlockOptions.Default)
+        { }
+
+        /// <summary>
+        /// Initializes the <see cref="TransformBlock{TInput,TOutput}"/> with the specified <see cref="System.Func{TInput,TOutput}"/> and 
+        /// <see cref="ExecutionDataflowBlockOptions"/>.
+        /// </summary>
+        /// <param name="transform">The function to invoke with each data element received.</param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="TransformBlock{TInput,TOutput}"/>.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="transform"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        public TransformBlock(Func<TInput, TOutput> transform, ExecutionDataflowBlockOptions dataflowBlockOptions) :
+            this(transform, null, dataflowBlockOptions)
+        { }
+
+        /// <summary>Initializes the <see cref="TransformBlock{TInput,TOutput}"/> with the specified <see cref="System.Func{TInput,TOutput}"/>.</summary>
+        /// <param name="transform">The function to invoke with each data element received.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="transform"/> is null (Nothing in Visual Basic).</exception>
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public TransformBlock(Func<TInput, Task<TOutput>> transform) :
+            this(null, transform, ExecutionDataflowBlockOptions.Default)
+        { }
+
+        /// <summary>
+        /// Initializes the <see cref="TransformBlock{TInput,TOutput}"/> with the specified <see cref="System.Func{TInput,TOutput}"/>
+        /// and <see cref="ExecutionDataflowBlockOptions"/>.
+        /// </summary>
+        /// <param name="transform">The function to invoke with each data element received.</param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="TransformBlock{TInput,TOutput}"/>.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="transform"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public TransformBlock(Func<TInput, Task<TOutput>> transform, ExecutionDataflowBlockOptions dataflowBlockOptions) :
+            this(null, transform, dataflowBlockOptions)
+        { }
+
+        /// <summary>
+        /// Initializes the <see cref="TransformBlock{TInput,TOutput}"/> with the specified <see cref="System.Func{TInput,TOutput}"/> 
+        /// and <see cref="DataflowBlockOptions"/>.
+        /// </summary>
+        /// <param name="transformSync">The synchronous function to invoke with each data element received.</param>
+        /// <param name="transformAsync">The asynchronous function to invoke with each data element received.</param>
+        /// <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)
+        {
+            if (transformSync == null && transformAsync == null) throw new ArgumentNullException("transform");
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+
+            Contract.Requires(transformSync == null ^ transformAsync == null, "Exactly one of transformSync and transformAsync must be null.");
+            Contract.EndContractBlock();
+
+            // Ensure we have options that can't be changed by the caller
+            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
+
+            // Initialize onItemsRemoved delegate if necessary
+            Action<ISourceBlock<TOutput>, int> onItemsRemoved = null;
+            if (dataflowBlockOptions.BoundedCapacity > 0)
+                onItemsRemoved = (owningSource, count) => ((TransformBlock<TInput, TOutput>)owningSource)._target.ChangeBoundingCount(-count);
+
+            // Initialize source component.
+            _source = new SourceCore<TOutput>(this, dataflowBlockOptions,
+                owningSource => ((TransformBlock<TInput, TOutput>)owningSource)._target.Complete(exception: null, dropPendingMessages: true),
+                onItemsRemoved);
+
+            // If parallelism is employed, we will need to support reordering messages that complete out-of-order
+            if (dataflowBlockOptions.SupportsParallelExecution)
+            {
+                _reorderingBuffer = new ReorderingBuffer<TOutput>(this, (owningSource, message) => ((TransformBlock<TInput, TOutput>)owningSource)._source.AddMessage(message));
+            }
+
+            // Create the underlying target
+            if (transformSync != null) // sync
+            {
+                _target = new TargetCore<TInput>(this,
+                    messageWithId => ProcessMessage(transformSync, messageWithId),
+                    _reorderingBuffer, dataflowBlockOptions, TargetCoreOptions.None);
+            }
+            else // async
+            {
+                Debug.Assert(transformAsync != null, "Incorrect delegate type.");
+                _target = new TargetCore<TInput>(this,
+                    messageWithId => ProcessMessageWithTask(transformAsync, messageWithId),
+                    _reorderingBuffer, dataflowBlockOptions, TargetCoreOptions.UsesAsyncCompletion);
+            }
+
+            // Link up the target half with the source half.  In doing so, 
+            // ensure exceptions are propagated, and let the source know no more messages will arrive.
+            // As the target has completed, and as the target synchronously pushes work
+            // through the reordering buffer when async processing completes, 
+            // 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);
+                sourceCore.Complete();
+            }, _source, CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default);
+
+            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
+            // In those cases we need to fault the target half to drop its buffered messages and to release its 
+            // reservations. This should not create an infinite loop, because all our implementations are designed
+            // to handle multiple completion requests and to carry over only one.
+            _source.Completion.ContinueWith((completed, state) =>
+            {
+                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);
+            }, 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);
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
+            }
+#endif
+        }
+
+        /// <summary>Processes the message with a user-provided transform function that returns a TOutput.</summary>
+        /// <param name="transform">The transform function to use to process the message.</param>
+        /// <param name="messageWithId">The message to be processed.</param>
+        private void ProcessMessage(Func<TInput, TOutput> transform, KeyValuePair<TInput, long> messageWithId)
+        {
+            // Process the input message to get the output message
+            TOutput outputItem = default(TOutput);
+            bool itemIsValid = false;
+            try
+            {
+                outputItem = transform(messageWithId.Key);
+                itemIsValid = true;
+            }
+            catch (Exception exc)
+            {
+                // If this exception represents cancellation, swallow it rather than shutting down the block.
+                if (!Common.IsCooperativeCancellation(exc)) throw;
+            }
+            finally
+            {
+                // If we were not successful in producing an item, update the bounding
+                // count to reflect that we're done with this input item.
+                if (!itemIsValid) _target.ChangeBoundingCount(-1);
+
+                // If there's no reordering buffer (because we're running sequentially),
+                // simply pass the output message through. Otherwise, there's a reordering buffer, 
+                // so add to it instead (if a reordering buffer is used, we always need
+                // to output the message to it, even if the operation failed and outputMessage
+                // is null... this is because the reordering buffer cares about a strict sequence
+                // of IDs, and it needs to know when a particular ID has completed. It will eliminate
+                // null messages accordingly.)
+                if (_reorderingBuffer == null)
+                {
+                    if (itemIsValid) _source.AddMessage(outputItem);
+                }
+                else _reorderingBuffer.AddItem(messageWithId.Value, outputItem, itemIsValid);
+            }
+        }
+
+        /// <summary>Processes the message with a user-provided transform function that returns a task of TOutput.</summary>
+        /// <param name="transform">The transform function to use to process the message.</param>
+        /// <param name="messageWithId">The message to be processed.</param>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private void ProcessMessageWithTask(Func<TInput, Task<TOutput>> transform, KeyValuePair<TInput, long> messageWithId)
+        {
+            Contract.Requires(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;
+            try
+            {
+                task = transform(messageWithId.Key);
+            }
+            catch (Exception exc) { caughtException = exc; }
+
+            // If no task is available, we're done.
+            if (task == null)
+            {
+                // If we didn't get a task because an exception occurred,
+                // store it (if the exception was cancellation, just ignore it).
+                if (caughtException != null && !Common.IsCooperativeCancellation(caughtException))
+                {
+                    Common.StoreDataflowMessageValueIntoExceptionData(caughtException, messageWithId.Key);
+                    _target.Complete(caughtException, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: false);
+                }
+
+                // If there's a reordering buffer, notify it that this message is done.
+                if (_reorderingBuffer != null) _reorderingBuffer.IgnoreItem(messageWithId.Value);
+
+                // Signal that we're done this async operation, and remove the bounding
+                // count for the input item that didn't yield any output.
+                _target.SignalOneAsyncMessageCompleted(boundingCountChange: -1);
+                return;
+            }
+
+            // Otherwise, join with the asynchronous operation when it completes.
+            task.ContinueWith((completed, 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);
+        }
+
+        /// <summary>Completes the processing of an asynchronous message.</summary>
+        /// <param name="completed">The completed task storing the output data generated for an input message.</param>
+        /// <param name="messageWithId">The originating message</param>
+        private void AsyncCompleteProcessMessageWithTask(Task<TOutput> completed, KeyValuePair<TInput, long> messageWithId)
+        {
+            Contract.Requires(completed != null, "Completed task is required.");
+            Contract.Requires(completed.IsCompleted, "Task must be completed to be here.");
+
+            bool isBounded = _target.IsBounded;
+            bool gotOutputItem = false;
+            TOutput outputItem = default(TOutput);
+
+            switch (completed.Status)
+            {
+                case TaskStatus.RanToCompletion:
+                    outputItem = completed.Result;
+                    gotOutputItem = true;
+                    break;
+
+                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;
+                    Common.StoreDataflowMessageValueIntoExceptionData(aggregate, messageWithId.Key, targetInnerExceptions: true);
+                    _target.Complete(aggregate, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: true);
+                    break;
+                    // Nothing special to do for cancellation
+            }
+
+            // Adjust the bounding count if necessary (we only need to decrement it for faulting
+            // and cancellation, since in the case of success we still have an item that's now in the output buffer).
+            // Even though this is more costly (again, only in the non-success case, we do this before we store the 
+            // message, so that if there's a race to remove the element from the source buffer, the count is 
+            // appropriately incremented before it's decremented.
+            if (!gotOutputItem && isBounded) _target.ChangeBoundingCount(-1);
+
+            // If there's no reordering buffer (because we're running sequentially),
+            // and we got a message, simply pass the output message through.
+            if (_reorderingBuffer == null)
+            {
+                if (gotOutputItem) _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);
+
+            // Let the target know that one of the asynchronous operations it launched has completed.
+            _target.SignalOneAsyncMessageCompleted();
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+        public void Complete() { _target.Complete(exception: null, dropPendingMessages: false); }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            _target.Complete(exception, dropPendingMessages: true);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+        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 Boolean TryReceive(Predicate<TOutput> filter, 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); }
+
+        /// <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="InputCount"]/*' />
+        public int InputCount { get { return _target.InputCount; } }
+
+        /// <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/Targets/Member[@name="OfferMessage"]/*' />
+        DataflowMessageStatus ITargetBlock<TInput>.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, Boolean consumeToAccept)
+        {
+            return _target.OfferMessage(messageHeader, messageValue, source, consumeToAccept);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+        TOutput ISourceBlock<TOutput>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out Boolean messageConsumed)
+        {
+            return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+        bool ISourceBlock<TOutput>.ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
+        {
+            return _source.ReserveMessage(messageHeader, target);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+        void ISourceBlock<TOutput>.ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
+        {
+            _source.ReleaseReservation(messageHeader, target);
+        }
+
+        /// <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 _target.GetDebuggingInformation().InputCount; } }
+        /// <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 OutputCountForDebugger { get { return _source.GetDebuggingInformation().OutputCount; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="ToString"]/*' />
+        public override string ToString() { return Common.GetNameForDebugger(this, _source.DataflowBlockOptions); }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                return string.Format("{0}, InputCount={1}, OutputCount={2}",
+                    Common.GetNameForDebugger(this, _source.DataflowBlockOptions),
+                    InputCountForDebugger,
+                    OutputCountForDebugger);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for the TransformBlock.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The transform being viewed.</summary>
+            private readonly TransformBlock<TInput, TOutput> _transformBlock;
+            /// <summary>The target half of the block being viewed.</summary>
+            private readonly TargetCore<TInput>.DebuggingInformation _targetDebuggingInformation;
+            /// <summary>The source half of the block being viewed.</summary>
+            private readonly SourceCore<TOutput>.DebuggingInformation _sourceDebuggingInformation;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="transformBlock">The transform being viewed.</param>
+            public DebugView(TransformBlock<TInput, TOutput> transformBlock)
+            {
+                Contract.Requires(transformBlock != null, "Need a block with which to construct the debug view.");
+                _transformBlock = transformBlock;
+                _targetDebuggingInformation = transformBlock._target.GetDebuggingInformation();
+                _sourceDebuggingInformation = transformBlock._source.GetDebuggingInformation();
+            }
+
+            /// <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; } }
+            /// <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 Int32 CurrentDegreeOfParallelism { get { return _targetDebuggingInformation.CurrentDegreeOfParallelism; } }
+            /// <summary>Gets the task being used for output processing.</summary>
+            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 whether the block is declining further messages.</summary>
+            public bool IsDecliningPermanently { get { return _targetDebuggingInformation.IsDecliningPermanently; } }
+            /// <summary>Gets whether the block is completed.</summary>
+            public bool IsCompleted { get { return _sourceDebuggingInformation.IsCompleted; } }
+            /// <summary>Gets the block's Id.</summary>
+            public int Id { get { return Common.GetBlockId(_transformBlock); } }
+
+            /// <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; } }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/TransformManyBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/TransformManyBlock.cs
new file mode 100644 (file)
index 0000000..0223feb
--- /dev/null
@@ -0,0 +1,644 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// TransformManyBlock.cs
+//
+//
+// A propagator block that runs a function on each input to produce zero or more outputs.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Threading.Tasks.Dataflow.Internal;
+using System.Collections.ObjectModel;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Provides a dataflow block that invokes a provided <see cref="System.Func{T,TResult}"/> delegate for every data element received.</summary>
+    /// <typeparam name="TInput">Specifies the type of data received and operated on by this <see cref="TransformManyBlock{TInput,TOutput}"/>.</typeparam>
+    /// <typeparam name="TOutput">Specifies the type of data output by this <see cref="TransformManyBlock{TInput,TOutput}"/>.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(TransformManyBlock<,>.DebugView))]
+    public sealed class TransformManyBlock<TInput, TOutput> : IPropagatorBlock<TInput, TOutput>, IReceivableSourceBlock<TOutput>, IDebuggerDisplay
+    {
+        /// <summary>The target side.</summary>
+        private readonly TargetCore<TInput> _target;
+        /// <summary>
+        /// 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;
+        /// <summary>The source side.</summary>
+        private readonly SourceCore<TOutput> _source;
+
+        /// <summary>Initializes the <see cref="TransformManyBlock{TInput,TOutput}"/> with the specified function.</summary>
+        /// <param name="transform">
+        /// The function to invoke with each data element received.  All of the data from the returned <see cref="System.Collections.Generic.IEnumerable{TOutput}"/>
+        /// will be made available as output from this <see cref="TransformManyBlock{TInput,TOutput}"/>.
+        /// </param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="transform"/> is null (Nothing in Visual Basic).</exception>
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public TransformManyBlock(Func<TInput, IEnumerable<TOutput>> transform) :
+            this(transform, null, ExecutionDataflowBlockOptions.Default)
+        { }
+
+        /// <summary>Initializes the <see cref="TransformManyBlock{TInput,TOutput}"/> with the specified function and <see cref="ExecutionDataflowBlockOptions"/>.</summary>
+        /// <param name="transform">
+        /// The function to invoke with each data element received.  All of the data from the returned in the <see cref="System.Collections.Generic.IEnumerable{TOutput}"/>
+        /// will be made available as output from this <see cref="TransformManyBlock{TInput,TOutput}"/>.
+        /// </param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="TransformManyBlock{TInput,TOutput}"/>.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="transform"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public TransformManyBlock(Func<TInput, IEnumerable<TOutput>> transform, ExecutionDataflowBlockOptions dataflowBlockOptions) :
+            this(transform, null, dataflowBlockOptions)
+        { }
+
+        /// <summary>Initializes the <see cref="TransformManyBlock{TInput,TOutput}"/> with the specified function.</summary>
+        /// <param name="transform">
+        /// The function to invoke with each data element received. All of the data asynchronously returned in the <see cref="System.Collections.Generic.IEnumerable{TOutput}"/>
+        /// will be made available as output from this <see cref="TransformManyBlock{TInput,TOutput}"/>.
+        /// </param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="transform"/> is null (Nothing in Visual Basic).</exception>
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public TransformManyBlock(Func<TInput, Task<IEnumerable<TOutput>>> transform) :
+            this(null, transform, ExecutionDataflowBlockOptions.Default)
+        { }
+
+        /// <summary>Initializes the <see cref="TransformManyBlock{TInput,TOutput}"/> with the specified function and <see cref="ExecutionDataflowBlockOptions"/>.</summary>
+        /// <param name="transform">
+        /// The function to invoke with each data element received. All of the data asynchronously returned in the <see cref="System.Collections.Generic.IEnumerable{TOutput}"/>
+        /// will be made available as output from this <see cref="TransformManyBlock{TInput,TOutput}"/>.
+        /// </param>
+        /// <param name="dataflowBlockOptions">The options with which to configure this <see cref="TransformManyBlock{TInput,TOutput}"/>.</param>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="transform"/> is null (Nothing in Visual Basic).</exception>
+        /// <exception cref="System.ArgumentNullException">The <paramref name="dataflowBlockOptions"/> is null (Nothing in Visual Basic).</exception>
+        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
+        public TransformManyBlock(Func<TInput, Task<IEnumerable<TOutput>>> transform, ExecutionDataflowBlockOptions dataflowBlockOptions) :
+            this(null, transform, dataflowBlockOptions)
+        { }
+
+        /// <summary>Initializes the <see cref="TransformManyBlock{TInput,TOutput}"/> with the specified function and <see cref="ExecutionDataflowBlockOptions"/>.</summary>
+        /// <param name="transformSync">The synchronous function to invoke with each data element received.</param>
+        /// <param name="transformAsync">The asynchronous function to invoke with each data element received.</param>
+        /// <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)
+        {
+            // 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");
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+
+            Contract.Requires(transformSync == null ^ transformAsync == null, "Exactly one of transformSync and transformAsync must be null.");
+            Contract.EndContractBlock();
+
+            // Ensure we have options that can't be changed by the caller
+            dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
+
+            // Initialize onItemsRemoved delegate if necessary
+            Action<ISourceBlock<TOutput>, int> onItemsRemoved = null;
+            if (dataflowBlockOptions.BoundedCapacity > 0)
+                onItemsRemoved = (owningSource, count) => ((TransformManyBlock<TInput, TOutput>)owningSource)._target.ChangeBoundingCount(-count);
+
+            // Initialize source component
+            _source = new SourceCore<TOutput>(this, dataflowBlockOptions,
+                owningSource => ((TransformManyBlock<TInput, TOutput>)owningSource)._target.Complete(exception: null, dropPendingMessages: true),
+                onItemsRemoved);
+
+            // If parallelism is employed, we will need to support reordering messages that complete out-of-order.
+            if (dataflowBlockOptions.SupportsParallelExecution)
+            {
+                _reorderingBuffer = new ReorderingBuffer<IEnumerable<TOutput>>(
+                    this, (source, messages) => ((TransformManyBlock<TInput, TOutput>)source)._source.AddMessages(messages));
+            }
+
+            // Create the underlying target and source
+            if (transformSync != null) // sync
+            {
+                // If an enumerable function was provided, we can use synchronous completion, meaning
+                // that the target will consider a message fully processed as soon as the
+                // delegate returns.
+                _target = new TargetCore<TInput>(this,
+                    messageWithId => ProcessMessage(transformSync, messageWithId),
+                    _reorderingBuffer, dataflowBlockOptions, TargetCoreOptions.None);
+            }
+            else // async
+            {
+                Debug.Assert(transformAsync != null, "Incorrect delegate type.");
+
+                // If a task-based function was provided, we need to use asynchronous completion, meaning
+                // that the target won't consider a message completed until the task
+                // returned from that delegate has completed.
+                _target = new TargetCore<TInput>(this,
+                    messageWithId => ProcessMessageWithTask(transformAsync, messageWithId),
+                    _reorderingBuffer, dataflowBlockOptions, TargetCoreOptions.UsesAsyncCompletion);
+            }
+
+            // Link up the target half with the source half.  In doing so, 
+            // ensure exceptions are propagated, and let the source know no more messages will arrive.
+            // As the target has completed, and as the target synchronously pushes work
+            // through the reordering buffer when async processing completes, 
+            // 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);
+                sourceCore.Complete();
+            }, _source, CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default);
+
+            // It is possible that the source half may fault on its own, e.g. due to a task scheduler exception.
+            // In those cases we need to fault the target half to drop its buffered messages and to release its 
+            // reservations. This should not create an infinite loop, because all our implementations are designed
+            // to handle multiple completion requests and to carry over only one.
+            _source.Completion.ContinueWith((completed, state) =>
+            {
+                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);
+            }, 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);
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
+            }
+#endif
+        }
+
+        /// <summary>Processes the message with a user-provided transform function that returns an enumerable.</summary>
+        /// <param name="transformFunction">The transform function to use to process the message.</param>
+        /// <param name="messageWithId">The message to be processed.</param>
+        private void ProcessMessage(Func<TInput, IEnumerable<TOutput>> transformFunction, KeyValuePair<TInput, long> messageWithId)
+        {
+            Contract.Requires(transformFunction != null, "Function to invoke is required.");
+
+            bool userDelegateSucceeded = false;
+            try
+            {
+                // Run the user transform and store the results.
+                IEnumerable<TOutput> outputItems = transformFunction(messageWithId.Key);
+                userDelegateSucceeded = true;
+                StoreOutputItems(messageWithId, outputItems);
+            }
+            catch (Exception exc)
+            {
+                // If this exception represents cancellation, swallow it rather than shutting down the block.
+                if (!Common.IsCooperativeCancellation(exc)) throw;
+            }
+            finally
+            {
+                // If the user delegate failed, store an empty set in order 
+                // to update the bounding count and reordering buffer.
+                if (!userDelegateSucceeded) StoreOutputItems(messageWithId, null);
+            }
+        }
+
+        /// <summary>Processes the message with a user-provided transform function that returns an observable.</summary>
+        /// <param name="function">The transform function to use to process the message.</param>
+        /// <param name="messageWithId">The message to be processed.</param>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private void ProcessMessageWithTask(Func<TInput, Task<IEnumerable<TOutput>>> function, KeyValuePair<TInput, long> messageWithId)
+        {
+            Contract.Requires(function != null, "Function to invoke is required.");
+
+            // Run the transform function to get the resulting task
+            Task<IEnumerable<TOutput>> task = null;
+            Exception caughtException = null;
+            try
+            {
+                task = function(messageWithId.Key);
+            }
+            catch (Exception exc) { caughtException = exc; }
+
+            // If no task is available, either because null was returned or an exception was thrown, we're done.
+            if (task == null)
+            {
+                // If we didn't get a task because an exception occurred, store it 
+                // (or if the exception was cancellation, just ignore it).
+                if (caughtException != null && !Common.IsCooperativeCancellation(caughtException))
+                {
+                    Common.StoreDataflowMessageValueIntoExceptionData(caughtException, messageWithId.Key);
+                    _target.Complete(caughtException, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: false);
+                }
+
+                // Notify that we're done with this input and that we got no output for the input.
+                if (_reorderingBuffer != null)
+                {
+                    // If there's a reordering buffer, "store" an empty output.  This will
+                    // internally both update the output buffer and decrement the bounding count
+                    // accordingly.
+                    StoreOutputItems(messageWithId, null);
+                    _target.SignalOneAsyncMessageCompleted();
+                }
+                else
+                {
+                    // As a fast path if we're not reordering, decrement the bounding
+                    // count as part of our signaling that we're done, since this will 
+                    // internally take the lock only once, whereas the above path will
+                    // take the lock twice.
+                    _target.SignalOneAsyncMessageCompleted(boundingCountChange: -1);
+                }
+                return;
+            }
+
+            // We got back a task.  Now wait for it to complete and store its results.
+            // Unlike with TransformBlock and ActionBlock, We run the continuation on the user-provided 
+            // 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;
+                tuple.Item1.AsyncCompleteProcessMessageWithTask(completed, tuple.Item2);
+            }, Tuple.Create(this, messageWithId),
+            CancellationToken.None,
+            Common.GetContinuationOptions(TaskContinuationOptions.ExecuteSynchronously),
+            _source.DataflowBlockOptions.TaskScheduler);
+        }
+
+        /// <summary>Completes the processing of an asynchronous message.</summary>
+        /// <param name="completed">The completed task storing the output data generated for an input message.</param>
+        /// <param name="messageWithId">The originating message</param>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private void AsyncCompleteProcessMessageWithTask(
+            Task<IEnumerable<TOutput>> completed, KeyValuePair<TInput, long> messageWithId)
+        {
+            Contract.Requires(completed != null, "A task should have been provided.");
+            Contract.Requires(completed.IsCompleted, "The task should have been in a final state.");
+
+            switch (completed.Status)
+            {
+                case TaskStatus.RanToCompletion:
+                    IEnumerable<TOutput> outputItems = completed.Result;
+                    try
+                    {
+                        // Get the resulting enumerable and persist it.
+                        StoreOutputItems(messageWithId, outputItems);
+                    }
+                    catch (Exception exc)
+                    {
+                        // Enumerating the user's collection failed. If this exception represents cancellation, 
+                        // swallow it rather than shutting down the block.
+                        if (!Common.IsCooperativeCancellation(exc))
+                        {
+                            // The exception was not for cancellation. 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.
+                            Common.StoreDataflowMessageValueIntoExceptionData(exc, messageWithId.Key);
+                            _target.Complete(exc, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: false);
+                        }
+                    }
+                    break;
+
+                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;
+                    Common.StoreDataflowMessageValueIntoExceptionData(aggregate, messageWithId.Key, targetInnerExceptions: true);
+                    _target.Complete(aggregate, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: true);
+                    goto case TaskStatus.Canceled;
+                case TaskStatus.Canceled:
+                    StoreOutputItems(messageWithId, null); // notify the reordering buffer and decrement the bounding count
+                    break;
+
+                default:
+                    Debug.Assert(false, "The task should have been in a final state.");
+                    break;
+            }
+
+            // Let the target know that one of the asynchronous operations it launched has completed.
+            _target.SignalOneAsyncMessageCompleted();
+        }
+
+        /// <summary>
+        /// Stores the output items, either into the reordering buffer or into the source half.
+        /// Ensures that the bounding count is correctly updated.
+        /// </summary>
+        /// <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)
+        {
+            // If there's a reordering buffer, pass the data along to it.
+            // The reordering buffer will handle all details, including bounding.
+            if (_reorderingBuffer != null)
+            {
+                StoreOutputItemsReordered(messageWithId.Value, outputItems);
+            }
+            // Otherwise, output the data directly.
+            else if (outputItems != null)
+            {
+                // If this is a trusted type, output the data en mass.
+                if (outputItems is TOutput[] || outputItems is List<TOutput>)
+                {
+                    StoreOutputItemsNonReorderedAtomic(outputItems);
+                }
+                else
+                {
+                    // Otherwise, we need to take the slow path of enumerating
+                    // each individual item.
+                    StoreOutputItemsNonReorderedWithIteration(outputItems);
+                }
+            }
+            else if (_target.IsBounded)
+            {
+                // outputItems is null and there's no reordering buffer
+                // and we're bounding, so decrement the bounding count to
+                // signify that the input element we already accounted for
+                // produced no output
+                _target.ChangeBoundingCount(count: -1);
+            }
+            // else there's no reordering buffer, there are no output items, and we're not bounded,
+            // so there's nothing more to be done.
+        }
+
+        /// <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)
+        {
+            Contract.Requires(_reorderingBuffer != null, "Expected a reordering buffer");
+            Contract.Requires(id != Common.INVALID_REORDERING_ID, "This ID should never have been handed out.");
+
+            // Grab info about the transform
+            TargetCore<TInput> target = _target;
+            bool isBounded = target.IsBounded;
+
+            // Handle invalid items (null enumerables) by delegating to the base
+            if (item == null)
+            {
+                _reorderingBuffer.AddItem(id, null, false);
+                if (isBounded) target.ChangeBoundingCount(count: -1);
+                return;
+            }
+
+            // If we can eagerly get the number of items in the collection, update the bounding count.
+            // 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[];
+            if (itemAsTrustedList == null) itemAsTrustedList = item as List<TOutput>;
+            if (itemAsTrustedList != null && isBounded)
+            {
+                UpdateBoundingCountWithOutputCount(count: itemAsTrustedList.Count);
+            }
+
+            // Determine whether this id is the next item, and if it is and if we have a trusted list,
+            // try to output it immediately on the fast path.  If it can be output, we're done.
+            // Otherwise, make forward progress based on whether we're next in line.
+            bool? isNextNullable = _reorderingBuffer.AddItemIfNextAndTrusted(id, itemAsTrustedList, itemAsTrustedList != null);
+            if (!isNextNullable.HasValue) return; // data was successfully output
+            bool isNextItem = isNextNullable.Value;
+
+            // 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;
+            try
+            {
+                // If this is the next item, we can output it now.
+                if (isNextItem)
+                {
+                    StoreOutputItemsNonReorderedWithIteration(item);
+                    // here itemCopy remains null, so that base.AddItem will finish our interactions with the reordering buffer
+                }
+                else if (itemAsTrustedList != null)
+                {
+                    itemCopy = itemAsTrustedList.ToList();
+                    // we already got the count and updated the bounding count previously
+                }
+                else
+                {
+                    // We're not the next item, and we're not trusted, so copy the data into a list.
+                    // We need to enumerate outside of the lock in the base class.
+                    int itemCount = 0;
+                    try
+                    {
+                        itemCopy = item.ToList(); // itemCopy will remain null in the case of exception
+                        itemCount = itemCopy.Count;
+                    }
+                    finally
+                    {
+                        // If we're here successfully, then itemCount is the number of output items
+                        // we actually received, and we should update the bounding count with it.
+                        // If we're here because ToList threw an exception, then itemCount will be 0,
+                        // and we still need to update the bounding count with this in order to counteract
+                        // the increased bounding count for the corresponding input.
+                        if (isBounded) UpdateBoundingCountWithOutputCount(count: itemCount);
+                    }
+                }
+                // else if the item isn't valid, the finally block will see itemCopy as null and output invalid
+            }
+            finally
+            {
+                // Tell the base reordering buffer that we're done.  If we already output
+                // all of the data, itemCopy will be null, and we just pass down the invalid item.  
+                // If we haven't, pass down the real thing.  We do this even in the case of an exception,
+                // in which case this will be a dummy element.
+                _reorderingBuffer.AddItem(id, itemCopy, itemIsValid: itemCopy != null);
+            }
+        }
+
+        /// <summary>
+        /// Stores the trusted enumerable en mass into the source core.
+        /// This method does not go through the reordering buffer.
+        /// </summary>
+        /// <param name="outputItems"></param>
+        private void StoreOutputItemsNonReorderedAtomic(IEnumerable<TOutput> outputItems)
+        {
+            Contract.Requires(_reorderingBuffer == null, "Expected not to have a reordering buffer");
+            Contract.Requires(outputItems is TOutput[] || outputItems is List<TOutput>, "outputItems must be a list we've already vetted as trusted");
+            if (_target.IsBounded) UpdateBoundingCountWithOutputCount(count: ((ICollection<TOutput>)outputItems).Count);
+            _source.AddMessages(outputItems);
+        }
+
+        /// <summary>
+        /// Stores the untrusted enumerable into the source core.
+        /// This method does not go through the reordering buffer.
+        /// </summary>
+        /// <param name="outputItems">The untrusted enumerable.</param>
+        private void StoreOutputItemsNonReorderedWithIteration(IEnumerable<TOutput> outputItems)
+        {
+            // If we're bounding, we need to increment the bounded count
+            // for each individual item as we enumerate it.
+            if (_target.IsBounded)
+            {
+                // When the input item that generated this
+                // output was loaded, we incremented the bounding count.  If it only
+                // output a single a item, then we don't need to touch the bounding count.
+                // Otherwise, we need to adjust the bounding count accordingly.
+                bool outputFirstItem = false;
+                try
+                {
+                    foreach (TOutput item in outputItems)
+                    {
+                        if (outputFirstItem) _target.ChangeBoundingCount(count: 1);
+                        else outputFirstItem = true;
+                        _source.AddMessage(item);
+                    }
+                }
+                finally
+                {
+                    if (!outputFirstItem) _target.ChangeBoundingCount(count: -1);
+                }
+            }
+            // If we're not bounding, just output each individual item.
+            else
+            {
+                foreach (TOutput item in outputItems) _source.AddMessage(item);
+            }
+        }
+
+        /// <summary>
+        /// Updates the bounding count based on the number of output items
+        /// generated for a single input.
+        /// </summary>
+        /// <param name="count">The number of output items.</param>
+        private void UpdateBoundingCountWithOutputCount(int count)
+        {
+            // We already incremented the count for a single input item, and
+            // that input spawned 0 or more outputs.  Take the input tracking
+            // into account when figuring out how much to increment or decrement
+            // the bounding count.
+
+            Contract.Requires(_target.IsBounded, "Expected to be in bounding mode.");
+            if (count > 1) _target.ChangeBoundingCount(count - 1);
+            else if (count == 0) _target.ChangeBoundingCount(-1);
+            else Debug.Assert(count == 1, "Count shouldn't be negative.");
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+        public void Complete() { _target.Complete(exception: null, dropPendingMessages: false); }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            _target.Complete(exception, dropPendingMessages: true);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+        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 Boolean TryReceive(Predicate<TOutput> filter, 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); }
+
+        /// <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="InputCount"]/*' />
+        public int InputCount { get { return _target.InputCount; } }
+
+        /// <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/Targets/Member[@name="OfferMessage"]/*' />
+        DataflowMessageStatus ITargetBlock<TInput>.OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, Boolean consumeToAccept)
+        {
+            return _target.OfferMessage(messageHeader, messageValue, source, consumeToAccept);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+        TOutput ISourceBlock<TOutput>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out Boolean messageConsumed)
+        {
+            return _source.ConsumeMessage(messageHeader, target, out messageConsumed);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+        bool ISourceBlock<TOutput>.ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
+        {
+            return _source.ReserveMessage(messageHeader, target);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+        void ISourceBlock<TOutput>.ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
+        {
+            _source.ReleaseReservation(messageHeader, target);
+        }
+
+        /// <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 _target.GetDebuggingInformation().InputCount; } }
+        /// <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 OutputCountForDebugger { get { return _source.GetDebuggingInformation().OutputCount; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="ToString"]/*' />
+        public override string ToString() { return Common.GetNameForDebugger(this, _source.DataflowBlockOptions); }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                return string.Format("{0}, InputCount={1}, OutputCount={2}",
+                    Common.GetNameForDebugger(this, _source.DataflowBlockOptions),
+                    InputCountForDebugger,
+                    OutputCountForDebugger);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for the TransformManyBlock.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The transform many block being viewed.</summary>
+            private readonly TransformManyBlock<TInput, TOutput> _transformManyBlock;
+            /// <summary>The target half of the block being viewed.</summary>
+            private readonly TargetCore<TInput>.DebuggingInformation _targetDebuggingInformation;
+            /// <summary>The source half of the block being viewed.</summary>
+            private readonly SourceCore<TOutput>.DebuggingInformation _sourceDebuggingInformation;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="transformManyBlock">The transform being viewed.</param>
+            public DebugView(TransformManyBlock<TInput, TOutput> transformManyBlock)
+            {
+                Contract.Requires(transformManyBlock != null, "Need a block with which to construct the debug view.");
+                _transformManyBlock = transformManyBlock;
+                _targetDebuggingInformation = transformManyBlock._target.GetDebuggingInformation();
+                _sourceDebuggingInformation = transformManyBlock._source.GetDebuggingInformation();
+            }
+
+            /// <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; } }
+            /// <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 Int32 CurrentDegreeOfParallelism { get { return _targetDebuggingInformation.CurrentDegreeOfParallelism; } }
+            /// <summary>Gets the task being used for output processing.</summary>
+            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 whether the block is declining further messages.</summary>
+            public bool IsDecliningPermanently { get { return _targetDebuggingInformation.IsDecliningPermanently; } }
+            /// <summary>Gets whether the block is completed.</summary>
+            public bool IsCompleted { get { return _sourceDebuggingInformation.IsCompleted; } }
+            /// <summary>Gets the block's Id.</summary>
+            public int Id { get { return Common.GetBlockId(_transformManyBlock); } }
+
+            /// <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; } }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/WriteOnceBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Blocks/WriteOnceBlock.cs
new file mode 100644 (file)
index 0000000..7b6fa7f
--- /dev/null
@@ -0,0 +1,568 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// WriteOnceBlock.cs
+//
+//
+// A propagator block capable of receiving and storing only one message, ever.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Security;
+using System.Threading.Tasks.Dataflow.Internal;
+
+namespace System.Threading.Tasks.Dataflow
+{
+    /// <summary>Provides a buffer for receiving and storing at most one element in a network of dataflow blocks.</summary>
+    /// <typeparam name="T">Specifies the type of the data buffered by this dataflow block.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    [DebuggerTypeProxy(typeof(WriteOnceBlock<>.DebugView))]
+    public sealed class WriteOnceBlock<T> : IPropagatorBlock<T, T>, IReceivableSourceBlock<T>, IDebuggerDisplay
+    {
+        /// <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;
+        /// <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;
+        /// <summary>Whether all future messages should be declined.</summary>
+        private bool _decliningPermanently;
+        /// <summary>Whether block completion is disallowed.</summary>
+        private bool _completionReserved;
+        /// <summary>The header of the singly-assigned value.</summary>
+        private DataflowMessageHeader _header;
+        /// <summary>The singly-assigned value.</summary>
+        private T _value;
+
+        /// <summary>Gets the object used as the value lock.</summary>
+        private object ValueLock { get { return _targetRegistry; } }
+
+        /// <summary>Initializes the <see cref="WriteOnceBlock{T}"/>.</summary>
+        /// <param name="cloningFunction">
+        /// 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) :
+            this(cloningFunction, DataflowBlockOptions.Default)
+        { }
+
+        /// <summary>Initializes the <see cref="WriteOnceBlock{T}"/> with the specified <see cref="DataflowBlockOptions"/>.</summary>
+        /// <param name="cloningFunction">
+        /// 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>
+        /// <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)
+        {
+            // Validate arguments
+            if (dataflowBlockOptions == null) throw new ArgumentNullException("dataflowBlockOptions");
+            Contract.EndContractBlock();
+
+            // Store the option
+            _cloningFunction = cloningFunction;
+            _dataflowBlockOptions = dataflowBlockOptions.DefaultOrClone();
+
+            // The target registry also serves as our ValueLock,
+            // and thus must always be initialized, even if the block is pre-canceled, as
+            // subsequent usage of the block may run through code paths that try to take this lock.
+            _targetRegistry = new TargetRegistry<T>(this);
+
+            // If a cancelable CancellationToken has been passed in, 
+            // we need to initialize the completion task's TCS now.
+            if (dataflowBlockOptions.CancellationToken.CanBeCanceled)
+            {
+                _lazyCompletionTaskSource = new TaskCompletionSource<VoidResult>();
+
+                // If we've already had cancellation requested, do as little work as we have to 
+                // in order to be done.
+                if (dataflowBlockOptions.CancellationToken.IsCancellationRequested)
+                {
+                    _completionReserved = _decliningPermanently = true;
+
+                    // Cancel the completion task's TCS
+                    _lazyCompletionTaskSource.SetCanceled();
+                }
+                else
+                {
+                    // Handle async cancellation requests by declining on the target
+                    Common.WireCancellationToComplete(
+                        dataflowBlockOptions.CancellationToken, _lazyCompletionTaskSource.Task, state => ((WriteOnceBlock<T>)state).Complete(), this);
+                }
+            }
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCreated(this, dataflowBlockOptions);
+            }
+#endif
+        }
+
+        /// <summary>Asynchronously completes the block on another task.</summary>
+        /// <remarks>
+        /// This must only be called once all of the completion conditions are met.
+        /// </remarks>
+        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
+        private void CompleteBlockAsync(IList<Exception> exceptions)
+        {
+            Contract.Requires(_decliningPermanently, "We may get here only after we have started to decline permanently.");
+            Contract.Requires(_completionReserved, "We may get here only after we have reserved completion.");
+            Common.ContractAssertMonitorStatus(ValueLock, held: false);
+
+            // If there is no exceptions list, we offer the message around, and then complete.
+            // If there is an exception list, we complete without offering the message.
+            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,
+                                                        Common.GetCreationOptionsForTask());
+
+#if FEATURE_TRACING
+                DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+                if (etwLog.IsEnabled())
+                {
+                    etwLog.TaskLaunchedForMessageHandling(
+                        this, taskForOutputProcessing, DataflowEtwProvider.TaskLaunchedReason.OfferingOutputMessages, _header.IsValid ? 1 : 0);
+                }
+#endif
+
+                // Start the task handling scheduling exceptions
+                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;
+                    blockAndList.Item1.CompleteBlock(blockAndList.Item2);
+                },
+                Tuple.Create(this, exceptions), CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+            }
+        }
+
+        /// <summary>Offers the message and completes the block.</summary>
+        /// <remarks>
+        /// This is called only once.
+        /// </remarks>
+        private void OfferToTargetsAndCompleteBlock()
+        {
+            // OfferToTargets calls to potentially multiple targets, each of which
+            // 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();
+            CompleteBlock(exceptions);
+        }
+
+        /// <summary>Completes the block.</summary>
+        /// <remarks>
+        /// This is called only once.
+        /// </remarks>
+        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 
+            // sole purpose of the TCS being lazily initialized.
+
+            Contract.Requires(_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();
+
+            // Complete the block's completion task
+            if (exceptions != null && exceptions.Count > 0)
+            {
+                CompletionTaskSource.TrySetException(exceptions);
+            }
+            else if (_dataflowBlockOptions.CancellationToken.IsCancellationRequested)
+            {
+                CompletionTaskSource.TrySetCanceled();
+            }
+            else
+            {
+                // Safely try to initialize the completion task's TCS with a cached completed TCS. 
+                // If our attempt succeeds (CompareExchange returns null), we have nothing more to do.
+                // If the completion task's TCS was already initialized (CompareExchange returns non-null), 
+                // we have to complete that TCS instance.
+                if (Interlocked.CompareExchange(ref _lazyCompletionTaskSource, Common.CompletedVoidResultTaskCompletionSource, null) != null)
+                {
+                    _lazyCompletionTaskSource.TrySetResult(default(VoidResult));
+                }
+            }
+
+            // Now that the completion task is completed, we may propagate completion to the linked targets
+            _targetRegistry.PropagateCompletion(linkedTargets);
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCompleted(this);
+            }
+#endif
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+        void IDataflowBlock.Fault(Exception exception)
+        {
+            if (exception == null) throw new ArgumentNullException("exception");
+            Contract.EndContractBlock();
+
+            CompleteCore(exception, storeExceptionEvenIfAlreadyCompleting: false);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+        public void Complete()
+        {
+            CompleteCore(exception: null, storeExceptionEvenIfAlreadyCompleting: false);
+        }
+
+        private void CompleteCore(Exception exception, bool storeExceptionEvenIfAlreadyCompleting)
+        {
+            Contract.Requires(exception != null || !storeExceptionEvenIfAlreadyCompleting,
+                            "When storeExceptionEvenIfAlreadyCompleting is set to true, an exception must be provided.");
+            Contract.EndContractBlock();
+
+            bool thisThreadReservedCompletion = false;
+            lock (ValueLock)
+            {
+                // Faulting from outside is allowed until we start declining permanently
+                if (_decliningPermanently && !storeExceptionEvenIfAlreadyCompleting) return;
+
+                // Decline further messages
+                _decliningPermanently = true;
+
+                // Reserve Completion.
+                // If storeExceptionEvenIfAlreadyCompleting is true, we are here to fault the block,
+                // because we couldn't launch the offer-and-complete task. 
+                // We have to retry to just complete. We do that by pretending completion wasn't reserved. 
+                if (!_completionReserved || storeExceptionEvenIfAlreadyCompleting) thisThreadReservedCompletion = _completionReserved = true;
+            }
+
+            // This call caused us to start declining further messages,
+            // there's nothing more this block needs to do... complete it if we just reserved completion.
+            if (thisThreadReservedCompletion)
+            {
+                List<Exception> exceptions = null;
+                if (exception != null)
+                {
+                    exceptions = new List<Exception>();
+                    exceptions.Add(exception);
+                }
+
+                CompleteBlockAsync(exceptions);
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
+        public Boolean TryReceive(Predicate<T> filter, 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)))
+            {
+                item = CloneItem(_value);
+                return true;
+            }
+            // Otherwise, nothing to receive
+            else
+            {
+                item = default(T);
+                return false;
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
+        Boolean IReceivableSourceBlock<T>.TryReceiveAll(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,
+            // give back null.
+            T item;
+            if (TryReceive(null, out item))
+            {
+                items = new T[] { item };
+                return true;
+            }
+            else
+            {
+                items = null;
+                return false;
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
+        public IDisposable LinkTo(ITargetBlock<T> target, DataflowLinkOptions linkOptions)
+        {
+            // Validate arguments
+            if (target == null) throw new ArgumentNullException("target");
+            if (linkOptions == null) throw new ArgumentNullException("linkOptions");
+            Contract.EndContractBlock();
+
+            bool hasValue;
+            bool isCompleted;
+            lock (ValueLock)
+            {
+                hasValue = HasValue;
+                isCompleted = _completionReserved;
+
+                // If we haven't gotten a value yet and the block is not complete, add the target and bail
+                if (!hasValue && !isCompleted)
+                {
+                    _targetRegistry.Add(ref target, linkOptions);
+                    return Common.CreateUnlinker(ValueLock, _targetRegistry, target);
+                }
+            }
+
+            // If we already have a value, send it along to the linking target
+            if (hasValue)
+            {
+                bool useCloning = _cloningFunction != null;
+                target.OfferMessage(_header, _value, this, consumeToAccept: useCloning);
+            }
+
+            // If completion propagation has been requested, do it safely.
+            // The Completion property will ensure the lazy TCS is initialized.
+            if (linkOptions.PropagateCompletion) Common.PropagateCompletionOnceCompleted(Completion, target);
+
+            return Disposables.Nop;
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+        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, Boolean consumeToAccept)
+        {
+            // Validate arguments
+            if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            if (source == null && consumeToAccept) throw new ArgumentException(SR.Argument_CantConsumeFromANullSource, "consumeToAccept");
+            Contract.EndContractBlock();
+
+            bool thisThreadReservedCompletion = false;
+            lock (ValueLock)
+            {
+                // If we are declining messages, bail
+                if (_decliningPermanently) return DataflowMessageStatus.DecliningPermanently;
+
+                // Consume the message from the source if necessary. We do this while holding ValueLock to prevent multiple concurrent
+                // offers from all succeeding.
+                if (consumeToAccept)
+                {
+                    bool consumed;
+                    messageValue = source.ConsumeMessage(messageHeader, this, out consumed);
+                    if (!consumed) return DataflowMessageStatus.NotAvailable;
+                }
+
+                // Update the header and the value
+                _header = Common.SingleMessageHeader;
+                _value = messageValue;
+
+                // We got what we needed. Start declining permanently.
+                _decliningPermanently = true;
+
+                // Reserve Completion
+                if (!_completionReserved) thisThreadReservedCompletion = _completionReserved = true;
+            }
+
+            // Since this call to OfferMessage succeeded (and only one can ever), complete the block
+            // (but asynchronously so as not to block the Post call while offering to 
+            // targets, running synchronous continuations off of the completion task, etc.)
+            if (thisThreadReservedCompletion) CompleteBlockAsync(exceptions: null);
+            return DataflowMessageStatus.Accepted;
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+        T ISourceBlock<T>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target, out Boolean messageConsumed)
+        {
+            // Validate arguments
+            if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            if (target == null) throw new ArgumentNullException("target");
+            Contract.EndContractBlock();
+
+            // As long as the message being requested is the one we have, allow it to be consumed,
+            // but make a copy using the provided cloning function.
+            if (_header.Id == messageHeader.Id)
+            {
+                messageConsumed = true;
+                return CloneItem(_value);
+            }
+            else
+            {
+                messageConsumed = false;
+                return default(T);
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+        Boolean ISourceBlock<T>.ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target)
+        {
+            // Validate arguments
+            if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            if (target == null) throw new ArgumentNullException("target");
+            Contract.EndContractBlock();
+
+            // As long as the message is the one we have, it can be "reserved."
+            // Reservations on a WriteOnceBlock are not exclusive, because
+            // everyone who wants a copy can get one.
+            return _header.Id == messageHeader.Id;
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+        void ISourceBlock<T>.ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<T> target)
+        {
+            // Validate arguments
+            if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            if (target == null) throw new ArgumentNullException("target");
+            Contract.EndContractBlock();
+
+            // As long as the message is the one we have, everything's fine.
+            if (_header.Id != messageHeader.Id) throw new InvalidOperationException(SR.InvalidOperation_MessageNotReservedByTarget);
+
+            // In other blocks, upon release we typically re-offer the message to all linked targets.
+            // We need to do the same thing for WriteOnceBlock, in order to account for cases where the block
+            // may be linked to a join or similar block, such that the join could never again be satisfied
+            // if it didn't receive another offer from this source.  However, since the message is broadcast
+            // and all targets can get a copy, we don't need to broadcast to all targets, only to
+            // the target that released the message.  Note that we don't care whether it's accepted
+            // 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);
+        }
+
+        /// <summary>Clones the item.</summary>
+        /// <param name="item">The item to clone.</param>
+        /// <returns>The cloned item.</returns>
+        private T CloneItem(T item)
+        {
+            return _cloningFunction != null ?
+                _cloningFunction(item) :
+                item;
+        }
+
+        /// <summary>Offers the WriteOnceBlock's message to all targets.</summary>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        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;
+            if (HasValue)
+            {
+                TargetRegistry<T>.LinkedTargetInfo cur = _targetRegistry.FirstTargetNode;
+                while (cur != null)
+                {
+                    TargetRegistry<T>.LinkedTargetInfo next = cur.Next;
+                    ITargetBlock<T> target = cur.Target;
+                    try
+                    {
+                        // Offer the message.  If there's a cloning function, we force the target to
+                        // come back to us to consume the message, allowing us the opportunity to run
+                        // 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);
+                    }
+                    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.AddException(ref exceptions, exc);
+                    }
+                    cur = next;
+                }
+            }
+            return exceptions;
+        }
+
+        /// <summary>Ensures the completion task's TCS is initialized.</summary>
+        /// <returns>The completion task's TCS.</returns>
+        private TaskCompletionSource<VoidResult> CompletionTaskSource
+        {
+            get
+            {
+                // If the completion task's TCS has not been initialized by now, safely try to initialize it.
+                // It is very important that once a completion task/source instance has been handed out,
+                // it remains the block's completion task.
+                if (_lazyCompletionTaskSource == null)
+                {
+                    Interlocked.CompareExchange(ref _lazyCompletionTaskSource, new TaskCompletionSource<VoidResult>(), null);
+                }
+
+                return _lazyCompletionTaskSource;
+            }
+        }
+
+        /// <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>
+        private T Value { get { return _header.IsValid ? _value : default(T); } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="ToString"]/*' />
+        public override string ToString() { return Common.GetNameForDebugger(this, _dataflowBlockOptions); }
+
+        /// <summary>The data to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                return string.Format("{0}, HasValue={1}, Value={2}",
+                    Common.GetNameForDebugger(this, _dataflowBlockOptions), HasValue, Value);
+            }
+        }
+        /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+        object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+        /// <summary>Provides a debugger type proxy for WriteOnceBlock.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The WriteOnceBlock being viewed.</summary>
+            private readonly WriteOnceBlock<T> _writeOnceBlock;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="writeOnceBlock">The WriteOnceBlock to view.</param>
+            public DebugView(WriteOnceBlock<T> writeOnceBlock)
+            {
+                Contract.Requires(writeOnceBlock != null, "Need a block with which to construct the debug view.");
+                _writeOnceBlock = writeOnceBlock;
+            }
+
+            /// <summary>Gets whether the WriteOnceBlock has completed.</summary>
+            public bool IsCompleted { get { return _writeOnceBlock.Completion.IsCompleted; } }
+            /// <summary>Gets the block's Id.</summary>
+            public int Id { get { return Common.GetBlockId(_writeOnceBlock); } }
+
+            /// <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>
+            public T Value { get { return _writeOnceBlock.Value; } }
+
+            /// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
+            public DataflowBlockOptions DataflowBlockOptions { get { return _writeOnceBlock._dataflowBlockOptions; } }
+            /// <summary>Gets the set of all targets linked from this block.</summary>
+            public TargetRegistry<T> LinkedTargets { get { return _writeOnceBlock._targetRegistry; } }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ActionOnDispose.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ActionOnDispose.cs
new file mode 100644 (file)
index 0000000..5d8a905
--- /dev/null
@@ -0,0 +1,142 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// ActionOnDispose.cs
+//
+//
+// Implemention of IDisposable that runs a delegate on Dispose.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    /// <summary>Provider of disposables that run actions.</summary>
+    internal sealed class Disposables
+    {
+        /// <summary>An IDisposable that does nothing.</summary>
+        internal readonly static IDisposable Nop = new NopDisposable();
+
+        /// <summary>Creates an IDisposable that runs an action when disposed.</summary>
+        /// <typeparam name="T1">Specifies the type of the first argument.</typeparam>
+        /// <typeparam name="T2">Specifies the type of the second argument.</typeparam>
+        /// <param name="action">The action to invoke.</param>
+        /// <param name="arg1">The first argument.</param>
+        /// <param name="arg2">The second argument.</param>
+        /// <returns>The created disposable.</returns>
+        internal static IDisposable Create<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
+        {
+            Contract.Requires(action != null, "Non-null disposer action required.");
+            return new Disposable<T1, T2>(action, arg1, arg2);
+        }
+
+        /// <summary>Creates an IDisposable that runs an action when disposed.</summary>
+        /// <typeparam name="T1">Specifies the type of the first argument.</typeparam>
+        /// <typeparam name="T2">Specifies the type of the second argument.</typeparam>
+        /// <typeparam name="T3">Specifies the type of the third argument.</typeparam>
+        /// <param name="action">The action to invoke.</param>
+        /// <param name="arg1">The first argument.</param>
+        /// <param name="arg2">The second argument.</param>
+        /// <param name="arg3">The third argument.</param>
+        /// <returns>The created disposable.</returns>
+        internal static IDisposable Create<T1, T2, T3>(Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3)
+        {
+            Contract.Requires(action != null, "Non-null disposer action required.");
+            return new Disposable<T1, T2, T3>(action, arg1, arg2, arg3);
+        }
+
+        /// <summary>A disposable that's a nop.</summary>
+        [DebuggerDisplay("Disposed = true")]
+        private sealed class NopDisposable : IDisposable
+        {
+            void IDisposable.Dispose() { }
+        }
+
+        /// <summary>An IDisposable that will run a delegate when disposed.</summary>
+        [DebuggerDisplay("Disposed = {Disposed}")]
+        private sealed class Disposable<T1, T2> : IDisposable
+        {
+            /// <summary>First state argument.</summary>
+            private readonly T1 _arg1;
+            /// <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;
+
+            /// <summary>Initializes the ActionOnDispose.</summary>
+            /// <param name="action">The action to run when disposed.</param>
+            /// <param name="arg1">The first argument.</param>
+            /// <param name="arg2">The second argument.</param>
+            internal Disposable(Action<T1, T2> action, T1 arg1, T2 arg2)
+            {
+                Contract.Requires(action != null, "Non-null action needed for disposable");
+                _action = action;
+                _arg1 = arg1;
+                _arg2 = arg2;
+            }
+
+            /// <summary>Gets whether the IDisposable has been disposed.</summary>
+            [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+            private bool Disposed { get { return _action == null; } }
+
+            /// <summary>Invoke the action.</summary>
+            void IDisposable.Dispose()
+            {
+                Action<T1, T2> toRun = _action;
+                if (toRun != null &&
+                    Interlocked.CompareExchange(ref _action, null, toRun) == toRun)
+                {
+                    toRun(_arg1, _arg2);
+                }
+            }
+        }
+
+        /// <summary>An IDisposable that will run a delegate when disposed.</summary>
+        [DebuggerDisplay("Disposed = {Disposed}")]
+        private sealed class Disposable<T1, T2, T3> : IDisposable
+        {
+            /// <summary>First state argument.</summary>
+            private readonly T1 _arg1;
+            /// <summary>Second state argument.</summary>
+            private readonly T2 _arg2;
+            /// <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;
+
+            /// <summary>Initializes the ActionOnDispose.</summary>
+            /// <param name="action">The action to run when disposed.</param>
+            /// <param name="arg1">The first argument.</param>
+            /// <param name="arg2">The second argument.</param>
+            /// <param name="arg3">The third argument.</param>
+            internal Disposable(Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3)
+            {
+                Contract.Requires(action != null, "Non-null action needed for disposable");
+                _action = action;
+                _arg1 = arg1;
+                _arg2 = arg2;
+                _arg3 = arg3;
+            }
+
+            /// <summary>Gets whether the IDisposable has been disposed.</summary>
+            [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+            private bool Disposed { get { return _action == null; } }
+
+            /// <summary>Invoke the action.</summary>
+            void IDisposable.Dispose()
+            {
+                Action<T1, T2, T3> toRun = _action;
+                if (toRun != null &&
+                    Interlocked.CompareExchange(ref _action, null, toRun) == toRun)
+                {
+                    toRun(_arg1, _arg2, _arg3);
+                }
+            }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/Common.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/Common.cs
new file mode 100644 (file)
index 0000000..2742206
--- /dev/null
@@ -0,0 +1,694 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// Common.cs
+//
+//
+// Helper routines for the rest of the TPL Dataflow implementation.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Security;
+using System.Collections;
+using System.Runtime.ExceptionServices;
+using System.Threading.Tasks.Dataflow.Internal.Threading;
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    /// <summary>Internal helper utilities.</summary>
+    internal static class Common
+    {
+        /// <summary>
+        /// An invalid ID to assign for reordering purposes.  This value is chosen to be the last of the 64-bit integers that
+        /// could ever be assigned as a reordering ID.
+        /// </summary>
+        internal const long INVALID_REORDERING_ID = -1;
+        /// <summary>A well-known message ID for code that will send exactly one message or 
+        /// where the exact message ID is not important.</summary>
+        internal const int SINGLE_MESSAGE_ID = 1;
+        /// <summary>A perf optimization for caching a well-known message header instead of
+        /// constructing one every time it is needed.</summary>
+        internal static readonly DataflowMessageHeader SingleMessageHeader = new DataflowMessageHeader(SINGLE_MESSAGE_ID);
+        /// <summary>The cached completed Task{bool} with a result of true.</summary>
+        internal static readonly Task<bool> CompletedTaskWithTrueResult = CreateCachedBooleanTask(true);
+        /// <summary>The cached completed Task{bool} with a result of false.</summary>
+        internal static readonly Task<bool> CompletedTaskWithFalseResult = CreateCachedBooleanTask(false);
+        /// <summary>The cached completed TaskCompletionSource{VoidResult}.</summary>
+        internal static readonly TaskCompletionSource<VoidResult> CompletedVoidResultTaskCompletionSource = CreateCachedTaskCompletionSource<VoidResult>();
+
+        /// <summary>Asserts that a given synchronization object is either held or not held.</summary>
+        /// <param name="syncObj">The monitor to check.</param>
+        /// <param name="held">Whether we want to assert that it's currently held or not held.</param>
+        [Conditional("DEBUG")]
+        internal static void ContractAssertMonitorStatus(object syncObj, bool held)
+        {
+            Contract.Requires(syncObj != null, "The monitor object to check must be provided.");
+            Debug.Assert(Monitor.IsEntered(syncObj) == held, "The locking scheme was not correctly followed.");
+        }
+
+        /// <summary>Keeping alive processing tasks: maximum number of processed messages.</summary>
+        internal const int KEEP_ALIVE_NUMBER_OF_MESSAGES_THRESHOLD = 1;
+        /// <summary>Keeping alive processing tasks: do not attempt this many times.</summary>
+        internal const int KEEP_ALIVE_BAN_COUNT = 1000;
+
+        /// <summary>A predicate type for TryKeepAliveUntil.</summary>
+        /// <param name="stateIn">Input state for the predicate in order to avoid closure allocations.</param>
+        /// <param name="stateOut">Output state for the predicate in order to avoid closure allocations.</param>
+        /// <returns>The state of the predicate.</returns>
+        internal delegate bool KeepAlivePredicate<TStateIn, TStateOut>(TStateIn stateIn, out TStateOut stateOut);
+
+        /// <summary>Actively waits for a predicate to become true.</summary>
+        /// <param name="predicate">The predicate to become true.</param>
+        /// <param name="stateIn">Input state for the predicate in order to avoid closure allocations.</param>
+        /// <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)
+        {
+            Contract.Requires(predicate != null, "Non-null predicate to execute is required.");
+            const int ITERATION_LIMIT = 16;
+
+            for (int c = ITERATION_LIMIT; c > 0; c--)
+            {
+                if (!Thread.Yield())
+                {
+                    // There was no other thread waiting. 
+                    // We may spend some more cycles to evaluate the predicate. 
+                    if (predicate(stateIn, out stateOut)) return true;
+                }
+            }
+
+            stateOut = default(TStateOut);
+            return false;
+        }
+
+        /// <summary>Unwraps an instance T from object state that is a WeakReference to that instance.</summary>
+        /// <typeparam name="T">The type of the data to be unwrapped.</typeparam>
+        /// <param name="state">The weak reference.</param>
+        /// <returns>The T instance.</returns>
+        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;
+        }
+
+        /// <summary>Gets an ID for the dataflow block.</summary>
+        /// <param name="block">The dataflow block.</param>
+        /// <returns>An ID for the dataflow block.</returns>
+        internal static int GetBlockId(IDataflowBlock block)
+        {
+            Contract.Requires(block != null, "Block required to extract an Id.");
+            const int NOTASKID = 0; // tasks don't have 0 as ids
+            Task t = Common.GetPotentiallyNotSupportedCompletionTask(block);
+            return t != null ? t.Id : NOTASKID;
+        }
+
+        /// <summary>Gets the name for the specified block, suitable to be rendered in a debugger window.</summary>
+        /// <param name="block">The block for which a name is needed.</param>
+        /// <param name="options">
+        /// The options to use when rendering the name. If no options are provided, the block's name is used directly.
+        /// </param>
+        /// <returns>The name of the object.</returns>
+        /// <remarks>This is used from DebuggerDisplay attributes.</remarks>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        internal static string GetNameForDebugger(
+            IDataflowBlock block, DataflowBlockOptions options = null)
+        {
+            Contract.Requires(block != null, "Should only be used with valid objects being displayed in the debugger.");
+            Contract.Requires(options == null || options.NameFormat != null, "If options are provided, NameFormat must be valid.");
+
+            if (block == null) return string.Empty;
+
+            string blockName = block.GetType().Name;
+            if (options == null) return blockName;
+
+            // {0} == block name
+            // {1} == block id
+            int blockId = GetBlockId(block);
+
+            // Since NameFormat is public, formatting may throw if the user has set
+            // a string that contains a reference to an argument higher than {1}.
+            // In the case of an exception, show the exception message.
+            try
+            {
+                return string.Format(options.NameFormat, blockName, blockId);
+            }
+            catch (Exception exception)
+            {
+                return exception.Message;
+            }
+        }
+
+        /// <summary>
+        /// Gets whether the exception represents a cooperative cancellation acknowledgment.
+        /// </summary>
+        /// <param name="exception">The exception to check.</param>
+        /// <returns>true if this exception represents a cooperative cancellation acknowledgment; otherwise, false.</returns>
+        internal static bool IsCooperativeCancellation(Exception exception)
+        {
+            Contract.Requires(exception != null, "An exception to check for cancellation must be provided.");
+            return exception is OperationCanceledException;
+            // Note that the behavior of this method does not exactly match that of Parallel.*, PLINQ, and Task.Factory.StartNew,
+            // in that it's more liberal and treats any OCE as acknowledgment of cancellation; in contrast, the other
+            // libraries only treat OCEs as such if they contain the same token that was provided by the user
+            // and if that token has cancellation requested.  Such logic could be achieved here with:
+            //   var oce = exception as OperationCanceledException;
+            //   return oce != null && 
+            //          oce.CancellationToken == dataflowBlockOptions.CancellationToken && 
+            //          oce.CancellationToken.IsCancellationRequested;
+            // However, that leads to a discrepancy with the async processing case of dataflow blocks,
+            // where tasks are returned to represent the message processing, potentially in the Canceled state, 
+            // and we simply ignore such tasks.  Further, for blocks like TransformBlock, it's useful to be able 
+            // to cancel an individual operation which must return a TOutput value, simply by throwing an OperationCanceledException.
+            // In such cases, you wouldn't want cancellation tied to the token, because you would only be able to
+            // cancel an individual message processing if the whole block was canceled.
+        }
+
+        /// <summary>Registers a block for cancellation by completing when cancellation is requested.</summary>
+        /// <param name="cancellationToken">The block's cancellation token.</param>
+        /// <param name="completionTask">The task that will complete when the block is completely done processing.</param>
+        /// <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)
+        {
+            Contract.Requires(completionTask != null, "A task to wire up for completion is needed.");
+            Contract.Requires(completeAction != null, "An action to invoke upon cancellation is required.");
+
+            // If a cancellation request has already occurred, just invoke the declining action synchronously.
+            // CancellationToken would do this anyway but we can short-circuit it further and avoid a bunch of unnecessary checks.
+            if (cancellationToken.IsCancellationRequested)
+            {
+                completeAction(completeState);
+            }
+            // Otherwise, if a cancellation request occurs, we want to prevent the block from accepting additional
+            // data, and we also want to dispose of that registration when we complete so that we don't
+            // leak into a long-living cancellation token.
+            else if (cancellationToken.CanBeCanceled)
+            {
+                CancellationTokenRegistration reg = cancellationToken.Register(completeAction, completeState);
+                completionTask.ContinueWith((completed, state) => ((CancellationTokenRegistration)state).Dispose(),
+                    reg, cancellationToken, Common.GetContinuationOptions(), TaskScheduler.Default);
+            }
+        }
+
+        /// <summary>Initializes the stack trace and watson bucket of an inactive exception.</summary>
+        /// <param name="exception">The exception to initialize.</param>
+        /// <returns>The initialized exception.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        internal static Exception InitializeStackTrace(Exception exception)
+        {
+            Contract.Requires(exception != null && exception.StackTrace == null,
+                "A valid but uninitialized exception should be provided.");
+            try { throw exception; }
+            catch { return exception; }
+        }
+
+        /// <summary>The name of the key in an Exception's Data collection used to store information on a dataflow message.</summary>
+        internal const string EXCEPTIONDATAKEY_DATAFLOWMESSAGEVALUE = "DataflowMessageValue"; // should not be localized
+
+        /// <summary>Stores details on a dataflow message into an Exception's Data collection.</summary>
+        /// <typeparam name="T">Specifies the type of data stored in the message.</typeparam>
+        /// <param name="exc">The Exception whose Data collection should store message information.</param>
+        /// <param name="messageValue">The message information to be stored.</param>
+        /// <param name="targetInnerExceptions">Whether to store the data into the exception's inner exception(s) in addition to the exception itself.</param>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        internal static void StoreDataflowMessageValueIntoExceptionData<T>(Exception exc, T messageValue, bool targetInnerExceptions = false)
+        {
+            Contract.Requires(exc != null, "The exception into which data should be stored must be provided.");
+
+            // Get the string value to store
+            string strValue = messageValue as string;
+            if (strValue == null && messageValue != null)
+            {
+                try
+                {
+                    strValue = messageValue.ToString();
+                }
+                catch { /* It's ok to eat all exceptions here.  If ToString throws, we'll just ignore it. */ }
+            }
+            if (strValue == null) return;
+
+            // Store the data into the exception itself
+            StoreStringIntoExceptionData(exc, Common.EXCEPTIONDATAKEY_DATAFLOWMESSAGEVALUE, strValue);
+
+            // If we also want to target inner exceptions...
+            if (targetInnerExceptions)
+            {
+                // If this is an aggregate, store into all inner exceptions.
+                var aggregate = exc as AggregateException;
+                if (aggregate != null)
+                {
+                    foreach (Exception innerException in aggregate.InnerExceptions)
+                    {
+                        StoreStringIntoExceptionData(innerException, Common.EXCEPTIONDATAKEY_DATAFLOWMESSAGEVALUE, strValue);
+                    }
+                }
+                // Otherwise, if there's an Exception.InnerException, store into that.
+                else if (exc.InnerException != null)
+                {
+                    StoreStringIntoExceptionData(exc.InnerException, Common.EXCEPTIONDATAKEY_DATAFLOWMESSAGEVALUE, strValue);
+                }
+            }
+        }
+
+        /// <summary>Stores the specified string value into the specified key slot of the specified exception's data dictionary.</summary>
+        /// <param name="exception">The exception into which the key/value should be stored.</param>
+        /// <param name="key">The key.</param>
+        /// <param name="value">The value to be serialized as a string and stored.</param>
+        /// <remarks>If the key is already present in the exception's data dictionary, the value is not overwritten.</remarks>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private static void StoreStringIntoExceptionData(Exception exception, string key, string value)
+        {
+            Contract.Requires(exception != null, "An exception is needed to store the data into.");
+            Contract.Requires(key != null, "A key into the exception's data collection is needed.");
+            Contract.Requires(value != null, "The value to store must be provided.");
+            try
+            {
+                IDictionary data = exception.Data;
+                if (data != null && !data.IsFixedSize && !data.IsReadOnly && data[key] == null)
+                {
+                    data[key] = value;
+                }
+            }
+            catch
+            {
+                // It's ok to eat all exceptions here.  This could throw if an Exception type 
+                // has overridden Data to behave differently than we expect.
+            }
+        }
+
+        /// <summary>Throws an exception asynchronously on the thread pool.</summary>
+        /// <param name="error">The exception to throw.</param>
+        /// <remarks>
+        /// This function is used when an exception needs to be propagated from a thread
+        /// other than the current context.  This could happen, for example, if the exception
+        /// should cause the standard CLR exception escalation behavior, but we're inside
+        /// of a task that will squirrel the exception away.
+        /// </remarks>
+        internal static void ThrowAsync(Exception error)
+        {
+            ExceptionDispatchInfo edi = ExceptionDispatchInfo.Capture(error);
+            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="list">The list to add the exception to, and initialize if null.</param>
+        /// <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)
+        {
+            Contract.Requires(exception != null, "An exception to add is required.");
+            Contract.Requires(!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>();
+
+            if (unwrapInnerExceptions)
+            {
+                AggregateException aggregate = exception as AggregateException;
+                if (aggregate != null)
+                {
+                    list.AddRange(aggregate.InnerExceptions);
+                }
+                else
+                {
+                    list.Add(exception.InnerException);
+                }
+            }
+            else list.Add(exception);
+        }
+
+        /// <summary>Creates a task we can cache for the desired Boolean result.</summary>
+        /// <param name="value">The value of the Boolean.</param>
+        /// <returns>A task that may be cached.</returns>
+        private static Task<Boolean> CreateCachedBooleanTask(bool value)
+        {
+            // AsyncTaskMethodBuilder<Boolean> caches tasks that are non-disposable.
+            // By using these same tasks, we're a bit more robust against disposals,
+            // in that such a disposed task's ((IAsyncResult)task).AsyncWaitHandle
+            // is still valid.
+            var atmb = System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Boolean>.Create();
+            atmb.SetResult(value);
+            return atmb.Task; // must be accessed after SetResult to get the cached task
+        }
+
+        /// <summary>Creates a TaskCompletionSource{T} completed with a value of default(T) that we can cache.</summary>
+        /// <returns>Completed TaskCompletionSource{T} that may be cached.</returns>
+        private static TaskCompletionSource<T> CreateCachedTaskCompletionSource<T>()
+        {
+            var tcs = new TaskCompletionSource<T>();
+            tcs.SetResult(default(T));
+            return tcs;
+        }
+
+        /// <summary>Creates a task faulted with the specified exception.</summary>
+        /// <typeparam name="TResult">Specifies the type of the result for this task.</typeparam>
+        /// <param name="exception">The exception with which to complete the task.</param>
+        /// <returns>The faulted task.</returns>
+        internal static Task<TResult> CreateTaskFromException<TResult>(Exception exception)
+        {
+            var atmb = System.Runtime.CompilerServices.AsyncTaskMethodBuilder<TResult>.Create();
+            atmb.SetException(exception);
+            return atmb.Task;
+        }
+
+        /// <summary>Creates a task canceled with the specified cancellation token.</summary>
+        /// <typeparam name="TResult">Specifies the type of the result for this task.</typeparam>
+        /// <returns>The canceled task.</returns>
+        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
+        internal static Task<TResult> CreateTaskFromCancellation<TResult>(CancellationToken cancellationToken)
+        {
+            Contract.Requires(cancellationToken.IsCancellationRequested,
+                "The task will only be immediately canceled if the token has cancellation requested already.");
+            var t = new Task<TResult>(CachedGenericDelegates<TResult>.DefaultTResultFunc, cancellationToken);
+            Debug.Assert(t.IsCanceled, "Task's constructor should cancel the task synchronously in the ctor.");
+            return t;
+        }
+
+        /// <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)
+        {
+            Contract.Requires(block != null, "We need a block from which to retrieve a cancellation task.");
+            try
+            {
+                return block.Completion;
+            }
+            catch (NotImplementedException) { }
+            catch (NotSupportedException) { }
+            return null;
+        }
+
+        /// <summary>
+        /// Creates an IDisposable that, when disposed, will acquire the outgoing lock while removing 
+        /// the target block from the target registry.
+        /// </summary>
+        /// <typeparam name="TOutput">Specifies the type of data in the block.</typeparam>
+        /// <param name="outgoingLock">The outgoing lock used to protect the target registry.</param>
+        /// <param name="targetRegistry">The target registry from which the target should be removed.</param>
+        /// <param name="targetBlock">The target to remove from the registry.</param>
+        /// <returns>An IDisposable that will unregister the target block from the registry while holding the outgoing lock.</returns>
+        internal static IDisposable CreateUnlinker<TOutput>(object outgoingLock, TargetRegistry<TOutput> targetRegistry, ITargetBlock<TOutput> targetBlock)
+        {
+            Contract.Requires(outgoingLock != null, "Monitor object needed to protect the operation.");
+            Contract.Requires(targetRegistry != null, "Registry from which to remove is required.");
+            Contract.Requires(targetBlock != null, "Target block to unlink is required.");
+            return Disposables.Create(CachedGenericDelegates<TOutput>.CreateUnlinkerShimAction,
+                outgoingLock, targetRegistry, targetBlock);
+        }
+
+        /// <summary>An infinite TimeSpan.</summary>
+        internal static readonly TimeSpan InfiniteTimeSpan = Timeout.InfiniteTimeSpan;
+
+        /// <summary>Validates that a timeout either is -1 or is non-negative and within the range of an Int32.</summary>
+        /// <param name="timeout">The timeout to validate.</param>
+        /// <returns>true if the timeout is valid; otherwise, false.</returns>
+        internal static bool IsValidTimeout(TimeSpan timeout)
+        {
+            long millisecondsTimeout = (long)timeout.TotalMilliseconds;
+            return millisecondsTimeout >= Timeout.Infinite && millisecondsTimeout <= Int32.MaxValue;
+        }
+
+        /// <summary>Gets the options to use for continuation tasks.</summary>
+        /// <param name="toInclude">Any options to include in the result.</param>
+        /// <returns>The options to use.</returns>
+        internal static TaskContinuationOptions GetContinuationOptions(TaskContinuationOptions toInclude = TaskContinuationOptions.None)
+        {
+            return toInclude | TaskContinuationOptions.DenyChildAttach;
+        }
+
+        /// <summary>Gets the options to use for tasks.</summary>
+        /// <param name="isReplacementReplica">If this task is being created to replace another.</param>
+        /// <remarks>
+        /// These options should be used for all tasks that have the potential to run user code or
+        /// that are repeatedly spawned and thus need a modicum of fair treatment.
+        /// </remarks>
+        /// <returns>The options to use.</returns>
+        internal static TaskCreationOptions GetCreationOptionsForTask(bool isReplacementReplica = false)
+        {
+            TaskCreationOptions options = TaskCreationOptions.DenyChildAttach;
+            if (isReplacementReplica) options |= TaskCreationOptions.PreferFairness;
+            return options;
+        }
+
+        /// <summary>Starts an already constructed task with handling and observing exceptions that may come from the scheduling process.</summary>
+        /// <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)
+        {
+            Contract.Requires(task != null, "Task to start is required.");
+            Contract.Requires(scheduler != null, "Scheduler on which to start the task is required.");
+
+            if (scheduler == TaskScheduler.Default)
+            {
+                task.Start(scheduler);
+                return null; // We don't need to worry about scheduler exceptions from the default scheduler.
+            }
+            // Slow path with try/catch separated out so that StartTaskSafe may be inlined in the common case.
+            else return StartTaskSafeCore(task, scheduler);
+        }
+
+        /// <summary>Starts an already constructed task with handling and observing exceptions that may come from the scheduling process.</summary>
+        /// <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>
+        [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")]
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private static Exception StartTaskSafeCore(Task task, TaskScheduler scheduler)
+        {
+            Contract.Requires(task != null, "Task to start is needed.");
+            Contract.Requires(scheduler != null, "Scheduler on which to start the task is required.");
+
+            Exception schedulingException = null;
+
+            try
+            {
+                task.Start(scheduler);
+            }
+            catch (Exception caughtException)
+            {
+                // Verify TPL has faulted the task
+                Debug.Assert(task.IsFaulted, "The task should have been faulted if it failed to start.");
+
+                // Observe the task's exception
+                AggregateException ignoredTaskException = task.Exception;
+
+                schedulingException = caughtException;
+            }
+
+            return schedulingException;
+        }
+
+        /// <summary>Pops and explicitly releases postponed messages after the block is done with processing.</summary>
+        /// <remarks>No locks should be held at this time. Unfortunately we cannot assert that.</remarks>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        internal static void ReleaseAllPostponedMessages<T>(ITargetBlock<T> target,
+                                    QueuedMap<ISourceBlock<T>, DataflowMessageHeader> postponedMessages,
+                                    ref List<Exception> exceptions)
+        {
+            Contract.Requires(target != null, "There must be a subject target.");
+            Contract.Requires(postponedMessages != null, "The stacked map of postponed messages must exist.");
+
+            // Note that we don't synchronize on lockObject for postponedMessages here, 
+            // because no one should be adding to it at this time.  We do a bit of 
+            // checking just for sanity's sake.
+            int initialCount = postponedMessages.Count;
+            int processedCount = 0;
+
+            KeyValuePair<ISourceBlock<T>, DataflowMessageHeader> sourceAndMessage;
+            while (postponedMessages.TryPop(out sourceAndMessage))
+            {
+                // Loop through all postponed messages declining each messages.
+                // The only way we have to do this is by reserving and then immediately releasing each message.
+                // This is important for sources like SendAsyncSource, which keep state around until
+                // they get a response to a postponed message.
+                try
+                {
+                    Debug.Assert(sourceAndMessage.Key != null, "Postponed messages must have an associated source.");
+                    if (sourceAndMessage.Key.ReserveMessage(sourceAndMessage.Value, target))
+                    {
+                        sourceAndMessage.Key.ReleaseReservation(sourceAndMessage.Value, target);
+                    }
+                }
+                catch (Exception exc)
+                {
+                    Common.AddException(ref exceptions, exc);
+                }
+
+                processedCount++;
+            }
+
+            Debug.Assert(processedCount == initialCount,
+                "We should have processed the exact number of elements that were initially there.");
+        }
+
+        /// <summary>Cache ThrowAsync to avoid allocations when it is passed into PropagateCompletionXxx.</summary>
+        internal static readonly Action<Exception> AsyncExceptionHandler = ThrowAsync;
+
+        /// <summary>
+        /// Propagates completion of sourceCompletionTask to target synchronously.
+        /// </summary>
+        /// <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)
+        {
+            Contract.Requires(sourceCompletionTask != null, "sourceCompletionTask may not be null.");
+            Contract.Requires(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;
+
+            try
+            {
+                if (exception != null) target.Fault(exception);
+                else target.Complete();
+            }
+            catch (Exception exc)
+            {
+                if (exceptionHandler != null) exceptionHandler(exc);
+                else throw;
+            }
+        }
+
+        /// <summary>
+        /// Creates a continuation off sourceCompletionTask to complete target. See PropagateCompletion.
+        /// </summary>
+        private static void PropagateCompletionAsContinuation(Task sourceCompletionTask, IDataflowBlock target)
+        {
+            Contract.Requires(sourceCompletionTask != null, "sourceCompletionTask may not be null.");
+            Contract.Requires(target != null, "The target where completion is to be propagated may not be null.");
+            sourceCompletionTask.ContinueWith((task, state) => Common.PropagateCompletion(task, (IDataflowBlock)state, AsyncExceptionHandler),
+                target, CancellationToken.None, Common.GetContinuationOptions(), TaskScheduler.Default);
+        }
+
+        /// <summary>
+        /// Propagates completion of sourceCompletionTask to target based on sourceCompletionTask's current state. See PropagateCompletion.
+        /// </summary>
+        internal static void PropagateCompletionOnceCompleted(Task sourceCompletionTask, IDataflowBlock target)
+        {
+            Contract.Requires(sourceCompletionTask != null, "sourceCompletionTask may not be null.");
+            Contract.Requires(target != null, "The target where completion is to be propagated may not be null.");
+
+            // If sourceCompletionTask is completed, propagate completion synchronously.
+            // Otherwise hook up a continuation.
+            if (sourceCompletionTask.IsCompleted) PropagateCompletion(sourceCompletionTask, target, exceptionHandler: null);
+            else PropagateCompletionAsContinuation(sourceCompletionTask, target);
+        }
+
+        /// <summary>Static class used to cache generic delegates the C# compiler doesn't cache by default.</summary>
+        /// <remarks>Without this, we end up allocating the generic delegate each time the operation is used.</remarks>
+        static class CachedGenericDelegates<T>
+        {
+            /// <summary>A function that returns the default value of T.</summary>
+            internal readonly static 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.
+            /// </summary>
+            internal readonly static Action<object, TargetRegistry<T>, ITargetBlock<T>> CreateUnlinkerShimAction =
+                (syncObj, registry, target) =>
+            {
+                lock (syncObj) registry.Remove(target);
+            };
+        }
+    }
+
+    /// <summary>State used only when bounding.</summary>
+    [DebuggerDisplay("BoundedCapacity={BoundedCapacity}}")]
+    internal class BoundingState
+    {
+        /// <summary>The maximum number of messages allowed to be buffered.</summary>
+        internal readonly int BoundedCapacity;
+        /// <summary>The number of messages currently stored.</summary>
+        /// <remarks>
+        /// This value may temporarily be higher than the actual number stored.  
+        /// That's ok, we just can't accept any new messages if CurrentCount >= BoundedCapacity.
+        /// Worst case is that we may temporarily have fewer items in the block than our maximum allows,
+        /// but we'll never have more.
+        /// </remarks>
+        internal int CurrentCount;
+
+        /// <summary>Initializes the BoundingState.</summary>
+        /// <param name="boundedCapacity">The positive bounded capacity.</param>
+        internal BoundingState(int boundedCapacity)
+        {
+            Contract.Requires(boundedCapacity > 0, "Bounded is only supported with positive values.");
+            BoundedCapacity = boundedCapacity;
+        }
+
+        /// <summary>Gets whether there's room available to add another message.</summary>
+        internal bool CountIsLessThanBound { get { return CurrentCount < BoundedCapacity; } }
+    }
+
+    /// <summary>Stated used only when bounding and when postponed messages are stored.</summary>
+    /// <typeparam name="TInput">Specifies the type of input messages.</typeparam>
+    [DebuggerDisplay("BoundedCapacity={BoundedCapacity}, PostponedMessages={PostponedMessagesCountForDebugger}")]
+    internal class BoundingStateWithPostponed<TInput> : BoundingState
+    {
+        /// <summary>Queue of postponed messages.</summary>
+        internal readonly QueuedMap<ISourceBlock<TInput>, DataflowMessageHeader> PostponedMessages =
+            new QueuedMap<ISourceBlock<TInput>, DataflowMessageHeader>();
+        /// <summary>
+        /// The number of transfers from the postponement queue to the input queue currently being processed.
+        /// </summary>
+        /// <remarks>
+        /// Blocks that use TargetCore need to transfer messages from the postponed queue to the input messages
+        /// queue.  While doing that, new incoming messages may arrive, and if they view the postponed queue
+        /// as being empty (after the block has removed the last postponed message and is consuming it, before
+        /// storing it into the input queue), they might go directly into the input queue... that will then mess
+        /// up the ordering between those postponed messages and the newly incoming messages.  To address that,
+        /// OutstandingTransfers is used to track the number of transfers currently in progress.  Incoming
+        /// messages must be postponed not only if there are already any postponed messages, but also if
+        /// there are any transfers in progress (i.e. this value is > 0).  It's an integer because the DOP could
+        /// be greater than 1, and thus we need to ref count multiple transfers that might be in progress.
+        /// </remarks>
+        internal int OutstandingTransfers;
+
+        /// <summary>Initializes the BoundingState.</summary>
+        /// <param name="boundedCapacity">The positive bounded capacity.</param>
+        internal BoundingStateWithPostponed(int boundedCapacity) : base(boundedCapacity)
+        {
+        }
+
+        /// <summary>Gets the number of postponed messages for the debugger.</summary>
+        private int PostponedMessagesCountForDebugger { get { return PostponedMessages.Count; } }
+    }
+
+    /// <summary>Stated used only when bounding and when postponed messages and a task are stored.</summary>
+    /// <typeparam name="TInput">Specifies the type of input messages.</typeparam>
+    internal class BoundingStateWithPostponedAndTask<TInput> : BoundingStateWithPostponed<TInput>
+    {
+        /// <summary>The task used to process messages.</summary>
+        internal Task TaskForInputProcessing;
+
+        /// <summary>Initializes the BoundingState.</summary>
+        /// <param name="boundedCapacity">The positive bounded capacity.</param>
+        internal BoundingStateWithPostponedAndTask(int boundedCapacity) : base(boundedCapacity)
+        {
+        }
+    }
+
+    /// <summary>
+    /// Type used with TaskCompletionSource(Of TResult) as the TResult
+    /// to ensure that the resulting task can't be upcast to something
+    /// that in the future could lead to compat problems.
+    /// </summary>
+    [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
+    [DebuggerNonUserCode]
+    internal struct VoidResult { }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ConcurrentQueue.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ConcurrentQueue.cs
new file mode 100644 (file)
index 0000000..33b1372
--- /dev/null
@@ -0,0 +1,947 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+#pragma warning disable 0420
+
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// ConcurrentQueue.cs
+//
+//
+// A lock-free, concurrent queue primitive, and its associated debugger view type.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Security;
+using System.Threading;
+
+namespace System.Threading.Tasks.Dataflow.Internal.Collections
+{
+    /// <summary>
+    /// Represents a thread-safe first-in, first-out collection of objects.
+    /// </summary>
+    /// <typeparam name="T">Specifies the type of elements in the queue.</typeparam>
+    /// <remarks>
+    /// All public  and protected members of <see cref="ConcurrentQueue{T}"/> are thread-safe and may be used
+    /// concurrently from multiple threads.
+    /// </remarks>
+    [DebuggerDisplay("Count = {Count}")]
+    [DebuggerTypeProxy(typeof(SystemCollectionsConcurrent_ProducerConsumerCollectionDebugView<>))]
+    internal class ConcurrentQueue<T> : IProducerConsumerCollection<T>
+    {
+        //fields of ConcurrentQueue
+        private volatile Segment _head;
+
+        private volatile Segment _tail;
+
+        private T[] _serializationArray; // Used for custom serialization.
+
+        private const int SEGMENT_SIZE = 32;
+
+        //number of snapshot takers, GetEnumerator(), ToList() and ToArray() operations take snapshot.
+        internal volatile int _numSnapshotTakers = 0;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ConcurrentQueue{T}"/> class.
+        /// </summary>
+        public ConcurrentQueue()
+        {
+            _head = _tail = new Segment(0, this);
+        }
+
+        /// <summary>
+        /// Initializes the contents of the queue from an existing collection.
+        /// </summary>
+        /// <param name="collection">A collection from which to copy elements.</param>
+        private void InitializeFromCollection(IEnumerable<T> collection)
+        {
+            Segment localTail = new Segment(0, this);//use this local variable to avoid the extra volatile read/write. this is safe because it is only called from ctor
+            _head = localTail;
+
+            int index = 0;
+            foreach (T element in collection)
+            {
+                Debug.Assert(index >= 0 && index < SEGMENT_SIZE);
+                localTail.UnsafeAdd(element);
+                index++;
+
+                if (index >= SEGMENT_SIZE)
+                {
+                    localTail = localTail.UnsafeGrow();
+                    index = 0;
+                }
+            }
+
+            _tail = localTail;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ConcurrentQueue{T}"/>
+        /// class that contains elements copied from the specified collection
+        /// </summary>
+        /// <param name="collection">The collection whose elements are copied to the new <see
+        /// cref="ConcurrentQueue{T}"/>.</param>
+        /// <exception cref="T:System.ArgumentNullException">The <paramref name="collection"/> argument is
+        /// null.</exception>
+        public ConcurrentQueue(IEnumerable<T> collection)
+        {
+            if (collection == null)
+            {
+                throw new ArgumentNullException("collection");
+            }
+
+            InitializeFromCollection(collection);
+        }
+
+        /// <summary>
+        /// Get the data array to be serialized
+        /// </summary>
+        [OnSerializing]
+        private void OnSerializing(StreamingContext context)
+        {
+            // save the data into the serialization array to be saved
+            _serializationArray = ToArray();
+        }
+
+        /// <summary>
+        /// Construct the queue from a previously serialized one
+        /// </summary>
+        [OnDeserialized]
+        private void OnDeserialized(StreamingContext context)
+        {
+            Debug.Assert(_serializationArray != null);
+            InitializeFromCollection(_serializationArray);
+            _serializationArray = null;
+        }
+
+        /// <summary>
+        /// Copies the elements of the <see cref="T:System.Collections.ICollection"/> to an <see
+        /// cref="T:System.Array"/>, starting at a particular
+        /// <see cref="T:System.Array"/> index.
+        /// </summary>
+        /// <param name="array">The one-dimensional <see cref="T:System.Array">Array</see> that is the
+        /// destination of the elements copied from the
+        /// <see cref="T:System.Collections.Concurrent.ConcurrentBag"/>. The <see
+        /// cref="T:System.Array">Array</see> must have zero-based indexing.</param>
+        /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
+        /// begins.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="array"/> is a null reference (Nothing in
+        /// Visual Basic).</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than
+        /// zero.</exception>
+        /// <exception cref="ArgumentException">
+        /// <paramref name="array"/> is multidimensional. -or-
+        /// <paramref name="array"/> does not have zero-based indexing. -or-
+        /// <paramref name="index"/> is equal to or greater than the length of the <paramref name="array"/>
+        /// -or- The number of elements in the source <see cref="T:System.Collections.ICollection"/> is
+        /// greater than the available space from <paramref name="index"/> to the end of the destination
+        /// <paramref name="array"/>. -or- The type of the source <see
+        /// cref="T:System.Collections.ICollection"/> cannot be cast automatically to the type of the
+        /// destination <paramref name="array"/>.
+        /// </exception>
+        void ICollection.CopyTo(Array array, int index)
+        {
+            // Validate arguments.
+            if (array == null)
+            {
+                throw new ArgumentNullException("array");
+            }
+
+            // We must be careful not to corrupt the array, so we will first accumulate an
+            // internal list of elements that we will then copy to the array. This requires
+            // some extra allocation, but is necessary since we don't know up front whether
+            // the array is sufficiently large to hold the stack's contents.
+            ((ICollection)ToList()).CopyTo(array, index);
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"/> is
+        /// synchronized with the SyncRoot.
+        /// </summary>
+        /// <value>true if access to the <see cref="T:System.Collections.ICollection"/> is synchronized
+        /// with the SyncRoot; otherwise, false. For <see cref="ConcurrentQueue{T}"/>, this property always
+        /// returns false.</value>
+        bool ICollection.IsSynchronized
+        {
+            // Gets a value indicating whether access to this collection is synchronized. Always returns
+            // false. The reason is subtle. While access is in face thread safe, it's not the case that
+            // locking on the SyncRoot would have prevented concurrent pushes and pops, as this property
+            // would typically indicate; that's because we internally use CAS operations vs. true locks.
+            get { return false; }
+        }
+
+
+        /// <summary>
+        /// Gets an object that can be used to synchronize access to the <see
+        /// cref="T:System.Collections.ICollection"/>. This property is not supported.
+        /// </summary>
+        /// <exception cref="T:System.NotSupportedException">The SyncRoot property is not supported.</exception>
+        object ICollection.SyncRoot
+        {
+            get
+            {
+                throw new NotSupportedException(SR.ConcurrentCollection_SyncRoot_NotSupported);
+            }
+        }
+
+        /// <summary>
+        /// Returns an enumerator that iterates through a collection.
+        /// </summary>
+        /// <returns>An <see cref="T:System.Collections.IEnumerator"/> that can be used to iterate through the collection.</returns>
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return ((IEnumerable<T>)this).GetEnumerator();
+        }
+
+        /// <summary>
+        /// Attempts to add an object to the <see
+        /// cref="T:System.Collections.Concurrent.IProducerConsumerCollection{T}"/>.
+        /// </summary>
+        /// <param name="item">The object to add to the <see
+        /// cref="T:System.Collections.Concurrent.IProducerConsumerCollection{T}"/>. The value can be a null
+        /// reference (Nothing in Visual Basic) for reference types.
+        /// </param>
+        /// <returns>true if the object was added successfully; otherwise, false.</returns>
+        /// <remarks>For <see cref="ConcurrentQueue{T}"/>, this operation will always add the object to the
+        /// end of the <see cref="ConcurrentQueue{T}"/>
+        /// and return true.</remarks>
+        bool IProducerConsumerCollection<T>.TryAdd(T item)
+        {
+            Enqueue(item);
+            return true;
+        }
+
+        /// <summary>
+        /// Attempts to remove and return an object from the <see
+        /// cref="T:System.Collections.Concurrent.IProducerConsumerCollection{T}"/>.
+        /// </summary>
+        /// <param name="item">
+        /// When this method returns, if the operation was successful, <paramref name="item"/> contains the
+        /// object removed. If no object was available to be removed, the value is unspecified.
+        /// </param>
+        /// <returns>true if an element was removed and returned successfully; otherwise, false.</returns>
+        /// <remarks>For <see cref="ConcurrentQueue{T}"/>, this operation will attempt to remove the object
+        /// from the beginning of the <see cref="ConcurrentQueue{T}"/>.
+        /// </remarks>
+        bool IProducerConsumerCollection<T>.TryTake(out T item)
+        {
+            return TryDequeue(out item);
+        }
+
+        /// <summary>
+        /// Gets a value that indicates whether the <see cref="ConcurrentQueue{T}"/> is empty.
+        /// </summary>
+        /// <value>true if the <see cref="ConcurrentQueue{T}"/> is empty; otherwise, false.</value>
+        /// <remarks>
+        /// For determining whether the collection contains any items, use of this property is recommended
+        /// rather than retrieving the number of items from the <see cref="Count"/> property and comparing it
+        /// to 0.  However, as this collection is intended to be accessed concurrently, it may be the case
+        /// that another thread will modify the collection after <see cref="IsEmpty"/> returns, thus invalidating
+        /// the result.
+        /// </remarks>
+        public bool IsEmpty
+        {
+            get
+            {
+                Segment head = _head;
+                if (!head.IsEmpty)
+                    //fast route 1:
+                    //if current head is not empty, then queue is not empty
+                    return false;
+                else if (head.Next == null)
+                    //fast route 2:
+                    //if current head is empty and it's the last segment
+                    //then queue is empty
+                    return true;
+                else
+                //slow route:
+                //current head is empty and it is NOT the last segment,
+                //it means another thread is growing new segment 
+                {
+                    SpinWait spin = new SpinWait();
+                    while (head.IsEmpty)
+                    {
+                        if (head.Next == null)
+                            return true;
+
+                        spin.SpinOnce();
+                        head = _head;
+                    }
+                    return false;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Copies the elements stored in the <see cref="ConcurrentQueue{T}"/> to a new array.
+        /// </summary>
+        /// <returns>A new array containing a snapshot of elements copied from the <see
+        /// cref="ConcurrentQueue{T}"/>.</returns>
+        public T[] ToArray()
+        {
+            return ToList().ToArray();
+        }
+
+        /// <summary>
+        /// Copies the <see cref="ConcurrentQueue{T}"/> elements to a new <see
+        /// cref="T:System.Collections.Generic.List{T}"/>.
+        /// </summary>
+        /// <returns>A new <see cref="T:System.Collections.Generic.List{T}"/> containing a snapshot of
+        /// elements copied from the <see cref="ConcurrentQueue{T}"/>.</returns>
+        private List<T> ToList()
+        {
+            // Increments the number of active snapshot takers. This increment must happen before the snapshot is 
+            // taken. At the same time, Decrement must happen after list copying is over. Only in this way, can it
+            // eliminate race condition when Segment.TryRemove() checks whether _numSnapshotTakers == 0. 
+            Interlocked.Increment(ref _numSnapshotTakers);
+
+            List<T> list = new List<T>();
+            try
+            {
+                //store head and tail positions in buffer, 
+                Segment head, tail;
+                int headLow, tailHigh;
+                GetHeadTailPositions(out head, out tail, out headLow, out tailHigh);
+
+                if (head == tail)
+                {
+                    head.AddToList(list, headLow, tailHigh);
+                }
+                else
+                {
+                    head.AddToList(list, headLow, SEGMENT_SIZE - 1);
+                    Segment curr = head.Next;
+                    while (curr != tail)
+                    {
+                        curr.AddToList(list, 0, SEGMENT_SIZE - 1);
+                        curr = curr.Next;
+                    }
+                    //Add tail segment
+                    tail.AddToList(list, 0, tailHigh);
+                }
+            }
+            finally
+            {
+                // This Decrement must happen after copying is over. 
+                Interlocked.Decrement(ref _numSnapshotTakers);
+            }
+            return list;
+        }
+
+        /// <summary>
+        /// Store the position of the current head and tail positions.
+        /// </summary>
+        /// <param name="head">return the head segment</param>
+        /// <param name="tail">return the tail segment</param>
+        /// <param name="headLow">return the head offset, value range [0, SEGMENT_SIZE]</param>
+        /// <param name="tailHigh">return the tail offset, value range [-1, SEGMENT_SIZE-1]</param>
+        private void GetHeadTailPositions(out Segment head, out Segment tail,
+            out int headLow, out int tailHigh)
+        {
+            head = _head;
+            tail = _tail;
+            headLow = head.Low;
+            tailHigh = tail.High;
+            SpinWait spin = new SpinWait();
+
+            //we loop until the observed values are stable and sensible.  
+            //This ensures that any update order by other methods can be tolerated.
+            while (
+                //if head and tail changed, retry
+                head != _head || tail != _tail
+                //if low and high pointers, retry
+                || headLow != head.Low || tailHigh != tail.High
+                //if head jumps ahead of tail because of concurrent grow and dequeue, retry
+                || head._index > tail._index)
+            {
+                spin.SpinOnce();
+                head = _head;
+                tail = _tail;
+                headLow = head.Low;
+                tailHigh = tail.High;
+            }
+        }
+
+
+        /// <summary>
+        /// Gets the number of elements contained in the <see cref="ConcurrentQueue{T}"/>.
+        /// </summary>
+        /// <value>The number of elements contained in the <see cref="ConcurrentQueue{T}"/>.</value>
+        /// <remarks>
+        /// For determining whether the collection contains any items, use of the <see cref="IsEmpty"/>
+        /// property is recommended rather than retrieving the number of items from the <see cref="Count"/>
+        /// property and comparing it to 0.
+        /// </remarks>
+        public int Count
+        {
+            get
+            {
+                //store head and tail positions in buffer, 
+                Segment head, tail;
+                int headLow, tailHigh;
+                GetHeadTailPositions(out head, out tail, out headLow, out tailHigh);
+
+                if (head == tail)
+                {
+                    return tailHigh - headLow + 1;
+                }
+
+                //head segment
+                int count = SEGMENT_SIZE - headLow;
+
+                //middle segment(s), if any, are full.
+                //We don't deal with overflow to be consistent with the behavior of generic types in CLR.
+                count += SEGMENT_SIZE * ((int)(tail._index - head._index - 1));
+
+                //tail segment
+                count += tailHigh + 1;
+
+                return count;
+            }
+        }
+
+
+        /// <summary>
+        /// Copies the <see cref="ConcurrentQueue{T}"/> elements to an existing one-dimensional <see
+        /// cref="T:System.Array">Array</see>, starting at the specified array index.
+        /// </summary>
+        /// <param name="array">The one-dimensional <see cref="T:System.Array">Array</see> that is the
+        /// destination of the elements copied from the
+        /// <see cref="ConcurrentQueue{T}"/>. The <see cref="T:System.Array">Array</see> must have zero-based
+        /// indexing.</param>
+        /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
+        /// begins.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="array"/> is a null reference (Nothing in
+        /// Visual Basic).</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than
+        /// zero.</exception>
+        /// <exception cref="ArgumentException"><paramref name="index"/> is equal to or greater than the
+        /// length of the <paramref name="array"/>
+        /// -or- The number of elements in the source <see cref="ConcurrentQueue{T}"/> is greater than the
+        /// available space from <paramref name="index"/> to the end of the destination <paramref
+        /// name="array"/>.
+        /// </exception>
+        public void CopyTo(T[] array, int index)
+        {
+            if (array == null)
+            {
+                throw new ArgumentNullException("array");
+            }
+
+            // We must be careful not to corrupt the array, so we will first accumulate an
+            // internal list of elements that we will then copy to the array. This requires
+            // some extra allocation, but is necessary since we don't know up front whether
+            // the array is sufficiently large to hold the stack's contents.
+            ToList().CopyTo(array, index);
+        }
+
+
+        /// <summary>
+        /// Returns an enumerator that iterates through the <see
+        /// cref="ConcurrentQueue{T}"/>.
+        /// </summary>
+        /// <returns>An enumerator for the contents of the <see
+        /// cref="ConcurrentQueue{T}"/>.</returns>
+        /// <remarks>
+        /// The enumeration represents a moment-in-time snapshot of the contents
+        /// of the queue.  It does not reflect any updates to the collection after 
+        /// <see cref="GetEnumerator()"/> was called.  The enumerator is safe to use
+        /// concurrently with reads from and writes to the queue.
+        /// </remarks>
+        public IEnumerator<T> GetEnumerator()
+        {
+            // Increments the number of active snapshot takers. This increment must happen before the snapshot is 
+            // taken. At the same time, Decrement must happen after the enumeration is over. Only in this way, can it
+            // eliminate race condition when Segment.TryRemove() checks whether _numSnapshotTakers == 0. 
+            Interlocked.Increment(ref _numSnapshotTakers);
+
+            // Takes a snapshot of the queue. 
+            // A design flaw here: if a Thread.Abort() happens, we cannot decrement _numSnapshotTakers. But we cannot 
+            // wrap the following with a try/finally block, otherwise the decrement will happen before the yield return 
+            // statements in the GetEnumerator (head, tail, headLow, tailHigh) method.           
+            Segment head, tail;
+            int headLow, tailHigh;
+            GetHeadTailPositions(out head, out tail, out headLow, out tailHigh);
+
+            //If we put yield-return here, the iterator will be lazily evaluated. As a result a snapshot of
+            // the queue is not taken when GetEnumerator is initialized but when MoveNext() is first called.
+            // This is inconsistent with existing generic collections. In order to prevent it, we capture the 
+            // value of _head in a buffer and call out to a helper method.
+            //The old way of doing this was to return the ToList().GetEnumerator(), but ToList() was an 
+            // unnecessary performance hit.
+            return GetEnumerator(head, tail, headLow, tailHigh);
+        }
+
+        /// <summary>
+        /// Helper method of GetEnumerator to separate out yield return statement, and prevent lazy evaluation. 
+        /// </summary>
+        private IEnumerator<T> GetEnumerator(Segment head, Segment tail, int headLow, int tailHigh)
+        {
+            try
+            {
+                SpinWait spin = new SpinWait();
+
+                if (head == tail)
+                {
+                    for (int i = headLow; i <= tailHigh; i++)
+                    {
+                        // If the position is reserved by an Enqueue operation, but the value is not written into,
+                        // spin until the value is available.
+                        spin.Reset();
+                        while (!head._state[i]._value)
+                        {
+                            spin.SpinOnce();
+                        }
+                        yield return head._array[i];
+                    }
+                }
+                else
+                {
+                    //iterate on head segment
+                    for (int i = headLow; i < SEGMENT_SIZE; i++)
+                    {
+                        // If the position is reserved by an Enqueue operation, but the value is not written into,
+                        // spin until the value is available.
+                        spin.Reset();
+                        while (!head._state[i]._value)
+                        {
+                            spin.SpinOnce();
+                        }
+                        yield return head._array[i];
+                    }
+                    //iterate on middle segments
+                    Segment curr = head.Next;
+                    while (curr != tail)
+                    {
+                        for (int i = 0; i < SEGMENT_SIZE; i++)
+                        {
+                            // If the position is reserved by an Enqueue operation, but the value is not written into,
+                            // spin until the value is available.
+                            spin.Reset();
+                            while (!curr._state[i]._value)
+                            {
+                                spin.SpinOnce();
+                            }
+                            yield return curr._array[i];
+                        }
+                        curr = curr.Next;
+                    }
+
+                    //iterate on tail segment
+                    for (int i = 0; i <= tailHigh; i++)
+                    {
+                        // If the position is reserved by an Enqueue operation, but the value is not written into,
+                        // spin until the value is available.
+                        spin.Reset();
+                        while (!tail._state[i]._value)
+                        {
+                            spin.SpinOnce();
+                        }
+                        yield return tail._array[i];
+                    }
+                }
+            }
+            finally
+            {
+                // This Decrement must happen after the enumeration is over. 
+                Interlocked.Decrement(ref _numSnapshotTakers);
+            }
+        }
+
+        /// <summary>
+        /// Adds an object to the end of the <see cref="ConcurrentQueue{T}"/>.
+        /// </summary>
+        /// <param name="item">The object to add to the end of the <see
+        /// cref="ConcurrentQueue{T}"/>. The value can be a null reference
+        /// (Nothing in Visual Basic) for reference types.
+        /// </param>
+        public void Enqueue(T item)
+        {
+            SpinWait spin = new SpinWait();
+            while (true)
+            {
+                Segment tail = _tail;
+                if (tail.TryAppend(item))
+                    return;
+                spin.SpinOnce();
+            }
+        }
+
+
+        /// <summary>
+        /// Attempts to remove and return the object at the beginning of the <see
+        /// cref="ConcurrentQueue{T}"/>.
+        /// </summary>
+        /// <param name="result">
+        /// When this method returns, if the operation was successful, <paramref name="result"/> contains the
+        /// object removed. If no object was available to be removed, the value is unspecified.
+        /// </param>
+        /// <returns>true if an element was removed and returned from the beginning of the <see
+        /// cref="ConcurrentQueue{T}"/>
+        /// successfully; otherwise, false.</returns>
+        public bool TryDequeue(out T result)
+        {
+            while (!IsEmpty)
+            {
+                Segment head = _head;
+                if (head.TryRemove(out result))
+                    return true;
+                //since method IsEmpty spins, we don't need to spin in the while loop
+            }
+            result = default(T);
+            return false;
+        }
+
+        /// <summary>
+        /// Attempts to return an object from the beginning of the <see cref="ConcurrentQueue{T}"/>
+        /// without removing it.
+        /// </summary>
+        /// <param name="result">When this method returns, <paramref name="result"/> contains an object from
+        /// the beginning of the <see cref="T:System.Collections.Concurrent.ConcurrentQueue{T}"/> or an
+        /// unspecified value if the operation failed.</param>
+        /// <returns>true if and object was returned successfully; otherwise, false.</returns>
+        public bool TryPeek(out T result)
+        {
+            Interlocked.Increment(ref _numSnapshotTakers);
+
+            while (!IsEmpty)
+            {
+                Segment head = _head;
+                if (head.TryPeek(out result))
+                {
+                    Interlocked.Decrement(ref _numSnapshotTakers);
+                    return true;
+                }
+                //since method IsEmpty spins, we don't need to spin in the while loop
+            }
+            result = default(T);
+            Interlocked.Decrement(ref _numSnapshotTakers);
+            return false;
+        }
+
+
+        /// <summary>
+        /// private class for ConcurrentQueue. 
+        /// a queue is a linked list of small arrays, each node is called a segment.
+        /// A segment contains an array, a pointer to the next segment, and _low, _high indices recording
+        /// the first and last valid elements of the array.
+        /// </summary>
+        private class Segment
+        {
+            //we define two volatile arrays: _array and _state. Note that the accesses to the array items 
+            //do not get volatile treatment. But we don't need to worry about loading adjacent elements or 
+            //store/load on adjacent elements would suffer reordering. 
+            // - Two stores:  these are at risk, but CLRv2 memory model guarantees store-release hence we are safe.
+            // - Two loads: because one item from two volatile arrays are accessed, the loads of the array references
+            //          are sufficient to prevent reordering of the loads of the elements.
+            internal volatile T[] _array;
+
+            // For each entry in _array, the corresponding entry in _state indicates whether this position contains 
+            // a valid value. _state is initially all false. 
+            internal volatile VolatileBool[] _state;
+
+            //pointer to the next segment. null if the current segment is the last segment
+            private volatile Segment _next;
+
+            //We use this zero based index to track how many segments have been created for the queue, and
+            //to compute how many active segments are there currently. 
+            // * The number of currently active segments is : _tail._index - _head._index + 1;
+            // * _index is incremented with every Segment.Grow operation. We use Int64 type, and we can safely 
+            //   assume that it never overflows. To overflow, we need to do 2^63 increments, even at a rate of 4 
+            //   billion (2^32) increments per second, it takes 2^31 seconds, which is about 64 years.
+            internal readonly long _index;
+
+            //indices of where the first and last valid values
+            // - _low points to the position of the next element to pop from this segment, range [0, infinity)
+            //      _low >= SEGMENT_SIZE implies the segment is disposable
+            // - _high points to the position of the latest pushed element, range [-1, infinity)
+            //      _high == -1 implies the segment is new and empty
+            //      _high >= SEGMENT_SIZE-1 means this segment is ready to grow. 
+            //        and the thread who sets _high to SEGMENT_SIZE-1 is responsible to grow the segment
+            // - Math.Min(_low, SEGMENT_SIZE) > Math.Min(_high, SEGMENT_SIZE-1) implies segment is empty
+            // - initially _low =0 and _high=-1;
+            private volatile int _low;
+            private volatile int _high;
+
+            private volatile ConcurrentQueue<T> _source;
+
+            /// <summary>
+            /// Create and initialize a segment with the specified index.
+            /// </summary>
+            internal Segment(long index, ConcurrentQueue<T> source)
+            {
+                _array = new T[SEGMENT_SIZE];
+                _state = new VolatileBool[SEGMENT_SIZE]; //all initialized to false
+                _high = -1;
+                Debug.Assert(index >= 0);
+                _index = index;
+                _source = source;
+            }
+
+            /// <summary>
+            /// return the next segment
+            /// </summary>
+            internal Segment Next
+            {
+                get { return _next; }
+            }
+
+
+            /// <summary>
+            /// return true if the current segment is empty (doesn't have any element available to dequeue, 
+            /// false otherwise
+            /// </summary>
+            internal bool IsEmpty
+            {
+                get { return (Low > High); }
+            }
+
+            /// <summary>
+            /// Add an element to the tail of the current segment
+            /// exclusively called by ConcurrentQueue.InitializedFromCollection
+            /// InitializeFromCollection is responsible to guarantee that there is no index overflow,
+            /// and there is no contention
+            /// </summary>
+            /// <param name="value"></param>
+            internal void UnsafeAdd(T value)
+            {
+                Debug.Assert(_high < SEGMENT_SIZE - 1);
+                _high++;
+                _array[_high] = value;
+                _state[_high]._value = true;
+            }
+
+            /// <summary>
+            /// Create a new segment and append to the current one
+            /// Does not update the _tail pointer
+            /// exclusively called by ConcurrentQueue.InitializedFromCollection
+            /// InitializeFromCollection is responsible to guarantee that there is no index overflow,
+            /// and there is no contention
+            /// </summary>
+            /// <returns>the reference to the new Segment</returns>
+            internal Segment UnsafeGrow()
+            {
+                Debug.Assert(_high >= SEGMENT_SIZE - 1);
+                Segment newSegment = new Segment(_index + 1, _source); //_index is Int64, we don't need to worry about overflow
+                _next = newSegment;
+                return newSegment;
+            }
+
+            /// <summary>
+            /// Create a new segment and append to the current one
+            /// Update the _tail pointer
+            /// This method is called when there is no contention
+            /// </summary>
+            internal void Grow()
+            {
+                //no CAS is needed, since there is no contention (other threads are blocked, busy waiting)
+                Segment newSegment = new Segment(_index + 1, _source);  //_index is Int64, we don't need to worry about overflow
+                _next = newSegment;
+                Debug.Assert(_source._tail == this);
+                _source._tail = _next;
+            }
+
+
+            /// <summary>
+            /// Try to append an element at the end of this segment.
+            /// </summary>
+            /// <param name="value">the element to append</param>
+            /// <returns>true if the element is appended, false if the current segment is full</returns>
+            /// <remarks>if appending the specified element succeeds, and after which the segment is full, 
+            /// then grow the segment</remarks>
+            internal bool TryAppend(T value)
+            {
+                //quickly check if _high is already over the boundary, if so, bail out
+                if (_high >= SEGMENT_SIZE - 1)
+                {
+                    return false;
+                }
+
+                //Now we will use a CAS to increment _high, and store the result in newhigh.
+                //Depending on how many free spots left in this segment and how many threads are doing this Increment
+                //at this time, the returning "newhigh" can be 
+                // 1) < SEGMENT_SIZE - 1 : we took a spot in this segment, and not the last one, just insert the value
+                // 2) == SEGMENT_SIZE - 1 : we took the last spot, insert the value AND grow the segment
+                // 3) > SEGMENT_SIZE - 1 : we failed to reserve a spot in this segment, we return false to 
+                //    Queue.Enqueue method, telling it to try again in the next segment.
+
+                int newhigh = SEGMENT_SIZE; //initial value set to be over the boundary
+
+                //We need do Interlocked.Increment and value/state update in a finally block to ensure that they run
+                //without interuption. This is to prevent anything from happening between them, and another dequeue
+                //thread maybe spinning forever to wait for _state[] to be true;
+                try
+                { }
+                finally
+                {
+                    newhigh = Interlocked.Increment(ref _high);
+                    if (newhigh <= SEGMENT_SIZE - 1)
+                    {
+                        _array[newhigh] = value;
+                        _state[newhigh]._value = true;
+                    }
+
+                    //if this thread takes up the last slot in the segment, then this thread is responsible
+                    //to grow a new segment. Calling Grow must be in the finally block too for reliability reason:
+                    //if thread abort during Grow, other threads will be left busy spinning forever.
+                    if (newhigh == SEGMENT_SIZE - 1)
+                    {
+                        Grow();
+                    }
+                }
+
+                //if newhigh <= SEGMENT_SIZE-1, it means the current thread successfully takes up a spot
+                return newhigh <= SEGMENT_SIZE - 1;
+            }
+
+
+            /// <summary>
+            /// try to remove an element from the head of current segment
+            /// </summary>
+            /// <param name="result">The result.</param>
+            /// <returns>return false only if the current segment is empty</returns>
+            internal bool TryRemove(out T result)
+            {
+                SpinWait spin = new SpinWait();
+                int lowLocal = Low, highLocal = High;
+                while (lowLocal <= highLocal)
+                {
+                    //try to update _low
+                    if (Interlocked.CompareExchange(ref _low, lowLocal + 1, lowLocal) == lowLocal)
+                    {
+                        //if the specified value is not available (this spot is taken by a push operation,
+                        // but the value is not written into yet), then spin
+                        SpinWait spinLocal = new SpinWait();
+                        while (!_state[lowLocal]._value)
+                        {
+                            spinLocal.SpinOnce();
+                        }
+                        result = _array[lowLocal];
+
+                        // If there is no other thread taking snapshot (GetEnumerator(), ToList(), etc), reset the deleted entry to null.
+                        // It is ok if after this conditional check _numSnapshotTakers becomes > 0, because new snapshots won't include 
+                        // the deleted entry at _array[lowLocal]. 
+                        if (_source._numSnapshotTakers <= 0)
+                        {
+                            _array[lowLocal] = default(T); //release the reference to the object. 
+                        }
+
+                        //if the current thread sets _low to SEGMENT_SIZE, which means the current segment becomes
+                        //disposable, then this thread is responsible to dispose this segment, and reset _head 
+                        if (lowLocal + 1 >= SEGMENT_SIZE)
+                        {
+                            //  Invariant: we only dispose the current _head, not any other segment
+                            //  In usual situation, disposing a segment is simply setting _head to _head._next
+                            //  But there is one special case, where _head and _tail points to the same and ONLY
+                            //segment of the queue: Another thread A is doing Enqueue and finds that it needs to grow,
+                            //while the *current* thread is doing *this* Dequeue operation, and finds that it needs to 
+                            //dispose the current (and ONLY) segment. Then we need to wait till thread A finishes its 
+                            //Grow operation, this is the reason of having the following while loop
+                            spinLocal = new SpinWait();
+                            while (_next == null)
+                            {
+                                spinLocal.SpinOnce();
+                            }
+                            Debug.Assert(_source._head == this);
+                            _source._head = _next;
+                        }
+                        return true;
+                    }
+                    else
+                    {
+                        //CAS failed due to contention: spin briefly and retry
+                        spin.SpinOnce();
+                        lowLocal = Low; highLocal = High;
+                    }
+                }//end of while
+                result = default(T);
+                return false;
+            }
+
+            /// <summary>
+            /// try to peek the current segment
+            /// </summary>
+            /// <param name="result">holds the return value of the element at the head position, 
+            /// value set to default(T) if there is no such an element</param>
+            /// <returns>true if there are elements in the current segment, false otherwise</returns>
+            internal bool TryPeek(out T result)
+            {
+                result = default(T);
+                int lowLocal = Low;
+                if (lowLocal > High)
+                    return false;
+                SpinWait spin = new SpinWait();
+                while (!_state[lowLocal]._value)
+                {
+                    spin.SpinOnce();
+                }
+                result = _array[lowLocal];
+                return true;
+            }
+
+            /// <summary>
+            /// Adds part or all of the current segment into a List.
+            /// </summary>
+            /// <param name="list">the list to which to add</param>
+            /// <param name="start">the start position</param>
+            /// <param name="end">the end position</param>
+            internal void AddToList(List<T> list, int start, int end)
+            {
+                for (int i = start; i <= end; i++)
+                {
+                    SpinWait spin = new SpinWait();
+                    while (!_state[i]._value)
+                    {
+                        spin.SpinOnce();
+                    }
+                    list.Add(_array[i]);
+                }
+            }
+
+            /// <summary>
+            /// return the position of the head of the current segment
+            /// Value range [0, SEGMENT_SIZE], if it's SEGMENT_SIZE, it means this segment is exhausted and thus empty
+            /// </summary>
+            internal int Low
+            {
+                get
+                {
+                    return Math.Min(_low, SEGMENT_SIZE);
+                }
+            }
+
+            /// <summary>
+            /// return the logical position of the tail of the current segment      
+            /// Value range [-1, SEGMENT_SIZE-1]. When it's -1, it means this is a new segment and has no elemnet yet
+            /// </summary>
+            internal int High
+            {
+                get
+                {
+                    //if _high > SEGMENT_SIZE, it means it's out of range, we should return
+                    //SEGMENT_SIZE-1 as the logical position
+                    return Math.Min(_high, SEGMENT_SIZE - 1);
+                }
+            }
+        }
+    }//end of class Segment
+
+    /// <summary>
+    /// A wrapper struct for volatile bool, please note the copy of the struct it self will not be volatile
+    /// for example this statement will not include in volatile operation volatileBool1 = volatileBool2 the jit will copy the struct and will ignore the volatile
+    /// </summary>
+    struct VolatileBool
+    {
+        public VolatileBool(bool value)
+        {
+            _value = value;
+        }
+        public volatile bool _value;
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/DataflowEtwProvider.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/DataflowEtwProvider.cs
new file mode 100644 (file)
index 0000000..1befecd
--- /dev/null
@@ -0,0 +1,235 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// DataflowEtwProvider.cs
+//
+//
+// EventSource for Dataflow.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Security;
+#if FEATURE_TRACING
+using System.Diagnostics.Tracing;
+#endif
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+#if FEATURE_TRACING
+    /// <summary>Provides an event source for tracing Dataflow information.</summary>
+    [EventSource(
+        Name = "System.Threading.Tasks.Dataflow.DataflowEventSource",
+        Guid = "16F53577-E41D-43D4-B47E-C17025BF4025",
+        LocalizationResources = "FxResources.System.Threading.Tasks.Dataflow.SR")]
+    internal sealed class DataflowEtwProvider : EventSource
+    {
+        /// <summary>
+        /// Defines the singleton instance for the dataflow ETW provider.
+        /// The dataflow provider GUID is {16F53577-E41D-43D4-B47E-C17025BF4025}.
+        /// </summary>
+        internal readonly static DataflowEtwProvider Log = new DataflowEtwProvider();
+        /// <summary>Prevent external instantiation.  All logging should go through the Log instance.</summary>
+        private DataflowEtwProvider() { }
+
+        /// <summary>Enabled for all keywords.</summary>
+        private const EventKeywords ALL_KEYWORDS = (EventKeywords)(-1);
+
+        //-----------------------------------------------------------------------------------
+        //        
+        // Dataflow Event IDs (must be unique)
+        //
+
+        /// <summary>The event ID for when we encounter a new dataflow block object that hasn't had its name traced to the trace file.</summary>
+        private const int DATAFLOWBLOCKCREATED_EVENTID = 1;
+        /// <summary>The event ID for the task launched event.</summary>
+        private const int TASKLAUNCHED_EVENTID = 2;
+        /// <summary>The event ID for the block completed event.</summary>
+        private const int BLOCKCOMPLETED_EVENTID = 3;
+        /// <summary>The event ID for the block linked event.</summary>
+        private const int BLOCKLINKED_EVENTID = 4;
+        /// <summary>The event ID for the block unlinked event.</summary>
+        private const int BLOCKUNLINKED_EVENTID = 5;
+
+        //-----------------------------------------------------------------------------------
+        //        
+        // Dataflow Events
+        //
+
+    #region Block Creation
+        /// <summary>Trace an event for when a new block is instantiated.</summary>
+        /// <param name="block">The dataflow block that was created.</param>
+        /// <param name="dataflowBlockOptions">The options with which the block was created.</param>
+        [NonEvent]
+        internal void DataflowBlockCreated(IDataflowBlock block, DataflowBlockOptions dataflowBlockOptions)
+        {
+            Contract.Requires(block != null, "Block needed for the ETW event.");
+            Contract.Requires(dataflowBlockOptions != null, "Options needed for the ETW event.");
+
+            if (IsEnabled(EventLevel.Informational, ALL_KEYWORDS))
+            {
+                DataflowBlockCreated(
+                    Common.GetNameForDebugger(block, dataflowBlockOptions),
+                    Common.GetBlockId(block));
+            }
+        }
+
+        [Event(DATAFLOWBLOCKCREATED_EVENTID, Level = EventLevel.Informational)]
+        private void DataflowBlockCreated(string blockName, int blockId)
+        {
+            WriteEvent(DATAFLOWBLOCKCREATED_EVENTID, blockName, blockId);
+        }
+    #endregion
+
+    #region Task Launching
+        /// <summary>Trace an event for a block launching a task to handle messages.</summary>
+        /// <param name="block">The owner block launching a task.</param>
+        /// <param name="task">The task being launched for processing.</param>
+        /// <param name="reason">The reason the task is being launched.</param>
+        /// <param name="availableMessages">The number of messages available to be handled by the task.</param>
+        [NonEvent]
+        internal void TaskLaunchedForMessageHandling(
+            IDataflowBlock block, Task task, TaskLaunchedReason reason, int availableMessages)
+        {
+            Contract.Requires(block != null, "Block needed for the ETW event.");
+            Contract.Requires(task != null, "Task needed for the ETW event.");
+            Contract.Requires(reason == TaskLaunchedReason.ProcessingInputMessages || reason == TaskLaunchedReason.OfferingOutputMessages,
+                "The reason should be a supported value from the TaskLaunchedReason enumeration.");
+            if (IsEnabled(EventLevel.Informational, ALL_KEYWORDS))
+            {
+                TaskLaunchedForMessageHandling(Common.GetBlockId(block), reason, availableMessages, task.Id);
+            }
+        }
+
+        [ThreadStatic]
+        private static object[] t_sharedArray;
+
+        [Event(TASKLAUNCHED_EVENTID, Level = EventLevel.Informational)]
+        private void TaskLaunchedForMessageHandling(int blockId, TaskLaunchedReason reason, int availableMessages, int taskId)
+        {
+            // There is no explicit WriteEvent() overload matching this event's fields:
+            //     WriteEvent(TASKLAUNCHED_EVENTID, blockId, (int)reason, availableMessages, taskId);
+            // Therefore this call would hit the "params" overload, which leads to multiple object 
+            // allocations every time this event is fired.
+
+            if (t_sharedArray == null)
+            {
+                t_sharedArray = new object[4];
+            }
+            t_sharedArray[0] = blockId;
+            t_sharedArray[1] = (int)reason;
+            t_sharedArray[2] = availableMessages;
+            t_sharedArray[3] = taskId;
+
+            WriteEvent(TASKLAUNCHED_EVENTID, t_sharedArray);
+        }
+
+        /// <summary>Describes the reason a task is being launched.</summary>
+        internal enum TaskLaunchedReason
+        {
+            /// <summary>A task is being launched to process incoming messages.</summary>
+            ProcessingInputMessages = 1,
+            /// <summary>A task is being launched to offer outgoing messages to linked targets.</summary>
+            OfferingOutputMessages = 2,
+        }
+    #endregion
+
+    #region Block Completion
+        /// <summary>Trace an event for a block completing.</summary>
+        /// <param name="block">The block that's completing.</param>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        [NonEvent]
+        internal void DataflowBlockCompleted(IDataflowBlock block)
+        {
+            Contract.Requires(block != null, "Block needed for the ETW event.");
+            if (IsEnabled(EventLevel.Informational, ALL_KEYWORDS))
+            {
+                Task completionTask = Common.GetPotentiallyNotSupportedCompletionTask(block);
+                bool blockIsCompleted = completionTask != null && completionTask.IsCompleted;
+                Debug.Assert(blockIsCompleted, "Block must be completed for this event to be valid.");
+                if (blockIsCompleted)
+                {
+                    var reason = (BlockCompletionReason)completionTask.Status;
+                    string exceptionData = string.Empty;
+
+                    if (completionTask.IsFaulted)
+                    {
+                        try { exceptionData = string.Join(Environment.NewLine, completionTask.Exception.InnerExceptions.Select(e => e.ToString())); }
+                        catch { }
+                    }
+
+                    DataflowBlockCompleted(Common.GetBlockId(block), reason, exceptionData);
+                }
+            }
+        }
+
+        /// <summary>Describes the reason a block completed.</summary>
+        internal enum BlockCompletionReason
+        {
+            /// <summary>The block completed successfully.</summary>
+            RanToCompletion = (int)TaskStatus.RanToCompletion,
+            /// <summary>The block completed due to an error.</summary>
+            Faulted = (int)TaskStatus.Faulted,
+            /// <summary>The block completed due to cancellation.</summary>
+            Canceled = (int)TaskStatus.Canceled
+        }
+
+        [Event(BLOCKCOMPLETED_EVENTID, Level = EventLevel.Informational)]
+        private void DataflowBlockCompleted(int blockId, BlockCompletionReason reason, string exceptionData)
+        {
+            WriteEvent(BLOCKCOMPLETED_EVENTID, blockId, (int)reason, exceptionData);
+        }
+    #endregion
+
+    #region Linking
+        /// <summary>Trace an event for a block linking.</summary>
+        /// <param name="source">The source block linking to a target.</param>
+        /// <param name="target">The target block being linked from a source.</param>
+        [NonEvent]
+        internal void DataflowBlockLinking<T>(ISourceBlock<T> source, ITargetBlock<T> target)
+        {
+            Contract.Requires(source != null, "Source needed for the ETW event.");
+            Contract.Requires(target != null, "Target needed for the ETW event.");
+            if (IsEnabled(EventLevel.Informational, ALL_KEYWORDS))
+            {
+                DataflowBlockLinking(Common.GetBlockId(source), Common.GetBlockId(target));
+            }
+        }
+
+        [Event(BLOCKLINKED_EVENTID, Level = EventLevel.Informational)]
+        private void DataflowBlockLinking(int sourceId, int targetId)
+        {
+            WriteEvent(BLOCKLINKED_EVENTID, sourceId, targetId);
+        }
+    #endregion
+
+    #region Unlinking
+        /// <summary>Trace an event for a block unlinking.</summary>
+        /// <param name="source">The source block unlinking from a target.</param>
+        /// <param name="target">The target block being unlinked from a source.</param>
+        [NonEvent]
+        internal void DataflowBlockUnlinking<T>(ISourceBlock<T> source, ITargetBlock<T> target)
+        {
+            Contract.Requires(source != null, "Source needed for the ETW event.");
+            Contract.Requires(target != null, "Target needed for the ETW event.");
+            if (IsEnabled(EventLevel.Informational, ALL_KEYWORDS))
+            {
+                // Try catch exists to prevent against faulty blocks or blocks that only partially implement the interface
+                DataflowBlockUnlinking(Common.GetBlockId(source), Common.GetBlockId(target));
+            }
+        }
+
+        [Event(BLOCKUNLINKED_EVENTID, Level = EventLevel.Informational)]
+        private void DataflowBlockUnlinking(int sourceId, int targetId)
+        {
+            WriteEvent(BLOCKUNLINKED_EVENTID, sourceId, targetId);
+        }
+    #endregion
+    }
+#endif
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/EnumerableDebugView.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/EnumerableDebugView.cs
new file mode 100644 (file)
index 0000000..4bcabb3
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// EnumerableDebugView.cs
+//
+//
+// Debugger type proxy for enumerables.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Linq;
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    /// <summary>Debugger type proxy for an enumerable of T.</summary>
+    internal sealed class EnumerableDebugView<TKey, TValue>
+    {
+        /// <summary>The enumerable being visualized.</summary>
+        private readonly IEnumerable<KeyValuePair<TKey, TValue>> _enumerable;
+
+        /// <summary>Initializes the debug view.</summary>
+        /// <param name="enumerable">The enumerable being debugged.</param>
+        public EnumerableDebugView(IEnumerable<KeyValuePair<TKey, TValue>> enumerable)
+        {
+            Contract.Requires(enumerable != null, "Expected a non-null enumerable.");
+            _enumerable = enumerable;
+        }
+
+        /// <summary>Gets the contents of the list.</summary>
+        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+        public KeyValuePair<TKey, TValue>[] Items { get { return _enumerable.ToArray(); } }
+    }
+
+    /// <summary>Debugger type proxy for an enumerable of T.</summary>
+    internal sealed class EnumerableDebugView<T>
+    {
+        /// <summary>The enumerable being visualized.</summary>
+        private readonly IEnumerable<T> _enumerable;
+
+        /// <summary>Initializes the debug view.</summary>
+        /// <param name="enumerable">The enumerable being debugged.</param>
+        public EnumerableDebugView(IEnumerable<T> enumerable)
+        {
+            Contract.Requires(enumerable != null, "Expected a non-null enumerable.");
+            _enumerable = enumerable;
+        }
+
+        /// <summary>Gets the contents of the list.</summary>
+        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+        public T[] Items { get { return _enumerable.ToArray(); } }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/IDebuggerDisplay.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/IDebuggerDisplay.cs
new file mode 100644 (file)
index 0000000..4b7118a
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// IDebuggerDisplay.cs
+//
+//
+// An interface implemented by objects that expose their debugger display
+// attribute content through a property, making it possible for code to query
+// for the same content.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    /// <summary>Implemented to provide customizable data for debugger displays.</summary>
+    internal interface IDebuggerDisplay
+    {
+        /// <summary>The object to be displayed as the content of a DebuggerDisplayAttribute.</summary>
+        /// <remarks>
+        /// The property returns an object to allow the debugger to interpret arbitrary .NET objects.
+        /// The return value may be, but need not be limited to be, a string.
+        /// </remarks>
+        object Content { get; }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/IProducerConsumerCollection.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/IProducerConsumerCollection.cs
new file mode 100644 (file)
index 0000000..2ca643a
--- /dev/null
@@ -0,0 +1,112 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// IProducerConsumerCollection.cs
+//
+//
+// A common interface for all concurrent collections.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace System.Threading.Tasks.Dataflow.Internal.Collections
+{
+    /// <summary>
+    /// Defines methods to manipulate thread-safe collections intended for producer/consumer usage.
+    /// </summary>
+    /// <typeparam name="T">Specifies the type of elements in the collection.</typeparam>
+    /// <remarks>
+    /// All implementations of this interface must enable all members of this interface
+    /// to be used concurrently from multiple threads.
+    /// </remarks>
+    internal interface IProducerConsumerCollection<T> : IEnumerable<T>, ICollection
+    {
+        /// <summary>
+        /// Copies the elements of the <see cref="IProducerConsumerCollection{T}"/> to
+        /// an
+        /// <see cref="T:System.Array"/>, starting at a specified index.
+        /// </summary>
+        /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of
+        /// the elements copied from the <see cref="IProducerConsumerCollection{T}"/>.
+        /// The array must have zero-based indexing.</param>
+        /// <param name="index">The zero-based index in <paramref name="array"/> at which copying
+        /// begins.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="array"/> is a null reference (Nothing in
+        /// Visual Basic).</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than
+        /// zero.</exception>
+        /// <exception cref="ArgumentException"><paramref name="index"/> is equal to or greater than the
+        /// length of the <paramref name="array"/>
+        /// -or- The number of elements in the source <see cref="ConcurrentQueue{T}"/> is greater than the
+        /// available space from <paramref name="index"/> to the end of the destination <paramref
+        /// name="array"/>.
+        /// </exception>
+        void CopyTo(T[] array, int index);
+
+        /// <summary>
+        /// Attempts to add an object to the <see
+        /// cref="IProducerConsumerCollection{T}"/>.
+        /// </summary>
+        /// <param name="item">The object to add to the <see
+        /// cref="IProducerConsumerCollection{T}"/>.</param>
+        /// <returns>true if the object was added successfully; otherwise, false.</returns>
+        /// <exception cref="T:System.ArgumentException">The <paramref name="item"/> was invalid for this collection.</exception>
+        bool TryAdd(T item);
+
+        /// <summary>
+        /// Attempts to remove and return an object from the <see cref="IProducerConsumerCollection{T}"/>.
+        /// </summary>
+        /// <param name="item">
+        /// When this method returns, if the object was removed and returned successfully, <paramref
+        /// name="item"/> contains the removed object. If no object was available to be removed, the value is
+        /// unspecified.
+        /// </param>
+        /// <returns>true if an object was removed and returned successfully; otherwise, false.</returns>
+        bool TryTake(out T item);
+
+        /// <summary>
+        /// Copies the elements contained in the <see cref="IProducerConsumerCollection{T}"/> to a new array.
+        /// </summary>
+        /// <returns>A new array containing the elements copied from the <see cref="IProducerConsumerCollection{T}"/>.</returns>
+        T[] ToArray();
+    }
+
+
+    /// <summary>
+    /// A debugger view of the IProducerConsumerCollection that makes it simple to browse the
+    /// collection's contents at a point in time.
+    /// </summary>
+    /// <typeparam name="T">The type of elements stored within.</typeparam>
+    internal sealed class SystemCollectionsConcurrent_ProducerConsumerCollectionDebugView<T>
+    {
+        private IProducerConsumerCollection<T> _collection; // The collection being viewed.
+
+        /// <summary>
+        /// Constructs a new debugger view object for the provided collection object.
+        /// </summary>
+        /// <param name="collection">A collection to browse in the debugger.</param>
+        public SystemCollectionsConcurrent_ProducerConsumerCollectionDebugView(IProducerConsumerCollection<T> collection)
+        {
+            if (collection == null)
+            {
+                throw new ArgumentNullException("collection");
+            }
+
+            _collection = collection;
+        }
+
+        /// <summary>
+        /// Returns a snapshot of the underlying collection's elements.
+        /// </summary>
+        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+        public T[] Items
+        {
+            get { return _collection.ToArray(); }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ImmutableList.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ImmutableList.cs
new file mode 100644 (file)
index 0000000..93943f7
--- /dev/null
@@ -0,0 +1,89 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// ImmutableList.cs
+//
+//
+// An immutable data structure that supports adding, removing, and enumerating elements.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Diagnostics;
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    /// <summary>Provides a simple, immutable list.</summary>
+    /// <typeparam name="T">Specifies the type of the data stored in the list.</typeparam>
+    [DebuggerDisplay("Count={Count}")]
+    [DebuggerTypeProxy(typeof(EnumerableDebugView<>))]
+    internal sealed class ImmutableList<T> : IEnumerable<T>
+    {
+        /// <summary>An empty list.</summary>
+        private readonly static ImmutableList<T> _empty = new ImmutableList<T>();
+        /// <summary>The immutable data in this list instance.</summary>
+        private readonly T[] _array;
+
+        /// <summary>Gets the empty list.</summary>
+        public static ImmutableList<T> Empty { get { return _empty; } }
+
+        /// <summary>Initializes the immutable list to be empty.</summary>
+        private ImmutableList() : this(new T[0]) { }
+
+        /// <summary>Initializes the immutable list with the specified elements.</summary>
+        /// <param name="elements">The element array to use for this list's data.</param>
+        private ImmutableList(T[] elements)
+        {
+            Contract.Requires(elements != null, "List requires an array to wrap.");
+            _array = elements;
+        }
+
+        /// <summary>Creates a new immutable list from this list and the additional element.</summary>
+        /// <param name="item">The item to add.</param>
+        /// <returns>The new list.</returns>
+        public ImmutableList<T> Add(T item)
+        {
+            // Copy the elements from this list and the item
+            // to a new list that's returned.
+            var newArray = new T[_array.Length + 1];
+            Array.Copy(_array, 0, newArray, 0, _array.Length);
+            newArray[newArray.Length - 1] = item;
+            return new ImmutableList<T>(newArray);
+        }
+
+        /// <summary>Creates a new immutable list from this list and without the specified element.</summary>
+        /// <param name="item">The item to remove.</param>
+        /// <returns>The new list.</returns>
+        public ImmutableList<T> Remove(T item)
+        {
+            // Get the index of the element.  If it's not in the list, just return this list.
+            int index = Array.IndexOf(_array, item);
+            if (index < 0) return this;
+
+            // It's in the list, so if it's the only one, just return the empty list
+            if (_array.Length == 1) return Empty;
+
+            // Otherwise, copy the other elements to a new list that's returned.
+            var newArray = new T[_array.Length - 1];
+            Array.Copy(_array, 0, newArray, 0, index);
+            Array.Copy(_array, index + 1, newArray, index, _array.Length - index - 1);
+            return new ImmutableList<T>(newArray);
+        }
+
+        /// <summary>Gets the number of elements in this list.</summary>
+        public int Count { get { return _array.Length; } }
+
+        /// <summary>Gets whether the list contains the specified item.</summary>
+        /// <param name="item">The item to lookup.</param>
+        /// <returns>true if the list contains the item; otherwise, false.</returns>
+        public bool Contains(T item) { return Array.IndexOf(_array, item) >= 0; }
+
+        /// <summary>Returns an enumerator that iterates through the collection.</summary>
+        public IEnumerator<T> GetEnumerator() { return ((IEnumerable<T>)_array).GetEnumerator(); }
+        /// <summary>Returns an enumerator that iterates through the collection.</summary>
+        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/Padding.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/Padding.cs
new file mode 100644 (file)
index 0000000..9786e14
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// Padding.cs
+//
+//
+// Helper structs for padding over CPU cache lines to avoid false sharing.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Runtime.InteropServices;
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    /// <summary>A placeholder class for common padding constants and eventually routines.</summary>
+    internal static class Padding
+    {
+        /// <summary>A size greater than or equal to the size of the most common CPU cache lines.</summary>
+        internal const int CACHE_LINE_SIZE = 128;
+    }
+
+    /// <summary>Padding structure used to minimize false sharing in SingleProducerSingleConsumerQueue{T}.</summary>
+    [StructLayout(LayoutKind.Explicit, Size = Padding.CACHE_LINE_SIZE - sizeof(Int32))] // Based on common case of 64-byte cache lines
+    internal struct PaddingForInt32
+    {
+    }
+
+    /// <summary>Value type that contains single Int64 value padded on both sides.</summary>
+    [StructLayout(LayoutKind.Explicit, Size = 2 * Padding.CACHE_LINE_SIZE)]
+    internal struct PaddedInt64
+    {
+        [FieldOffset(Padding.CACHE_LINE_SIZE)]
+        internal Int64 Value;
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ProducerConsumerQueues.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ProducerConsumerQueues.cs
new file mode 100644 (file)
index 0000000..4f2fbf5
--- /dev/null
@@ -0,0 +1,558 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// ProducerConsumerQueues.cs
+//
+//
+// Specialized producer/consumer queues.
+//
+//
+// ************<IMPORTANT NOTE>*************
+//
+// There are two exact copies of this file:
+//  src\ndp\clr\src\bcl\system\threading\tasks\producerConsumerQueue.cs
+//  src\ndp\fx\src\dataflow\system\threading\tasks\dataflow\internal\producerConsumerQueue.cs
+// Keep both of them consistent by changing the other file when you change this one, also avoid:
+//  1- To reference internal types in mscorlib
+//  2- To reference any dataflow specific types
+// This should be fixed post Dev11 when this class becomes public.
+//
+// ************</IMPORTANT NOTE>*************
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections;
+#if CONCURRENT_COLLECTIONS
+using System.Collections.Concurrent;
+#else
+using System.Threading.Tasks.Dataflow.Internal.Collections;
+#endif
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+
+namespace System.Threading.Tasks
+{
+    /// <summary>Represents a producer/consumer queue used internally by dataflow blocks.</summary>
+    /// <typeparam name="T">Specifies the type of data contained in the queue.</typeparam>
+    internal interface IProducerConsumerQueue<T> : IEnumerable<T>
+    {
+        /// <summary>Enqueues an item into the queue.</summary>
+        /// <param name="item">The item to enqueue.</param>
+        /// <remarks>This method is meant to be thread-safe subject to the particular nature of the implementation.</remarks>
+        void Enqueue(T item);
+
+        /// <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>
+        /// <remarks>This method is meant to be thread-safe subject to the particular nature of the implementation.</remarks>
+        bool TryDequeue(out T result);
+
+        /// <summary>Gets whether the collection is currently empty.</summary>
+        /// <remarks>This method may or may not be thread-safe.</remarks>
+        bool IsEmpty { get; }
+
+        /// <summary>Gets the number of items in the collection.</summary>
+        /// <remarks>In many implementations, this method will not be thread-safe.</remarks>
+        int Count { get; }
+
+        /// <summary>A thread-safe way to get the number of items in the collection. May synchronize access by locking the provided synchronization object.</summary>
+        /// <param name="syncObj">The sync object used to lock</param>
+        /// <returns>The collection count</returns>
+        int GetCountSafe(object syncObj);
+    }
+
+    /// <summary>
+    /// Provides a producer/consumer queue safe to be used by any number of producers and consumers concurrently.
+    /// </summary>
+    /// <typeparam name="T">Specifies the type of data contained in the queue.</typeparam>
+    [DebuggerDisplay("Count = {Count}")]
+    internal sealed class MultiProducerMultiConsumerQueue<T> : ConcurrentQueue<T>, IProducerConsumerQueue<T>
+    {
+        /// <summary>Enqueues an item into the queue.</summary>
+        /// <param name="item">The item to enqueue.</param>
+        void IProducerConsumerQueue<T>.Enqueue(T item) { base.Enqueue(item); }
+
+        /// <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>
+        bool IProducerConsumerQueue<T>.TryDequeue(out T result) { return base.TryDequeue(out result); }
+
+        /// <summary>Gets whether the collection is currently empty.</summary>
+        bool IProducerConsumerQueue<T>.IsEmpty { get { return base.IsEmpty; } }
+
+        /// <summary>Gets the number of items in the collection.</summary>
+        int IProducerConsumerQueue<T>.Count { get { return base.Count; } }
+
+        /// <summary>A thread-safe way to get the number of items in the collection. May synchronize access by locking the provided synchronization object.</summary>
+        /// <remarks>ConcurrentQueue.Count is thread safe, no need to acquire the lock.</remarks>
+        int IProducerConsumerQueue<T>.GetCountSafe(object syncObj) { return base.Count; }
+    }
+
+    /// <summary>
+    /// Provides a producer/consumer queue safe to be used by only one producer and one consumer concurrently.
+    /// </summary>
+    /// <typeparam name="T">Specifies the type of data contained in the queue.</typeparam>
+    [DebuggerDisplay("Count = {Count}")]
+    [DebuggerTypeProxy(typeof(SingleProducerSingleConsumerQueue<>.SingleProducerSingleConsumerQueue_DebugView))]
+    internal sealed class SingleProducerSingleConsumerQueue<T> : IProducerConsumerQueue<T>
+    {
+        // Design:
+        //
+        // SingleProducerSingleConsumerQueue (SPSCQueue) is a concurrent queue designed to be used 
+        // by one producer thread and one consumer thread. SPSCQueue does not work correctly when used by 
+        // multiple producer threads concurrently or multiple consumer threads concurrently.
+        // 
+        // SPSCQueue is based on segments that behave like circular buffers. Each circular buffer is represented 
+        // as an array with two indexes: _first and _last. _first is the index of the array slot for the consumer 
+        // to read next, and _last is the slot for the producer to write next. The circular buffer is empty when 
+        // (_first == _last), and full when ((_last+1) % _array.Length == _first).
+        //
+        // Since _first is only ever modified by the consumer thread and _last by the producer, the two indices can 
+        // be updated without interlocked operations. As long as the queue size fits inside a single circular buffer, 
+        // enqueues and dequeues simply advance the corresponding indices around the circular buffer. If an enqueue finds 
+        // that there is no room in the existing buffer, however, a new circular buffer is allocated that is twice as big 
+        // as the old buffer. From then on, the producer will insert values into the new buffer. The consumer will first 
+        // empty out the old buffer and only then follow the producer into the new (larger) buffer.
+        //
+        // As described above, the enqueue operation on the fast path only modifies the _first field of the current segment. 
+        // However, it also needs to read _last in order to verify that there is room in the current segment. Similarly, the 
+        // dequeue operation on the fast path only needs to modify _last, but also needs to read _first to verify that the 
+        // queue is non-empty. This results in true cache line sharing between the producer and the consumer.
+        //
+        // The cache line sharing issue can be mitigating by having a possibly stale copy of _first that is owned by the producer, 
+        // and a possibly stale copy of _last that is owned by the consumer. So, the consumer state is described using 
+        // (_first, _lastCopy) and the producer state using (_firstCopy, _last). The consumer state is separated from 
+        // the producer state by padding, which allows fast-path enqueues and dequeues from hitting shared cache lines. 
+        // _lastCopy is the consumer's copy of _last. Whenever the consumer can tell that there is room in the buffer 
+        // simply by observing _lastCopy, the consumer thread does not need to read _last and thus encounter a cache miss. Only 
+        // when the buffer appears to be empty will the consumer refresh _lastCopy from _last. _firstCopy is used by the producer 
+        // in the same way to avoid reading _first on the hot path.
+
+        /// <summary>The initial size to use for segments (in number of elements).</summary>
+        private const int INIT_SEGMENT_SIZE = 32; // must be a power of 2
+        /// <summary>The maximum size to use for segments (in number of elements).</summary>
+        private const int MAX_SEGMENT_SIZE = 0x1000000; // this could be made as large as Int32.MaxValue / 2
+
+        /// <summary>The head of the linked list of segments.</summary>
+        private volatile Segment _head;
+        /// <summary>The tail of the linked list of segments.</summary>
+        private volatile Segment _tail;
+
+        /// <summary>Initializes the queue.</summary>
+        internal SingleProducerSingleConsumerQueue()
+        {
+            // Validate constants in ctor rather than in an explicit cctor that would cause perf degradation
+            Debug.Assert(INIT_SEGMENT_SIZE > 0, "Initial segment size must be > 0.");
+            Debug.Assert((INIT_SEGMENT_SIZE & (INIT_SEGMENT_SIZE - 1)) == 0, "Initial segment size must be a power of 2");
+            Debug.Assert(INIT_SEGMENT_SIZE <= MAX_SEGMENT_SIZE, "Initial segment size should be <= maximum.");
+            Debug.Assert(MAX_SEGMENT_SIZE < Int32.MaxValue / 2, "Max segment size * 2 must be < Int32.MaxValue, or else overflow could occur.");
+
+            // Initialize the queue
+            _head = _tail = new Segment(INIT_SEGMENT_SIZE);
+        }
+
+        /// <summary>Enqueues an item into the queue.</summary>
+        /// <param name="item">The item to enqueue.</param>
+        public void Enqueue(T item)
+        {
+            Segment segment = _tail;
+            T[] array = segment._array;
+            int last = segment._state._last; // local copy to avoid multiple volatile reads
+
+            // Fast path: there's obviously room in the current segment
+            int tail2 = (last + 1) & (array.Length - 1);
+            if (tail2 != segment._state._firstCopy)
+            {
+                array[last] = item;
+                segment._state._last = tail2;
+            }
+            // Slow path: there may not be room in the current segment.
+            else EnqueueSlow(item, ref segment);
+        }
+
+        /// <summary>Enqueues an item into the queue.</summary>
+        /// <param name="item">The item to enqueue.</param>
+        /// <param name="segment">The segment in which to first attempt to store the item.</param>
+        private void EnqueueSlow(T item, ref Segment segment)
+        {
+            Contract.Requires(segment != null, "Expected a non-null segment.");
+
+            if (segment._state._firstCopy != segment._state._first)
+            {
+                segment._state._firstCopy = segment._state._first;
+                Enqueue(item); // will only recur once for this enqueue operation
+                return;
+            }
+
+            int newSegmentSize = _tail._array.Length << 1; // double size
+            Debug.Assert(newSegmentSize > 0, "The max size should always be small enough that we don't overflow.");
+            if (newSegmentSize > MAX_SEGMENT_SIZE) newSegmentSize = MAX_SEGMENT_SIZE;
+
+            var newSegment = new Segment(newSegmentSize);
+            newSegment._array[0] = item;
+            newSegment._state._last = 1;
+            newSegment._state._lastCopy = 1;
+
+            try { }
+            finally
+            {
+                // Finally block to protect against corruption due to a thread abort 
+                // between setting _next and setting _tail.
+                Volatile.Write(ref _tail._next, newSegment); // ensure segment not published until item is fully stored
+                _tail = newSegment;
+            }
+        }
+
+        /// <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)
+        {
+            Segment segment = _head;
+            T[] array = segment._array;
+            int first = segment._state._first; // local copy to avoid multiple volatile reads
+
+            // Fast path: there's obviously data available in the current segment
+            if (first != segment._state._lastCopy)
+            {
+                result = array[first];
+                array[first] = default(T); // Clear the slot to release the element
+                segment._state._first = (first + 1) & (array.Length - 1);
+                return true;
+            }
+            // Slow path: there may not be data available in the current segment
+            else return TryDequeueSlow(ref segment, ref array, out result);
+        }
+
+        /// <summary>Attempts to dequeue an item from the queue.</summary>
+        /// <param name="array">The array from which the item was dequeued.</param>
+        /// <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)
+        {
+            Contract.Requires(segment != null, "Expected a non-null segment.");
+            Contract.Requires(array != null, "Expected a non-null item array.");
+
+            if (segment._state._last != segment._state._lastCopy)
+            {
+                segment._state._lastCopy = segment._state._last;
+                return TryDequeue(out result); // will only recur once for this dequeue operation
+            }
+
+            if (segment._next != null && segment._state._first == segment._state._last)
+            {
+                segment = segment._next;
+                array = segment._array;
+                _head = segment;
+            }
+
+            int first = segment._state._first; // local copy to avoid extraneous volatile reads
+
+            if (first == segment._state._last)
+            {
+                result = default(T);
+                return false;
+            }
+
+            result = array[first];
+            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;
+        }
+
+        /// <summary>Attempts to peek at an item in the queue.</summary>
+        /// <param name="result">The peeked item.</param>
+        /// <returns>true if an item could be peeked; otherwise, false.</returns>
+        public bool TryPeek(out T result)
+        {
+            Segment segment = _head;
+            T[] array = segment._array;
+            int first = segment._state._first; // local copy to avoid multiple volatile reads
+
+            // Fast path: there's obviously data available in the current segment
+            if (first != segment._state._lastCopy)
+            {
+                result = array[first];
+                return true;
+            }
+            // Slow path: there may not be data available in the current segment
+            else return TryPeekSlow(ref segment, ref array, out result);
+        }
+
+        /// <summary>Attempts to peek at an item in the queue.</summary>
+        /// <param name="array">The array from which the item is peeked.</param>
+        /// <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)
+        {
+            Contract.Requires(segment != null, "Expected a non-null segment.");
+            Contract.Requires(array != null, "Expected a non-null item array.");
+
+            if (segment._state._last != segment._state._lastCopy)
+            {
+                segment._state._lastCopy = segment._state._last;
+                return TryPeek(out result); // will only recur once for this peek operation
+            }
+
+            if (segment._next != null && segment._state._first == segment._state._last)
+            {
+                segment = segment._next;
+                array = segment._array;
+                _head = segment;
+            }
+
+            int first = segment._state._first; // local copy to avoid extraneous volatile reads
+
+            if (first == segment._state._last)
+            {
+                result = default(T);
+                return false;
+            }
+
+            result = array[first];
+            return true;
+        }
+
+        /// <summary>Attempts to dequeue an item from the queue.</summary>
+        /// <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)
+        {
+            Segment segment = _head;
+            T[] array = segment._array;
+            int first = segment._state._first; // local copy to avoid multiple volatile reads
+
+            // Fast path: there's obviously data available in the current segment
+            if (first != segment._state._lastCopy)
+            {
+                result = array[first];
+                if (predicate == null || predicate(result))
+                {
+                    array[first] = default(T); // Clear the slot to release the element
+                    segment._state._first = (first + 1) & (array.Length - 1);
+                    return true;
+                }
+                else
+                {
+                    result = default(T);
+                    return false;
+                }
+            }
+            // Slow path: there may not be data available in the current segment
+            else return TryDequeueIfSlow(predicate, ref segment, ref array, out result);
+        }
+
+        /// <summary>Attempts to dequeue an item from the queue.</summary>
+        /// <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="array">The array from which the item was dequeued.</param>
+        /// <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)
+        {
+            Contract.Requires(segment != null, "Expected a non-null segment.");
+            Contract.Requires(array != null, "Expected a non-null item array.");
+
+            if (segment._state._last != segment._state._lastCopy)
+            {
+                segment._state._lastCopy = segment._state._last;
+                return TryDequeueIf(predicate, out result); // will only recur once for this dequeue operation
+            }
+
+            if (segment._next != null && segment._state._first == segment._state._last)
+            {
+                segment = segment._next;
+                array = segment._array;
+                _head = segment;
+            }
+
+            int first = segment._state._first; // local copy to avoid extraneous volatile reads
+
+            if (first == segment._state._last)
+            {
+                result = default(T);
+                return false;
+            }
+
+            result = array[first];
+            if (predicate == null || predicate(result))
+            {
+                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;
+            }
+            else
+            {
+                result = default(T);
+                return false;
+            }
+        }
+
+        public void Clear()
+        {
+            T ignored;
+            while (TryDequeue(out ignored)) ;
+        }
+
+        /// <summary>Gets whether the collection is currently empty.</summary>
+        /// <remarks>WARNING: This should not be used concurrently without further vetting.</remarks>
+        public bool IsEmpty
+        {
+            // This implementation is optimized for calls from the consumer.
+            get
+            {
+                Segment head = _head;
+                if (head._state._first != head._state._lastCopy) return false; // _first is volatile, so the read of _lastCopy cannot get reordered
+                if (head._state._first != head._state._last) return false;
+                return head._next == null;
+            }
+        }
+
+        /// <summary>Gets an enumerable for the collection.</summary>
+        /// <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 (int pt = segment._state._first;
+                    pt != segment._state._last;
+                    pt = (pt + 1) & (segment._array.Length - 1))
+                {
+                    yield return segment._array[pt];
+                }
+            }
+        }
+        /// <summary>Gets an enumerable for the collection.</summary>
+        /// <remarks>WARNING: This should only be used for debugging purposes.  It is not safe to be used concurrently.</remarks>
+        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
+
+        /// <summary>Gets the number of items in the collection.</summary>
+        /// <remarks>WARNING: This should only be used for debugging purposes.  It is not meant to be used concurrently.</remarks>
+        public int Count
+        {
+            get
+            {
+                int count = 0;
+                for (Segment segment = _head; segment != null; segment = segment._next)
+                {
+                    int arraySize = segment._array.Length;
+                    int first, last;
+                    while (true) // Count is not meant to be used concurrently, but this helps to avoid issues if it is
+                    {
+                        first = segment._state._first;
+                        last = segment._state._last;
+                        if (first == segment._state._first) break;
+                    }
+                    count += (last - first) & (arraySize - 1);
+                }
+                return count;
+            }
+        }
+
+        /// <summary>A thread-safe way to get the number of items in the collection. May synchronize access by locking the provided synchronization object.</summary>
+        /// <remarks>The Count is not thread safe, so we need to acquire the lock.</remarks>
+        int IProducerConsumerQueue<T>.GetCountSafe(object syncObj)
+        {
+            Debug.Assert(syncObj != null, "The syncObj parameter is null.");
+            lock (syncObj)
+            {
+                return Count;
+            }
+        }
+
+        /// <summary>A segment in the queue containing one or more items.</summary>
+        [StructLayout(LayoutKind.Sequential)]
+        private sealed class Segment
+        {
+            /// <summary>The next segment in the linked list of segments.</summary>
+            internal Segment _next;
+            /// <summary>The data stored in this segment.</summary>
+            internal readonly T[] _array;
+            /// <summary>Details about the segment.</summary>
+            internal SegmentState _state; // separated out to enable StructLayout attribute to take effect
+
+            /// <summary>Initializes the segment.</summary>
+            /// <param name="size">The size to use for this segment.</param>
+            internal Segment(int size)
+            {
+                Contract.Requires((size & (size - 1)) == 0, "Size must be a power of 2");
+                _array = new T[size];
+            }
+        }
+
+        /// <summary>Stores information about a segment.</summary>
+        [StructLayout(LayoutKind.Sequential)] // enforce layout so that padding reduces false sharing
+        private struct SegmentState
+        {
+            /// <summary>Padding to reduce false sharing between the segment's array and _first.</summary>
+            internal PaddingFor32 _pad0;
+
+            /// <summary>The index of the current head in the segment.</summary>
+            internal volatile int _first;
+            /// <summary>A copy of the current tail index.</summary>
+            internal int _lastCopy; // not volatile as read and written by the producer, except for IsEmpty, and there _lastCopy is only read after reading the volatile _first
+
+            /// <summary>Padding to reduce false sharing between the first and last.</summary>
+            internal PaddingFor32 _pad1;
+
+            /// <summary>A copy of the current head index.</summary>
+            internal int _firstCopy; // not volatile as only read and written by the consumer thread
+            /// <summary>The index of the current tail in the segment.</summary>
+            internal volatile int _last;
+
+            /// <summary>Padding to reduce false sharing with the last and what's after the segment.</summary>
+            internal PaddingFor32 _pad2;
+        }
+
+        /// <summary>Debugger type proxy for a SingleProducerSingleConsumerQueue of T.</summary>
+        private sealed class SingleProducerSingleConsumerQueue_DebugView
+        {
+            /// <summary>The queue being visualized.</summary>
+            private readonly SingleProducerSingleConsumerQueue<T> _queue;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="queue">The queue being debugged.</param>
+            public SingleProducerSingleConsumerQueue_DebugView(SingleProducerSingleConsumerQueue<T> queue)
+            {
+                Contract.Requires(queue != null, "Expected a non-null queue.");
+                _queue = queue;
+            }
+
+            /// <summary>Gets the contents of the list.</summary>
+            [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+            public T[] Items
+            {
+                get
+                {
+                    List<T> list = new List<T>();
+                    foreach (T item in _queue)
+                        list.Add(item);
+                    return list.ToArray();
+                }
+            }
+        }
+    }
+
+
+    /// <summary>A placeholder class for common padding constants and eventually routines.</summary>
+    static class PaddingHelpers
+    {
+        /// <summary>A size greater than or equal to the size of the most common CPU cache lines.</summary>
+        internal const int CACHE_LINE_SIZE = 128;
+    }
+
+    /// <summary>Padding structure used to minimize false sharing in SingleProducerSingleConsumerQueue{T}.</summary>
+    [StructLayout(LayoutKind.Explicit, Size = PaddingHelpers.CACHE_LINE_SIZE - sizeof(Int32))] // Based on common case of 64-byte cache lines
+    struct PaddingFor32
+    {
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/QueuedMap.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/QueuedMap.cs
new file mode 100644 (file)
index 0000000..f70de5b
--- /dev/null
@@ -0,0 +1,230 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// QueuedMap.cs
+//
+//
+// A key-value pair queue, where pushing an existing key into the collection overwrites
+// the existing value.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    /// <summary>
+    /// Provides a data structure that supports pushing and popping key/value pairs.
+    /// Pushing a key/value pair for which the key already exists results in overwriting
+    /// the existing key entry's value.
+    /// </summary>
+    /// <typeparam name="TKey">Specifies the type of keys in the map.</typeparam>
+    /// <typeparam name="TValue">Specifies the type of values in the map.</typeparam>
+    /// <remarks>This type is not thread-safe.</remarks>
+    [DebuggerDisplay("Count = {Count}")]
+    [DebuggerTypeProxy(typeof(EnumerableDebugView<,>))]
+    internal sealed class QueuedMap<TKey, TValue>
+    {
+        /// <summary>
+        /// A queue structure that uses an array-based list to store its items
+        /// and that supports overwriting elements at specific indices.
+        /// </summary>
+        /// <typeparam name="T">The type of the items storedin the queue</typeparam>
+        /// <remarks>This type is not thread-safe.</remarks>
+        private sealed class ArrayBasedLinkedQueue<T>
+        {
+            /// <summary>Terminator index.</summary>
+            private const int TERMINATOR_INDEX = -1;
+            /// <summary>
+            /// The queue where the items will be stored.
+            /// The key of each entry is the index of the next entry in the queue.
+            /// </summary>
+            private readonly List<KeyValuePair<int, T>> _storage;
+            /// <summary>Index of the first queue item.</summary>
+            private int _headIndex = TERMINATOR_INDEX;
+            /// <summary>Index of the last queue item.</summary>
+            private int _tailIndex = TERMINATOR_INDEX;
+            /// <summary>Index of the first free slot.</summary>
+            private int _freeIndex = TERMINATOR_INDEX;
+
+            /// <summary>Initializes the Queue instance.</summary>
+            internal ArrayBasedLinkedQueue()
+            {
+                _storage = new List<KeyValuePair<int, T>>();
+            }
+
+            /// <summary>Initializes the Queue instance.</summary>
+            /// <param name="capacity">The capacity of the internal storage.</param>
+            internal ArrayBasedLinkedQueue(int capacity)
+            {
+                _storage = new List<KeyValuePair<int, T>>(capacity);
+            }
+
+            /// <summary>Enqueues an item.</summary>
+            /// <param name="item">The item to be enqueued.</param>
+            /// <returns>The index of the slot where item was stored.</returns>
+            internal int Enqueue(T item)
+            {
+                int newIndex;
+
+                // If there is a free slot, reuse it
+                if (_freeIndex != TERMINATOR_INDEX)
+                {
+                    Debug.Assert(0 <= _freeIndex && _freeIndex < _storage.Count, "Index is out of range.");
+                    newIndex = _freeIndex;
+                    _freeIndex = _storage[_freeIndex].Key;
+                    _storage[newIndex] = new KeyValuePair<int, T>(TERMINATOR_INDEX, item);
+                }
+                // If there is no free slot, add one
+                else
+                {
+                    newIndex = _storage.Count;
+                    _storage.Add(new KeyValuePair<int, T>(TERMINATOR_INDEX, item));
+                }
+
+                if (_headIndex == TERMINATOR_INDEX)
+                {
+                    // Point _headIndex to newIndex if the queue was empty
+                    Debug.Assert(_tailIndex == TERMINATOR_INDEX, "If head indicates empty, so too should tail.");
+                    _headIndex = newIndex;
+                }
+                else
+                {
+                    // Point the tail slot to newIndex if the queue was not empty
+                    Debug.Assert(_tailIndex != TERMINATOR_INDEX, "If head does not indicate empty, neither should tail.");
+                    _storage[_tailIndex] = new KeyValuePair<int, T>(newIndex, _storage[_tailIndex].Value);
+                }
+
+                // Point the tail slot newIndex
+                _tailIndex = newIndex;
+
+                return newIndex;
+            }
+
+            /// <summary>Tries to dequeue an item.</summary>
+            /// <param name="item">The item that is dequeued.</param>
+            internal bool TryDequeue(out T item)
+            {
+                // If the queue is empty, just initialize the output item and return false
+                if (_headIndex == TERMINATOR_INDEX)
+                {
+                    Debug.Assert(_tailIndex == TERMINATOR_INDEX, "If head indicates empty, so too should tail.");
+                    item = default(T);
+                    return false;
+                }
+
+                // If there are items in the queue, start with populating the output item
+                Debug.Assert(0 <= _headIndex && _headIndex < _storage.Count, "Head is out of range.");
+                item = _storage[_headIndex].Value;
+
+                // 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));
+                _freeIndex = _headIndex;
+                _headIndex = newHeadIndex;
+                if (_headIndex == TERMINATOR_INDEX) _tailIndex = TERMINATOR_INDEX;
+
+                return true;
+            }
+
+            /// <summary>Replaces the item of a given slot.</summary>
+            /// <param name="index">The index of the slot where the value should be replaced.</param>
+            /// <param name="item">The item to be places.</param>
+            internal void Replace(int index, T item)
+            {
+                Debug.Assert(0 <= index && index < _storage.Count, "Index is out of range.");
+#if DEBUG
+                // Also assert that index does not belong to the list of free slots
+                for (int idx = _freeIndex; idx != TERMINATOR_INDEX; idx = _storage[idx].Key)
+                    Debug.Assert(idx != index, "Index should not belong to the list of free slots.");
+#endif
+                _storage[index] = new KeyValuePair<int, T>(_storage[index].Key, item);
+            }
+
+            internal bool IsEmpty { get { return _headIndex == TERMINATOR_INDEX; } }
+        }
+
+        /// <summary>The queue of elements.</summary>
+        private readonly ArrayBasedLinkedQueue<KeyValuePair<TKey, TValue>> _queue;
+        /// <summary>A map from key to index into the list.</summary>
+        /// <remarks>The correctness of this map relies on the list only having elements removed from its end.</remarks>
+        private readonly Dictionary<TKey, int> _mapKeyToIndex;
+
+        /// <summary>Initializes the QueuedMap.</summary>
+        internal QueuedMap()
+        {
+            _queue = new ArrayBasedLinkedQueue<KeyValuePair<TKey, TValue>>();
+            _mapKeyToIndex = new Dictionary<TKey, int>();
+        }
+
+        /// <summary>Initializes the QueuedMap.</summary>
+        /// <param name="capacity">The initial capacity of the data structure.</param>
+        internal QueuedMap(int capacity)
+        {
+            _queue = new ArrayBasedLinkedQueue<KeyValuePair<TKey, TValue>>(capacity);
+            _mapKeyToIndex = new Dictionary<TKey, int>(capacity);
+        }
+
+        /// <summary>Pushes a key/value pair into the data structure.</summary>
+        /// <param name="key">The key for the pair.</param>
+        /// <param name="value">The value for the pair.</param>
+        internal void Push(TKey key, TValue value)
+        {
+            // Try to get the index of the key in the queue. If it's there, replace the value.
+            int indexOfKeyInQueue;
+            if (!_queue.IsEmpty && _mapKeyToIndex.TryGetValue(key, out indexOfKeyInQueue))
+            {
+                _queue.Replace(indexOfKeyInQueue, new KeyValuePair<TKey, TValue>(key, value));
+            }
+            // If it's not there, add it to the queue and then add the mapping.
+            else
+            {
+                indexOfKeyInQueue = _queue.Enqueue(new KeyValuePair<TKey, TValue>(key, value));
+                _mapKeyToIndex.Add(key, indexOfKeyInQueue);
+            }
+        }
+
+        /// <summary>Try to pop the next element from the data structure.</summary>
+        /// <param name="item">The popped pair.</param>
+        /// <returns>true if an item could be popped; otherwise, false.</returns>
+        internal bool TryPop(out KeyValuePair<TKey, TValue> item)
+        {
+            bool popped = _queue.TryDequeue(out item);
+            if (popped) _mapKeyToIndex.Remove(item.Key);
+            return popped;
+        }
+
+        /// <summary>Tries to pop one or more elements from the data structure.</summary>
+        /// <param name="items">The items array into which the popped elements should be stored.</param>
+        /// <param name="arrayOffset">The offset into the array at which to start storing popped items.</param>
+        /// <param name="count">The number of items to be popped.</param>
+        /// <returns>The number of items popped, which may be less than the requested number if fewer existed in the data structure.</returns>
+        internal int PopRange(KeyValuePair<TKey, TValue>[] items, int arrayOffset, int count)
+        {
+            // As this data structure is internal, only assert incorrect usage.
+            // If this were to ever be made public, these would need to be real argument checks.
+            Contract.Requires(items != null, "Requires non-null array to store into.");
+            Contract.Requires(count >= 0 && arrayOffset >= 0, "Count and offset must be non-negative");
+            Contract.Requires(arrayOffset + count >= 0, "Offset plus count overflowed");
+            Contract.Requires(arrayOffset + count <= items.Length, "Range must be within array size");
+
+            int actualCount = 0;
+            for (int i = arrayOffset; actualCount < count; i++, actualCount++)
+            {
+                KeyValuePair<TKey, TValue> item;
+                if (TryPop(out item)) items[i] = item;
+                else break;
+            }
+
+            return actualCount;
+        }
+
+        /// <summary>Gets the number of items in the data structure.</summary>
+        internal int Count { get { return _mapKeyToIndex.Count; } }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ReorderingBuffer.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/ReorderingBuffer.cs
new file mode 100644 (file)
index 0000000..472f691
--- /dev/null
@@ -0,0 +1,184 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// ReorderingBuffer.cs
+//
+//
+// An intermediate buffer that ensures messages are output in the right order.
+// Used by blocks (e.g. TransformBlock, TransformManyBlock) when operating in 
+// parallel modes that could result in messages being processed out of order.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Linq;
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    /// <summary>Base interface for reordering buffers.</summary>
+    internal interface IReorderingBuffer
+    {
+        /// <summary>Informs the reordering buffer not to expect the message with the specified id.</summary>
+        /// <param name="id">The id of the message to be ignored.</param>
+        void IgnoreItem(long id);
+    }
+
+    /// <summary>Provides a buffer that reorders items according to their incoming IDs.</summary>
+    /// <typeparam name="TOutput">Specifies the type of data stored in the items being reordered.</typeparam>
+    /// <remarks>
+    /// This type expects the first item to be ID==0 and for all subsequent items
+    /// to increase IDs sequentially.
+    /// </remarks>
+    [DebuggerDisplay("Count={CountForDebugging}")]
+    [DebuggerTypeProxy(typeof(ReorderingBuffer<>.DebugView))]
+    internal sealed class ReorderingBuffer<TOutput> : IReorderingBuffer
+    {
+        /// <summary>The source that owns this reordering buffer.</summary>
+        private readonly object _owningSource;
+        /// <summary>A reordering buffer used when parallelism is employed and items may be completed out-of-order.</summary>
+        /// <remarks>Also serves as the sync object to protect the contents of this class.</remarks>
+        private readonly Dictionary<long, KeyValuePair<bool, TOutput>> _reorderingBuffer = new Dictionary<long, KeyValuePair<bool, TOutput>>();
+        /// <summary>Action used to output items in order.</summary>
+        private readonly Action<object, TOutput> _outputAction;
+        /// <summary>The ID of the next item that should be released from the reordering buffer.</summary>
+        private long _nextReorderedIdToOutput = 0;
+
+        /// <summary>Gets the object used to synchronize all access to the reordering buffer's internals.</summary>
+        private object ValueLock { get { return _reorderingBuffer; } }
+
+        /// <summary>Initializes the reordering buffer.</summary>
+        /// <param name="owningSource">The source that owns this reordering buffer.</param>
+        /// <param name="outputAction">The action to invoke when the next in-order item is available to be output.</param>
+        internal ReorderingBuffer(object owningSource, Action<object, TOutput> outputAction)
+        {
+            // Validate and store internal arguments
+            Contract.Requires(owningSource != null, "Buffer must be associated with a source.");
+            Contract.Requires(outputAction != null, "Action required for when items are to be released.");
+            _owningSource = owningSource;
+            _outputAction = outputAction;
+        }
+
+        /// <summary>Stores the next item as it completes processing.</summary>
+        /// <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)
+        {
+            Contract.Requires(id != Common.INVALID_REORDERING_ID, "This ID should never have been handed out.");
+            Common.ContractAssertMonitorStatus(ValueLock, held: false);
+
+            // This may be called concurrently, so protect the buffer...
+            lock (ValueLock)
+            {
+                // If this is the next item we need in our ordering, output it.
+                if (_nextReorderedIdToOutput == id)
+                {
+                    OutputNextItem(item, itemIsValid);
+                }
+                // Otherwise, we're using reordering and we're not ready for this item yet, so store
+                // it until we can use it.
+                else
+                {
+                    Debug.Assert((ulong)id > (ulong)_nextReorderedIdToOutput, "Duplicate id.");
+                    _reorderingBuffer.Add(id, new KeyValuePair<bool, TOutput>(itemIsValid, item));
+                }
+            }
+        }
+
+        /// <summary>
+        /// Determines whether the specified id is next to be output, and if it is
+        /// and if the item is "trusted" (meaning it may be output into the output
+        /// action as-is), adds it.
+        /// </summary>
+        /// <param name="id">The id of the item.</param>
+        /// <param name="item">The item.</param>
+        /// <param name="isTrusted">
+        /// Whether to allow the item to be output directly if it is the next item.
+        /// </param>
+        /// <returns>
+        /// null if the item was added.
+        /// 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)
+        {
+            Contract.Requires(id != Common.INVALID_REORDERING_ID, "This ID should never have been handed out.");
+            Common.ContractAssertMonitorStatus(ValueLock, held: false);
+
+            lock (ValueLock)
+            {
+                // If this is in the next item, try to take the fast path.
+                if (_nextReorderedIdToOutput == id)
+                {
+                    // If we trust this data structure to be stored as-is,
+                    // output it immediately.  Otherwise, return that it is
+                    // next to be output.
+                    if (isTrusted)
+                    {
+                        OutputNextItem(item, itemIsValid: true);
+                        return null;
+                    }
+                    else return true;
+                }
+                else return false;
+            }
+        }
+
+        /// <summary>Informs the reordering buffer not to expect the message with the specified id.</summary>
+        /// <param name="id">The id of the message to be ignored.</param>
+        public void IgnoreItem(long id)
+        {
+            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)
+        {
+            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);
+
+            // 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.
+            KeyValuePair<bool, TOutput> nextOutputItemWithValidity;
+            while (_reorderingBuffer.TryGetValue(_nextReorderedIdToOutput, out nextOutputItemWithValidity))
+            {
+                _reorderingBuffer.Remove(_nextReorderedIdToOutput);
+                _nextReorderedIdToOutput++;
+                if (nextOutputItemWithValidity.Key) _outputAction(_owningSource, nextOutputItemWithValidity.Value);
+            }
+        }
+
+        /// <summary>Gets a item count for debugging purposes.</summary>
+        private int CountForDebugging { get { return _reorderingBuffer.Count; } }
+
+        /// <summary>Provides a debugger type proxy for the buffer.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The buffer being debugged.</summary>
+            private readonly ReorderingBuffer<TOutput> _buffer;
+
+            /// <summary>Initializes the debug view.</summary>
+            /// <param name="buffer">The buffer being debugged.</param>
+            public DebugView(ReorderingBuffer<TOutput> buffer)
+            {
+                Contract.Requires(buffer != null, "Need a buffer with which to construct the debug view.");
+                _buffer = buffer;
+            }
+
+            /// <summary>Gets a dictionary of buffered items and their reordering IDs.</summary>
+            public Dictionary<long, KeyValuePair<Boolean, TOutput>> ItemsBuffered { get { return _buffer._reorderingBuffer; } }
+            /// <summary>Gets the next ID required for outputting.</summary>
+            public long NextIdRequired { get { return _buffer._nextReorderedIdToOutput; } }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/SourceCore.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/SourceCore.cs
new file mode 100644 (file)
index 0000000..8aa140d
--- /dev/null
@@ -0,0 +1,1035 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// SourceCore.cs
+//
+//
+// The core implementation of a standard ISourceBlock<TOutput>.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Security;
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    // LOCK-LEVELING SCHEME
+    // --------------------
+    // SourceCore employs two locks: OutgoingLock and ValueLock.  Additionally, targets we call out to
+    // likely utilize their own IncomingLock.  We can hold OutgoingLock while acquiring ValueLock or IncomingLock.
+    // However, we cannot hold ValueLock while calling out to external code or while acquiring OutgoingLock, and 
+    // we cannot hold IncomingLock when acquiring OutgoingLock. Additionally, the locks employed must be reentrant.
+
+    /// <summary>Provides a core implementation for blocks that implement <see cref="ISourceBlock{TOutput}"/>.</summary>
+    /// <typeparam name="TOutput">Specifies the type of data supplied by the <see cref="SourceCore{TOutput}"/>.</typeparam>
+    [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    internal sealed class SourceCore<TOutput>
+    {
+        // *** These fields are readonly and are initialized to new instances at construction.
+
+        /// <summary>A TaskCompletionSource that represents the completion of this block.</summary>
+        private readonly TaskCompletionSource<VoidResult> _completionTask = new TaskCompletionSource<VoidResult>();
+        /// <summary>A registry used to store all linked targets and information about them.</summary>
+        private readonly TargetRegistry<TOutput> _targetRegistry;
+        /// <summary>The output messages queued up to be received by consumers/targets.</summary>
+        /// <remarks>
+        /// The queue is only ever accessed by a single producer and single consumer at a time.  On the producer side,
+        /// we require that AddMessage/AddMessages are the only places the queue is added to, and we require that those
+        /// methods not be used concurrently with anything else.  All of our target halves today follow that restriction;
+        /// for example, TransformBlock with DOP==1 will have at most a single task processing the user provided delegate,
+        /// and thus at most one task calling AddMessage.  If it has a DOP > 1, it'll go through the ReorderingBuffer,
+        /// which will use a lock to synchronize the output of all of the processing tasks such that only one is using
+        /// AddMessage at a time.  On the consumer side of SourceCore, all consumption is protected by ValueLock, and thus
+        /// all consumption is serialized.
+        /// </remarks>
+        private readonly SingleProducerSingleConsumerQueue<TOutput> _messages = new SingleProducerSingleConsumerQueue<TOutput>(); // protected by AddMessage/ValueLock
+
+        /// <summary>Gets the object to use as the outgoing lock.</summary>
+        private object OutgoingLock { get { return _completionTask; } }
+        /// <summary>Gets the object to use as the value lock.</summary>
+        private object ValueLock { get { return _targetRegistry; } }
+
+        // *** These fields are readonly and are initialized by arguments to the constructor.
+
+        /// <summary>The source utilizing this helper.</summary>
+        private readonly ISourceBlock<TOutput> _owningSource;
+        /// <summary>The options used to configure this block's execution.</summary>
+        private readonly DataflowBlockOptions _dataflowBlockOptions;
+        /// <summary>
+        /// An action to be invoked on the owner block to stop accepting messages.
+        /// This action is invoked when SourceCore encounters an exception.
+        /// </summary>
+        private readonly Action<ISourceBlock<TOutput>> _completeAction;
+        /// <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<ISourceBlock<TOutput>, int> _itemsRemovedAction;
+        /// <summary>Item counting function</summary>
+        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
+        /// <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
+        /// <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>
+        private bool _enableOffering = true; // Protected by ValueLock, sometimes read with volatile reads
+        /// <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
+
+        /// <summary>Initializes the source core.</summary>
+        /// <param name="owningSource">The source utilizing this core.</param>
+        /// <param name="dataflowBlockOptions">The options to use to configure the block.</param>
+        /// <param name="completeAction">Action to invoke in order to decline the associated target half, which will in turn decline this source core.</param>
+        /// <param name="itemsRemovedAction">Action to invoke when one or more items is removed.  This may be null.</param>
+        /// <param name="itemCountingFunc">
+        /// Action to invoke when the owner needs to be able to count the number of individual
+        /// items in an output or set of outputs.
+        /// </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)
+        {
+            Contract.Requires(owningSource != null, "Core must be associated with a source.");
+            Contract.Requires(dataflowBlockOptions != null, "Options must be provided to configure the core.");
+            Contract.Requires(completeAction != null, "Action to invoke on completion is required.");
+
+            // Store the args
+            _owningSource = owningSource;
+            _dataflowBlockOptions = dataflowBlockOptions;
+            _itemsRemovedAction = itemsRemovedAction;
+            _itemCountingFunc = itemCountingFunc;
+            _completeAction = completeAction;
+
+            // Construct members that depend on the args
+            _targetRegistry = new TargetRegistry<TOutput>(_owningSource);
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
+        internal IDisposable LinkTo(ITargetBlock<TOutput> target, DataflowLinkOptions linkOptions)
+        {
+            // Validate arguments
+            if (target == null) throw new ArgumentNullException("target");
+            if (linkOptions == null) throw new ArgumentNullException("linkOptions");
+            Contract.EndContractBlock();
+
+            // If the block is already completed, there is not much to do -
+            // we have to propagate completion if that was requested, and
+            // then bail without taking the lock.
+            if (_completionTask.Task.IsCompleted)
+            {
+                if (linkOptions.PropagateCompletion) Common.PropagateCompletion(_completionTask.Task, target, exceptionHandler: null);
+                return Disposables.Nop;
+            }
+
+            lock (OutgoingLock)
+            {
+                // If completion has been reserved, the target registry has either been cleared already
+                // or is about to be cleared. So we can link and offer only if completion is not reserved. 
+                if (!_completionReserved)
+                {
+                    _targetRegistry.Add(ref target, linkOptions);
+                    OfferToTargets(linkToTarget: target);
+                    return Common.CreateUnlinker(OutgoingLock, _targetRegistry, target);
+                }
+            }
+
+            // The block should not offer any messages when it is in this state, but
+            // it should still propagate completion if that has been requested.
+            if (linkOptions.PropagateCompletion) Common.PropagateCompletionOnceCompleted(_completionTask.Task, target);
+            return Disposables.Nop;
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ConsumeMessage"]/*' />
+        internal TOutput ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out Boolean messageConsumed)
+        {
+            // Validate arguments
+            if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            if (target == null) throw new ArgumentNullException("target");
+            Contract.EndContractBlock();
+
+            TOutput consumedMessageValue = default(TOutput);
+
+            lock (OutgoingLock)
+            {
+                // If this target doesn't hold the reservation, then for this ConsumeMessage
+                // to be valid, there must not be any reservation (since otherwise we can't 
+                // consume a message destined for someone else).
+                if (_nextMessageReservedFor != target &&
+                    _nextMessageReservedFor != null)
+                {
+                    messageConsumed = false;
+                    return default(TOutput);
+                }
+
+                lock (ValueLock)
+                {
+                    // If the requested message isn't the next message to be served up, bail.
+                    // Otherwise, we're good to go: dequeue the message as it will now be owned by the target,
+                    // signal that we can resume enabling offering as there's potentially a new "next message",
+                    // complete if necessary, and offer asynchronously all messages as is appropriate.
+
+                    if (messageHeader.Id != _nextMessageId.Value ||
+                        !_messages.TryDequeue(out consumedMessageValue))
+                    {
+                        messageConsumed = false;
+                        return default(TOutput);
+                    }
+
+                    _nextMessageReservedFor = null;
+                    _targetRegistry.Remove(target, onlyIfReachedMaxMessages: true);
+                    _enableOffering = true; // reenable offering if it was disabled
+                    _nextMessageId.Value++;
+                    CompleteBlockIfPossible();
+                    OfferAsyncIfNecessary(isReplacementReplica: false, outgoingLockKnownAcquired: true);
+                }
+            }
+
+            // Notify the owner block that our count has decreased
+            if (_itemsRemovedAction != null)
+            {
+                int count = _itemCountingFunc != null ? _itemCountingFunc(_owningSource, consumedMessageValue, null) : 1;
+                _itemsRemovedAction(_owningSource, count);
+            }
+
+            // Return the consumed message value
+            messageConsumed = true;
+            return consumedMessageValue;
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+        internal Boolean ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
+        {
+            // Validate arguments
+            if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            if (target == null) throw new ArgumentNullException("target");
+            Contract.EndContractBlock();
+
+            lock (OutgoingLock)
+            {
+                // If no one currently holds a reservation...
+                if (_nextMessageReservedFor == null)
+                {
+                    lock (ValueLock)
+                    {
+                        // ...and if the requested message is next in the queue, allow it
+                        if (messageHeader.Id == _nextMessageId.Value && !_messages.IsEmpty)
+                        {
+                            _nextMessageReservedFor = target;
+                            _enableOffering = false;
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+        internal void ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
+        {
+            // Validate arguments
+            if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            if (target == null) throw new ArgumentNullException("target");
+            Contract.EndContractBlock();
+
+            lock (OutgoingLock)
+            {
+                // If someone else holds the reservation, bail.
+                if (_nextMessageReservedFor != target) throw new InvalidOperationException(SR.InvalidOperation_MessageNotReservedByTarget);
+
+                lock (ValueLock)
+                {
+                    // If this is not the message at the head of the queue, bail
+                    if (messageHeader.Id != _nextMessageId.Value || _messages.IsEmpty) throw new InvalidOperationException(SR.InvalidOperation_MessageNotReservedByTarget);
+
+                    // Otherwise, release the reservation
+                    _nextMessageReservedFor = null;
+                    Debug.Assert(!_enableOffering, "Offering should have been disabled if there was a valid reservation");
+                    _enableOffering = true;
+
+                    // Now there is at least one message ready for offering. So offer it.
+                    // If a cancellation is pending, this method will bail out.
+                    OfferAsyncIfNecessary(isReplacementReplica: false, outgoingLockKnownAcquired: true);
+
+                    // This reservation may be holding the block's completion. So try to complete.
+                    CompleteBlockIfPossible();
+                }
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+        internal Task Completion { get { return _completionTask.Task; } }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceive"]/*' />
+        internal Boolean TryReceive(Predicate<TOutput> filter, out TOutput item)
+        {
+            item = default(TOutput);
+            bool itemReceived = false;
+
+            lock (OutgoingLock)
+            {
+                // If the next message is reserved for someone, we can't receive right now.  Otherwise...
+                if (_nextMessageReservedFor == null)
+                {
+                    lock (ValueLock)
+                    {
+                        // If there's at least one message, and there's no filter or the next item
+                        // passes the filter, dequeue it to be returned.
+                        if (_messages.TryDequeueIf(filter, out item))
+                        {
+                            _nextMessageId.Value++;
+
+                            // Now that the next message has changed, reenable offering if it was disabled
+                            _enableOffering = true;
+
+                            // If removing this item was the last thing this block will ever do, complete it,
+                            CompleteBlockIfPossible();
+
+                            // Now, try to offer up messages asynchronously, since we've
+                            // changed what's at the head of the queue
+                            OfferAsyncIfNecessary(isReplacementReplica: false, outgoingLockKnownAcquired: true);
+
+                            itemReceived = true;
+                        }
+                    }
+                }
+            }
+
+            if (itemReceived)
+            {
+                // Notify the owner block that our count has decreased
+                if (_itemsRemovedAction != null)
+                {
+                    int count = _itemCountingFunc != null ? _itemCountingFunc(_owningSource, item, null) : 1;
+                    _itemsRemovedAction(_owningSource, count);
+                }
+            }
+            return itemReceived;
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="TryReceiveAll"]/*' />
+        internal bool TryReceiveAll(out IList<TOutput> items)
+        {
+            items = null;
+            int countReceived = 0;
+
+            lock (OutgoingLock)
+            {
+                // If the next message is reserved for someone, we can't receive right now.  Otherwise...
+                if (_nextMessageReservedFor == null)
+                {
+                    lock (ValueLock)
+                    {
+                        if (!_messages.IsEmpty)
+                        {
+                            // Receive all of the data, clearing it out in the process.
+                            var tmpList = new List<TOutput>();
+                            TOutput item;
+                            while (_messages.TryDequeue(out item)) tmpList.Add(item);
+                            countReceived = tmpList.Count;
+                            items = tmpList;
+
+                            // Increment the next ID. Any new value is good.
+                            _nextMessageId.Value++;
+
+                            // Now that the next message has changed, reenable offering if it was disabled
+                            _enableOffering = true;
+
+                            // Now that the block is empty, check to see whether we should complete.
+                            CompleteBlockIfPossible();
+                        }
+                    }
+                }
+            }
+
+            if (countReceived > 0)
+            {
+                // Notify the owner block that our count has decreased
+                if (_itemsRemovedAction != null)
+                {
+                    int count = _itemCountingFunc != null ? _itemCountingFunc(_owningSource, default(TOutput), items) : countReceived;
+                    _itemsRemovedAction(_owningSource, count);
+                }
+                return true;
+            }
+            else return false;
+        }
+
+        /// <summary>Gets the number of items available to be received from this block.</summary>
+        internal int OutputCount { get { lock (OutgoingLock) lock (ValueLock) return _messages.Count; } }
+
+        /// <summary>
+        /// Adds a message to the source block for propagation. 
+        /// This method must only be used by one thread at a time, and must not be used concurrently
+        /// with any other producer side methods, e.g. AddMessages, Complete.
+        /// </summary>
+        /// <param name="item">The item to be wrapped in a message to be added.</param>
+        internal void AddMessage(TOutput item)
+        {
+            // This method must not take the OutgoingLock, as it will likely be called in situations
+            // where an IncomingLock is held.
+
+            if (_decliningPermanently) return;
+            _messages.Enqueue(item);
+
+            Interlocked.MemoryBarrier(); // ensure the read of _taskForOutputProcessing doesn't move up before the writes in Enqueue
+
+            if (_taskForOutputProcessing == null)
+            {
+                // Separated out to enable inlining of AddMessage
+                OfferAsyncIfNecessaryWithValueLock();
+            }
+        }
+
+        /// <summary>
+        /// Adds messages to the source block for propagation. 
+        /// This method must only be used by one thread at a time, and must not be used concurrently
+        /// with any other producer side methods, e.g. AddMessage, Complete.
+        /// </summary>
+        /// <param name="items">The list of items to be wrapped in messages to be added.</param>
+        internal void AddMessages(IEnumerable<TOutput> items)
+        {
+            Contract.Requires(items != null, "Items list must be valid.");
+
+            // This method must not take the OutgoingLock, as it will likely be called in situations
+            // where an IncomingLock is held.
+
+            if (_decliningPermanently) return;
+
+            // Special case arrays and lists, for which we can avoid the 
+            // enumerator allocation that'll result from using a foreach.
+            // This also avoids virtual method calls that we'd get if we
+            // didn't special case.
+            var itemsAsList = items as List<TOutput>;
+            if (itemsAsList != null)
+            {
+                for (int i = 0; i < itemsAsList.Count; i++)
+                {
+                    _messages.Enqueue(itemsAsList[i]);
+                }
+            }
+            else
+            {
+                TOutput[] itemsAsArray = items as TOutput[];
+                if (itemsAsArray != null)
+                {
+                    for (int i = 0; i < itemsAsArray.Length; i++)
+                    {
+                        _messages.Enqueue(itemsAsArray[i]);
+                    }
+                }
+                else
+                {
+                    foreach (TOutput item in items)
+                    {
+                        _messages.Enqueue(item);
+                    }
+                }
+            }
+
+            Interlocked.MemoryBarrier(); // ensure the read of _taskForOutputProcessing doesn't move up before the writes in Enqueue
+
+            if (_taskForOutputProcessing == null)
+            {
+                OfferAsyncIfNecessaryWithValueLock();
+            }
+        }
+
+        /// <summary>Adds an individual exceptionto this source.</summary>
+        /// <param name="exception">The exception to add</param>
+        internal void AddException(Exception exception)
+        {
+            Contract.Requires(exception != null, "Valid exception must be provided to be added.");
+            Contract.Requires(!Completion.IsCompleted || Completion.IsFaulted, "The block must either not be completed or be faulted if we're still storing exceptions.");
+            lock (ValueLock)
+            {
+                Common.AddException(ref _exceptions, exception);
+            }
+        }
+
+        /// <summary>Adds exceptions to this source.</summary>
+        /// <param name="exceptions">The exceptions to add</param>
+        internal void AddExceptions(List<Exception> exceptions)
+        {
+            Contract.Requires(exceptions != null, "Valid exceptions must be provided to be added.");
+            Contract.Requires(!Completion.IsCompleted || Completion.IsFaulted, "The block must either not be completed or be faulted if we're still storing exceptions.");
+            lock (ValueLock)
+            {
+                foreach (Exception exception in exceptions)
+                {
+                    Common.AddException(ref _exceptions, exception);
+                }
+            }
+        }
+
+        /// <summary>Adds the exceptions contained in an AggregateException to this source.</summary>
+        /// <param name="aggregateException">The exception to add</param>
+        internal void AddAndUnwrapAggregateException(AggregateException aggregateException)
+        {
+            Contract.Requires(aggregateException != null && aggregateException.InnerExceptions.Count > 0, "Aggregate must be valid and contain inner exceptions to unwrap.");
+            Contract.Requires(!Completion.IsCompleted || Completion.IsFaulted, "The block must either not be completed or be faulted if we're still storing exceptions.");
+            lock (ValueLock)
+            {
+                Common.AddException(ref _exceptions, aggregateException, unwrapInnerExceptions: true);
+            }
+        }
+
+        /// <summary>Gets whether the _exceptions list is non-null.</summary>
+        internal bool HasExceptions
+        {
+            get
+            {
+                // We may check whether _exceptions is null without taking a lock because it is volatile
+                return Volatile.Read(ref _exceptions) != null;
+            }
+        }
+
+        /// <summary>Informs the block that it will not be receiving additional messages.</summary>
+        internal void Complete()
+        {
+            lock (ValueLock)
+            {
+                _decliningPermanently = true;
+
+                // CompleteAdding may be called in a context where an incoming lock is held.  We need to 
+                // call CompleteBlockIfPossible, but we can't do so if the incoming lock is held.
+                // However, we know that _decliningPermanently has been set, and thus the timing of
+                // CompleteBlockIfPossible doesn't matter, so we schedule it to run asynchronously
+                // 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;
+                    lock (thisSourceCore.OutgoingLock)
+                    {
+                        lock (thisSourceCore.ValueLock)
+                        {
+                            thisSourceCore.CompleteBlockIfPossible();
+                        }
+                    }
+                }, this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+            }
+        }
+
+        /// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
+        internal DataflowBlockOptions DataflowBlockOptions { get { return _dataflowBlockOptions; } }
+
+        /// <summary>Offers messages to all targets.</summary>
+        /// <param name="linkToTarget">
+        /// 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)
+        {
+            Common.ContractAssertMonitorStatus(OutgoingLock, held: true);
+            Common.ContractAssertMonitorStatus(ValueLock, held: false);
+
+            // If the next message is reserved, we can't offer anything
+            if (_nextMessageReservedFor != null)
+                return false;
+
+            // Peek at the next message if there is one, so we can offer it.
+            DataflowMessageHeader header = default(DataflowMessageHeader);
+            TOutput message = default(TOutput);
+            bool offerJustToLinkToTarget = false;
+
+            // If offering isn't enabled and if we're not doing this as 
+            // a result of LinkTo, bail. Otherwise, with offering disabled, we must have 
+            // already offered this message to all existing targets, so we can just offer 
+            // it to the newly linked target.
+            if (!Volatile.Read(ref _enableOffering))
+            {
+                if (linkToTarget == null) return false;
+                else offerJustToLinkToTarget = true;
+            }
+
+            // Otherwise, peek at message to offer
+            if (_messages.TryPeek(out message))
+            {
+                header = new DataflowMessageHeader(_nextMessageId.Value);
+            }
+
+            // If there is a message, offer it.
+            bool messageWasAccepted = false;
+            if (header.IsValid)
+            {
+                if (offerJustToLinkToTarget)
+                {
+                    // If we've already offered the message to everyone else,
+                    // we can just offer it to the newly linked target
+                    Debug.Assert(linkToTarget != null, "Must have a valid target to offer to.");
+                    OfferMessageToTarget(header, message, linkToTarget, out messageWasAccepted);
+                }
+                else
+                {
+                    // Otherwise, we've not yet offered this message to anyone, so even 
+                    // if linkToTarget is non-null, we need to propagate the message in order
+                    // through all of the registered targets, the last of which will be the linkToTarget
+                    // if it's non-null (no need to special-case it, though).
+
+                    // Note that during OfferMessageToTarget, a target may call ConsumeMessage (taking advantage of the
+                    // reentrancy of OutgoingLock), which may unlink the target if the target is registered as "unlinkAfterOne".  
+                    // Doing so will remove the target from the targets list. As such, we maintain the next node
+                    // 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;
+                    while (cur != null)
+                    {
+                        TargetRegistry<TOutput>.LinkedTargetInfo next = cur.Next;
+                        if (OfferMessageToTarget(header, message, cur.Target, out messageWasAccepted)) break;
+                        cur = next;
+                    }
+
+                    // If none of the targets accepted the message, disable offering.
+                    if (!messageWasAccepted)
+                    {
+                        lock (ValueLock)
+                        {
+                            _enableOffering = false;
+                        }
+                    }
+                }
+            }
+
+            // If a message got accepted, consume it and reenable offering.
+            if (messageWasAccepted)
+            {
+                lock (ValueLock)
+                {
+                    // SourceCore set consumeToAccept to false.  However, it's possible
+                    // that an incorrectly written target may ignore that parameter and synchronously consume
+                    // even though they weren't supposed to.  To recover from that, 
+                    // we'll only dequeue if the correct message is still at the head of the queue.
+                    // However, we'll assert so that we can at least catch this in our own debug builds.
+                    TOutput dropped;
+                    if (_nextMessageId.Value != header.Id ||
+                        !_messages.TryDequeue(out dropped)) // remove the next message
+                    {
+                        Debug.Assert(false, "The target did not follow the protocol.");
+                    }
+                    _nextMessageId.Value++;
+
+                    // The message was accepted, so there's now going to be a new next message.
+                    // If offering had been disabled, reenable it.
+                    _enableOffering = true;
+
+                    // Now that a message has been removed, we need to complete if possible or
+                    // or asynchronously offer if necessary.  However, if we're calling this as part of our
+                    // offering loop, we won't be able to do either, since by definition there's already
+                    // a processing task spun up (us) that would prevent these things.  So we only
+                    // do the checks if we're being called to link a new target rather than as part
+                    // of normal processing.
+                    if (linkToTarget != null)
+                    {
+                        CompleteBlockIfPossible();
+                        OfferAsyncIfNecessary(isReplacementReplica: false, outgoingLockKnownAcquired: true);
+                    }
+                }
+
+                // Notify the owner block that our count has decreased
+                if (_itemsRemovedAction != null)
+                {
+                    int count = _itemCountingFunc != null ? _itemCountingFunc(_owningSource, message, null) : 1;
+                    _itemsRemovedAction(_owningSource, count);
+                }
+            }
+
+            return messageWasAccepted;
+        }
+
+        /// <summary>Offers the message to the target.</summary>
+        /// <param name="header">The header of the message to offer.</param>
+        /// <param name="message">The message being offered.</param>
+        /// <param name="target">The single target to which the message should be offered.</param>
+        /// <param name="messageWasAccepted">true if the message was accepted by the target; otherwise, false.</param>
+        /// <returns>
+        /// true if the message should not be offered to additional targets; 
+        /// false if propagation should be allowed to continue.
+        /// </returns>
+        private bool OfferMessageToTarget(
+            DataflowMessageHeader header, TOutput message, ITargetBlock<TOutput> target,
+            out bool messageWasAccepted)
+        {
+            Contract.Requires(target != null, "Valid target to offer to is required.");
+            Common.ContractAssertMonitorStatus(OutgoingLock, held: true);
+            Common.ContractAssertMonitorStatus(ValueLock, held: false);
+
+            DataflowMessageStatus result = target.OfferMessage(header, message, _owningSource, consumeToAccept: false);
+            Debug.Assert(result != DataflowMessageStatus.NotAvailable, "Messages are not being offered concurrently, so nothing should be missed.");
+            messageWasAccepted = false;
+
+            // If accepted, note it, and if the target was linked as "once", remove it
+            if (result == DataflowMessageStatus.Accepted)
+            {
+                _targetRegistry.Remove(target, onlyIfReachedMaxMessages: true);
+                messageWasAccepted = true;
+                return true; // the message should not be offered to anyone else
+            }
+            // If declined permanently, remove the target
+            else if (result == DataflowMessageStatus.DecliningPermanently)
+            {
+                _targetRegistry.Remove(target);
+            }
+            // If the message was reserved by the target, stop propagating
+            else if (_nextMessageReservedFor != null)
+            {
+                Debug.Assert(result == DataflowMessageStatus.Postponed,
+                    "If the message was reserved, it should also have been postponed.");
+                return true; // the message should not be offered to anyone else
+            }
+            // If the result was Declined, there's nothing more to be done.
+            // This message will sit at the front of the queue until someone claims it.
+
+            return false; // allow the message to be offered to someone else
+        }
+
+        /// <summary>
+        /// Called when we want to enable asynchronously offering message to targets.
+        /// Takes the ValueLock before delegating to OfferAsyncIfNecessary.
+        /// </summary>
+        private void OfferAsyncIfNecessaryWithValueLock()
+        {
+            lock (ValueLock)
+            {
+                OfferAsyncIfNecessary(isReplacementReplica: false, outgoingLockKnownAcquired: false);
+            }
+        }
+
+        /// <summary>Called when we want to enable asynchronously offering message to targets.</summary>
+        /// <param name="isReplacementReplica">Whether this call is the continuation of a previous message loop.</param>
+        /// <param name="outgoingLockKnownAcquired">Whether the caller is sure that the outgoing lock is currently held by this thread.</param>
+        private void OfferAsyncIfNecessary(bool isReplacementReplica, bool outgoingLockKnownAcquired)
+        {
+            Common.ContractAssertMonitorStatus(ValueLock, held: true);
+
+            // Fast path to enable OfferAsyncIfNecessary to be inlined.  We only need
+            // to proceed if there's no task processing, offering is enabled, and
+            // there are no messages to be processed.
+            if (_taskForOutputProcessing == null && _enableOffering && !_messages.IsEmpty)
+            {
+                // Slow path: do additional checks and potentially launch new task
+                OfferAsyncIfNecessary_Slow(isReplacementReplica, outgoingLockKnownAcquired);
+            }
+        }
+
+        /// <summary>Called when we want to enable asynchronously offering message to targets.</summary>
+        /// <param name="isReplacementReplica">Whether this call is the continuation of a previous message loop.</param>
+        /// <param name="outgoingLockKnownAcquired">Whether the caller is sure that the outgoing lock is currently held by this thread.</param>
+        private void OfferAsyncIfNecessary_Slow(bool isReplacementReplica, bool outgoingLockKnownAcquired)
+        {
+            Common.ContractAssertMonitorStatus(ValueLock, held: true);
+            Debug.Assert(_taskForOutputProcessing == null && _enableOffering && !_messages.IsEmpty,
+                "The block must be enabled for offering, not currently be processing, and have messages available to process.");
+
+            // This method must not take the outgoing lock, as it will likely be called in situations
+            // where a derived type's incoming lock is held.
+
+            bool targetsAvailable = true;
+            if (outgoingLockKnownAcquired || Monitor.IsEntered(OutgoingLock))
+            {
+                Common.ContractAssertMonitorStatus(OutgoingLock, held: true);
+                targetsAvailable = _targetRegistry.FirstTargetNode != null;
+            }
+
+            // If there's any work to be done...
+            if (targetsAvailable && !CanceledOrFaulted)
+            {
+                // 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,
+                                                     Common.GetCreationOptionsForTask(isReplacementReplica));
+
+#if FEATURE_TRACING
+                DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+                if (etwLog.IsEnabled())
+                {
+                    etwLog.TaskLaunchedForMessageHandling(
+                        _owningSource, _taskForOutputProcessing, DataflowEtwProvider.TaskLaunchedReason.OfferingOutputMessages, _messages.Count);
+                }
+#endif
+
+                // Start the task handling scheduling exceptions
+#pragma warning disable 0420
+                Exception exception = Common.StartTaskSafe(_taskForOutputProcessing, _dataflowBlockOptions.TaskScheduler);
+#pragma warning restore 0420
+                if (exception != null)
+                {
+                    // First, log the exception while the processing state is dirty which is preventing the block from completing.
+                    // Then revert the proactive processing state changes.
+                    // And last, try to complete the block.
+                    AddException(exception);
+                    _taskForOutputProcessing = null;
+                    _decliningPermanently = true;
+
+                    // Get out from under currently held locks - ValueLock is taken, but OutgoingLock may not be.
+                    // Re-take the locks on a separate thread.
+                    Task.Factory.StartNew(state =>
+                    {
+                        var thisSourceCore = (SourceCore<TOutput>)state;
+                        lock (thisSourceCore.OutgoingLock)
+                        {
+                            lock (thisSourceCore.ValueLock)
+                            {
+                                thisSourceCore.CompleteBlockIfPossible();
+                            }
+                        }
+                    }, this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+                }
+                if (exception != null) AddException(exception);
+            }
+        }
+
+        /// <summary>Task body used to process messages.</summary>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private void OfferMessagesLoopCore()
+        {
+            Debug.Assert(_taskForOutputProcessing != null && _taskForOutputProcessing.Id == Task.CurrentId,
+                "Must be part of the current processing task.");
+            try
+            {
+                int maxMessagesPerTask = _dataflowBlockOptions.ActualMaxMessagesPerTask;
+
+                // We need to hold the outgoing lock while offering messages.  We can either
+                // lock and unlock for each individual offering, or we can lock around multiple or all
+                // possible offerings.  The former ensures that other operations don't get starved,
+                // while the latter is much more efficient (not continually acquiring and releasing
+                // the lock).  For blocks that aren't linked to any targets, this won't matter
+                // (no offering is done), and for blocks that are only linked to targets, this shouldn't 
+                // matter (no one is contending for the lock), thus
+                // the only case it would matter is when a block both has targets and is being
+                // explicitly received from, which is an uncommon scenario.  Thus, we want to lock
+                // around the whole thing to improve performance, but just in case we do hit
+                // an uncommon scenario, in the default case we release the lock every now and again.  
+                // If a developer wants to control this, they can limit the duration of the 
+                // lock by using MaxMessagesPerTask.
+
+                const int DEFAULT_RELEASE_LOCK_ITERATIONS = 10; // Dialable
+                int releaseLockIterations =
+                    _dataflowBlockOptions.MaxMessagesPerTask == DataflowBlockOptions.Unbounded ?
+                        DEFAULT_RELEASE_LOCK_ITERATIONS : maxMessagesPerTask;
+
+                for (int messageCounter = 0;
+                    messageCounter < maxMessagesPerTask && !CanceledOrFaulted;)
+                {
+                    lock (OutgoingLock)
+                    {
+                        // While there are more messages to process, offer each in turn
+                        // to the targets.  If we're unable to propagate a particular message,
+                        // stop trying until something changes in the future.
+                        for (
+                            int lockReleaseCounter = 0;
+                            messageCounter < maxMessagesPerTask && lockReleaseCounter < releaseLockIterations && !CanceledOrFaulted;
+                            ++messageCounter, ++lockReleaseCounter)
+                        {
+                            if (!OfferToTargets()) return;
+                        }
+                    }
+                }
+            }
+            catch (Exception exc)
+            {
+                // Record the exception
+                AddException(exc);
+
+                // Notify the owning block it should stop accepting new messages
+                _completeAction(_owningSource);
+            }
+            finally
+            {
+                lock (OutgoingLock)
+                {
+                    lock (ValueLock)
+                    {
+                        // We're no longer processing, so null out the processing task
+                        Debug.Assert(_taskForOutputProcessing != null && _taskForOutputProcessing.Id == Task.CurrentId,
+                            "Must be part of the current processing task.");
+                        _taskForOutputProcessing = null;
+                        Interlocked.MemoryBarrier(); // synchronize with AddMessage(s) and its read of _taskForOutputProcessing
+
+                        // However, we may have given up early because we hit our own configured
+                        // processing limits rather than because we ran out of work to do.  If that's
+                        // the case, make sure we spin up another task to keep going.
+                        OfferAsyncIfNecessary(isReplacementReplica: true, outgoingLockKnownAcquired: true);
+
+                        // If, however, we stopped because we ran out of work to do and we
+                        // know we'll never get more, then complete.
+                        CompleteBlockIfPossible();
+                    }
+                }
+            }
+        }
+
+        /// <summary>Gets whether the source has had cancellation requested or an exception has occurred.</summary>
+        private bool CanceledOrFaulted
+        {
+            get
+            {
+                // Cancellation is honored as soon as the CancellationToken has been signaled.
+                // Faulting is honored after an exception has been encountered and the owning block
+                // has invoked Complete on us.
+                return _dataflowBlockOptions.CancellationToken.IsCancellationRequested ||
+                    (HasExceptions && _decliningPermanently);
+            }
+        }
+
+        /// <summary>Completes the block's processing if there's nothing left to do and never will be.</summary>
+        private void CompleteBlockIfPossible()
+        {
+            Common.ContractAssertMonitorStatus(OutgoingLock, held: true);
+            Common.ContractAssertMonitorStatus(ValueLock, held: true);
+
+            if (!_completionReserved)
+            {
+                if (_decliningPermanently && // declining permanently, so no more messages will arrive
+                    _taskForOutputProcessing == null && // no current processing
+                    _nextMessageReservedFor == null) // no pending reservation
+                {
+                    CompleteBlockIfPossible_Slow();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Slow path for CompleteBlockIfPossible. 
+        /// Separating out the slow path into its own method makes it more likely that the fast path method will get inlined.
+        /// </summary>
+        private void CompleteBlockIfPossible_Slow()
+        {
+            Contract.Requires(
+                _decliningPermanently && _taskForOutputProcessing == null && _nextMessageReservedFor == null,
+                "The block must be declining permanently, there must be no reservations, and there must be no processing tasks");
+            Common.ContractAssertMonitorStatus(OutgoingLock, held: true);
+            Common.ContractAssertMonitorStatus(ValueLock, held: true);
+
+            if (_messages.IsEmpty || CanceledOrFaulted)
+            {
+                _completionReserved = true;
+
+                // 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(),
+                    this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+            }
+        }
+
+        /// <summary>
+        /// Completes the block.  This must only be called once, and only once all of the completion conditions are met.
+        /// As such, it must only be called from CompleteBlockIfPossible.
+        /// </summary>
+        private void CompleteBlockOncePossible()
+        {
+            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.
+            lock (OutgoingLock)
+            {
+                // Save the linked list of targets so that it could be traversed later to propagate completion
+                linkedTargets = _targetRegistry.ClearEntryPoints();
+                lock (ValueLock)
+                {
+                    _messages.Clear();
+
+                    // Save a local reference to the exceptions list and null out the field,
+                    // so that if the target side tries to add an exception this late,
+                    // it will go to a separate list (that will be ignored.)
+                    exceptions = _exceptions;
+                    _exceptions = null;
+                }
+            }
+
+            // If it's due to an unhandled exception, finish in an error state
+            if (exceptions != null)
+            {
+                _completionTask.TrySetException(exceptions);
+            }
+            // If it's due to cancellation, finish in a canceled state
+            else if (_dataflowBlockOptions.CancellationToken.IsCancellationRequested)
+            {
+                _completionTask.TrySetCanceled();
+            }
+            // Otherwise, finish in a successful state.
+            else
+            {
+                _completionTask.TrySetResult(default(VoidResult));
+            }
+
+            // Now that the completion task is completed, we may propagate completion to the linked targets
+            _targetRegistry.PropagateCompletion(linkedTargets);
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCompleted(_owningSource);
+            }
+#endif
+        }
+
+        /// <summary>Gets the object to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                var displaySource = _owningSource as IDebuggerDisplay;
+                return string.Format("Block=\"{0}\"",
+                    displaySource != null ? displaySource.Content : _owningSource);
+            }
+        }
+
+        /// <summary>Gets information about this helper to be used for display in a debugger.</summary>
+        /// <returns>Debugging information about this source core.</returns>
+        internal DebuggingInformation GetDebuggingInformation() { return new DebuggingInformation(this); }
+
+        /// <summary>Provides debugging information about the source core.</summary>
+        internal sealed class DebuggingInformation
+        {
+            /// <summary>The source being viewed.</summary>
+            private SourceCore<TOutput> _source;
+
+            /// <summary>Initializes the type proxy.</summary>
+            /// <param name="source">The source being viewed.</param>
+            internal DebuggingInformation(SourceCore<TOutput> source) { _source = source; }
+
+            /// <summary>Gets the number of messages available for receiving.</summary>
+            internal int OutputCount { get { return _source._messages.Count; } }
+            /// <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; } }
+
+            /// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
+            internal DataflowBlockOptions DataflowBlockOptions { get { return _source._dataflowBlockOptions; } }
+            /// <summary>Gets whether the block is declining further messages.</summary>
+            internal bool IsDecliningPermanently { get { return _source._decliningPermanently; } }
+            /// <summary>Gets whether the block is completed.</summary>
+            internal bool IsCompleted { get { return _source.Completion.IsCompleted; } }
+
+            /// <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; } }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/SpscTargetCore.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/SpscTargetCore.cs
new file mode 100644 (file)
index 0000000..0e036e8
--- /dev/null
@@ -0,0 +1,413 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// SpscTargetCore.cs
+//
+//
+// A fast single-producer-single-consumer core for a target block.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Security;
+
+#pragma warning disable 0420 // turn off warning for passing volatiles to interlocked operations
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    // SpscTargetCore provides a fast target core for use in blocks that will only have single-producer-single-consumer
+    // semantics.  Blocks configured with the default DOP==1 will be single consumer, so whether this core may be
+    // used is largely up to whether the block is also single-producer.  The ExecutionDataflowBlockOptions.SingleProducerConstrained
+    // option can be used by a developer to inform a block that it will only be accessed by one producer at a time,
+    // and a block like ActionBlock can utilize that knowledge to choose this target instead of the default TargetCore.
+    // However, there are further constraints that might prevent this core from being used.
+    //     - If the user specifies a CancellationToken, this core can't be used, as the cancellation request
+    //       could come in concurrently with the single producer accessing the block, thus resulting in multiple producers.
+    //     - If the user specifies a bounding capacity, this core can't be used, as the consumer processing items
+    //       needs to synchronize with producers around the change in bounding count, and the consumer is again
+    //       in effect another producer.
+    //     - If the block has a source half (e.g. TransformBlock) and that source could potentially call back
+    //       to the target half to, for example, notify it of exceptions occurring, again there would potentially
+    //       be multiple producers.
+    // Thus, when and how this SpscTargetCore may be applied is significantly constrained.
+
+    /// <summary>
+    /// Provides a core implementation of <see cref="ITargetBlock{TInput}"/> for use when there's only a single producer posting data.
+    /// </summary>
+    /// <typeparam name="TInput">Specifies the type of data accepted by the <see cref="TargetCore{TInput}"/>.</typeparam>
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    internal sealed class SpscTargetCore<TInput>
+    {
+        /// <summary>The target block using this helper.</summary>
+        private readonly ITargetBlock<TInput> _owningTarget;
+        /// <summary>The messages in this target.</summary>
+        private readonly SingleProducerSingleConsumerQueue<TInput> _messages = new SingleProducerSingleConsumerQueue<TInput>();
+        /// <summary>The options to use to configure this block. The target core assumes these options are immutable.</summary>
+        private readonly ExecutionDataflowBlockOptions _dataflowBlockOptions;
+        /// <summary>An action to invoke for every accepted message.</summary>
+        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;
+        /// <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>
+        private volatile bool _completionReserved;
+        /// <summary>
+        /// The Task currently active to process the block. This field is used to synchronize between producer and consumer, 
+        /// 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;
+        /// <summary>A task representing the completion of the block.  This field is lazily initialized.</summary>
+        private TaskCompletionSource<VoidResult> _completionTask;
+
+        /// <summary>Initialize the SPSC target core.</summary>
+        /// <param name="owningTarget">The owning target block.</param>
+        /// <param name="action">The action to be invoked for every message.</param>
+        /// <param name="dataflowBlockOptions">The options to use to configure this block. The target core assumes these options are immutable.</param>
+        internal SpscTargetCore(
+            ITargetBlock<TInput> owningTarget, Action<TInput> action, ExecutionDataflowBlockOptions dataflowBlockOptions)
+        {
+            Contract.Requires(owningTarget != null, "Expected non-null owningTarget");
+            Contract.Requires(action != null, "Expected non-null action");
+            Contract.Requires(dataflowBlockOptions != null, "Expected non-null dataflowBlockOptions");
+
+            _owningTarget = owningTarget;
+            _action = action;
+            _dataflowBlockOptions = dataflowBlockOptions;
+        }
+
+        internal bool Post(TInput messageValue)
+        {
+            if (_decliningPermanently)
+                return false;
+
+            // Store the offered message into the queue.
+            _messages.Enqueue(messageValue);
+
+            Interlocked.MemoryBarrier(); // ensure the read of _activeConsumer doesn't move up before the writes in Enqueue
+
+            // Make sure there's an active task available to handle processing this message.  If we find the task
+            // is null, we'll try to schedule one using an interlocked operation.  If we find the task is non-null,
+            // then there must be a task actively running.  If there's a race where the task is about to complete
+            // and nulls out its reference (using a barrier), it'll subsequently check whether there are any messages in the queue,
+            // and since we put the messages into the queue before now, it'll find them and use an interlocked
+            // to re-launch itself.
+            if (_activeConsumer == null)
+            {
+                ScheduleConsumerIfNecessary(false);
+            }
+
+            return true;
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
+        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) ? 
+                DataflowMessageStatus.Accepted :
+                OfferMessage_Slow(messageHeader, messageValue, source, consumeToAccept);
+        }
+
+        /// <summary>Implements the slow path for OfferMessage.</summary>
+        /// <param name="messageHeader">The message header for the offered value.</param>
+        /// <param name="messageValue">The offered value.</param>
+        /// <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)
+        {
+            // If we're declining permanently, let the caller know.
+            if (_decliningPermanently)
+            {
+                return DataflowMessageStatus.DecliningPermanently;
+            }
+
+            // If the message header is invalid, throw.
+            if (!messageHeader.IsValid)
+            {
+                throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            }
+
+            // If the caller has requested we consume the message using ConsumeMessage, do so.
+            if (consumeToAccept)
+            {
+                if (source == null) throw new ArgumentException(SR.Argument_CantConsumeFromANullSource, "consumeToAccept");
+                bool consumed;
+                messageValue = source.ConsumeMessage(messageHeader, _owningTarget, out consumed);
+                if (!consumed) return DataflowMessageStatus.NotAvailable;
+            }
+
+            // See the "fast path" comments in Post
+            _messages.Enqueue(messageValue);
+            Interlocked.MemoryBarrier(); // ensure the read of _activeConsumer doesn't move up before the writes in Enqueue
+            if (_activeConsumer == null)
+            {
+                ScheduleConsumerIfNecessary(isReplica: false);
+            }
+            return DataflowMessageStatus.Accepted;
+        }
+
+        /// <summary>Schedules a consumer task if there's none currently running.</summary>
+        /// <param name="isReplica">Whether the new consumer is being scheduled to replace a currently running consumer.</param>
+        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
+        private void ScheduleConsumerIfNecessary(bool isReplica)
+        {
+            // If there's currently no active task...
+            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(),
+                    this, CancellationToken.None, Common.GetCreationOptionsForTask(isReplica));
+                if (Interlocked.CompareExchange(ref _activeConsumer, newConsumer, null) == null)
+                {
+                    // We won the race.  This task is now the consumer.
+
+#if FEATURE_TRACING
+                    DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+                    if (etwLog.IsEnabled())
+                    {
+                        etwLog.TaskLaunchedForMessageHandling(
+                            _owningTarget, newConsumer, DataflowEtwProvider.TaskLaunchedReason.ProcessingInputMessages, _messages.Count);
+                    }
+#endif
+
+                    // Start the task.  In the erroneous case where the scheduler throws an exception, 
+                    // just allow it to propagate. Our other option would be to fault the block with 
+                    // that exception, but in order for the block to complete we need to schedule a consumer
+                    // task to do so, and it's very likely that if the scheduler is throwing an exception 
+                    // now, it would do so again.
+                    newConsumer.Start(_dataflowBlockOptions.TaskScheduler);
+                }
+            }
+        }
+
+        /// <summary>Task body used to process messages.</summary>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private void ProcessMessagesLoopCore()
+        {
+            Debug.Assert(
+                _activeConsumer != null && _activeConsumer.Id == Task.CurrentId,
+                "This method should only be called when it's the active consumer.");
+
+            int messagesProcessed = 0;
+            int maxMessagesToProcess = _dataflowBlockOptions.ActualMaxMessagesPerTask;
+
+            // Continue processing as long as there's more processing to be done
+            bool continueProcessing = true;
+            while (continueProcessing)
+            {
+                continueProcessing = false;
+                TInput nextMessage = default(TInput);
+                try
+                {
+                    // While there are more messages to be processed, process each one.
+                    // NOTE: This loop is critical for performance.  It must be super lean.
+                    while (
+                        _exceptions == null &&
+                        messagesProcessed < maxMessagesToProcess &&
+                        _messages.TryDequeue(out nextMessage))
+                    {
+                        messagesProcessed++; // done before _action invoked in case it throws exception
+                        _action(nextMessage);
+                    }
+                }
+                catch (Exception exc)
+                {
+                    // If the exception is for cancellation, just ignore it.
+                    // Otherwise, store it, and the finally block will handle completion.
+                    if (!Common.IsCooperativeCancellation(exc))
+                    {
+                        _decliningPermanently = true; // stop accepting from producers
+                        Common.StoreDataflowMessageValueIntoExceptionData<TInput>(exc, nextMessage, false);
+                        StoreException(exc);
+                    }
+                }
+                finally
+                {
+                    // If more messages just arrived and we should still process them,
+                    // loop back around and keep going.
+                    if (!_messages.IsEmpty && _exceptions == null && (messagesProcessed < maxMessagesToProcess))
+                    {
+                        continueProcessing = true;
+                    }
+                    else
+                    {
+                        // If messages are being declined and we're empty, or if there's an exception,
+                        // then there's no more work to be done and we should complete the block.
+                        bool wasDecliningPermanently = _decliningPermanently;
+                        if ((wasDecliningPermanently && _messages.IsEmpty) || _exceptions != null)
+                        {
+                            // Complete the block, as long as we're not already completing or completed.
+                            if (!_completionReserved) // no synchronization necessary; this can't happen concurrently
+                            {
+                                _completionReserved = true;
+                                CompleteBlockOncePossible();
+                            }
+                        }
+                        else
+                        {
+                            // Mark that we're exiting.
+                            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.");
+
+                            // Now that we're no longer the active task, double
+                            // check to make sure there's really nothing to do,
+                            // which could include processing more messages or completing.
+                            // If there is more to do, schedule a task to try to do it.
+                            // This is to handle a race with Post/Complete/Fault and this
+                            // task completing.
+                            if (!_messages.IsEmpty || // messages to be processed
+                                (!wasDecliningPermanently && _decliningPermanently) || // potentially completion to be processed
+                                _exceptions != null) // exceptions/completion to be processed
+                            {
+                                ScheduleConsumerIfNecessary(isReplica: true);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        /// <summary>Gets the number of messages waiting to be processed.</summary>
+        internal int InputCount { get { return _messages.Count; } }
+
+        /// <summary>
+        /// Completes the target core.  If an exception is provided, the block will end up in a faulted state.
+        /// If Complete is invoked more than once, or if it's invoked after the block is already
+        /// completing, all invocations after the first are ignored.
+        /// </summary>
+        /// <param name="exception">The exception to be stored.</param>
+        internal void Complete(Exception exception)
+        {
+            // If we're not yet declining permanently...
+            if (!_decliningPermanently)
+            {
+                // Mark us as declining permanently, and then kick off a processing task
+                // if we need one.  It's this processing task's job to complete the block
+                // once all data has been consumed and/or we're in a valid state for completion.
+                if (exception != null) StoreException(exception);
+                _decliningPermanently = true;
+                ScheduleConsumerIfNecessary(isReplica: false);
+            }
+        }
+
+        /// <summary>
+        /// Ensures the exceptions list is initialized and stores the exception into the list using a lock.
+        /// </summary>
+        /// <param name="exception">The exception to store.</param>
+        private void StoreException(Exception exception)
+        {
+            // Ensure that the _exceptions field has been initialized.
+            // We need to synchronize the initialization and storing of
+            // the exception because this method could be accessed concurrently
+            // by the producer and consumer, a producer calling Fault and the 
+            // processing task processing the user delegate which might throw.
+            lock (LazyInitializer.EnsureInitialized(ref _exceptions, () => new List<Exception>()))
+            {
+                _exceptions.Add(exception);
+            }
+        }
+
+        /// <summary>
+        /// Completes the block.  This must only be called once, and only once all of the completion conditions are met.
+        /// </summary>
+        private void CompleteBlockOncePossible()
+        {
+            Debug.Assert(_completionReserved, "Should only invoke once completion has been reserved.");
+
+            // Dump any messages that might remain in the queue, which could happen if we completed due to exceptions.
+            TInput dumpedMessage;
+            while (_messages.TryDequeue(out dumpedMessage)) ;
+
+            // Complete the completion task
+            bool result;
+            if (_exceptions != null)
+            {
+                Exception[] exceptions;
+                lock (_exceptions) exceptions = _exceptions.ToArray();
+                result = CompletionSource.TrySetException(exceptions);
+            }
+            else
+            {
+                result = CompletionSource.TrySetResult(default(VoidResult));
+            }
+            Debug.Assert(result, "Expected completion task to not yet be completed");
+            // We explicitly do not set the _activeTask to null here, as that would
+            // allow for races where a producer calling OfferMessage could end up
+            // seeing _activeTask as null and queueing a new consumer task even
+            // though the block has completed.
+
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockCompleted(_owningTarget);
+            }
+#endif
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+        internal Task Completion { get { return CompletionSource.Task; } }
+
+        /// <summary>Gets the lazily-initialized completion source.</summary>
+        private TaskCompletionSource<VoidResult> CompletionSource
+        {
+            get { return LazyInitializer.EnsureInitialized(ref _completionTask, () => new TaskCompletionSource<VoidResult>()); }
+        }
+
+        /// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
+        internal ExecutionDataflowBlockOptions DataflowBlockOptions { get { return _dataflowBlockOptions; } }
+
+        /// <summary>Gets information about this helper to be used for display in a debugger.</summary>
+        /// <returns>Debugging information about this target.</returns>
+        internal DebuggingInformation GetDebuggingInformation() { return new DebuggingInformation(this); }
+
+        /// <summary>Gets the object to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                var displayTarget = _owningTarget as IDebuggerDisplay;
+                return string.Format("Block=\"{0}\"",
+                    displayTarget != null ? displayTarget.Content : _owningTarget);
+            }
+        }
+
+        /// <summary>Provides a wrapper for commonly needed debugging information.</summary>
+        internal sealed class DebuggingInformation
+        {
+            /// <summary>The target being viewed.</summary>
+            private readonly SpscTargetCore<TInput> _target;
+
+            /// <summary>Initializes the debugging helper.</summary>
+            /// <param name="target">The target being viewed.</param>
+            internal DebuggingInformation(SpscTargetCore<TInput> target) { _target = target; }
+
+            /// <summary>Gets the number of messages waiting to be processed.</summary>
+            internal int InputCount { get { return _target.InputCount; } }
+            /// <summary>Gets the messages waiting to be processed.</summary>
+            internal IEnumerable<TInput> InputQueue { get { return _target._messages.ToList(); } }
+
+            /// <summary>Gets the current number of outstanding input processing operations.</summary>
+            internal Int32 CurrentDegreeOfParallelism { get { return _target._activeConsumer != null && !_target.Completion.IsCompleted ? 1 : 0; } }
+            /// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
+            internal ExecutionDataflowBlockOptions DataflowBlockOptions { get { return _target._dataflowBlockOptions; } }
+            /// <summary>Gets whether the block is declining further messages.</summary>
+            internal bool IsDecliningPermanently { get { return _target._decliningPermanently; } }
+            /// <summary>Gets whether the block is completed.</summary>
+            internal bool IsCompleted { get { return _target.Completion.IsCompleted; } }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/TargetCore.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/TargetCore.cs
new file mode 100644 (file)
index 0000000..ed1c806
--- /dev/null
@@ -0,0 +1,881 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// TargetCore.cs
+//
+//
+// The core implementation of a standard ITargetBlock<TInput>.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Linq;
+using System.Security;
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    // LOCK-LEVELING SCHEME
+    // --------------------
+    // TargetCore employs a single lock: IncomingLock.  This lock must not be used when calling out to any targets,
+    // which TargetCore should not have, anyway.  It also must not be held when calling back to any sources, except
+    // during calls to OfferMessage from that same source.
+
+    /// <summary>Options used to configure a target core.</summary>
+    [Flags]
+    internal enum TargetCoreOptions : byte
+    {
+        /// <summary>Synchronous completion, both a target and a source, etc.</summary>
+        None = 0x0,
+        /// <summary>Whether the block relies on the delegate to signal when an async operation has completed.</summary>
+        UsesAsyncCompletion = 0x1,
+        /// <summary>
+        /// Whether the block containing this target core is just a target or also has a source side.
+        /// If it's just a target, then this target core's completion represents the entire block's completion.
+        /// </summary>
+        RepresentsBlockCompletion = 0x2
+    }
+
+    /// <summary>
+    /// Provides a core implementation of <see cref="ITargetBlock{TInput}"/>.</summary>
+    /// <typeparam name="TInput">Specifies the type of data accepted by the <see cref="TargetCore{TInput}"/>.</typeparam>
+    [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
+    [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+    internal sealed class TargetCore<TInput>
+    {
+        // *** These fields are readonly and are initialized at AppDomain startup.
+
+        /// <summary>Caching the keep alive predicate.</summary>
+        private static readonly Common.KeepAlivePredicate<TargetCore<TInput>, KeyValuePair<TInput, long>> _keepAlivePredicate =
+                (TargetCore<TInput> thisTargetCore, out KeyValuePair<TInput, long> messageWithId) =>
+                    thisTargetCore.TryGetNextAvailableOrPostponedMessage(out messageWithId);
+
+        // *** These fields are readonly and are initialized to new instances at construction.
+
+        /// <summary>A task representing the completion of the block.</summary>
+        private readonly TaskCompletionSource<VoidResult> _completionSource = new TaskCompletionSource<VoidResult>();
+
+        // *** These fields are readonly and are initialized by arguments to the constructor.
+
+        /// <summary>The target block using this helper.</summary>
+        private readonly ITargetBlock<TInput> _owningTarget;
+        /// <summary>The messages in this target.</summary>
+        /// <remarks>This field doubles as the IncomingLock.</remarks>
+        private readonly IProducerConsumerQueue<KeyValuePair<TInput, long>> _messages;
+        /// <summary>The options associated with this block.</summary>
+        private readonly ExecutionDataflowBlockOptions _dataflowBlockOptions;
+        /// <summary>An action to invoke for every accepted message.</summary>
+        private readonly Action<KeyValuePair<TInput, long>> _callAction;
+        /// <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;
+        /// <summary>The reordering buffer used by the owner.  May be null.</summary>
+        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;
+        /// <summary>Whether to stop accepting new messages.</summary>
+        private bool _decliningPermanently;
+        /// <summary>The number of operations (including service tasks) currently running asynchronously.</summary>
+        /// <remarks>Must always be accessed from inside a lock.</remarks>
+        private int _numberOfOutstandingOperations;
+        /// <summary>The number of service tasks in async mode currently running.</summary>
+        /// <remarks>Must always be accessed from inside a lock.</remarks>
+        private int _numberOfOutstandingServiceTasks;
+        /// <summary>The next available ID we can assign to a message about to be processed.</summary>
+        private PaddedInt64 _nextAvailableInputMessageId; // initialized to 0... very important for a reordering buffer
+        /// <summary>A task has reserved the right to run the completion routine.</summary>
+        private bool _completionReserved;
+        /// <summary>This counter is set by the processing loop to prevent itself from trying to keep alive.</summary>
+        private int _keepAliveBanCounter;
+
+        /// <summary>Initializes the target core.</summary>
+        /// <param name="owningTarget">The target using this helper.</param>
+        /// <param name="callAction">An action to invoke for all accepted items.</param>
+        /// <param name="reorderingBuffer">The reordering buffer used by the owner; may be null.</param>
+        /// <param name="dataflowBlockOptions">The options to use to configure this block. The target core assumes these options are immutable.</param>
+        /// <param name="targetCoreOptions">Options for how the target core should behave.</param>
+        internal TargetCore(
+            ITargetBlock<TInput> owningTarget,
+            Action<KeyValuePair<TInput, long>> callAction,
+            IReorderingBuffer reorderingBuffer,
+            ExecutionDataflowBlockOptions dataflowBlockOptions,
+            TargetCoreOptions targetCoreOptions)
+        {
+            // Validate internal arguments
+            Contract.Requires(owningTarget != null, "Core must be associated with a target block.");
+            Contract.Requires(dataflowBlockOptions != null, "Options must be provided to configure the core.");
+            Contract.Requires(callAction != null, "Action to invoke for each item is required.");
+
+            // Store arguments and do additional initialization
+            _owningTarget = owningTarget;
+            _callAction = callAction;
+            _reorderingBuffer = reorderingBuffer;
+            _dataflowBlockOptions = dataflowBlockOptions;
+            _targetCoreOptions = targetCoreOptions;
+            _messages = (dataflowBlockOptions.MaxDegreeOfParallelism == 1) ?
+                (IProducerConsumerQueue<KeyValuePair<TInput, long>>)new SingleProducerSingleConsumerQueue<KeyValuePair<TInput, long>>() :
+                (IProducerConsumerQueue<KeyValuePair<TInput, long>>)new MultiProducerMultiConsumerQueue<KeyValuePair<TInput, long>>();
+            if (_dataflowBlockOptions.BoundedCapacity != System.Threading.Tasks.Dataflow.DataflowBlockOptions.Unbounded)
+            {
+                Debug.Assert(_dataflowBlockOptions.BoundedCapacity > 0, "Positive bounding count expected; should have been verified by options ctor");
+                _boundingState = new BoundingStateWithPostponed<TInput>(_dataflowBlockOptions.BoundedCapacity);
+            }
+        }
+
+        /// <summary>Internal Complete entry point with extra parameters for different contexts.</summary>
+        /// <param name="exception">If not null, the block will be faulted.</param>
+        /// <param name="dropPendingMessages">If true, any unprocessed input messages will be dropped.</param>
+        /// <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,
+            bool unwrapInnerExceptions = false, bool revertProcessingState = false)
+        {
+            Contract.Requires(storeExceptionEvenIfAlreadyCompleting || !revertProcessingState,
+                            "Indicating dirty processing state may only come with storeExceptionEvenIfAlreadyCompleting==true.");
+            Contract.EndContractBlock();
+
+            // Ensure that no new messages may be added
+            lock (IncomingLock)
+            {
+                // Faulting from outside is allowed until we start declining permanently.
+                // Faulting from inside is allowed at any time.
+                if (exception != null && (!_decliningPermanently || storeExceptionEvenIfAlreadyCompleting))
+                {
+                    Debug.Assert(_numberOfOutstandingOperations > 0 || !storeExceptionEvenIfAlreadyCompleting,
+                                "Calls with storeExceptionEvenIfAlreadyCompleting==true may only be coming from processing task.");
+
+#pragma warning disable 0420
+                    Common.AddException(ref _exceptions, exception, unwrapInnerExceptions);
+                }
+
+                // Clear the messages queue if requested
+                if (dropPendingMessages)
+                {
+                    KeyValuePair<TInput, long> dummy;
+                    while (_messages.TryDequeue(out dummy)) ;
+                }
+
+                // Revert the dirty processing state if requested
+                if (revertProcessingState)
+                {
+                    Debug.Assert(_numberOfOutstandingOperations > 0 && (!UsesAsyncCompletion || _numberOfOutstandingServiceTasks > 0),
+                                    "The processing state must be dirty when revertProcessingState==true.");
+                    _numberOfOutstandingOperations--;
+                    if (UsesAsyncCompletion) _numberOfOutstandingServiceTasks--;
+                }
+
+                // Trigger completion
+                _decliningPermanently = true;
+                CompleteBlockIfPossible();
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
+        internal DataflowMessageStatus OfferMessage(DataflowMessageHeader messageHeader, TInput messageValue, ISourceBlock<TInput> source, Boolean consumeToAccept)
+        {
+            // Validate arguments
+            if (!messageHeader.IsValid) throw new ArgumentException(SR.Argument_InvalidMessageHeader, "messageHeader");
+            if (source == null && consumeToAccept) throw new ArgumentException(SR.Argument_CantConsumeFromANullSource, "consumeToAccept");
+            Contract.EndContractBlock();
+
+            lock (IncomingLock)
+            {
+                // If we shouldn't be accepting more messages, don't.
+                if (_decliningPermanently)
+                {
+                    CompleteBlockIfPossible();
+                    return DataflowMessageStatus.DecliningPermanently;
+                }
+
+                // We can directly accept the message if:
+                //      1) we are not bounding, OR 
+                //      2) we are bounding AND there is room available AND there are no postponed messages AND no messages are currently being transfered to the input queue.
+                // (If there were any postponed messages, we would need to postpone so that ordering would be maintained.)
+                // (Unlike all other blocks, TargetCore can accept messages while processing, because 
+                // input message IDs are properly assigned and the correct order is preserved.)
+                if (_boundingState == null ||
+                    (_boundingState.OutstandingTransfers == 0 && _boundingState.CountIsLessThanBound && _boundingState.PostponedMessages.Count == 0))
+                {
+                    // Consume the message from the source if necessary
+                    if (consumeToAccept)
+                    {
+                        Debug.Assert(source != null, "We must have thrown if source == null && consumeToAccept == true.");
+
+                        bool consumed;
+                        messageValue = source.ConsumeMessage(messageHeader, _owningTarget, out consumed);
+                        if (!consumed) return DataflowMessageStatus.NotAvailable;
+                    }
+
+                    // Assign a message ID - strictly sequential, no gaps.
+                    // Once consumed, enqueue the message with its ID and kick off asynchronous processing.
+                    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));
+                    ProcessAsyncIfNecessary();
+                    return DataflowMessageStatus.Accepted;
+                }
+                // Otherwise, we try to postpone if a source was provided
+                else if (source != null)
+                {
+                    Debug.Assert(_boundingState != null && _boundingState.PostponedMessages != null,
+                        "PostponedMessages must have been initialized during construction in non-greedy mode.");
+
+                    // Store the message's info and kick off asynchronous processing
+                    _boundingState.PostponedMessages.Push(source, messageHeader);
+                    ProcessAsyncIfNecessary();
+                    return DataflowMessageStatus.Postponed;
+                }
+                // We can't do anything else about this message
+                return DataflowMessageStatus.Declined;
+            }
+        }
+
+        /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+        internal Task Completion { get { return _completionSource.Task; } }
+
+        /// <summary>Gets the number of items waiting to be processed by this target.</summary>
+        internal int InputCount { get { return _messages.GetCountSafe(IncomingLock); } }
+
+        /// <summary>Signals to the target core that a previously launched asynchronous operation has now completed.</summary>
+        internal void SignalOneAsyncMessageCompleted()
+        {
+            SignalOneAsyncMessageCompleted(boundingCountChange: 0);
+        }
+
+        /// <summary>Signals to the target core that a previously launched asynchronous operation has now completed.</summary>
+        /// <param name="boundingCountChange">The number of elements by which to change the bounding count, if bounding is occurring.</param>
+        internal void SignalOneAsyncMessageCompleted(int boundingCountChange)
+        {
+            lock (IncomingLock)
+            {
+                // We're no longer processing, so decrement the DOP counter
+                Debug.Assert(_numberOfOutstandingOperations > 0, "Operations may only be completed if any are outstanding.");
+                if (_numberOfOutstandingOperations > 0) _numberOfOutstandingOperations--;
+
+                // Fix up the bounding count if necessary
+                if (_boundingState != null && boundingCountChange != 0)
+                {
+                    Debug.Assert(boundingCountChange <= 0 && _boundingState.CurrentCount + boundingCountChange >= 0,
+                        "Expected a negative bounding change and not to drop below zero.");
+                    _boundingState.CurrentCount += boundingCountChange;
+                }
+
+                // However, we may have given up early because we hit our own configured
+                // processing limits rather than because we ran out of work to do.  If that's
+                // the case, make sure we spin up another task to keep going.
+                ProcessAsyncIfNecessary(repeat: true);
+
+                // If, however, we stopped because we ran out of work to do and we
+                // know we'll never get more, then complete.
+                CompleteBlockIfPossible();
+            }
+        }
+
+        /// <summary>Gets whether this instance has been constructed for async processing.</summary>
+        private bool UsesAsyncCompletion
+        {
+            get
+            {
+                return (_targetCoreOptions & TargetCoreOptions.UsesAsyncCompletion) != 0;
+            }
+        }
+
+        /// <summary>Gets whether there's room to launch more processing operations.</summary>
+        private bool HasRoomForMoreOperations
+        {
+            get
+            {
+                Contract.Requires(_numberOfOutstandingOperations >= 0, "Number of outstanding operations should never be negative.");
+                Contract.Requires(_numberOfOutstandingServiceTasks >= 0, "Number of outstanding service tasks should never be negative.");
+                Contract.Requires(_numberOfOutstandingOperations >= _numberOfOutstandingServiceTasks, "Number of outstanding service tasks should never exceed the number of outstanding operations.");
+                Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+                // In async mode, we increment _numberOfOutstandingOperations before we start 
+                // our own processing loop which should not count towards the MaxDOP.
+                return (_numberOfOutstandingOperations - _numberOfOutstandingServiceTasks) < _dataflowBlockOptions.ActualMaxDegreeOfParallelism;
+            }
+        }
+
+        /// <summary>Gets whether there's room to launch more service tasks for doing/launching processing operations.</summary>
+        private bool HasRoomForMoreServiceTasks
+        {
+            get
+            {
+                Contract.Requires(_numberOfOutstandingOperations >= 0, "Number of outstanding operations should never be negative.");
+                Contract.Requires(_numberOfOutstandingServiceTasks >= 0, "Number of outstanding service tasks should never be negative.");
+                Contract.Requires(_numberOfOutstandingOperations >= _numberOfOutstandingServiceTasks, "Number of outstanding service tasks should never exceed the number of outstanding operations.");
+                Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+                if (!UsesAsyncCompletion)
+                {
+                    // Sync mode: 
+                    // We don't count service tasks, because our tasks are counted as operations.
+                    // Therefore, return HasRoomForMoreOperations.
+                    return HasRoomForMoreOperations;
+                }
+                else
+                {
+                    // Async mode:
+                    // We allow up to MaxDOP true service tasks.
+                    // Checking whether there is room for more processing operations is not necessary, 
+                    // but doing so will help us avoid spinning up a task that will go away without 
+                    // launching any processing operation.
+                    return HasRoomForMoreOperations &&
+                           _numberOfOutstandingServiceTasks < _dataflowBlockOptions.ActualMaxDegreeOfParallelism;
+                }
+            }
+        }
+
+        /// <summary>Called when new messages are available to be processed.</summary>
+        /// <param name="repeat">Whether this call is the continuation of a previous message loop.</param>
+        private void ProcessAsyncIfNecessary(bool repeat = false)
+        {
+            Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+            if (HasRoomForMoreServiceTasks)
+            {
+                ProcessAsyncIfNecessary_Slow(repeat);
+            }
+        }
+
+        /// <summary>
+        /// Slow path for ProcessAsyncIfNecessary. 
+        /// Separating out the slow path into its own method makes it more likely that the fast path method will get inlined.
+        /// </summary>
+        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
+        private void ProcessAsyncIfNecessary_Slow(bool repeat)
+        {
+            Contract.Requires(HasRoomForMoreServiceTasks, "There must be room to process asynchronously.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+            // Determine preconditions to launching a processing task
+            bool messagesAvailableOrPostponed =
+                !_messages.IsEmpty ||
+                (!_decliningPermanently && _boundingState != null && _boundingState.CountIsLessThanBound && _boundingState.PostponedMessages.Count > 0);
+
+            // If all conditions are met, launch away
+            if (messagesAvailableOrPostponed && !CanceledOrFaulted)
+            {
+                // Any book keeping related to the processing task like incrementing the 
+                // DOP counter or eventually recording the tasks reference must be done
+                // before the task starts. That is because the task itself will do the 
+                // reverse operation upon its completion.
+                _numberOfOutstandingOperations++;
+                if (UsesAsyncCompletion) _numberOfOutstandingServiceTasks++;
+
+                var taskForInputProcessing = new Task(thisTargetCore => ((TargetCore<TInput>)thisTargetCore).ProcessMessagesLoopCore(), this,
+                                                      Common.GetCreationOptionsForTask(repeat));
+
+#if FEATURE_TRACING
+                DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+                if (etwLog.IsEnabled())
+                {
+                    etwLog.TaskLaunchedForMessageHandling(
+                        _owningTarget, taskForInputProcessing, DataflowEtwProvider.TaskLaunchedReason.ProcessingInputMessages,
+                        _messages.Count + (_boundingState != null ? _boundingState.PostponedMessages.Count : 0));
+                }
+#endif
+
+                // Start the task handling scheduling exceptions
+                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,
+                                                        unwrapInnerExceptions: false, revertProcessingState: true),
+                                        exception, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+                }
+            }
+        }
+
+        /// <summary>Task body used to process messages.</summary>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private void ProcessMessagesLoopCore()
+        {
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            KeyValuePair<TInput, long> messageWithId = default(KeyValuePair<TInput, long>);
+            try
+            {
+                bool useAsyncCompletion = UsesAsyncCompletion;
+                bool shouldAttemptPostponedTransfer = _boundingState != null && _boundingState.BoundedCapacity > 1;
+                int numberOfMessagesProcessedByThisTask = 0;
+                int numberOfMessagesProcessedSinceTheLastKeepAlive = 0;
+                int maxMessagesPerTask = _dataflowBlockOptions.ActualMaxMessagesPerTask;
+
+                while (numberOfMessagesProcessedByThisTask < maxMessagesPerTask && !CanceledOrFaulted)
+                {
+                    // If we're bounding, try to transfer a message from the postponed queue
+                    // to the input queue.  This enables us to more quickly unblock sources
+                    // sending data to the block (otherwise, no postponed messages will be consumed
+                    // until the input queue is entirely empty).  If the bounded size is 1,
+                    // there's no need to transfer, as attempting to get the next message will
+                    // just go and consume the postponed message anyway, and we'll save
+                    // the extra trip through the _messages queue.
+                    KeyValuePair<TInput, long> transferMessageWithId;
+                    if (shouldAttemptPostponedTransfer &&
+                        TryConsumePostponedMessage(forPostponementTransfer: true, result: out transferMessageWithId))
+                    {
+                        lock (IncomingLock)
+                        {
+                            Debug.Assert(
+                                _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
+                            _messages.Enqueue(transferMessageWithId);
+                            ProcessAsyncIfNecessary();
+                        }
+                    }
+
+                    if (useAsyncCompletion)
+                    {
+                        // Get the next message if DOP is available.
+                        // If we can't get a message or DOP is not available, bail out.
+                        if (!TryGetNextMessageForNewAsyncOperation(out messageWithId)) break;
+                    }
+                    else
+                    {
+                        // Try to get a message for sequential execution, i.e. without checking DOP availability 
+                        if (!TryGetNextAvailableOrPostponedMessage(out messageWithId))
+                        {
+                            // Try to keep the task alive only if MaxDOP=1
+                            if (_dataflowBlockOptions.MaxDegreeOfParallelism != 1) break;
+
+                            // If this task has processed enough messages without being kept alive, 
+                            // it has served its purpose. Don't keep it alive.
+                            if (numberOfMessagesProcessedSinceTheLastKeepAlive > Common.KEEP_ALIVE_NUMBER_OF_MESSAGES_THRESHOLD) break;
+
+                            // If keep alive is banned, don't attempt it
+                            if (_keepAliveBanCounter > 0)
+                            {
+                                _keepAliveBanCounter--;
+                                break;
+                            }
+
+                            // Reset the keep alive counter. (Keep this line together with TryKeepAliveUntil.)
+                            numberOfMessagesProcessedSinceTheLastKeepAlive = 0;
+
+                            // Try to keep the task alive briefly until a new message arrives
+                            if (!Common.TryKeepAliveUntil(_keepAlivePredicate, this, out messageWithId))
+                            {
+                                // Keep alive was unsuccessful. 
+                                // Therefore ban further attempts temporarily.
+                                _keepAliveBanCounter = Common.KEEP_ALIVE_BAN_COUNT;
+                                break;
+                            }
+                        }
+                    }
+
+                    // We have popped a message from the queue.
+                    // So increment the counter of processed messages.
+                    numberOfMessagesProcessedByThisTask++;
+                    numberOfMessagesProcessedSinceTheLastKeepAlive++;
+
+                    // Invoke the user action
+                    _callAction(messageWithId);
+                }
+            }
+            catch (Exception exc)
+            {
+                Common.StoreDataflowMessageValueIntoExceptionData(exc, messageWithId.Key);
+                Complete(exc, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: false);
+            }
+            finally
+            {
+                lock (IncomingLock)
+                {
+                    // We incremented _numberOfOutstandingOperations before we launched this task.
+                    // So we must decremented it before exiting.
+                    // Note that each async task additionally incremented it before starting and 
+                    // is responsible for decrementing it prior to exiting.
+                    Debug.Assert(_numberOfOutstandingOperations > 0, "Expected a positive number of outstanding operations, since we're completing one here.");
+                    _numberOfOutstandingOperations--;
+
+                    // If we are in async mode, we've also incremented _numberOfOutstandingServiceTasks.
+                    // Now it's time to decrement it.
+                    if (UsesAsyncCompletion)
+                    {
+                        Debug.Assert(_numberOfOutstandingServiceTasks > 0, "Expected a positive number of outstanding service tasks, since we're completing one here.");
+                        _numberOfOutstandingServiceTasks--;
+                    }
+
+                    // However, we may have given up early because we hit our own configured
+                    // processing limits rather than because we ran out of work to do.  If that's
+                    // the case, make sure we spin up another task to keep going.
+                    ProcessAsyncIfNecessary(repeat: true);
+
+                    // If, however, we stopped because we ran out of work to do and we
+                    // know we'll never get more, then complete.
+                    CompleteBlockIfPossible();
+                }
+            }
+        }
+
+        /// <summary>Retrieves the next message from the input queue for the useAsyncCompletion mode.</summary>
+        /// <param name="messageWithId">The next message retrieved.</param>
+        /// <returns>true if a message was found and removed; otherwise, false.</returns>
+        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+        private bool TryGetNextMessageForNewAsyncOperation(out KeyValuePair<TInput, long> messageWithId)
+        {
+            Contract.Requires(UsesAsyncCompletion, "Only valid to use when in async mode.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            bool parallelismAvailable;
+
+            lock (IncomingLock)
+            {
+                // If we have room for another asynchronous operation, reserve it.
+                // If later it turns out that we had no work to fill the slot, we'll undo the addition.
+                parallelismAvailable = HasRoomForMoreOperations;
+                if (parallelismAvailable) ++_numberOfOutstandingOperations;
+            }
+
+            messageWithId = default(KeyValuePair<TInput, long>);
+            if (parallelismAvailable)
+            {
+                // If a parallelism slot was available, try to get an item.
+                // Be careful, because an exception may be thrown from ConsumeMessage
+                // and we have already incremented _numberOfOutstandingOperations.
+                bool gotMessage = false;
+                try
+                {
+                    gotMessage = TryGetNextAvailableOrPostponedMessage(out messageWithId);
+                }
+                catch
+                {
+                    // We have incremented the counter, but we didn't get a message.
+                    // So we must undo the increment and eventually complete the block.
+                    SignalOneAsyncMessageCompleted();
+
+                    // Re-throw the exception. The processing loop will catch it.
+                    throw;
+                }
+
+                // There may not be an error, but may have still failed to get a message.
+                // So we must undo the increment and eventually complete the block.
+                if (!gotMessage) SignalOneAsyncMessageCompleted();
+
+                return gotMessage;
+            }
+
+            // If there was no parallelism available, we didn't increment _numberOfOutstandingOperations.
+            // So there is nothing to do except to return false.
+            return false;
+        }
+
+        /// <summary>
+        /// Either takes the next available message from the input queue or retrieves a postponed 
+        /// message from a source, based on whether we're in greedy or non-greedy mode.
+        /// </summary>
+        /// <param name="messageWithId">The retrieved item with its Id.</param>
+        /// <returns>true if a message could be removed and returned; otherwise, false.</returns>
+        private bool TryGetNextAvailableOrPostponedMessage(out KeyValuePair<TInput, long> messageWithId)
+        {
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            // First try to get a message from our input buffer.
+            if (_messages.TryDequeue(out messageWithId))
+            {
+                return true;
+            }
+            // If we can't, but if we have any postponed messages due to bounding, then
+            // try to consume one of these postponed messages.
+            // Since we are not currently holding the lock, it is possible that new messages get queued up
+            // by the time we take the lock to manipulate _boundingState. So we have to double-check the
+            // input queue once we take the lock before we consider postponed messages.
+            else if (_boundingState != null && TryConsumePostponedMessage(forPostponementTransfer: false, result: out messageWithId))
+            {
+                return true;
+            }
+            // Otherwise, there's no message available.
+            else
+            {
+                messageWithId = default(KeyValuePair<TInput, long>);
+                return false;
+            }
+        }
+
+        /// <summary>Consumes a single postponed message.</summary>
+        /// <param name="forPostponementTransfer">
+        /// true if the method is being called to consume a message that'll then be stored into the input queue;
+        /// false if the method is being called to consume a message that'll be processed immediately.
+        /// If true, the bounding state's ForcePostponement will be updated.
+        /// If false, the method will first try (while holding the lock) to consume from the input queue before
+        /// consuming a postponed message.
+        /// </param>
+        /// <param name="result">The consumed message.</param>
+        /// <returns>true if a message was consumed; otherwise, false.</returns>
+        private bool TryConsumePostponedMessage(
+            bool forPostponementTransfer,
+            out KeyValuePair<TInput, long> result)
+        {
+            Contract.Requires(
+                _dataflowBlockOptions.BoundedCapacity !=
+                System.Threading.Tasks.Dataflow.DataflowBlockOptions.Unbounded, "Only valid to use when in bounded mode.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+
+            // Iterate until we either consume a message successfully or there are no more postponed messages.
+            bool countIncrementedExpectingToGetItem = false;
+            long messageId = Common.INVALID_REORDERING_ID;
+            while (true)
+            {
+                KeyValuePair<ISourceBlock<TInput>, DataflowMessageHeader> element;
+                lock (IncomingLock)
+                {
+                    // If we are declining permanently, don't consume postponed messages.
+                    if (_decliningPermanently) break;
+
+                    // New messages may have been queued up while we weren't holding the lock.
+                    // In particular, the input queue may have been filled up and messages may have
+                    // gotten postponed. If we process such a postponed message, we would mess up the
+                    // order. Therefore, we have to double-check the input queue first.
+                    if (!forPostponementTransfer && _messages.TryDequeue(out result)) return true;
+
+                    // 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 (countIncrementedExpectingToGetItem)
+                        {
+                            countIncrementedExpectingToGetItem = false;
+                            _boundingState.CurrentCount -= 1;
+                        }
+                        break;
+                    }
+                    if (!countIncrementedExpectingToGetItem)
+                    {
+                        countIncrementedExpectingToGetItem = true;
+                        messageId = _nextAvailableInputMessageId.Value++; // optimistically assign an ID
+                        Debug.Assert(messageId != Common.INVALID_REORDERING_ID, "The assigned message ID is invalid.");
+                        _boundingState.CurrentCount += 1; // optimistically take bounding space
+                        if (forPostponementTransfer)
+                        {
+                            Debug.Assert(_boundingState.OutstandingTransfers >= 0, "Expected TryConsumePostponedMessage to not be negative.");
+                            _boundingState.OutstandingTransfers++; // temporarily force postponement until we've successfully consumed the element
+                        }
+                    }
+                } // Must not call to source while holding lock
+
+                bool consumed;
+                TInput consumedValue = element.Key.ConsumeMessage(element.Value, _owningTarget, out consumed);
+                if (consumed)
+                {
+                    result = new KeyValuePair<TInput, long>(consumedValue, messageId);
+                    return true;
+                }
+                else
+                {
+                    if (forPostponementTransfer)
+                    {
+                        // We didn't consume message so we need to decrement because we havent consumed the element.
+                        _boundingState.OutstandingTransfers--;
+                    }
+                }
+            }
+
+            // We optimistically acquired a message ID for a message that, in the end, we never got.
+            // So, we need to let the reordering buffer (if one exists) know that it should not
+            // expect an item with this ID.  Otherwise, it would stall forever.
+            if (_reorderingBuffer != null && messageId != Common.INVALID_REORDERING_ID) _reorderingBuffer.IgnoreItem(messageId);
+
+            // Similarly, we optimistically increased the bounding count, expecting to get another message in.
+            // Since we didn't, we need to fix the bounding count back to what it should have been.
+            if (countIncrementedExpectingToGetItem) ChangeBoundingCount(-1);
+
+            // Inform the caller that no message could be consumed.
+            result = default(KeyValuePair<TInput, long>);
+            return false;
+        }
+
+        /// <summary>Gets whether the target has had cancellation requested or an exception has occurred.</summary>
+        private bool CanceledOrFaulted
+        {
+            get
+            {
+                return _dataflowBlockOptions.CancellationToken.IsCancellationRequested || Volatile.Read(ref _exceptions) != null;
+            }
+        }
+
+        /// <summary>Completes the block once all completion conditions are met.</summary>
+        private void CompleteBlockIfPossible()
+        {
+            Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+            bool noMoreMessages = _decliningPermanently && _messages.IsEmpty;
+            if (noMoreMessages || CanceledOrFaulted)
+            {
+                CompleteBlockIfPossible_Slow();
+            }
+        }
+
+        /// <summary>
+        /// Slow path for CompleteBlockIfPossible. 
+        /// Separating out the slow path into its own method makes it more likely that the fast path method will get inlined.
+        /// </summary>
+        private void CompleteBlockIfPossible_Slow()
+        {
+            Contract.Requires((_decliningPermanently && _messages.IsEmpty) || CanceledOrFaulted, "There must be no more messages.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: true);
+
+            bool notCurrentlyProcessing = _numberOfOutstandingOperations == 0;
+            if (notCurrentlyProcessing && !_completionReserved)
+            {
+                // Make sure no one else tries to call CompleteBlockOncePossible
+                _completionReserved = true;
+
+                // Make sure the target is declining
+                _decliningPermanently = true;
+
+                // 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(),
+                    this, CancellationToken.None, Common.GetCreationOptionsForTask(), TaskScheduler.Default);
+            }
+        }
+
+        /// <summary>
+        /// Completes the block.  This must only be called once, and only once all of the completion conditions are met.
+        /// As such, it must only be called from CompleteBlockIfPossible.
+        /// </summary>
+        private void CompleteBlockOncePossible()
+        {
+            // Since the lock is needed only for the Assert, we do this only in DEBUG mode
+#if DEBUG
+            lock (IncomingLock) Debug.Assert(_numberOfOutstandingOperations == 0, "Everything must be done by now.");
+#endif
+
+            // Release any postponed messages
+            if (_boundingState != null)
+            {
+                // Note: No locks should be held at this point.
+                Common.ReleaseAllPostponedMessages(_owningTarget, _boundingState.PostponedMessages, ref _exceptions);
+            }
+
+            // For good measure and help in preventing leaks, clear out the incoming message queue, 
+            // which may still contain orphaned data if we were canceled or faulted.  However,
+            // we don't reset the bounding count here, as the block as a whole may still be active.
+            KeyValuePair<TInput, long> ignored;
+            IProducerConsumerQueue<KeyValuePair<TInput, long>> messages = _messages;
+            while (messages.TryDequeue(out ignored)) ;
+
+            // If we completed with any unhandled exception, finish in an error state
+            if (Volatile.Read(ref _exceptions) != null)
+            {
+                // 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));
+            }
+            // If we completed with cancellation, finish in a canceled state
+            else if (_dataflowBlockOptions.CancellationToken.IsCancellationRequested)
+            {
+                _completionSource.TrySetCanceled();
+            }
+            // Otherwise, finish in a successful state.
+            else
+            {
+                _completionSource.TrySetResult(default(VoidResult));
+            }
+#if FEATURE_TRACING
+            // We only want to do tracing for block completion if this target core represents the whole block.
+            // If it only represents a part of the block (i.e. there's a source associated with it as well),
+            // then we shouldn't log just for the first half of the block; the source half will handle logging.
+            DataflowEtwProvider etwLog;
+            if ((_targetCoreOptions & TargetCoreOptions.RepresentsBlockCompletion) != 0 &&
+                (etwLog = DataflowEtwProvider.Log).IsEnabled())
+            {
+                etwLog.DataflowBlockCompleted(_owningTarget);
+            }
+#endif
+        }
+
+        /// <summary>Gets whether the target core is operating in a bounded mode.</summary>
+        internal bool IsBounded { get { return _boundingState != null; } }
+
+        /// <summary>Increases or decreases the bounding count.</summary>
+        /// <param name="count">The incremental addition (positive to increase, negative to decrease).</param>
+        internal void ChangeBoundingCount(int count)
+        {
+            Contract.Requires(count != 0, "Should only be called when the count is actually changing.");
+            Common.ContractAssertMonitorStatus(IncomingLock, held: false);
+            if (_boundingState != null)
+            {
+                lock (IncomingLock)
+                {
+                    Debug.Assert(count > 0 || (count < 0 && _boundingState.CurrentCount + count >= 0),
+                        "If count is negative, it must not take the total count negative.");
+                    _boundingState.CurrentCount += count;
+                    ProcessAsyncIfNecessary();
+                    CompleteBlockIfPossible();
+                }
+            }
+        }
+
+        /// <summary>Gets the object to display in the debugger display attribute.</summary>
+        [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+        private object DebuggerDisplayContent
+        {
+            get
+            {
+                var displayTarget = _owningTarget as IDebuggerDisplay;
+                return string.Format("Block=\"{0}\"",
+                    displayTarget != null ? displayTarget.Content : _owningTarget);
+            }
+        }
+
+        /// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
+        internal ExecutionDataflowBlockOptions DataflowBlockOptions { get { return _dataflowBlockOptions; } }
+
+        /// <summary>Gets information about this helper to be used for display in a debugger.</summary>
+        /// <returns>Debugging information about this target.</returns>
+        internal DebuggingInformation GetDebuggingInformation() { return new DebuggingInformation(this); }
+
+        /// <summary>Provides a wrapper for commonly needed debugging information.</summary>
+        internal sealed class DebuggingInformation
+        {
+            /// <summary>The target being viewed.</summary>
+            private readonly TargetCore<TInput> _target;
+
+            /// <summary>Initializes the debugging helper.</summary>
+            /// <param name="target">The target being viewed.</param>
+            internal DebuggingInformation(TargetCore<TInput> target) { _target = target; }
+
+            /// <summary>Gets the number of messages waiting to be processed.</summary>
+            internal int InputCount { get { return _target._messages.Count; } }
+            /// <summary>Gets the messages waiting to be processed.</summary>
+            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
+            {
+                get { return _target._boundingState != null ? _target._boundingState.PostponedMessages : null; }
+            }
+
+            /// <summary>Gets the current number of outstanding input processing operations.</summary>
+            internal Int32 CurrentDegreeOfParallelism { get { return _target._numberOfOutstandingOperations - _target._numberOfOutstandingServiceTasks; } }
+
+            /// <summary>Gets the DataflowBlockOptions used to configure this block.</summary>
+            internal ExecutionDataflowBlockOptions DataflowBlockOptions { get { return _target._dataflowBlockOptions; } }
+            /// <summary>Gets whether the block is declining further messages.</summary>
+            internal bool IsDecliningPermanently { get { return _target._decliningPermanently; } }
+            /// <summary>Gets whether the block is completed.</summary>
+            internal bool IsCompleted { get { return _target.Completion.IsCompleted; } }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/TargetRegistry.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/TargetRegistry.cs
new file mode 100644 (file)
index 0000000..ef30353
--- /dev/null
@@ -0,0 +1,418 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// TargetRegistry.cs
+//
+//
+// A store of registered targets with a target block.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+using System.Linq;
+
+namespace System.Threading.Tasks.Dataflow.Internal
+{
+    /// <summary>Stores targets registered with a source.</summary>
+    /// <typeparam name="T">Specifies the type of data accepted by the targets.</typeparam>
+    /// <remarks>This type is not thread-safe.</remarks>
+    [DebuggerDisplay("Count={Count}")]
+    [DebuggerTypeProxy(typeof(TargetRegistry<>.DebugView))]
+    internal sealed class TargetRegistry<T>
+    {
+        /// <summary>
+        /// Information about a registered target. This class represents a self-sufficient node in a linked list.
+        /// </summary>
+        internal sealed class LinkedTargetInfo
+        {
+            /// <summary>Initializes the LinkedTargetInfo.</summary>
+            /// <param name="target">The target block reference for this entry.</param>
+            /// <param name="linkOptions">The link options.</param>
+            internal LinkedTargetInfo(ITargetBlock<T> target, DataflowLinkOptions linkOptions)
+            {
+                Contract.Requires(target != null, "The target that is supposed to be linked must not be null.");
+                Contract.Requires(linkOptions != null, "The linkOptions must not be null.");
+
+                Target = target;
+                PropagateCompletion = linkOptions.PropagateCompletion;
+                RemainingMessages = linkOptions.MaxMessages;
+            }
+
+            /// <summary>The target block reference for this entry.</summary>
+            internal readonly ITargetBlock<T> Target;
+            /// <summary>The value of the PropagateCompletion link option.</summary>
+            internal readonly bool PropagateCompletion;
+            /// <summary>Number of remaining messages to propagate. 
+            /// This counter is initialized to the MaxMessages option and 
+            /// gets decremented after each successful propagation.</summary>
+            internal int RemainingMessages;
+            /// <summary>The previous node in the list.</summary>
+            internal LinkedTargetInfo Previous;
+            /// <summary>The next node in the list.</summary>
+            internal LinkedTargetInfo Next;
+        }
+
+        /// <summary>A reference to the owning source block.</summary>
+        private readonly ISourceBlock<T> _owningSource;
+        /// <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;
+        /// <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;
+        /// <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;
+
+        /// <summary>Initializes the registry.</summary>
+        internal TargetRegistry(ISourceBlock<T> owningSource)
+        {
+            Contract.Requires(owningSource != null, "The TargetRegistry instance must be owned by a source block.");
+
+            _owningSource = owningSource;
+            _targetInformation = new Dictionary<ITargetBlock<T>, LinkedTargetInfo>();
+        }
+
+        /// <summary>Adds a target to the registry.</summary>
+        /// <param name="target">The target to add.</param>
+        /// <param name="linkOptions">The link options.</param>
+        internal void Add(ref ITargetBlock<T> target, DataflowLinkOptions linkOptions)
+        {
+            Contract.Requires(target != null, "The target that is supposed to be linked must not be null.");
+            Contract.Requires(linkOptions != null, "The link options must not be null.");
+
+            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);
+
+            // Add the target to both stores, the list and the dictionary, which are used for different purposes
+            var node = new LinkedTargetInfo(target, linkOptions);
+            AddToList(node, linkOptions.Append);
+            _targetInformation.Add(target, node);
+
+            // Increment the optimization counter if needed
+            Debug.Assert(_linksWithRemainingMessages >= 0, "_linksWithRemainingMessages must be non-negative at any time.");
+            if (node.RemainingMessages > 0) _linksWithRemainingMessages++;
+#if FEATURE_TRACING
+            DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+            if (etwLog.IsEnabled())
+            {
+                etwLog.DataflowBlockLinking(_owningSource, target);
+            }
+#endif
+        }
+
+        /// <summary>Gets whether the registry contains a particular target.</summary>
+        /// <param name="target">The target.</param>
+        /// <returns>true if the registry contains the target; otherwise, false.</returns>
+        internal bool Contains(ITargetBlock<T> target)
+        {
+            return _targetInformation.ContainsKey(target);
+        }
+
+        /// <summary>Removes the target from the registry.</summary>
+        /// <param name="target">The target to remove.</param>
+        /// <param name="onlyIfReachedMaxMessages">
+        /// Only remove the target if it's configured to be unlinked after one propagation.
+        /// </param>
+        internal void Remove(ITargetBlock<T> target, bool onlyIfReachedMaxMessages = false)
+        {
+            Contract.Requires(target != null, "Target to remove is required.");
+
+            // If we are implicitly unlinking and there is nothing to be unlinked implicitly, bail
+            Debug.Assert(_linksWithRemainingMessages >= 0, "_linksWithRemainingMessages must be non-negative at any time.");
+            if (onlyIfReachedMaxMessages && _linksWithRemainingMessages == 0) return;
+
+            // Otherwise take the slow path
+            Remove_Slow(target, onlyIfReachedMaxMessages);
+        }
+
+        /// <summary>Actually removes the target from the registry.</summary>
+        /// <param name="target">The target to remove.</param>
+        /// <param name="onlyIfReachedMaxMessages">
+        /// Only remove the target if it's configured to be unlinked after one propagation.
+        /// </param>
+        private void Remove_Slow(ITargetBlock<T> target, bool onlyIfReachedMaxMessages)
+        {
+            Contract.Requires(target != null, "Target to remove is required.");
+
+            // Make sure we've intended to go the slow route
+            Debug.Assert(_linksWithRemainingMessages >= 0, "_linksWithRemainingMessages must be non-negative at any time.");
+            Debug.Assert(!onlyIfReachedMaxMessages || _linksWithRemainingMessages > 0, "We shouldn't have ended on the slow path.");
+
+            // If the target is registered...
+            LinkedTargetInfo node;
+            if (_targetInformation.TryGetValue(target, out node))
+            {
+                Debug.Assert(node != null, "The LinkedTargetInfo node referenced in the Dictionary must be non-null.");
+
+                // Remove the target, if either there's no constraint on the removal
+                // or if this was the last remaining message.
+                if (!onlyIfReachedMaxMessages || node.RemainingMessages == 1)
+                {
+                    RemoveFromList(node);
+                    _targetInformation.Remove(target);
+
+                    // Decrement the optimization counter if needed
+                    if (node.RemainingMessages == 0) _linksWithRemainingMessages--;
+                    Debug.Assert(_linksWithRemainingMessages >= 0, "_linksWithRemainingMessages must be non-negative at any time.");
+#if FEATURE_TRACING
+                    DataflowEtwProvider etwLog = DataflowEtwProvider.Log;
+                    if (etwLog.IsEnabled())
+                    {
+                        etwLog.DataflowBlockUnlinking(_owningSource, target);
+                    }
+#endif
+                }
+                // If the target is to stay and we are counting the remaining messages for this link, decrement the counter
+                else if (node.RemainingMessages > 0)
+                {
+                    Debug.Assert(node.RemainingMessages > 1, "The target should have been removed, because there are no remaining messages.");
+                    node.RemainingMessages--;
+                }
+            }
+        }
+
+        /// <summary>Clears the target registry entry points while allowing subsequent traversals of the linked list.</summary>
+        internal LinkedTargetInfo ClearEntryPoints()
+        {
+            // Save _firstTarget so we can return it
+            LinkedTargetInfo firstTarget = _firstTarget;
+
+            // Clear out the entry points
+            _firstTarget = _lastTarget = null;
+            _targetInformation.Clear();
+            Debug.Assert(_linksWithRemainingMessages >= 0, "_linksWithRemainingMessages must be non-negative at any time.");
+            _linksWithRemainingMessages = 0;
+
+            return firstTarget;
+        }
+
+        /// <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)
+        {
+            Debug.Assert(_owningSource.Completion.IsCompleted, "The owning source must have completed before propagating completion.");
+
+            // Cache the owning source's completion task to avoid calling the getter many times
+            Task owningSourceCompletion = _owningSource.Completion;
+
+            // Propagate completion to those targets that have requested it
+            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; } }
+
+        /// <summary>Adds a LinkedTargetInfo node to the doubly-linked list.</summary>
+        /// <param name="node">The node to be added.</param>
+        /// <param name="append">Whether to append or to prepend the node.</param>
+        internal void AddToList(LinkedTargetInfo node, bool append)
+        {
+            Contract.Requires(node != null, "Requires a node to be added.");
+
+            // If the list is empty, assign the ends to point to the new node and we are done
+            if (_firstTarget == null && _lastTarget == null)
+            {
+                _firstTarget = _lastTarget = node;
+            }
+            else
+            {
+                Debug.Assert(_firstTarget != null && _lastTarget != null, "Both first and last node must either be null or non-null.");
+                Debug.Assert(_lastTarget.Next == null, "The last node must not have a successor.");
+                Debug.Assert(_firstTarget.Previous == null, "The first node must not have a predecessor.");
+
+                if (append)
+                {
+                    // Link the new node to the end of the existing list
+                    node.Previous = _lastTarget;
+                    _lastTarget.Next = node;
+                    _lastTarget = node;
+                }
+                else
+                {
+                    // Link the new node to the front of the existing list
+                    node.Next = _firstTarget;
+                    _firstTarget.Previous = node;
+                    _firstTarget = node;
+                }
+            }
+
+            Debug.Assert(_firstTarget != null && _lastTarget != null, "Both first and last node must be non-null after AddToList.");
+        }
+
+        /// <summary>Removes the LinkedTargetInfo node from the doubly-linked list.</summary>
+        /// <param name="node">The node to be removed.</param>
+        internal void RemoveFromList(LinkedTargetInfo node)
+        {
+            Contract.Requires(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;
+
+            // Remove the node by linking the adjacent nodes
+            if (node.Previous != null)
+            {
+                node.Previous.Next = next;
+                node.Previous = null;
+            }
+
+            if (node.Next != null)
+            {
+                node.Next.Previous = previous;
+                node.Next = null;
+            }
+
+            // Adjust the list ends
+            if (_firstTarget == node) _firstTarget = next;
+            if (_lastTarget == node) _lastTarget = previous;
+
+            Debug.Assert((_firstTarget != null) == (_lastTarget != null), "Both first and last node must either be null or non-null after RemoveFromList.");
+        }
+
+        /// <summary>Gets the number of items in the registry.</summary>
+        private int Count { get { return _targetInformation.Count; } }
+
+        /// <summary>Converts the linked list of targets to an array for rendering in a debugger.</summary>
+        private ITargetBlock<T>[] TargetsForDebugger
+        {
+            get
+            {
+                var targets = new ITargetBlock<T>[Count];
+                int i = 0;
+                for (LinkedTargetInfo node = _firstTarget; node != null; node = node.Next)
+                {
+                    targets[i++] = node.Target;
+                }
+
+                return targets;
+            }
+        }
+
+
+
+        /// <summary>Provides a nop passthrough for use with TargetRegistry.</summary>
+        [DebuggerDisplay("{DebuggerDisplayContent,nq}")]
+        [DebuggerTypeProxy(typeof(TargetRegistry<>.NopLinkPropagator.DebugView))]
+        private sealed class NopLinkPropagator : IPropagatorBlock<T, T>, ISourceBlock<T>, IDebuggerDisplay
+        {
+            /// <summary>The source that encapsulates this block.</summary>
+            private readonly ISourceBlock<T> _owningSource;
+            /// <summary>The target with which this block is associated.</summary>
+            private readonly ITargetBlock<T> _target;
+
+            /// <summary>Initializes the passthrough.</summary>
+            /// <param name="owningSource">The source that encapsulates this block.</param>
+            /// <param name="target">The target to which messages should be forwarded.</param>
+            internal NopLinkPropagator(ISourceBlock<T> owningSource, ITargetBlock<T> target)
+            {
+                Contract.Requires(owningSource != null, "Propagator must be associated with a source.");
+                Contract.Requires(target != null, "Target to propagate to is required.");
+
+                // Store the arguments
+                _owningSource = owningSource;
+                _target = target;
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Targets/Member[@name="OfferMessage"]/*' />
+            DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source, Boolean 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"]/*' />
+            T ISourceBlock<T>.ConsumeMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target, out Boolean messageConsumed)
+            {
+                return _owningSource.ConsumeMessage(messageHeader, this, out messageConsumed);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReserveMessage"]/*' />
+            bool ISourceBlock<T>.ReserveMessage(DataflowMessageHeader messageHeader, ITargetBlock<T> target)
+            {
+                return _owningSource.ReserveMessage(messageHeader, this);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="ReleaseReservation"]/*' />
+            void ISourceBlock<T>.ReleaseReservation(DataflowMessageHeader messageHeader, ITargetBlock<T> target)
+            {
+                _owningSource.ReleaseReservation(messageHeader, this);
+            }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Completion"]/*' />
+            Task IDataflowBlock.Completion { get { return _owningSource.Completion; } }
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Complete"]/*' />
+            void IDataflowBlock.Complete() { _target.Complete(); }
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Blocks/Member[@name="Fault"]/*' />
+            void IDataflowBlock.Fault(Exception exception) { _target.Fault(exception); }
+
+            /// <include file='XmlDocs/CommonXmlDocComments.xml' path='CommonXmlDocComments/Sources/Member[@name="LinkTo"]/*' />
+            IDisposable ISourceBlock<T>.LinkTo(ITargetBlock<T> target, DataflowLinkOptions linkOptions) { throw new NotSupportedException(SR.NotSupported_MemberNotNeeded); }
+
+            /// <summary>The data to display in the debugger display attribute.</summary>
+            [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
+            private object DebuggerDisplayContent
+            {
+                get
+                {
+                    var displaySource = _owningSource as IDebuggerDisplay;
+                    var displayTarget = _target as IDebuggerDisplay;
+                    return string.Format("{0} Source=\"{1}\", Target=\"{2}\"",
+                        Common.GetNameForDebugger(this),
+                        displaySource != null ? displaySource.Content : _owningSource,
+                        displayTarget != null ? displayTarget.Content : _target);
+                }
+            }
+            /// <summary>Gets the data to display in the debugger display attribute for this instance.</summary>
+            object IDebuggerDisplay.Content { get { return DebuggerDisplayContent; } }
+
+            /// <summary>Provides a debugger type proxy for a passthrough.</summary>
+            private sealed class DebugView
+            {
+                /// <summary>The passthrough.</summary>
+                private readonly NopLinkPropagator _passthrough;
+
+                /// <summary>Initializes the debug view.</summary>
+                /// <param name="passthrough">The passthrough to view.</param>
+                public DebugView(NopLinkPropagator passthrough)
+                {
+                    Contract.Requires(passthrough != null, "Need a propagator with which to construct the debug view.");
+                    _passthrough = passthrough;
+                }
+
+                /// <summary>The linked target for this block.</summary>
+                public ITargetBlock<T> LinkedTarget { get { return _passthrough._target; } }
+            }
+        }
+
+
+        /// <summary>Provides a debugger type proxy for the target registry.</summary>
+        private sealed class DebugView
+        {
+            /// <summary>The registry being debugged.</summary>
+            private readonly TargetRegistry<T> _registry;
+
+            /// <summary>Initializes the type proxy.</summary>
+            /// <param name="registry">The target registry.</param>
+            public DebugView(TargetRegistry<T> registry)
+            {
+                Contract.Requires(registry != null, "Need a registry with which to construct the debug view.");
+                _registry = registry;
+            }
+
+            /// <summary>Gets a list of all targets to show in the debugger.</summary>
+            [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+            public ITargetBlock<T>[] Targets { get { return _registry.TargetsForDebugger; } }
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/Threading.cs b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Internal/Threading.cs
new file mode 100644 (file)
index 0000000..aa65a58
--- /dev/null
@@ -0,0 +1,52 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace System.Threading.Tasks.Dataflow.Internal.Threading
+{
+    internal delegate void TimerCallback(object state);
+
+    internal sealed class Timer : CancellationTokenSource, IDisposable
+    {
+        internal Timer(TimerCallback callback, object state, int dueTime, int period)
+        {
+            Debug.Assert(period == -1, "This stub implementation only supports dueTime.");
+            Task.Delay(dueTime, Token).ContinueWith((t, s) =>
+            {
+                var tuple = (Tuple<TimerCallback, object>)s;
+                tuple.Item1(tuple.Item2);
+            }, Tuple.Create(callback, state), CancellationToken.None,
+                TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
+                TaskScheduler.Default);
+        }
+
+        public new void Dispose() { base.Cancel(); }
+    }
+
+    internal sealed class Thread
+    {
+        internal static bool Yield() { return true; }
+    }
+
+    internal delegate void WaitCallback(object state);
+
+    internal sealed class ThreadPool
+    {
+        private static readonly SynchronizationContext _ctx = new SynchronizationContext();
+
+        internal static void QueueUserWorkItem(WaitCallback callback, object state)
+        {
+            _ctx.Post(s =>
+            {
+                var tuple = (Tuple<WaitCallback, object>)s;
+                tuple.Item1(tuple.Item2);
+            }, Tuple.Create(callback, state));
+        }
+    }
+}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Resources/Strings.resx b/mcs/class/System.Threading.Tasks.Dataflow/CoreFxSources/Resources/Strings.resx
new file mode 100644 (file)
index 0000000..4e0b7be
--- /dev/null
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="ArgumentOutOfRange_BatchSizeMustBeNoGreaterThanBoundedCapacity" xml:space="preserve">
+    <value>Number must be no greater than the value specified in BoundedCapacity.</value>
+  </data>
+  <data name="ArgumentOutOfRange_GenericPositive" xml:space="preserve">
+    <value>Number must be positive.</value>
+  </data>
+  <data name="ArgumentOutOfRange_NeedNonNegOrNegative1" xml:space="preserve">
+    <value>Number must be either non-negative and less than or equal to Int32.MaxValue or -1</value>
+  </data>
+  <data name="Argument_BoundedCapacityNotSupported" xml:space="preserve">
+    <value>BoundedCapacity must be Unbounded or -1 for this dataflow block.</value>
+  </data>
+  <data name="Argument_CantConsumeFromANullSource" xml:space="preserve">
+    <value>The argument must be false if no source from which to consume is specified.</value>
+  </data>
+  <data name="Argument_InvalidMessageHeader" xml:space="preserve">
+    <value>The DataflowMessageHeader instance does not represent a valid message header.</value>
+  </data>
+  <data name="Argument_InvalidMessageId" xml:space="preserve">
+    <value>To construct a DataflowMessageHeader instance, either pass a non-zero value or use the parameterless constructor.</value>
+  </data>
+  <data name="Argument_InvalidSourceForFilteredLink" xml:space="preserve">
+    <value>This block must only be used with the source from which it was created.</value>
+  </data>
+  <data name="Argument_NonGreedyNotSupported" xml:space="preserve">
+    <value>Greedy must be true for this dataflow block.</value>
+  </data>
+  <data name="event_DataflowBlockCompleted" xml:space="preserve">
+    <value>Block {0} completed as {1}. {2}</value>
+  </data>
+  <data name="event_DataflowBlockCreated" xml:space="preserve">
+    <value>Block of type {0} instantiated with Id {1}.</value>
+  </data>
+  <data name="event_DataflowBlockLinking" xml:space="preserve">
+    <value>Source {0} linked to target {1}.</value>
+  </data>
+  <data name="event_DataflowBlockUnlinking" xml:space="preserve">
+    <value>Source {0} unlinked from target {1}.</value>
+  </data>
+  <data name="event_TaskLaunchedForMessageHandling" xml:space="preserve">
+    <value>{1} task launched from block {0} with {2} message(s) pending.</value>
+  </data>
+  <data name="InvalidOperation_DataNotAvailableForReceive" xml:space="preserve">
+    <value>The source completed without providing data to receive.</value>
+  </data>
+  <data name="InvalidOperation_FailedToConsumeReservedMessage" xml:space="preserve">
+    <value>The target block failed to consume a message it had successfully reserved.</value>
+  </data>
+  <data name="InvalidOperation_MessageNotReservedByTarget" xml:space="preserve">
+    <value>The target does not have the message reserved.</value>
+  </data>
+  <data name="NotSupported_MemberNotNeeded" xml:space="preserve">
+    <value>This member is not supported on this dataflow block. The block is intended for a specific purpose that does not utilize this member.</value>
+  </data>
+  <data name="ConcurrentCollection_SyncRoot_NotSupported" xml:space="preserve">
+    <value>The SyncRoot property may not be used for the synchronization of concurrent collections.</value>
+  </data>
+</root>
\ No newline at end of file
index 83bdafab3cc7ceb6d65eb32e864ef8396b53ded5..349e5c75754d6e3996c58e5041b9253198c7b489 100644 (file)
@@ -7,7 +7,8 @@ LIBRARY = System.Threading.Tasks.Dataflow.dll
 include ../../build/library.make
 
 LIB_REFS += System.Core System
-LIB_MCS_FLAGS += -r:$(corlib)
+LIB_MCS_FLAGS += -r:$(corlib) -d:CONCURRENT_COLLECTIONS
 
 TEST_MCS_FLAGS = -r:System.Core.dll -r:System.dll
 
+EXTRA_DISTFILES=README.md
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/README.md b/mcs/class/System.Threading.Tasks.Dataflow/README.md
new file mode 100644 (file)
index 0000000..aa7e41f
--- /dev/null
@@ -0,0 +1,6 @@
+The CoreFxSources folder contains the implementation taken from MS CoreFx
+repository at 905a1940bcda0afdca2f14ceb2b0161ebc4d1d02.
+
+While we'd ideally not ship this assembly at all with Mono (it doesn't ship
+with .NET Framework, there's only as a NuGet package), we shipped it in
+the past and as such people might rely on it so we can't remove it.
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/SR.cs b/mcs/class/System.Threading.Tasks.Dataflow/SR.cs
new file mode 100644 (file)
index 0000000..7113138
--- /dev/null
@@ -0,0 +1,27 @@
+//
+// Resource strings referenced by the code.
+//
+// Copyright 2015 Xamarin Inc
+//
+// Use the following script to extract strings from CoreFxSources/Resources/Strings.resx
+//
+// var d = XDocument.Load ("Strings.resx");
+// foreach (var j in d.XPathSelectElements ("/root/data")){ var v = j.XPathSelectElement ("value"); Console.WriteLine ("\tpublic const string {0}=\"{1}\";", j.Attribute ("name").Value, v.Value); }
+//
+partial class SR
+{
+       public const string ArgumentOutOfRange_BatchSizeMustBeNoGreaterThanBoundedCapacity="Number must be no greater than the value specified in BoundedCapacity.";
+       public const string ArgumentOutOfRange_GenericPositive="Number must be positive.";
+       public const string ArgumentOutOfRange_NeedNonNegOrNegative1="Number must be either non-negative and less than or equal to Int32.MaxValue or -1";
+       public const string Argument_BoundedCapacityNotSupported="BoundedCapacity must be Unbounded or -1 for this dataflow block.";
+       public const string Argument_CantConsumeFromANullSource="The argument must be false if no source from which to consume is specified.";
+       public const string Argument_InvalidMessageHeader="The DataflowMessageHeader instance does not represent a valid message header.";
+       public const string Argument_InvalidMessageId="To construct a DataflowMessageHeader instance, either pass a non-zero value or use the parameterless constructor.";
+       public const string Argument_InvalidSourceForFilteredLink="This block must only be used with the source from which it was created.";
+       public const string Argument_NonGreedyNotSupported="Greedy must be true for this dataflow block.";
+       public const string InvalidOperation_DataNotAvailableForReceive="The source completed without providing data to receive.";
+       public const string InvalidOperation_FailedToConsumeReservedMessage="The target block failed to consume a message it had successfully reserved.";
+       public const string InvalidOperation_MessageNotReservedByTarget="The target does not have the message reserved.";
+       public const string NotSupported_MemberNotNeeded="This member is not supported on this dataflow block. The block is intended for a specific purpose that does not utilize this member.";
+       public const string ConcurrentCollection_SyncRoot_NotSupported="The SyncRoot property may not be used for the synchronization of concurrent collections.";
+}
\ No newline at end of file
index 3c479dc8737415afdf5de744e1432ef284539834..c2437d0f8fe66ab8363fa6d438fed25a55ac80b8 100644 (file)
@@ -1,49 +1,39 @@
 ../../build/common/Consts.cs
 ../../build/common/Locale.cs
 ../../build/common/MonoTODOAttribute.cs
+../../build/common/SR.cs
+SR.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
-../Mono.Parallel/Mono.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
+CoreFxSources/Base/DataflowBlock.cs
+CoreFxSources/Base/DataflowBlockOptions.cs
+CoreFxSources/Base/DataflowLinkOptions.cs
+CoreFxSources/Base/DataflowMessageHeader.cs
+CoreFxSources/Base/DataflowMessageStatus.cs
+CoreFxSources/Base/IDataflowBlock.cs
+CoreFxSources/Base/IPropagatorBlock.cs
+CoreFxSources/Base/IReceivableSourceBlock.cs
+CoreFxSources/Base/ISourceBlock.cs
+CoreFxSources/Base/ITargetBlock.cs
+CoreFxSources/Blocks/ActionBlock.cs
+CoreFxSources/Blocks/BatchBlock.cs
+CoreFxSources/Blocks/BatchedJoinBlock.cs
+CoreFxSources/Blocks/BroadcastBlock.cs
+CoreFxSources/Blocks/BufferBlock.cs
+CoreFxSources/Blocks/JoinBlock.cs
+CoreFxSources/Blocks/TransformBlock.cs
+CoreFxSources/Blocks/TransformManyBlock.cs
+CoreFxSources/Blocks/WriteOnceBlock.cs
+CoreFxSources/Internal/ActionOnDispose.cs
+CoreFxSources/Internal/Common.cs
+CoreFxSources/Internal/EnumerableDebugView.cs
+CoreFxSources/Internal/IDebuggerDisplay.cs
+CoreFxSources/Internal/ImmutableList.cs
+CoreFxSources/Internal/Padding.cs
+CoreFxSources/Internal/ProducerConsumerQueues.cs
+CoreFxSources/Internal/QueuedMap.cs
+CoreFxSources/Internal/ReorderingBuffer.cs
+CoreFxSources/Internal/SourceCore.cs
+CoreFxSources/Internal/SpscTargetCore.cs
+CoreFxSources/Internal/TargetCore.cs
+CoreFxSources/Internal/TargetRegistry.cs
+CoreFxSources/Internal/Threading.cs
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ActionBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ActionBlock.cs
deleted file mode 100644 (file)
index 1b8455c..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-// 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);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/AsyncExecutingMessageBox.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/AsyncExecutingMessageBox.cs
deleted file mode 100644 (file)
index 3cf41c3..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-// 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 ();
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BatchBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BatchBlock.cs
deleted file mode 100644 (file)
index 312e5f2..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
-// 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);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BatchedJoinBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BatchedJoinBlock.cs
deleted file mode 100644 (file)
index cfafa02..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-// 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);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BatchedJoinBlock`3.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BatchedJoinBlock`3.cs
deleted file mode 100644 (file)
index 5e8c402..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-// 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);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BroadcastBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BroadcastBlock.cs
deleted file mode 100644 (file)
index b51d2b3..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-// 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);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BroadcastOutgoingQueue.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BroadcastOutgoingQueue.cs
deleted file mode 100644 (file)
index b2e2884..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-// 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;
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BufferBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/BufferBlock.cs
deleted file mode 100644 (file)
index b6b60bf..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-// 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);
-               }
-       }
-}
-
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ChooserBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ChooserBlock.cs
deleted file mode 100644 (file)
index bf2d0b0..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-// 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; }
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/CompletionHelper.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/CompletionHelper.cs
deleted file mode 100644 (file)
index cc6a005..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-// 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 ();
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowBlock.cs
deleted file mode 100644 (file)
index e9f23a8..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-// 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> ();
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowBlockOptions.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowBlockOptions.cs
deleted file mode 100644 (file)
index 608e22f..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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;
-                       }
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowLinkOptions.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowLinkOptions.cs
deleted file mode 100644 (file)
index 6411b2c..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-// 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; }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowMessageHeader.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowMessageHeader.cs
deleted file mode 100644 (file)
index 38ceb02..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-// 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);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowMessageStatus.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/DataflowMessageStatus.cs
deleted file mode 100644 (file)
index 63ec610..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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
-       }
-}
-
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ExecutingMessageBox.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ExecutingMessageBox.cs
deleted file mode 100644 (file)
index bbeccb5..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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 ();
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ExecutingMessageBoxBase.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ExecutingMessageBoxBase.cs
deleted file mode 100644 (file)
index d601566..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-// 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
-                           && Volatile.Read (ref degreeOfParallelism) == 1)
-                               outgoingQueueComplete ();
-               }
-
-               /// <summary>
-               /// Makes sure the block is completed if it should be.
-               /// </summary>
-               protected override void VerifyCompleteness ()
-               {
-                       if (Volatile.Read (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);
-               }
-       }
-}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ExecutionDataflowBlockOptions.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ExecutionDataflowBlockOptions.cs
deleted file mode 100644 (file)
index 2b56582..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-// ExecutionDataflowBlockOptions.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 ExecutionDataflowBlockOptions : DataflowBlockOptions {
-               static readonly ExecutionDataflowBlockOptions DefaultOptions =
-                       new ExecutionDataflowBlockOptions ();
-
-               int maxDegreeOfParallelism;
-
-               /// <summary>
-               /// Cached default block options
-               /// </summary>
-               internal static new ExecutionDataflowBlockOptions Default {
-                       get { return DefaultOptions; }
-               }
-
-               public ExecutionDataflowBlockOptions ()
-               {
-                       MaxDegreeOfParallelism = 1;
-               }
-
-               public int MaxDegreeOfParallelism {
-                       get { return maxDegreeOfParallelism; }
-                       set {
-                               if (value < -1)
-                                       throw new ArgumentOutOfRangeException("value");
-
-                               maxDegreeOfParallelism = value;
-                       }
-               }
-
-               public bool SingleProducerConstrained { get; set; }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/GroupingDataflowBlockOptions.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/GroupingDataflowBlockOptions.cs
deleted file mode 100644 (file)
index f7a24d4..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-// GroupingDataflowBlockOptions.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 GroupingDataflowBlockOptions : DataflowBlockOptions {
-               static readonly GroupingDataflowBlockOptions DefaultOptions =
-                       new GroupingDataflowBlockOptions ();
-
-               long maxNumberOfGroups;
-
-               /// <summary>
-               /// Cached default block options
-               /// </summary>
-               internal static new GroupingDataflowBlockOptions Default {
-                       get { return DefaultOptions; }
-               }
-
-               public GroupingDataflowBlockOptions ()
-               {
-                       Greedy = true;
-                       MaxNumberOfGroups = -1;
-               }
-
-               public bool Greedy { get; set; }
-
-               public long MaxNumberOfGroups {
-                       get { return maxNumberOfGroups; }
-                       set {
-                               if (value < -1)
-                                       throw new ArgumentOutOfRangeException("value");
-                               
-                               maxNumberOfGroups = value;
-                       }
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/IDataflowBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/IDataflowBlock.cs
deleted file mode 100644 (file)
index a6e69b5..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-// IDataflowBlock.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.
-
-namespace System.Threading.Tasks.Dataflow {
-       public interface IDataflowBlock {
-               Task Completion { get; }
-
-               void Complete ();
-               void Fault (Exception exception);
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/IPropagatorBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/IPropagatorBlock.cs
deleted file mode 100644 (file)
index 0ac3f2b..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-// IPropagatorBlock.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 interface IPropagatorBlock<in TInput, out TOutput>
-               : ITargetBlock<TInput>, ISourceBlock<TOutput> {
-               }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/IReceivableSourceBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/IReceivableSourceBlock.cs
deleted file mode 100644 (file)
index dc35f58..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-// DataflowBlockOptions.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;
-
-namespace System.Threading.Tasks.Dataflow {
-       public interface IReceivableSourceBlock<TOutput> : ISourceBlock<TOutput> {
-               bool TryReceive (Predicate<TOutput> filter, out TOutput item);
-               bool TryReceiveAll (out IList<TOutput> items);
-                                                          }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ISourceBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ISourceBlock.cs
deleted file mode 100644 (file)
index 325a4fa..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-// ISourceBlock.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 interface ISourceBlock<out TOutput> : IDataflowBlock {
-               TOutput ConsumeMessage (DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target, out bool messageConsumed);
-               IDisposable LinkTo (ITargetBlock<TOutput> target, DataflowLinkOptions linkOptions);
-               void ReleaseReservation (DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target);
-               bool ReserveMessage (DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target);
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ITargetBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ITargetBlock.cs
deleted file mode 100644 (file)
index a64569e..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-// ITargetBlock.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 interface ITargetBlock<in TInput> : IDataflowBlock {
-               DataflowMessageStatus OfferMessage (
-                       DataflowMessageHeader messageHeader, TInput messageValue,
-                       ISourceBlock<TInput> source, bool consumeToAccept);
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/JoinBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/JoinBlock.cs
deleted file mode 100644 (file)
index fb424fc..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-// 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.
-
-using System.Collections.Generic;
-
-namespace System.Threading.Tasks.Dataflow
-{
-       public sealed class JoinBlock<T1, T2> : IReceivableSourceBlock<Tuple<T1, T2>>
-       {
-               readonly CompletionHelper compHelper;
-               readonly GroupingDataflowBlockOptions dataflowBlockOptions;
-               readonly OutgoingQueue<Tuple<T1, T2>> outgoing;
-
-               readonly JoinTarget<T1> target1;
-               readonly JoinTarget<T2> target2;
-
-               SpinLock targetLock = new SpinLock(false);
-               readonly AtomicBoolean nonGreedyProcessing = new AtomicBoolean ();
-
-               long target1Count;
-               long target2Count;
-               long numberOfGroups;
-
-               public JoinBlock () : this (GroupingDataflowBlockOptions.Default)
-               {
-               }
-
-               public JoinBlock (GroupingDataflowBlockOptions dataflowBlockOptions)
-               {
-                       if (dataflowBlockOptions == null)
-                               throw new ArgumentNullException ("dataflowBlockOptions");
-
-                       this.dataflowBlockOptions = dataflowBlockOptions;
-                       compHelper = CompletionHelper.GetNew (dataflowBlockOptions);
-                       target1 = new JoinTarget<T1> (this, SignalArrivalTarget, compHelper,
-                               () => outgoing.IsCompleted, dataflowBlockOptions,
-                               dataflowBlockOptions.Greedy, TryAdd1);
-                       target2 = new JoinTarget<T2> (this, SignalArrivalTarget, compHelper,
-                               () => outgoing.IsCompleted, dataflowBlockOptions,
-                               dataflowBlockOptions.Greedy, TryAdd2);
-                       outgoing = new OutgoingQueue<Tuple<T1, T2>> (this, compHelper,
-                               () => target1.Buffer.IsCompleted || target2.Buffer.IsCompleted,
-                               _ =>
-                               {
-                                       target1.DecreaseCount ();
-                                       target2.DecreaseCount ();
-                               }, dataflowBlockOptions);
-               }
-
-               public IDisposable LinkTo (ITargetBlock<Tuple<T1, T2>> target, DataflowLinkOptions linkOptions)
-               {
-                       return outgoing.AddTarget (target, linkOptions);
-               }
-
-               public bool TryReceive (Predicate<Tuple<T1, T2>> filter, out Tuple<T1, T2> item)
-               {
-                       return outgoing.TryReceive (filter, out item);
-               }
-
-               public bool TryReceiveAll (out IList<Tuple<T1, T2>> items)
-               {
-                       return outgoing.TryReceiveAll (out items);
-               }
-
-               Tuple<T1, T2> ISourceBlock<Tuple<T1, T2>>.ConsumeMessage (
-                       DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target,
-                       out bool messageConsumed)
-               {
-                       return outgoing.ConsumeMessage (messageHeader, target, out messageConsumed);
-               }
-
-               void ISourceBlock<Tuple<T1, T2>>.ReleaseReservation (
-                       DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target)
-               {
-                       outgoing.ReleaseReservation (messageHeader, target);
-               }
-
-               bool ISourceBlock<Tuple<T1, T2>>.ReserveMessage (
-                       DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2>> target)
-               {
-                       return outgoing.ReserveMessage (messageHeader, target);
-               }
-
-               public void Complete ()
-               {
-                       target1.Complete ();
-                       target2.Complete ();
-                       outgoing.Complete ();
-               }
-
-               void IDataflowBlock.Fault (Exception exception)
-               {
-                       compHelper.RequestFault (exception);
-               }
-
-               public Task Completion {
-                       get { return compHelper.Completion; }
-               }
-
-               /// <summary>
-               /// Returns whether a new item can be accepted by the first target,
-               /// and increments a counter if it can.
-               /// </summary>
-               bool TryAdd1 ()
-               {
-                       return dataflowBlockOptions.MaxNumberOfGroups == -1
-                              || Interlocked.Increment (ref target1Count)
-                              <= dataflowBlockOptions.MaxNumberOfGroups;
-               }
-
-               /// <summary>
-               /// Returns whether a new item can be accepted by the second target,
-               /// and increments a counter if it can.
-               /// </summary>
-               bool TryAdd2 ()
-               {
-                       return dataflowBlockOptions.MaxNumberOfGroups == -1
-                              || Interlocked.Increment (ref target2Count)
-                              <= dataflowBlockOptions.MaxNumberOfGroups;
-               }
-
-               /// <summary>
-               /// Decides whether to create a new tuple or not.
-               /// </summary>
-               void SignalArrivalTarget ()
-               {
-                       if (dataflowBlockOptions.Greedy) {
-                               bool taken = false;
-                               T1 value1;
-                               T2 value2;
-
-                               try {
-                                       targetLock.Enter (ref taken);
-
-                                       if (target1.Buffer.Count == 0 || target2.Buffer.Count == 0)
-                                               return;
-
-                                       value1 = target1.Buffer.Take ();
-                                       value2 = target2.Buffer.Take ();
-                               } finally {
-                                       if (taken)
-                                               targetLock.Exit ();
-                               }
-
-                               TriggerMessage (value1, value2);
-                       } else {
-                               if (ShouldProcessNonGreedy ())
-                                       EnsureNonGreedyProcessing ();
-                       }
-               }
-
-               /// <summary>
-               /// Returns whether non-greedy creation of a tuple should be started.
-               /// </summary>
-               bool ShouldProcessNonGreedy ()
-               {
-                       return target1.PostponedMessagesCount >= 1
-                              && target2.PostponedMessagesCount >= 1
-                              && (dataflowBlockOptions.BoundedCapacity == -1
-                                  || outgoing.Count < dataflowBlockOptions.BoundedCapacity);
-               }
-
-               /// <summary>
-               /// Starts non-greedy creation of tuples, if one doesn't already run.
-               /// </summary>
-               void EnsureNonGreedyProcessing ()
-               {
-                       if (nonGreedyProcessing.TrySet ())
-                               Task.Factory.StartNew (NonGreedyProcess,
-                                       dataflowBlockOptions.CancellationToken,
-                                       TaskCreationOptions.PreferFairness,
-                                       dataflowBlockOptions.TaskScheduler);
-               }
-
-               /// <summary>
-               /// Creates tuples in non-greedy mode,
-               /// making sure the whole tuple is available by using reservations.
-               /// </summary>
-               void NonGreedyProcess()
-               {
-                       while (ShouldProcessNonGreedy ()) {
-                               var reservation1 = target1.ReserveMessage ();
-
-                               if (reservation1 == null)
-                                       break;
-
-                               var reservation2 = target2.ReserveMessage ();
-                               if (reservation2 == null) {
-                                       target1.RelaseReservation (reservation1);
-                                       break;
-                               }
-
-                               var value1 = target1.ConsumeReserved (reservation1);
-                               var value2 = target2.ConsumeReserved (reservation2);
-
-                               TriggerMessage (value1, value2);
-                       }
-
-                       nonGreedyProcessing.Value = false;
-
-                       if (ShouldProcessNonGreedy ())
-                               EnsureNonGreedyProcessing ();
-               }
-
-
-               /// <summary>
-               /// Creates a tuple from the given values and adds the result to the output queue.
-               /// </summary>
-               void TriggerMessage (T1 val1, T2 val2)
-               {
-                       outgoing.AddData (Tuple.Create (val1, val2));
-
-                       if (dataflowBlockOptions.MaxNumberOfGroups != -1
-                           && Interlocked.Increment (ref numberOfGroups)
-                           >= dataflowBlockOptions.MaxNumberOfGroups)
-                               Complete ();
-               }
-
-               public ITargetBlock<T1> Target1 {
-                       get { return target1; }
-               }
-
-               public ITargetBlock<T2> Target2 {
-                       get { return target2; }
-               }
-
-               public int OutputCount {
-                       get { return outgoing.Count; }
-               }
-
-               public override string ToString ()
-               {
-                       return NameHelper.GetName (this, dataflowBlockOptions);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/JoinBlock`3.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/JoinBlock`3.cs
deleted file mode 100644 (file)
index 419beb7..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-// JoinBlock`3.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;
-
-namespace System.Threading.Tasks.Dataflow
-{
-       public sealed class JoinBlock<T1, T2, T3> : IReceivableSourceBlock<Tuple<T1, T2, T3>>
-       {
-               readonly CompletionHelper compHelper;
-               readonly GroupingDataflowBlockOptions dataflowBlockOptions;
-               readonly OutgoingQueue<Tuple<T1, T2, T3>> outgoing;
-
-               readonly JoinTarget<T1> target1;
-               readonly JoinTarget<T2> target2;
-               readonly JoinTarget<T3> target3;
-
-               SpinLock targetLock = new SpinLock (false);
-               readonly AtomicBoolean nonGreedyProcessing = new AtomicBoolean ();
-
-               long target1Count;
-               long target2Count;
-               long target3Count;
-               long numberOfGroups;
-
-               public JoinBlock () : this (GroupingDataflowBlockOptions.Default)
-               {
-               }
-
-               public JoinBlock (GroupingDataflowBlockOptions dataflowBlockOptions)
-               {
-                       if (dataflowBlockOptions == null)
-                               throw new ArgumentNullException ("dataflowBlockOptions");
-
-                       this.dataflowBlockOptions = dataflowBlockOptions;
-                       this.compHelper = CompletionHelper.GetNew (dataflowBlockOptions);
-
-                       target1 = new JoinTarget<T1> (this, SignalArrivalTarget, compHelper,
-                               () => outgoing.IsCompleted, dataflowBlockOptions,
-                               dataflowBlockOptions.Greedy, TryAdd1);
-                       target2 = new JoinTarget<T2> (this, SignalArrivalTarget, compHelper,
-                               () => outgoing.IsCompleted, dataflowBlockOptions,
-                               dataflowBlockOptions.Greedy, TryAdd2);
-                       target3 = new JoinTarget<T3> (this, SignalArrivalTarget, compHelper,
-                               () => outgoing.IsCompleted, dataflowBlockOptions,
-                               dataflowBlockOptions.Greedy, TryAdd3);
-                       outgoing = new OutgoingQueue<Tuple<T1, T2, T3>> (
-                               this, compHelper,
-                               () => target1.Buffer.IsCompleted || target2.Buffer.IsCompleted
-                                     || target3.Buffer.IsCompleted,
-                               _ =>
-                               {
-                                       target1.DecreaseCount ();
-                                       target2.DecreaseCount ();
-                                       target3.DecreaseCount ();
-                               }, dataflowBlockOptions);
-               }
-
-               public IDisposable LinkTo (ITargetBlock<Tuple<T1, T2, T3>> target, DataflowLinkOptions linkOptions)
-               {
-                       return outgoing.AddTarget (target, linkOptions);
-               }
-
-               public bool TryReceive (Predicate<Tuple<T1, T2, T3>> filter, out Tuple<T1, T2, T3> item)
-               {
-                       return outgoing.TryReceive (filter, out item);
-               }
-
-               public bool TryReceiveAll (out IList<Tuple<T1, T2, T3>> items)
-               {
-                       return outgoing.TryReceiveAll (out items);
-               }
-
-               Tuple<T1, T2, T3> ISourceBlock<Tuple<T1, T2, T3>>.ConsumeMessage (
-                       DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2, T3>> target,
-                       out bool messageConsumed)
-               {
-                       return outgoing.ConsumeMessage (messageHeader, target, out messageConsumed);
-               }
-
-               void ISourceBlock<Tuple<T1, T2, T3>>.ReleaseReservation (
-                       DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2, T3>> target)
-               {
-                       outgoing.ReleaseReservation (messageHeader, target);
-               }
-
-               bool ISourceBlock<Tuple<T1, T2, T3>>.ReserveMessage (
-                       DataflowMessageHeader messageHeader, ITargetBlock<Tuple<T1, T2, T3>> target)
-               {
-                       return outgoing.ReserveMessage (messageHeader, target);
-               }
-
-               public void Complete ()
-               {
-                       target1.Complete ();
-                       target2.Complete ();
-                       target3.Complete ();
-                       outgoing.Complete ();
-               }
-
-               void IDataflowBlock.Fault (Exception exception)
-               {
-                       compHelper.RequestFault (exception);
-               }
-
-               public Task Completion {
-                       get { return compHelper.Completion; }
-               }
-
-               /// <summary>
-               /// Returns whether a new item can be accepted by the first target,
-               /// and increments a counter if it can.
-               /// </summary>
-               bool TryAdd1 ()
-               {
-                       return dataflowBlockOptions.MaxNumberOfGroups == -1
-                              || Interlocked.Increment (ref target1Count)
-                              <= dataflowBlockOptions.MaxNumberOfGroups;
-               }
-
-               /// <summary>
-               /// Returns whether a new item can be accepted by the second target,
-               /// and increments a counter if it can.
-               /// </summary>
-               bool TryAdd2 ()
-               {
-                       return dataflowBlockOptions.MaxNumberOfGroups == -1
-                              || Interlocked.Increment (ref target2Count)
-                              <= dataflowBlockOptions.MaxNumberOfGroups;
-               }
-
-               /// <summary>
-               /// Returns whether a new item can be accepted by the third target,
-               /// and increments a counter if it can.
-               /// </summary>
-               bool TryAdd3 ()
-               {
-                       return dataflowBlockOptions.MaxNumberOfGroups == -1
-                              || Interlocked.Increment (ref target3Count)
-                              <= dataflowBlockOptions.MaxNumberOfGroups;
-               }
-
-               /// <summary>
-               /// Decides whether to create a new tuple or not.
-               /// </summary>
-               void SignalArrivalTarget ()
-               {
-                       if (dataflowBlockOptions.Greedy) {
-                               bool taken = false;
-                               T1 value1;
-                               T2 value2;
-                               T3 value3;
-
-                               try {
-                                       targetLock.Enter (ref taken);
-
-                                       if (target1.Buffer.Count == 0 || target2.Buffer.Count == 0
-                                           || target3.Buffer.Count == 0)
-                                               return;
-
-                                       value1 = target1.Buffer.Take ();
-                                       value2 = target2.Buffer.Take ();
-                                       value3 = target3.Buffer.Take ();
-                               } finally {
-                                       if (taken)
-                                               targetLock.Exit ();
-                               }
-
-                               TriggerMessage (value1, value2, value3);
-                       } else {
-                               if (ShouldProcesNonGreedy ())
-                                       EnsureNonGreedyProcessing ();
-                       }
-               }
-
-               /// <summary>
-               /// Returns whether non-greedy creation of a tuple should be started.
-               /// </summary>
-               bool ShouldProcesNonGreedy ()
-               {
-                       return target1.PostponedMessagesCount >= 1
-                              && target2.PostponedMessagesCount >= 1
-                              && target3.PostponedMessagesCount >= 1
-                              && (dataflowBlockOptions.BoundedCapacity == -1
-                                  || outgoing.Count < dataflowBlockOptions.BoundedCapacity);
-               }
-
-               /// <summary>
-               /// Starts non-greedy creation of tuples, if one doesn't already run.
-               /// </summary>
-               void EnsureNonGreedyProcessing ()
-               {
-                       if (nonGreedyProcessing.TrySet())
-                               Task.Factory.StartNew (NonGreedyProcess,
-                                       dataflowBlockOptions.CancellationToken,
-                                       TaskCreationOptions.PreferFairness,
-                                       dataflowBlockOptions.TaskScheduler);
-               }
-
-               /// <summary>
-               /// Creates tuples in non-greedy mode,
-               /// making sure the whole tuple is available by using reservations.
-               /// </summary>
-               void NonGreedyProcess ()
-               {
-                       while (ShouldProcesNonGreedy ()) {
-                               var reservation1 = target1.ReserveMessage ();
-
-                               if (reservation1 == null)
-                                       break;
-
-                               var reservation2 = target2.ReserveMessage ();
-                               if (reservation2 == null) {
-                                       target1.RelaseReservation (reservation1);
-                                       break;
-                               }
-
-                               var reservation3 = target3.ReserveMessage ();
-                               if (reservation3 == null) {
-                                       target1.RelaseReservation (reservation1);
-                                       target2.RelaseReservation (reservation2);
-                                       break;
-                               }
-
-                               var value1 = target1.ConsumeReserved (reservation1);
-                               var value2 = target2.ConsumeReserved (reservation2);
-                               var value3 = target3.ConsumeReserved (reservation3);
-
-                               TriggerMessage (value1, value2, value3);
-                       }
-
-                       nonGreedyProcessing.Value = false;
-
-                       if (ShouldProcesNonGreedy ())
-                               EnsureNonGreedyProcessing ();
-               }
-
-               /// <summary>
-               /// Creates a tuple from the given values and adds the result to the output queue.
-               /// </summary>
-               void TriggerMessage (T1 val1, T2 val2, T3 val3)
-               {
-                       outgoing.AddData (Tuple.Create (val1, val2, val3));
-
-                       if (dataflowBlockOptions.MaxNumberOfGroups != -1
-                           && Interlocked.Increment (ref numberOfGroups)
-                           >= dataflowBlockOptions.MaxNumberOfGroups)
-                               Complete ();
-               }
-
-               public ITargetBlock<T1> Target1 {
-                       get { return target1; }
-               }
-
-               public ITargetBlock<T2> Target2 {
-                       get { return target2; }
-               }
-
-               public ITargetBlock<T3> Target3 {
-                       get { return target3; }
-               }
-
-               public int OutputCount {
-                       get { return outgoing.Count; }
-               }
-
-               public override string ToString ()
-               {
-                       return NameHelper.GetName (this, dataflowBlockOptions);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/JoinTarget.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/JoinTarget.cs
deleted file mode 100644 (file)
index 48f293e..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-// 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.
-
-using System.Collections.Concurrent;
-
-namespace System.Threading.Tasks.Dataflow {
-       /// <summary>
-       /// Target block use by join blocks in their TargetN properties.
-       /// Also serves as its own <see cref="MessageBox{TInput}"/>.
-       /// </summary>
-       class JoinTarget<TTarget> : MessageBox<TTarget>, ITargetBlock<TTarget> {
-               readonly IDataflowBlock joinBlock;
-               readonly Action signal;
-
-               public JoinTarget (
-                       IDataflowBlock joinBlock, Action signal, CompletionHelper helper,
-                       Func<bool> externalCompleteTester, DataflowBlockOptions options,
-                       bool greedy, Func<bool> canAccept)
-                       : base (null, new BlockingCollection<TTarget> (), helper, externalCompleteTester,
-                               options, greedy, canAccept)
-               {
-                       this.joinBlock = joinBlock;
-                       this.signal = signal;
-                       Target = this;
-               }
-
-               /// <summary>
-               /// Makes sure the input queue is processed the way it needs to,
-               /// by signaling the parent join block.
-               /// </summary>
-               protected override void EnsureProcessing (bool newItem)
-               {
-                       signal ();
-               }
-
-               /// <summary>
-               /// The input queue of this block.
-               /// </summary>
-               public BlockingCollection<TTarget> Buffer {
-                       get { return MessageQueue; }
-               }
-
-               DataflowMessageStatus ITargetBlock<TTarget>.OfferMessage (
-                       DataflowMessageHeader messageHeader, TTarget messageValue,
-                       ISourceBlock<TTarget> source, bool consumeToAccept)
-               {
-                       return OfferMessage (messageHeader, messageValue, source, consumeToAccept);
-               }
-
-               void IDataflowBlock.Complete ()
-               {
-                       Complete ();
-               }
-
-               Task IDataflowBlock.Completion {
-                       get { throw new NotSupportedException (); }
-               }
-
-               void IDataflowBlock.Fault (Exception exception)
-               {
-                       joinBlock.Fault (exception);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/MessageBox.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/MessageBox.cs
deleted file mode 100644 (file)
index 867f803..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-// MessageBox.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.Linq;
-
-namespace System.Threading.Tasks.Dataflow {
-       /// <summary>
-       /// In MessageBox we store message that have been offered to us so that they can be
-       /// later processed 
-       /// </summary>
-       internal abstract class MessageBox<TInput> {
-               protected ITargetBlock<TInput> Target { get; set; }
-               protected CompletionHelper CompHelper { get; private set; }
-               readonly Func<bool> externalCompleteTester;
-               readonly DataflowBlockOptions options;
-               readonly bool greedy;
-               readonly Func<bool> canAccept;
-
-               readonly ConcurrentDictionary<ISourceBlock<TInput>, DataflowMessageHeader>
-                       postponedMessages =
-                               new ConcurrentDictionary<ISourceBlock<TInput>, DataflowMessageHeader> ();
-               int itemCount;
-               readonly AtomicBoolean postponedProcessing = new AtomicBoolean ();
-
-               // these two fields are used only in one special case
-               SpinLock consumingLock;
-               // this is necessary, because canAccept is not pure
-               bool canAcceptFromBefore;
-
-               protected BlockingCollection<TInput> MessageQueue { get; private set; }
-
-               protected MessageBox (
-                       ITargetBlock<TInput> target, BlockingCollection<TInput> messageQueue,
-                       CompletionHelper compHelper, Func<bool> externalCompleteTester,
-                       DataflowBlockOptions options, bool greedy = true, Func<bool> canAccept = null)
-               {
-                       this.Target = target;
-                       this.CompHelper = compHelper;
-                       this.MessageQueue = messageQueue;
-                       this.externalCompleteTester = externalCompleteTester;
-                       this.options = options;
-                       this.greedy = greedy;
-                       this.canAccept = canAccept;
-               }
-
-               public DataflowMessageStatus OfferMessage (
-                       DataflowMessageHeader messageHeader, TInput messageValue,
-                       ISourceBlock<TInput> source, bool consumeToAccept)
-               {
-                       if (!messageHeader.IsValid)
-                               throw new ArgumentException ("The messageHeader is not valid.",
-                                       "messageHeader");
-                       if (consumeToAccept && source == null)
-                               throw new ArgumentException (
-                                       "consumeToAccept may only be true if provided with a non-null source.",
-                                       "consumeToAccept");
-
-                       if (MessageQueue.IsAddingCompleted || !CompHelper.CanRun)
-                               return DataflowMessageStatus.DecliningPermanently;
-
-                       var full = options.BoundedCapacity != -1
-                                  && Volatile.Read (ref itemCount) >= options.BoundedCapacity;
-                       if (!greedy || full) {
-                               if (source == null)
-                                       return DataflowMessageStatus.Declined;
-
-                               postponedMessages [source] = messageHeader;
-
-                               // necessary to avoid race condition
-                               DecreaseCount (0);
-
-                               if (!greedy && !full)
-                                       EnsureProcessing (true);
-                               
-                               return DataflowMessageStatus.Postponed;
-                       }
-
-                       // in this case, we need to use locking to make sure
-                       // we don't consume when we can't accept
-                       if (consumeToAccept && canAccept != null) {
-                               bool lockTaken = false;
-                               try {
-                                       consumingLock.Enter (ref lockTaken);
-                                       if (!canAcceptFromBefore && !canAccept ())
-                                               return DataflowMessageStatus.DecliningPermanently;
-
-                                       bool consummed;
-                                       messageValue = source.ConsumeMessage (messageHeader, Target, out consummed);
-                                       if (!consummed) {
-                                               canAcceptFromBefore = true;
-                                               return DataflowMessageStatus.NotAvailable;
-                                       }
-
-                                       canAcceptFromBefore = false;
-                               } finally {
-                                       if (lockTaken)
-                                               consumingLock.Exit ();
-                               }
-                       } else {
-                               if (consumeToAccept) {
-                                       bool consummed;
-                                       messageValue = source.ConsumeMessage (messageHeader, Target, out consummed);
-                                       if (!consummed)
-                                               return DataflowMessageStatus.NotAvailable;
-                               }
-
-                               if (canAccept != null && !canAccept ())
-                                       return DataflowMessageStatus.DecliningPermanently;
-                       }
-
-                       try {
-                               MessageQueue.Add (messageValue);
-                       } catch (InvalidOperationException) {
-                               // This is triggered either if the underlying collection didn't accept the item
-                               // or if the messageQueue has been marked complete, either way it corresponds to a false
-                               return DataflowMessageStatus.DecliningPermanently;
-                       }
-
-                       IncreaseCount ();
-
-                       EnsureProcessing (true);
-
-                       VerifyCompleteness ();
-
-                       return DataflowMessageStatus.Accepted;
-               }
-
-               /// <summary>
-               /// Increses the count of items in the block by 1.
-               /// </summary>
-               public void IncreaseCount ()
-               {
-                       Interlocked.Increment (ref itemCount);
-               }
-
-               /// <summary>
-               /// Decreses the number of items in the block by the given count.
-               /// </summary>
-               /// <remarks>
-               /// The <paramref name="count"/> parameter is used when one object
-               /// can represent many items, like a batch in <see cref="BatchBlock{T}"/>.
-               /// </remarks>
-               public void DecreaseCount (int count = 1)
-               {
-                       int decreased = Interlocked.Add (ref itemCount, -count);
-
-                       // if BoundedCapacity is -1, there is no need to do this
-                       if (decreased < options.BoundedCapacity && !postponedMessages.IsEmpty) {
-                               if (greedy)
-                                       EnsurePostponedProcessing ();
-                               else
-                                       EnsureProcessing (false);
-                       }
-               }
-
-               /// <summary>
-               /// The number of messages that were postponed
-               /// and can be attempted to be consumed.
-               /// </summary>
-               public int PostponedMessagesCount {
-                       get { return postponedMessages.Count; }
-               }
-
-               /// <summary>
-               /// Reserves a message from those that were postponed.
-               /// Does not guarantee any order of the messages being reserved.
-               /// </summary>
-               /// <returns>
-               /// An object representing the reservation on success,
-               /// <c>null</c> on failure.
-               /// </returns>
-               public Tuple<ISourceBlock<TInput>, DataflowMessageHeader> ReserveMessage()
-               {
-                       while (!postponedMessages.IsEmpty) {
-                               // KeyValuePair is a struct, so default value is not null
-                               var block = postponedMessages.FirstOrDefault () .Key;
-
-                               // collection is empty
-                               if (block == null)
-                                       break;
-
-                               DataflowMessageHeader header;
-                               bool removed = postponedMessages.TryRemove (block, out header);
-
-                               // another thread was faster, try again
-                               if (!removed)
-                                       continue;
-
-                               bool reserved = block.ReserveMessage (header, Target);
-                               if (reserved)
-                                       return Tuple.Create (block, header);
-                       }
-
-                       return null;
-               }
-
-               /// <summary>
-               /// Releases the given reservation.
-               /// </summary>
-               public void RelaseReservation(Tuple<ISourceBlock<TInput>, DataflowMessageHeader> reservation)
-               {
-                       reservation.Item1.ReleaseReservation (reservation.Item2, Target);
-               }
-
-               /// <summary>
-               /// Consumes previously reserved item.
-               /// </summary>
-               public TInput ConsumeReserved(Tuple<ISourceBlock<TInput>, DataflowMessageHeader> reservation)
-               {
-                       bool consumed;
-                       return reservation.Item1.ConsumeMessage (
-                               reservation.Item2, Target, out consumed);
-               }
-
-               /// <summary>
-               /// Makes sure retrieving items that were postponed,
-               /// because they would exceed <see cref="DataflowBlockOptions.BoundedCapacity"/>,
-               /// is currently running.
-               /// </summary>
-               void EnsurePostponedProcessing ()
-               {
-                       if (postponedProcessing.TrySet())
-                               Task.Factory.StartNew (RetrievePostponed, options.CancellationToken,
-                                       TaskCreationOptions.PreferFairness, options.TaskScheduler);
-               }
-
-               /// <summary>
-               /// Retrieves items that were postponed,
-               /// because they would exceed <see cref="DataflowBlockOptions.BoundedCapacity"/>.
-               /// </summary>
-               void RetrievePostponed ()
-               {
-                       // BoundedCapacity can't be -1 here, because in that case there would be no postponing
-                       while (Volatile.Read (ref itemCount) < options.BoundedCapacity
-                              && !postponedMessages.IsEmpty && !MessageQueue.IsAddingCompleted) {
-                               var block = postponedMessages.First ().Key;
-                               DataflowMessageHeader header;
-                               postponedMessages.TryRemove (block, out header);
-
-                               bool consumed;
-                               var item = block.ConsumeMessage (header, Target, out consumed);
-                               if (consumed) {
-                                       try {
-                                               MessageQueue.Add (item);
-                                               IncreaseCount ();
-                                               EnsureProcessing (false);
-                                       } catch (InvalidOperationException) {
-                                               break;
-                                       }
-                               }
-                       }
-
-                       // release all postponed messages
-                       if (MessageQueue.IsAddingCompleted) {
-                               while (!postponedMessages.IsEmpty) {
-                                       var block = postponedMessages.First ().Key;
-                                       DataflowMessageHeader header;
-                                       postponedMessages.TryRemove (block, out header);
-
-                                       if (block.ReserveMessage (header, Target))
-                                               block.ReleaseReservation (header, Target);
-                               }
-                       }
-
-                       postponedProcessing.Value = false;
-
-                       // because of race
-                       if ((Volatile.Read (ref itemCount) < options.BoundedCapacity
-                            || MessageQueue.IsAddingCompleted)
-                           && !postponedMessages.IsEmpty)
-                               EnsurePostponedProcessing ();
-               }
-
-               /// <summary>
-               /// Makes sure the input queue is processed the way it needs to.
-               /// </summary>
-               /// <param name="newItem">Was new item just added?</param>
-               protected abstract void EnsureProcessing (bool newItem);
-
-               /// <summary>
-               /// Completes the box, no new messages will be accepted.
-               /// Also starts the process of completing the output queue.
-               /// </summary>
-               public void Complete ()
-               {
-                       // Make message queue complete
-                       MessageQueue.CompleteAdding ();
-                       OutgoingQueueComplete ();
-                       VerifyCompleteness ();
-
-                       if (!postponedMessages.IsEmpty)
-                               EnsurePostponedProcessing ();
-               }
-
-               /// <summary>
-               /// Notifies that outgoing queue should be completed, if possible.
-               /// </summary>
-               protected virtual void OutgoingQueueComplete ()
-               {
-               }
-
-               /// <summary>
-               /// Makes sure the block is completed if it should be.
-               /// </summary>
-               protected virtual void VerifyCompleteness ()
-               {
-                       if (MessageQueue.IsCompleted && externalCompleteTester ())
-                               CompHelper.Complete ();
-               }
-       }
-}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/NameHelper.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/NameHelper.cs
deleted file mode 100644 (file)
index 8c70224..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-// NameHelper.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 {
-       /// <summary>
-       /// Helper class for figuring out the name of a block.
-       /// </summary>
-       static class NameHelper {
-               /// <summary>
-               /// Returns the name of the block, based on <see cref="DataflowBlockOptions.NameFormat"/>.
-               /// </summary>
-               /// <remarks>
-               /// If the NameFormat is invalid, returns the exception message.
-               /// </remarks>
-               public static string GetName(IDataflowBlock block, DataflowBlockOptions options)
-               {
-                       try {
-                               return string.Format (
-                                       options.NameFormat, block.GetType ().Name, block.Completion.Id);
-                       } catch (FormatException e) {
-                               return e.Message;
-                       }
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/NullTargetBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/NullTargetBlock.cs
deleted file mode 100644 (file)
index 2b89b5b..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-// NullTargetBlock.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 {
-       /// <summary>
-       /// Target block returned by <see cref="DataflowBlock.NullTarget{TInput}"/>.
-       /// </summary>
-       class NullTargetBlock<TInput> : ITargetBlock<TInput> {
-               public NullTargetBlock ()
-               {
-                       Completion = new TaskCompletionSource<bool> ().Task;
-               }
-
-               public DataflowMessageStatus OfferMessage (
-                       DataflowMessageHeader messageHeader, TInput messageValue,
-                       ISourceBlock<TInput> source, bool consumeToAccept)
-               {
-                       if (!messageHeader.IsValid)
-                               throw new ArgumentException ("The messageHeader is not valid.",
-                                       "messageHeader");
-                       if (consumeToAccept && source == null)
-                               throw new ArgumentException (
-                                       "consumeToAccept may only be true if provided with a non-null source.",
-                                       "consumeToAccept");
-
-                       if (consumeToAccept) {
-                               if (!source.ReserveMessage (messageHeader, this))
-                                       return DataflowMessageStatus.NotAvailable;
-                               bool consummed;
-                               source.ConsumeMessage (messageHeader, this, out consummed);
-                               if (!consummed)
-                                       return DataflowMessageStatus.NotAvailable;
-                       }
-
-                       return DataflowMessageStatus.Accepted;
-               }
-
-               public Task Completion { get; private set; }
-
-               public void Complete ()
-               {
-               }
-
-               public void Fault (Exception exception)
-               {
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ObservableDataflowBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ObservableDataflowBlock.cs
deleted file mode 100644 (file)
index e82d130..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-// ObservableDataflowBlock.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>
-       /// Rx Observable that represents a source block.
-       /// </summary>
-       class ObservableDataflowBlock<TSource> : IObservable<TSource> {
-               class ObserverWrapper : ITargetBlock<TSource> {
-                       readonly IObserver<TSource> observer;
-
-                       public ObserverWrapper (IObserver<TSource> observer)
-                       {
-                               this.observer = observer;
-                       }
-
-                       public void Complete ()
-                       {
-                               observer.OnCompleted ();
-                       }
-
-                       public void Fault (Exception exception)
-                       {
-                               observer.OnError (exception);
-                       }
-
-                       public Task Completion {
-                               get { return null; }
-                       }
-
-                       public DataflowMessageStatus OfferMessage (
-                               DataflowMessageHeader messageHeader, TSource messageValue,
-                               ISourceBlock<TSource> source, bool consumeToAccept)
-                       {
-                               if (consumeToAccept) {
-                                       if (!source.ReserveMessage (messageHeader, this))
-                                               return DataflowMessageStatus.NotAvailable;
-                                       bool consumed;
-                                       messageValue = source.ConsumeMessage (messageHeader, this, out consumed);
-                                       if (!consumed)
-                                               return DataflowMessageStatus.NotAvailable;
-                               }
-
-                               observer.OnNext (messageValue);
-
-                               return DataflowMessageStatus.Accepted;
-                       }
-               }
-
-               readonly ISourceBlock<TSource> source;
-
-               public ObservableDataflowBlock (ISourceBlock<TSource> source)
-               {
-                       this.source = source;
-               }
-
-               public IDisposable Subscribe (IObserver<TSource> observer)
-               {
-                       var wrapper = new ObserverWrapper (observer);
-                       return source.LinkTo (wrapper);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ObserverDataflowBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ObserverDataflowBlock.cs
deleted file mode 100644 (file)
index 55f3b6e..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-// ObserverDataflowBlock.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>
-       /// Rx Observer that represents a target block.
-       /// </summary>
-       class ObserverDataflowBlock<TInput> : IObserver<TInput> {
-               readonly ITargetBlock<TInput> target;
-
-               public ObserverDataflowBlock (ITargetBlock<TInput> target)
-               {
-                       this.target = target;
-               }
-
-               public void OnCompleted ()
-               {
-                       target.Complete ();
-               }
-
-               public void OnError (Exception ex)
-               {
-                       target.Fault (ex);
-               }
-
-               public void OnNext (TInput value)
-               {
-                       target.Post (value);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/OutgoingQueue.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/OutgoingQueue.cs
deleted file mode 100644 (file)
index aa273ab..0000000
+++ /dev/null
@@ -1,281 +0,0 @@
-// OutgoingQueue.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;
-
-namespace System.Threading.Tasks.Dataflow {
-       /// <summary>
-       /// Version of <see cref="OutgoingQueueBase{T}"/> for
-       /// non-broadcast blocks.
-       /// </summary>
-       class OutgoingQueue<T> : OutgoingQueueBase<T> {
-               readonly Func<T, int> countSelector;
-               SpinLock firstItemLock = new SpinLock();
-               volatile ITargetBlock<T> reservedForTargetBlock;
-               readonly TargetCollection<T> targets;
-
-               protected override TargetCollectionBase<T> Targets {
-                       get { return targets; }
-               }
-
-               public OutgoingQueue (
-                       ISourceBlock<T> block, CompletionHelper compHelper,
-                       Func<bool> externalCompleteTester, Action<int> decreaseItemsCount,
-                       DataflowBlockOptions options, Func<T, int> countSelector = null)
-                       : base (compHelper, externalCompleteTester,
-                               decreaseItemsCount, options)
-               {
-                       targets = new TargetCollection<T> (block);
-                       this.countSelector = countSelector;
-               }
-
-               /// <summary>
-               /// Calculates the count of items in the given object.
-               /// </summary>
-               protected override int GetModifiedCount(T data)
-               {
-                       if (countSelector == null)
-                               return 1;
-
-                       return countSelector (data);
-               }
-
-               /// <summary>
-               /// Sends messages to targets.
-               /// </summary>
-               protected override void Process ()
-               {
-                       bool processed;
-                       do {
-                               ForceProcessing = false;
-
-                               bool lockTaken = false;
-                               try {
-                                       firstItemLock.Enter (ref lockTaken);
-
-                                       T item;
-                                       if (!Store.TryPeek (out item))
-                                               break;
-
-                                       if (!targets.HasCurrentItem)
-                                               targets.SetCurrentItem (item);
-
-                                       if (reservedForTargetBlock != null)
-                                               break;
-
-                                       processed = targets.OfferItemToTargets ();
-                                       if (processed) {
-                                               Outgoing.TryTake (out item);
-                                               DecreaseCounts (item);
-                                               FirstItemChanged ();
-                                       }
-                               } finally {
-                                       if (lockTaken)
-                                               firstItemLock.Exit ();
-                               }
-                       } while (processed);
-
-                       IsProcessing.Value = false;
-
-                       // to guard against race condition
-                       if (ForceProcessing && reservedForTargetBlock == null)
-                               EnsureProcessing ();
-
-                       VerifyCompleteness ();
-               }
-
-               public T ConsumeMessage (DataflowMessageHeader messageHeader,
-                                        ITargetBlock<T> targetBlock, out bool messageConsumed)
-               {
-                       if (!messageHeader.IsValid)
-                               throw new ArgumentException ("The messageHeader is not valid.",
-                                       "messageHeader");
-                       if (targetBlock == null)
-                               throw new ArgumentNullException("target");
-
-                       T result = default(T);
-                       messageConsumed = false;
-
-                       bool lockTaken = false;
-                       try {
-                               firstItemLock.Enter (ref lockTaken);
-
-                               if (targets.VerifyHeader (messageHeader, targetBlock)
-                                   && (reservedForTargetBlock == null
-                                       || reservedForTargetBlock == targetBlock)) {
-                                       // cannot consume from faulted block, unless reserved
-                                       if (reservedForTargetBlock == null && IsFaultedOrCancelled)
-                                               return result;
-
-                                       Outgoing.TryTake (out result);
-                                       messageConsumed = true;
-                                       DecreaseCounts (result);
-                                       reservedForTargetBlock = null;
-                                       FirstItemChanged ();
-                               }
-                       } finally {
-                               if (lockTaken)
-                                       firstItemLock.Exit ();
-                       }
-
-                       targets.UnpostponeTarget (targetBlock, messageConsumed);
-                       EnsureProcessing ();
-                       VerifyCompleteness ();
-
-                       return result;
-               }
-
-               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");
-
-                       bool lockTaken = false;
-                       try {
-                               firstItemLock.Enter (ref lockTaken);
-
-                               if (targets.VerifyHeader(messageHeader, target)) {
-                                       reservedForTargetBlock = target;
-                                       return true;
-                               }
-
-                               targets.UnpostponeTarget (target, false);
-                               EnsureProcessing ();
-
-                               return false;
-                       } finally {
-                               if (lockTaken)
-                                       firstItemLock.Exit ();
-                       }
-               }
-
-               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");
-
-                       bool lockTaken = false;
-                       try
-                       {
-                               firstItemLock.Enter(ref lockTaken);
-
-                               if (!targets.VerifyHeader(messageHeader, target)
-                                   || reservedForTargetBlock != target)
-                                       throw new InvalidOperationException(
-                                               "The target did not have the message reserved.");
-
-                               reservedForTargetBlock = null;
-                       } finally {
-                               if (lockTaken)
-                                       firstItemLock.Exit ();
-                       }
-
-                       targets.UnpostponeTarget (target, false);
-                       EnsureProcessing ();
-               }
-
-               /// <summary>
-               /// Notifies that the first item in the queue changed.
-               /// </summary>
-               void FirstItemChanged ()
-               {
-                       T firstItem;
-                       if (Store.TryPeek (out firstItem))
-                               targets.SetCurrentItem (firstItem);
-                       else
-                               targets.ResetCurrentItem ();
-               }
-
-               public bool TryReceive (Predicate<T> filter, out T item)
-               {
-                       bool success = false;
-                       item = default (T);
-
-                       bool lockTaken = false;
-                       try {
-                               firstItemLock.Enter (ref lockTaken);
-
-                               if (reservedForTargetBlock != null)
-                                       return false;
-
-                               T result;
-                               if (Store.TryPeek (out result) && (filter == null || filter (result))) {
-                                       Outgoing.TryTake (out item);
-                                       success = true;
-                                       DecreaseCounts (item);
-                                       FirstItemChanged ();
-                               }
-                       } finally {
-                               if (lockTaken)
-                                       firstItemLock.Exit ();
-                       }
-
-                       EnsureProcessing ();
-                       VerifyCompleteness ();
-
-                       return success;
-               }
-
-               public bool TryReceiveAll (out IList<T> items)
-               {
-                       items = null;
-
-                       if (Store.IsEmpty)
-                               return false;
-
-                       bool lockTaken = false;
-                       try {
-                               firstItemLock.Enter (ref lockTaken);
-
-                               if (reservedForTargetBlock != null)
-                                       return false;
-
-                               var list = new List<T> (Outgoing.Count);
-
-                               T item;
-                               while (Outgoing.TryTake (out item)) {
-                                       DecreaseCounts (item);
-                                       list.Add (item);
-                               }
-
-                               items = list;
-
-                               FirstItemChanged ();
-                       } finally {
-                               if (lockTaken)
-                                       firstItemLock.Exit ();
-                       }
-
-                       EnsureProcessing ();
-                       VerifyCompleteness ();
-
-                       return items.Count > 0;
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/OutgoingQueueBase.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/OutgoingQueueBase.cs
deleted file mode 100644 (file)
index b1c1c29..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-// OutgoingQueueBase.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>
-       /// Handles outgoing messages that get queued when there is no
-       /// block on the other end to proces it. It also allows receive operations.
-       /// </summary>
-       abstract class OutgoingQueueBase<T> {
-               protected ConcurrentQueue<T> Store { get; private set; }
-               protected BlockingCollection<T> Outgoing { get; private set; }
-               int outgoingCount;
-               readonly CompletionHelper compHelper;
-               readonly Func<bool> externalCompleteTester;
-               readonly DataflowBlockOptions options;
-               protected AtomicBoolean IsProcessing { get; private set; }
-               protected abstract TargetCollectionBase<T> Targets { get; }
-               int totalModifiedCount;
-               readonly Action<int> decreaseItemsCount;
-               volatile bool forceProcessing;
-
-               protected OutgoingQueueBase (
-                       CompletionHelper compHelper, Func<bool> externalCompleteTester,
-                       Action<int> decreaseItemsCount, DataflowBlockOptions options)
-               {
-                       IsProcessing = new AtomicBoolean ();
-                       Store = new ConcurrentQueue<T> ();
-                       Outgoing = new BlockingCollection<T> (Store);
-                       this.compHelper = compHelper;
-                       this.externalCompleteTester = externalCompleteTester;
-                       this.options = options;
-                       this.decreaseItemsCount = decreaseItemsCount;
-               }
-
-               /// <summary>
-               /// Is the queue completed?
-               /// Queue is completed after <see cref="Complete"/> is called
-               /// and all items are retrieved from it.
-               /// </summary>
-               public bool IsCompleted {
-                       get { return Outgoing.IsCompleted; }
-               }
-
-               /// <summary>
-               /// Current number of items in the queue.
-               /// Item are counted the way <see cref="DataflowBlockOptions.BoundedCapacity"/>
-               /// counts them, e.g. each item in a batch counts, even if batch is a single object.
-               /// </summary>
-               public int Count {
-                       get { return totalModifiedCount; }
-               }
-
-               /// <summary>
-               /// Calculates the count of items in the given object.
-               /// </summary>
-               protected virtual int GetModifiedCount (T data)
-               {
-                       return 1;
-               }
-
-               /// <summary>
-               /// Adds an object to the queue.
-               /// </summary>
-               public void AddData (T data)
-               {
-                       try {
-                               Outgoing.Add (data);
-                               Interlocked.Add (ref totalModifiedCount, GetModifiedCount (data));
-                               if (Interlocked.Increment (ref outgoingCount) == 1)
-                                       EnsureProcessing ();
-                       } catch (InvalidOperationException) {
-                               VerifyCompleteness ();
-                       }
-               }
-
-               /// <summary>
-               /// Makes sure sending messages to targets is running.
-               /// </summary>
-               protected void EnsureProcessing ()
-               {
-                       ForceProcessing = true;
-                       if (IsProcessing.TrySet())
-                               Task.Factory.StartNew (Process, CancellationToken.None,
-                                       TaskCreationOptions.PreferFairness, options.TaskScheduler);
-               }
-
-               /// <summary>
-               /// Indicates whether sending messages should be forced to start.
-               /// </summary>
-               protected bool ForceProcessing {
-                       get { return forceProcessing; }
-                       set { forceProcessing = value; }
-               }
-
-               /// <summary>
-               /// Sends messages to targets.
-               /// </summary>
-               protected abstract void Process ();
-
-               /// <summary>
-               /// Adds a target block to send messages to.
-               /// </summary>
-               /// <returns>
-               /// An object that can be used to destroy the link to the added target.
-               /// </returns>
-               public IDisposable AddTarget (ITargetBlock<T> targetBlock, DataflowLinkOptions linkOptions)
-               {
-                       if (targetBlock == null)
-                               throw new ArgumentNullException ("targetBlock");
-                       if (linkOptions == null)
-                               throw new ArgumentNullException ("linkOptions");
-
-                       var result = Targets.AddTarget (targetBlock, linkOptions);
-                       EnsureProcessing ();
-                       return result;
-               }
-
-               /// <summary>
-               /// Makes sure the block is completed if it should be.
-               /// </summary>
-               protected void VerifyCompleteness ()
-               {
-                       if (Outgoing.IsCompleted && externalCompleteTester ())
-                               compHelper.Complete ();
-               }
-
-               /// <summary>
-               /// Is the block faulted or cancelled?
-               /// </summary>
-               protected bool IsFaultedOrCancelled {
-                       get { return compHelper.Completion.IsFaulted || compHelper.Completion.IsCanceled; }
-               }
-
-               /// <summary>
-               /// Used to notify that object was removed from the queue
-               /// and to update counts.
-               /// </summary>
-               protected void DecreaseCounts (T data)
-               {
-                       var modifiedCount = GetModifiedCount (data);
-                       Interlocked.Add (ref totalModifiedCount, -modifiedCount);
-                       Interlocked.Decrement (ref outgoingCount);
-                       decreaseItemsCount (modifiedCount);
-               }
-
-               /// <summary>
-               /// Marks the queue for completion.
-               /// </summary>
-               public void Complete ()
-               {
-                       Outgoing.CompleteAdding ();
-                       VerifyCompleteness ();
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/OutputAvailableBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/OutputAvailableBlock.cs
deleted file mode 100644 (file)
index b53be90..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-// OutputAvailableBlock.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>
-       /// This internal block is used by the <see cref="DataflowBlock.OutputAvailableAsync"/> methods
-       /// to check for available items in an asynchronous way.
-       /// </summary>
-       class OutputAvailableBlock<TOutput> : ITargetBlock<TOutput> {
-               readonly TaskCompletionSource<bool> completion =
-                       new TaskCompletionSource<bool> ();
-               IDisposable linkBridge;
-               CancellationTokenRegistration cancellationRegistration;
-
-               public DataflowMessageStatus OfferMessage (
-                       DataflowMessageHeader messageHeader, TOutput messageValue,
-                       ISourceBlock<TOutput> source, bool consumeToAccept)
-               {
-                       if (!messageHeader.IsValid)
-                               return DataflowMessageStatus.Declined;
-
-                       if (completion.Task.Status != TaskStatus.WaitingForActivation)
-                               return DataflowMessageStatus.DecliningPermanently;
-
-                       completion.TrySetResult (true);
-                       CompletionSet ();
-
-                       return DataflowMessageStatus.DecliningPermanently;
-               }
-
-               /// <summary>
-               /// Returns a Task that can be used to wait until output from a block is available.
-               /// </summary>
-               /// <param name="bridge">The disposable object returned by <see cref="ISourceBlock{TOutput}.LinkTo"/>.</param>
-               /// <param name="token">Cancellation token for this operation.</param>
-               public Task<bool> AsyncGet (IDisposable bridge, CancellationToken token)
-               {
-                       linkBridge = bridge;
-                       cancellationRegistration = token.Register (() =>
-                       {
-                               completion.TrySetCanceled ();
-                               CompletionSet ();
-                       });
-
-                       return completion.Task;
-               }
-
-               /// <summary>
-               /// Called after the result has been set,
-               /// cleans up after this block.
-               /// </summary>
-               void CompletionSet ()
-               {
-                       if (linkBridge != null) {
-                               linkBridge.Dispose ();
-                               linkBridge = null;
-                       }
-
-                       cancellationRegistration.Dispose ();
-               }
-
-               public Task Completion {
-                       get { throw new NotSupportedException (); }
-               }
-
-               public void Complete ()
-               {
-                       completion.TrySetResult (false);
-                       CompletionSet ();
-               }
-
-               public void Fault (Exception exception)
-               {
-                       Complete ();
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/PassingMessageBox.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/PassingMessageBox.cs
deleted file mode 100644 (file)
index 962a0ba..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-// PassingMessageBox.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 blocks that don't need any special processing of incoming items.
-       /// </summary>
-       class PassingMessageBox<TInput> : MessageBox<TInput> {
-               readonly Action<bool> processQueue;
-
-               public PassingMessageBox (
-                       ITargetBlock<TInput> target, BlockingCollection<TInput> messageQueue,
-                       CompletionHelper compHelper, Func<bool> externalCompleteTester,
-                       Action<bool> processQueue, DataflowBlockOptions dataflowBlockOptions,
-                       bool greedy = true, Func<bool> canAccept = null)
-                       : base (target, messageQueue, compHelper, externalCompleteTester,
-                               dataflowBlockOptions, greedy, canAccept)
-               {
-                       this.processQueue = processQueue;
-               }
-
-               /// <summary>
-               /// Makes sure the input queue is processed the way it needs to.
-               /// Executes synchronously, so shouldn't cause any long processing.
-               /// </summary>
-               /// <param name="newItem">Was new item just added?</param>
-               protected override void EnsureProcessing (bool newItem)
-               {
-                       processQueue (newItem);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/PredicateBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/PredicateBlock.cs
deleted file mode 100644 (file)
index 60cfdb7..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-// PredicateBlock.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 {
-       /// <summary>
-       /// This block is used by the version of <see cref="DataflowBlock.LinkTo"/>
-       /// that has a predicate to wrap the target block,
-       /// so that the predicate can be checked.
-       /// </summary>
-       class PredicateBlock<T> : ITargetBlock<T> {
-               /// <summary>
-               /// Wraps the source block of the link.
-               /// This is necessary so that the communication from target to source works correctly.
-               /// </summary>
-               class SourceBlock : ISourceBlock<T> {
-                       readonly ISourceBlock<T> actualSource;
-                       readonly PredicateBlock<T> predicateBlock;
-
-                       public SourceBlock (ISourceBlock<T> actualSource,
-                                           PredicateBlock<T> predicateBlock)
-                       {
-                               this.actualSource = actualSource;
-                               this.predicateBlock = predicateBlock;
-                       }
-
-                       public Task Completion
-                       {
-                               get { return actualSource.Completion; }
-                       }
-
-                       public void Complete ()
-                       {
-                               actualSource.Complete ();
-                       }
-
-                       public void Fault (Exception exception)
-                       {
-                               actualSource.Fault (exception);
-                       }
-
-                       public T ConsumeMessage (DataflowMessageHeader messageHeader,
-                                                ITargetBlock<T> target, out bool messageConsumed)
-                       {
-                               return actualSource.ConsumeMessage (messageHeader, predicateBlock,
-                                       out messageConsumed);
-                       }
-
-                       public IDisposable LinkTo (ITargetBlock<T> target,
-                                                  DataflowLinkOptions linkOptions)
-                       {
-                               return actualSource.LinkTo (target, linkOptions);
-                       }
-
-                       public void ReleaseReservation (DataflowMessageHeader messageHeader,
-                                                       ITargetBlock<T> target)
-                       {
-                               actualSource.ReleaseReservation (messageHeader, predicateBlock);
-                       }
-
-                       public bool ReserveMessage (DataflowMessageHeader messageHeader,
-                                                   ITargetBlock<T> target)
-                       {
-                               return actualSource.ReserveMessage (messageHeader, predicateBlock);
-                       }
-               }
-
-               readonly ITargetBlock<T> actualTarget;
-               readonly Predicate<T> predicate;
-               readonly SourceBlock sourceBlock;
-
-               public PredicateBlock (ISourceBlock<T> actualSource,
-                                      ITargetBlock<T> actualTarget, Predicate<T> predicate)
-               {
-                       this.actualTarget = actualTarget;
-                       this.predicate = predicate;
-                       sourceBlock = new SourceBlock (actualSource, this);
-               }
-
-               public DataflowMessageStatus OfferMessage (
-                       DataflowMessageHeader messageHeader, T messageValue, ISourceBlock<T> source,
-                       bool consumeToAccept)
-               {
-                       if (!messageHeader.IsValid)
-                               throw new ArgumentException ("The messageHeader is not valid.",
-                                       "messageHeader");
-                       if (consumeToAccept && source == null)
-                               throw new ArgumentException (
-                                       "consumeToAccept may only be true if provided with a non-null source.",
-                                       "consumeToAccept");
-
-                       if (!predicate(messageValue))
-                               return DataflowMessageStatus.Declined;
-
-                       return actualTarget.OfferMessage (messageHeader, messageValue, sourceBlock,
-                               consumeToAccept);
-               }
-
-               public Task Completion {
-                       get { return actualTarget.Completion; }
-               }
-
-               public void Complete ()
-               {
-                       actualTarget.Complete ();
-               }
-
-               public void Fault (Exception exception)
-               {
-                       actualTarget.Fault (exception);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/PropagatorWrapperBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/PropagatorWrapperBlock.cs
deleted file mode 100644 (file)
index 42b45d1..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-// PropagatorWrapperBlock.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 returned by <see cref="DataflowBlock.Encapsulate{TInput,TOutput}"/>.
-       /// </summary>
-       class PropagatorWrapperBlock<TInput, TOutput> :
-               IPropagatorBlock<TInput, TOutput> {
-               readonly ITargetBlock<TInput> targetBlock;
-               readonly ISourceBlock<TOutput> sourceBlock;
-
-               public PropagatorWrapperBlock (
-                       ITargetBlock<TInput> target, ISourceBlock<TOutput> source)
-               {
-                       if (target == null)
-                               throw new ArgumentNullException ("target");
-                       if (source == null)
-                               throw new ArgumentNullException ("source");
-
-                       targetBlock = target;
-                       sourceBlock = source;
-               }
-
-               public DataflowMessageStatus OfferMessage (
-                       DataflowMessageHeader messageHeader, TInput messageValue,
-                       ISourceBlock<TInput> source, bool consumeToAccept)
-               {
-                       return targetBlock.OfferMessage (
-                               messageHeader, messageValue, source, consumeToAccept);
-               }
-
-               public TOutput ConsumeMessage (
-                       DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target,
-                       out bool messageConsumed)
-               {
-                       return sourceBlock.ConsumeMessage (messageHeader, target, out messageConsumed);
-               }
-
-               public IDisposable LinkTo (
-                       ITargetBlock<TOutput> target, DataflowLinkOptions linkOptions)
-               {
-                       return sourceBlock.LinkTo (target, linkOptions);
-               }
-
-               public void ReleaseReservation (
-                       DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
-               {
-                       sourceBlock.ReleaseReservation (messageHeader, target);
-               }
-
-               public bool ReserveMessage (
-                       DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
-               {
-                       return sourceBlock.ReserveMessage (messageHeader, target);
-               }
-
-               public void Complete ()
-               {
-                       targetBlock.Complete ();
-               }
-
-               public void Fault (Exception exception)
-               {
-                       targetBlock.Fault (exception);
-               }
-
-               public Task Completion {
-                       get { return sourceBlock.Completion; }
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ReceiveBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/ReceiveBlock.cs
deleted file mode 100644 (file)
index 1e632e5..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-// ReceiveBlock.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>
-       /// This internal block is used by the <see cref="DataflowBlock.Receive"/> methods
-       /// to retrieve elements in either blocking or asynchronous way.
-       /// </summary>
-       class ReceiveBlock<TOutput> : ITargetBlock<TOutput> {
-               readonly TaskCompletionSource<TOutput> completion =
-                       new TaskCompletionSource<TOutput> ();
-
-               readonly CancellationToken token;
-               CancellationTokenRegistration cancellationRegistration;
-               readonly Timer timeoutTimer;
-
-               IDisposable linkBridge;
-
-               public ReceiveBlock (CancellationToken token, int timeout)
-               {
-                       this.token = token;
-                       cancellationRegistration = token.Register (() =>
-                       {
-                               lock (completion) {
-                                       completion.TrySetCanceled ();
-                               }
-                               CompletionSet ();
-                       });
-                       timeoutTimer = new Timer (
-                               _ =>
-                               {
-                                       lock (completion) {
-                                               completion.TrySetException (new TimeoutException ());
-                                       }
-                                       CompletionSet ();
-                               }, null, timeout,
-                               Timeout.Infinite);
-               }
-
-               public DataflowMessageStatus OfferMessage (
-                       DataflowMessageHeader messageHeader, TOutput messageValue,
-                       ISourceBlock<TOutput> source, bool consumeToAccept)
-               {
-                       if (!messageHeader.IsValid)
-                               return DataflowMessageStatus.Declined;
-
-                       if (completion.Task.Status != TaskStatus.WaitingForActivation)
-                               return DataflowMessageStatus.DecliningPermanently;
-
-                       lock (completion) {
-                               if (completion.Task.Status != TaskStatus.WaitingForActivation)
-                                       return DataflowMessageStatus.DecliningPermanently;
-
-                               if (consumeToAccept) {
-                                       bool consummed;
-                                       if (!source.ReserveMessage (messageHeader, this))
-                                               return DataflowMessageStatus.NotAvailable;
-                                       messageValue = source.ConsumeMessage (messageHeader, this, out consummed);
-                                       if (!consummed)
-                                               return DataflowMessageStatus.NotAvailable;
-                               }
-
-                               completion.TrySetResult (messageValue);
-                       }
-                       CompletionSet ();
-                       return DataflowMessageStatus.Accepted;
-               }
-
-               /// <summary>
-               /// Synchronously waits until an item is available.
-               /// </summary>
-               /// <param name="bridge">The disposable object returned by <see cref="ISourceBlock{TOutput}.LinkTo"/>.</param>
-               public TOutput WaitAndGet (IDisposable bridge)
-               {
-                       try {
-                               return AsyncGet (bridge).Result;
-                       } catch (AggregateException e) {
-                               if (e.InnerException is TaskCanceledException)
-                                       throw new OperationCanceledException (token);
-                               // resets the stack trace, but that shouldn't matter here
-                               throw e.InnerException;
-                       }
-               }
-
-               /// <summary>
-               /// Asynchronously waits until an item is available.
-               /// </summary>
-               /// <param name="bridge">The disposable object returned by <see cref="ISourceBlock{TOutput}.LinkTo"/>.</param>
-               public Task<TOutput> AsyncGet (IDisposable bridge)
-               {
-                       linkBridge = bridge;
-
-                       return completion.Task;
-               }
-
-               /// <summary>
-               /// Called after the result has been set,
-               /// cleans up after this block.
-               /// </summary>
-               void CompletionSet ()
-               {
-                       if (linkBridge != null) {
-                               linkBridge.Dispose ();
-                               linkBridge = null;
-                       }
-
-                       cancellationRegistration.Dispose ();
-                       timeoutTimer.Dispose ();
-               }
-
-               public Task Completion {
-                       get { throw new NotSupportedException (); }
-               }
-
-               public void Complete ()
-               {
-                       lock (completion) {
-                               completion.TrySetException (new InvalidOperationException (
-                                       "No item could be received from the source."));
-                       }
-                       CompletionSet ();
-               }
-
-               public void Fault (Exception exception)
-               {
-                       Complete ();
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/SendBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/SendBlock.cs
deleted file mode 100644 (file)
index ec9b087..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-// SendBlock.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 {
-       /// <summary>
-       /// This block is used in <see cref="DataflowBlock.SendAsync"/>
-       /// to asynchronously wait until a single item is sent to a given target.
-       /// </summary>
-       class SendBlock<T> : ISourceBlock<T> {
-               readonly ITargetBlock<T> sendTarget;
-               readonly T item;
-               CancellationToken cancellationToken;
-               readonly TaskCompletionSource<bool> taskCompletionSource =
-                       new TaskCompletionSource<bool> ();
-               readonly DataflowMessageHeader sendHeader = new DataflowMessageHeader (1);
-               CancellationTokenRegistration cancellationTokenRegistration;
-
-               bool isReserved;
-
-               volatile bool cancelDisabled;
-
-               public SendBlock (ITargetBlock<T> sendTarget, T item,
-                                 CancellationToken cancellationToken)
-               {
-                       this.sendTarget = sendTarget;
-                       this.item = item;
-                       this.cancellationToken = cancellationToken;
-               }
-
-               /// <summary>
-               /// Sends the item given in the constructor to the target block.
-               /// </summary>
-               /// <returns>Task that completes when the sending is done, or can't be performed.</returns>
-               public Task<bool> Send ()
-               {
-                       cancellationTokenRegistration = cancellationToken.Register (
-                               () =>
-                               {
-                                       if (!cancelDisabled)
-                                               taskCompletionSource.SetCanceled ();
-                               });
-
-                       PerformSend ();
-
-                       return taskCompletionSource.Task;
-               }
-
-               /// <summary>
-               /// Offers the item to the target and hadles its response.
-               /// </summary>
-               void PerformSend ()
-               {
-                       DisableCancel ();
-
-                       if (taskCompletionSource.Task.IsCanceled)
-                               return;
-
-                       var status = sendTarget.OfferMessage (sendHeader, item, this, false);
-
-                       if (status == DataflowMessageStatus.Accepted)
-                               SetResult (true);
-                       else if (status != DataflowMessageStatus.Postponed)
-                               SetResult (false);
-                       else
-                               EnableCancel ();
-               }
-
-               public Task Completion {
-                       get { throw new NotSupportedException (); }
-               }
-
-               public void Complete ()
-               {
-                       throw new NotSupportedException ();
-               }
-
-               public void Fault (Exception exception)
-               {
-                       throw new NotSupportedException ();
-               }
-
-               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");
-
-                       DisableCancel ();
-
-                       messageConsumed = false;
-
-                       if (taskCompletionSource.Task.IsCanceled)
-                               return default(T);
-
-                       if (messageHeader != sendHeader || target != sendTarget) {
-                               EnableCancel ();
-                               return default(T);
-                       }
-
-                       SetResult (true);
-
-                       messageConsumed = true;
-                       return item;
-               }
-
-               public IDisposable LinkTo (ITargetBlock<T> target, DataflowLinkOptions linkOptions)
-               {
-                       throw new NotSupportedException ();
-               }
-
-               public void ReleaseReservation (DataflowMessageHeader messageHeader, ITargetBlock<T> target)
-               {
-                       if (messageHeader != sendHeader || target != sendTarget || !isReserved)
-                               throw new InvalidOperationException (
-                                       "The target did not have the message reserved.");
-
-                       isReserved = false;
-                       EnableCancel ();
-                       PerformSend ();
-               }
-
-               public bool ReserveMessage (DataflowMessageHeader messageHeader, ITargetBlock<T> target)
-               {
-                       DisableCancel ();
-
-                       if (messageHeader == sendHeader && target == sendTarget) {
-                               isReserved = true;
-                               return true;
-                       }
-
-                       EnableCancel ();
-
-                       return false;
-               }
-
-               /// <summary>
-               /// Temporarily disables cancelling.
-               /// </summary>
-               void DisableCancel ()
-               {
-                       cancelDisabled = true;
-               }
-
-               /// <summary>
-               /// Enables cancelling after it was disabled.
-               /// If cancellation was attempted in the meantime,
-               /// actually performs the cancelling.
-               /// </summary>
-               void EnableCancel ()
-               {
-                       cancelDisabled = false;
-
-                       if (cancellationToken.IsCancellationRequested)
-                               taskCompletionSource.SetCanceled ();
-               }
-
-               /// <summary>
-               /// Sets the result of the operation.
-               /// </summary>
-               void SetResult (bool result)
-               {
-                       cancellationTokenRegistration.Dispose ();
-                       taskCompletionSource.SetResult (result);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/TargetCollection.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/TargetCollection.cs
deleted file mode 100644 (file)
index cdd9bcb..0000000
+++ /dev/null
@@ -1,523 +0,0 @@
-// TargetCollection.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;
-using System.Collections.Generic;
-
-namespace System.Threading.Tasks.Dataflow {
-       /// <summary>
-       /// Base class for collection of target blocks for a source block.
-       /// Also handles sending messages to the target blocks.
-       /// </summary>
-       abstract class TargetCollectionBase<T> {
-               /// <summary>
-               /// Represents a target block with its options.
-               /// </summary>
-               protected class Target : IDisposable {
-                       readonly TargetCollectionBase<T> targetCollection;
-                       volatile int remainingMessages;
-                       readonly CancellationTokenSource cancellationTokenSource;
-
-                       public ITargetBlock<T> TargetBlock { get; private set; }
-
-                       public Target (TargetCollectionBase<T> targetCollection,
-                                      ITargetBlock<T> targetBlock, int maxMessages,
-                                      CancellationTokenSource cancellationTokenSource)
-                       {
-                               TargetBlock = targetBlock;
-                               this.targetCollection = targetCollection;
-                               remainingMessages = maxMessages;
-                               this.cancellationTokenSource = cancellationTokenSource;
-
-                               Postponed = new AtomicBoolean ();
-                               Reserved = new AtomicBoolean ();
-                       }
-
-                       /// <summary>
-                       /// Is called after a message was sent,  makes sure the linked is destroyed after
-                       /// <see cref="DataflowLinkOptions.MaxMessages"/> were sent.
-                       /// </summary>
-                       public void MessageSent()
-                       {
-                               if (remainingMessages != -1)
-                                       remainingMessages--;
-                               if (remainingMessages == 0)
-                                       Dispose ();
-                       }
-
-                       readonly AtomicBoolean disabled = new AtomicBoolean ();
-                       /// <summary>
-                       /// Is the link destroyed?
-                       /// </summary>
-                       public bool Disabled
-                       {
-                               get { return disabled.Value; }
-                       }
-
-                       /// <summary>
-                       /// Destroys the link to this target.
-                       /// </summary>
-                       public void Dispose ()
-                       {
-                               disabled.Value = true;
-
-                               if (cancellationTokenSource != null)
-                                       cancellationTokenSource.Cancel ();
-
-                               Target ignored;
-                               targetCollection.TargetDictionary.TryRemove (TargetBlock, out ignored);
-
-                               // to avoid memory leak; it could take a long time
-                               // before this object is actually removed from the collection
-                               TargetBlock = null;
-                       }
-
-                       /// <summary>
-                       /// Does this target have a postponed message?
-                       /// </summary>
-                       public AtomicBoolean Postponed { get; private set; }
-                       
-                       /// <summary>
-                       /// Does this target have a reserved message?
-                       /// </summary>
-                       /// <remarks>Used only by broadcast blocks.</remarks>
-                       public AtomicBoolean Reserved { get; private set; }
-               }
-
-               readonly ISourceBlock<T> block;
-               readonly bool broadcast;
-               readonly bool consumeToAccept;
-
-               readonly ConcurrentQueue<Target> prependQueue = new ConcurrentQueue<Target> ();
-               readonly ConcurrentQueue<Target> appendQueue = new ConcurrentQueue<Target> ();
-               readonly LinkedList<Target> targets = new LinkedList<Target> ();
-
-               protected readonly ConcurrentDictionary<ITargetBlock<T>, Target> TargetDictionary =
-                       new ConcurrentDictionary<ITargetBlock<T>, Target> ();
-
-               // lastMessageHeaderId will be always accessed only from one thread
-               long lastMessageHeaderId;
-               // currentMessageHeaderId can be read from multiple threads at the same time
-               long currentMessageHeaderId;
-
-               bool firstOffering;
-               T currentItem;
-
-               protected TargetCollectionBase (ISourceBlock<T> block, bool broadcast, bool consumeToAccept)
-               {
-                       this.block = block;
-                       this.broadcast = broadcast;
-                       this.consumeToAccept = consumeToAccept;
-               }
-
-               /// <summary>
-               /// Adds a target block to send messages to.
-               /// </summary>
-               /// <returns>
-               /// An object that can be used to destroy the link to the added target.
-               /// </returns>
-               public IDisposable AddTarget (ITargetBlock<T> targetBlock, DataflowLinkOptions options)
-               {
-                       CancellationTokenSource cancellationTokenSource = null;
-                       if (options.PropagateCompletion) {
-                               cancellationTokenSource = new CancellationTokenSource();
-                               block.Completion.ContinueWith (t =>
-                               {
-                                       if (t.IsFaulted)
-                                               targetBlock.Fault (t.Exception);
-                                       else
-                                               targetBlock.Complete ();
-                               }, cancellationTokenSource.Token);
-                       }
-
-                       var target = new Target (
-                               this, targetBlock, options.MaxMessages, cancellationTokenSource);
-                       TargetDictionary [targetBlock] = target;
-                       if (options.Append)
-                               appendQueue.Enqueue (target);
-                       else
-                               prependQueue.Enqueue (target);
-
-                       return target;
-               }
-
-               /// <summary>
-               /// Sets the current item to be offered to targets
-               /// </summary>
-               public void SetCurrentItem (T item)
-               {
-                       firstOffering = true;
-                       currentItem = item;
-                       Volatile.Write (ref currentMessageHeaderId, ++lastMessageHeaderId);
-
-                       ClearUnpostponed ();
-               }
-
-               /// <summary>
-               /// Clears the collection of "unpostponed" targets.
-               /// </summary>
-               protected abstract void ClearUnpostponed ();
-
-               /// <summary>
-               /// Resets the current item to be offered to targets.
-               /// This means there is currently nothing to offer.
-               /// </summary>
-               public void ResetCurrentItem ()
-               {
-                       currentItem = default(T);
-                       Volatile.Write (ref currentMessageHeaderId, 0);
-               }
-
-               /// <summary>
-               /// Is there an item to send right now?
-               /// </summary>
-               public bool HasCurrentItem {
-                       get { return Volatile.Read (ref currentMessageHeaderId) != 0; }
-               }
-
-               /// <summary>
-               /// Offers the current item to all eligible targets.
-               /// </summary>
-               /// <returns>Was the item accepted? (Always <c>false</c> for broadcast blocks.)</returns>
-               public bool OfferItemToTargets ()
-               {
-                       // is there an item to offer?
-                       if (!HasCurrentItem)
-                               return false;
-
-                       var old = Tuple.Create (targets.First, targets.Last);
-
-                       do {
-                               // order is important here, we want to make sure that prepended target
-                               // added before appended target is processed first
-                               var appended = PrependOrAppend (false);
-                               var prepended = PrependOrAppend (true);
-
-                               if (OfferItemToTargets (prepended))
-                                       return true;
-
-                               if (firstOffering) {
-                                       if (OfferItemToTargets (old))
-                                               return true;
-                                       firstOffering = false;
-                               } else {
-                                       if (OfferItemToUnpostponed ())
-                                               return true;
-                               }
-
-                               if (OfferItemToTargets (appended))
-                                       return true;
-                       } while (NeedsProcessing);
-
-                       return false;
-               }
-
-               /// <summary>
-               /// Are there any targets that currently require a message to be sent to them?
-               /// </summary>
-               public bool NeedsProcessing {
-                       get {
-                               return !appendQueue.IsEmpty || !prependQueue.IsEmpty
-                                      || !UnpostponedIsEmpty;
-                       }
-               }
-
-               /// <summary>
-               /// Is the collection of unpostponed targets empty?
-               /// </summary>
-               protected abstract bool UnpostponedIsEmpty { get; }
-
-               /// <summary>
-               /// Prepends (appends) targets that should be prepended (appended) to the collection of targets.
-               /// </summary>
-               /// <param name="prepend"><c>true</c> to prepend, <c>false</c> to append.</param>
-               /// <returns>
-               /// Nodes that contain first and last target added to the list,
-               /// or <c>null</c> if no nodes were added.
-               /// </returns>
-               Tuple<LinkedListNode<Target>, LinkedListNode<Target>> PrependOrAppend (
-                       bool prepend)
-               {
-                       var queue = prepend ? prependQueue : appendQueue;
-
-                       if (queue.IsEmpty)
-                               return null;
-
-                       LinkedListNode<Target> first = null;
-                       LinkedListNode<Target> last = null;
-
-                       Target target;
-                       while (queue.TryDequeue (out target)) {
-                               var node = prepend
-                                                  ? targets.AddFirst (target)
-                                                  : targets.AddLast (target);
-                               if (first == null)
-                                       first = node;
-                               last = node;
-                       }
-
-                       return prepend
-                                      ? Tuple.Create (last, first)
-                                      : Tuple.Create (first, last);
-               }
-
-               /// <summary>
-               /// Offers the current item to the targets between the given nodes (inclusive).
-               /// </summary>
-               /// <returns>Was the item accepted? (Always <c>false</c> for broadcast blocks.)</returns>
-               bool OfferItemToTargets (
-                       Tuple<LinkedListNode<Target>, LinkedListNode<Target>> targetPair)
-               {
-                       if (targetPair == null
-                           || targetPair.Item1 == null || targetPair.Item2 == null)
-                               return false;
-
-                       var node = targetPair.Item1;
-                       while (node != targetPair.Item2.Next) {
-                               if (node.Value.Disabled) {
-                                       var nodeToRemove = node;
-                                       node = node.Next;
-                                       targets.Remove (nodeToRemove);
-                                       continue;
-                               }
-
-                               if (OfferItem (node.Value) && !broadcast)
-                                       return true;
-
-                               node = node.Next;
-                       }
-
-                       return false;
-               }
-
-               /// <summary>
-               /// Offers the current item to unpostponed targets.
-               /// </summary>
-               /// <returns>Was the item accepted? (Always <c>false</c> for broadcast blocks.)</returns>
-               protected abstract bool OfferItemToUnpostponed ();
-
-               /// <summary>
-               /// Offers the current item to the given target.
-               /// </summary>
-               /// <returns>Was the item accepted?</returns>
-               protected bool OfferItem (Target target)
-               {
-                       if (target.Reserved.Value)
-                               return false;
-                       if (!broadcast && target.Postponed.Value)
-                               return false;
-
-                       var result = target.TargetBlock.OfferMessage (
-                               // volatile read is not necessary here,
-                               // because currentMessageHeaderId is always written from this thread
-                               new DataflowMessageHeader (currentMessageHeaderId), currentItem, block,
-                               consumeToAccept);
-
-                       switch (result) {
-                       case DataflowMessageStatus.Accepted:
-                               target.MessageSent ();
-                               return true;
-                       case DataflowMessageStatus.Postponed:
-                               target.Postponed.Value = true;
-                               return false;
-                       case DataflowMessageStatus.DecliningPermanently:
-                               target.Dispose ();
-                               return false;
-                       default:
-                               return false;
-                       }
-               }
-
-               /// <summary>
-               /// Returns whether the given header corresponds to the current item.
-               /// </summary>
-               public bool VerifyHeader (DataflowMessageHeader header)
-               {
-                       return header.Id == Volatile.Read (ref currentMessageHeaderId);
-               }
-       }
-
-       /// <summary>
-       /// Target collection for non-broadcast blocks.
-       /// </summary>
-       class TargetCollection<T> : TargetCollectionBase<T> {
-               readonly ConcurrentQueue<Target> unpostponedTargets =
-                       new ConcurrentQueue<Target> ();
-
-               public TargetCollection (ISourceBlock<T> block)
-                       : base (block, false, false)
-               {
-               }
-
-               /// <summary>
-               /// Is the collection of unpostponed targets empty?
-               /// </summary>
-               protected override bool UnpostponedIsEmpty {
-                       get { return unpostponedTargets.IsEmpty; }
-               }
-
-               /// <summary>
-               /// Returns whether the given header corresponds to the current item
-               /// and that the given target block postponed this item.
-               /// </summary>
-               public bool VerifyHeader (DataflowMessageHeader header, ITargetBlock<T> targetBlock)
-               {
-                       return VerifyHeader (header)
-                              && TargetDictionary[targetBlock].Postponed.Value;
-               }
-
-               /// <summary>
-               /// Unpostpones the given target.
-               /// </summary>
-               /// <param name="targetBlock">Target to unpostpone.</param>
-               /// <param name="messageConsumed">Did the target consume an item?</param>
-               public void UnpostponeTarget (ITargetBlock<T> targetBlock, bool messageConsumed)
-               {
-                       Target target;
-                       if (!TargetDictionary.TryGetValue (targetBlock, out target))
-                               return;
-
-                       if (messageConsumed)
-                               target.MessageSent ();
-                       unpostponedTargets.Enqueue (target);
-
-                       target.Postponed.Value = false;
-               }
-
-               /// <summary>
-               /// Clears the collection of "unpostponed" targets.
-               /// </summary>
-               protected override void ClearUnpostponed ()
-               {
-                       Target ignored;
-                       while (unpostponedTargets.TryDequeue (out ignored)) {
-                       }
-               }
-
-               /// <summary>
-               /// Offers the current item to unpostponed targets.
-               /// </summary>
-               /// <returns>Was the item accepted?</returns>
-               protected override bool OfferItemToUnpostponed ()
-               {
-                       Target target;
-                       while (unpostponedTargets.TryDequeue (out target)) {
-                               if (!target.Disabled && OfferItem (target))
-                                       return true;
-                       }
-
-                       return false;
-               }
-       }
-
-       /// <summary>
-       /// Target collection for broadcast blocks.
-       /// </summary>
-       class BroadcastTargetCollection<T> : TargetCollectionBase<T> {
-               // it's necessary to store the headers because of a race between
-               // UnpostponeTargetConsumed and SetCurrentItem
-               readonly ConcurrentQueue<Tuple<Target, DataflowMessageHeader>>
-                       unpostponedTargets =
-                               new ConcurrentQueue<Tuple<Target, DataflowMessageHeader>> ();
-
-               public BroadcastTargetCollection (ISourceBlock<T> block, bool consumeToAccept)
-                       : base (block, true, consumeToAccept)
-               {
-               }
-
-               /// <summary>
-               /// Is the collection of unpostponed targets empty?
-               /// </summary>
-               protected override bool UnpostponedIsEmpty {
-                       get { return unpostponedTargets.IsEmpty; }
-               }
-
-               /// <summary>
-               /// Marks the target as having a reserved message.
-               /// </summary>
-               public void ReserveTarget (ITargetBlock<T> targetBlock)
-               {
-                       TargetDictionary [targetBlock].Reserved.Value = true;
-               }
-
-               /// <summary>
-               /// Unpostpone target after it consumed a message.
-               /// </summary>
-               /// <param name="targetBlock">The target to unpostpone.</param>
-               /// <param name="header">Header of the message the target consumed.</param>
-               public void UnpostponeTargetConsumed (ITargetBlock<T> targetBlock,
-                                                     DataflowMessageHeader header)
-               {
-                       Target target = TargetDictionary [targetBlock];
-
-                       target.MessageSent ();
-                       unpostponedTargets.Enqueue (Tuple.Create (target, header));
-
-                       target.Postponed.Value = false;
-                       target.Reserved.Value = false;
-               }
-
-               /// <summary>
-               /// Unpostpone target in the case when it didn't successfuly consume a message.
-               /// </summary>
-               public void UnpostponeTargetNotConsumed (ITargetBlock<T> targetBlock)
-               {
-                       Target target;
-                       if (!TargetDictionary.TryGetValue (targetBlock, out target))
-                               return;
-
-                       unpostponedTargets.Enqueue (Tuple.Create (target,
-                               new DataflowMessageHeader ()));
-
-                       target.Postponed.Value = false;
-                       target.Reserved.Value = false;
-               }
-
-               /// <summary>
-               /// Clears the collection of "unpostponed" targets.
-               /// </summary>
-               protected override void ClearUnpostponed ()
-               {
-                       Tuple<Target, DataflowMessageHeader> ignored;
-                       while (unpostponedTargets.TryDequeue (out ignored)) {
-                       }
-               }
-
-               /// <summary>
-               /// Offers the current item to unpostponed targets.
-               /// </summary>
-               /// <returns>Always <c>false</c>.</returns>
-               protected override bool OfferItemToUnpostponed ()
-               {
-                       Tuple<Target, DataflowMessageHeader> tuple;
-                       while (unpostponedTargets.TryDequeue (out tuple)) {
-                               // offer to unconditionaly unpostponed
-                               // and those that consumed some old value
-                               if (!tuple.Item1.Disabled
-                                   && (!tuple.Item2.IsValid || !VerifyHeader (tuple.Item2)))
-                                       OfferItem (tuple.Item1);
-                       }
-
-                       return false;
-               }
-       }
-}
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/TransformBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/TransformBlock.cs
deleted file mode 100644 (file)
index a1c37f4..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-// TransformBlock.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 TransformBlock<TInput, TOutput> :
-               IPropagatorBlock<TInput, TOutput>, IReceivableSourceBlock<TOutput> {
-               readonly ExecutionDataflowBlockOptions dataflowBlockOptions;
-               readonly CompletionHelper compHelper;
-               readonly BlockingCollection<TInput> messageQueue = new BlockingCollection<TInput> ();
-               readonly MessageBox<TInput> messageBox;
-               readonly OutgoingQueue<TOutput> outgoing;
-               readonly Func<TInput, TOutput> transform;
-               readonly Func<TInput, Task<TOutput>> asyncTransform;
-
-               TransformBlock (ExecutionDataflowBlockOptions dataflowBlockOptions)
-               {
-                       if (dataflowBlockOptions == null)
-                               throw new ArgumentNullException ("dataflowBlockOptions");
-               
-                       this.dataflowBlockOptions = dataflowBlockOptions;
-                       this.compHelper = new CompletionHelper (dataflowBlockOptions);
-               }
-
-               public TransformBlock (Func<TInput, TOutput> transform)
-                       : this (transform, ExecutionDataflowBlockOptions.Default)
-               {
-               }
-
-               public TransformBlock (Func<TInput, TOutput> transform,
-                                      ExecutionDataflowBlockOptions dataflowBlockOptions)
-                       : this (dataflowBlockOptions)
-               {
-                       if (transform == null)
-                               throw new ArgumentNullException("transform");
-
-                       this.transform = transform;
-                       this.messageBox = new ExecutingMessageBox<TInput> (
-                               this, messageQueue, compHelper,
-                               () => outgoing.IsCompleted, TransformProcess, () => outgoing.Complete (),
-                               dataflowBlockOptions);
-                       this.outgoing = new OutgoingQueue<TOutput> (this, compHelper,
-                               () => messageQueue.IsCompleted, messageBox.DecreaseCount,
-                               dataflowBlockOptions);
-               }
-
-               public TransformBlock(Func<TInput, Task<TOutput>> transform)
-                       : this(transform, ExecutionDataflowBlockOptions.Default)
-               {
-               }
-
-               public TransformBlock (Func<TInput, Task<TOutput>> transform,
-                                      ExecutionDataflowBlockOptions dataflowBlockOptions)
-                       : this (dataflowBlockOptions)
-               {
-                       if (transform == null)
-                               throw new ArgumentNullException("transform");
-
-                       this.asyncTransform = transform;
-                       this.messageBox = new AsyncExecutingMessageBox<TInput, Task<TOutput>> (
-                               this, messageQueue, compHelper, () => outgoing.IsCompleted,
-                               AsyncTransformProcess, AsyncProcessFinishedTask, () => outgoing.Complete (),
-                               dataflowBlockOptions);
-                       this.outgoing = new OutgoingQueue<TOutput> (this, compHelper,
-                               () => messageQueue.IsCompleted, messageBox.DecreaseCount,
-                               dataflowBlockOptions);
-               }
-
-               DataflowMessageStatus ITargetBlock<TInput>.OfferMessage (
-                       DataflowMessageHeader messageHeader, TInput messageValue,
-                       ISourceBlock<TInput> source, bool consumeToAccept)
-               {
-                       return messageBox.OfferMessage (messageHeader, messageValue, source, consumeToAccept);
-               }
-
-               public IDisposable LinkTo (ITargetBlock<TOutput> target, DataflowLinkOptions linkOptions)
-               {
-                       return outgoing.AddTarget (target, linkOptions);
-               }
-
-               TOutput ISourceBlock<TOutput>.ConsumeMessage (
-                       DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target,
-                       out bool messageConsumed)
-               {
-                       return outgoing.ConsumeMessage (messageHeader, target, out messageConsumed);
-               }
-
-               void ISourceBlock<TOutput>.ReleaseReservation (
-                       DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
-               {
-                       outgoing.ReleaseReservation (messageHeader, target);
-               }
-
-               bool ISourceBlock<TOutput>.ReserveMessage (
-                       DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
-               {
-                       return outgoing.ReserveMessage (messageHeader, target);
-               }
-
-               public bool TryReceive (Predicate<TOutput> filter, out TOutput item)
-               {
-                       return outgoing.TryReceive (filter, out item);
-               }
-
-               public bool TryReceiveAll (out IList<TOutput> items)
-               {
-                       return outgoing.TryReceiveAll (out items);
-               }
-
-               /// <summary>
-               /// Transforms one item from the queue if the transform delegate is synchronous.
-               /// </summary>
-               /// <returns>Returns whether an item was processed. Returns <c>false</c> if the queue is empty.</returns>
-               bool TransformProcess ()
-               {
-                       TInput input;
-
-                       var dequeued = messageQueue.TryTake (out input);
-                       if (dequeued)
-                               outgoing.AddData (transform (input));
-
-                       return dequeued;
-               }
-
-               /// <summary>
-               /// Processes one item from the queue if the transform delegate is asynchronous.
-               /// </summary>
-               /// <param name="task">The Task that was returned by the synchronous part of the delegate.</param>
-               /// <returns>Returns whether an item was processed. Returns <c>false</c> if the queue was empty.</returns>
-               bool AsyncTransformProcess (out Task<TOutput> task)
-               {
-                       TInput input;
-
-                       var dequeued = messageQueue.TryTake (out input);
-                       if (dequeued)
-                               task = asyncTransform (input);
-                       else
-                               task = null;
-
-                       return dequeued;
-               }
-
-               /// <summary>
-               /// Process result of finished asynchronous transformation.
-               /// </summary>
-               void AsyncProcessFinishedTask (Task<TOutput> task)
-               {
-                       if (task == null || task.IsCanceled)
-                               messageBox.DecreaseCount ();
-                       else
-                               outgoing.AddData (task.Result);
-               }
-
-               public void Complete ()
-               {
-                       messageBox.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 InputCount {
-                       get { return messageQueue.Count; }
-               }
-
-               public override string ToString ()
-               {
-                       return NameHelper.GetName (this, dataflowBlockOptions);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/TransformManyBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/TransformManyBlock.cs
deleted file mode 100644 (file)
index 7bb4657..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-// TransformManyBlock.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 TransformManyBlock<TInput, TOutput> :
-               IPropagatorBlock<TInput, TOutput>, IReceivableSourceBlock<TOutput> {
-               readonly CompletionHelper compHelper;
-               readonly BlockingCollection<TInput> messageQueue = new BlockingCollection<TInput> ();
-               readonly MessageBox<TInput> messageBox;
-               readonly ExecutionDataflowBlockOptions dataflowBlockOptions;
-               readonly Func<TInput, IEnumerable<TOutput>> transform;
-               readonly Func<TInput, Task<IEnumerable<TOutput>>> asyncTransform;
-               readonly OutgoingQueue<TOutput> outgoing;
-
-               TransformManyBlock (ExecutionDataflowBlockOptions dataflowBlockOptions)
-               {
-                       if (dataflowBlockOptions == null)
-                               throw new ArgumentNullException ("dataflowBlockOptions");
-                       
-                       this.dataflowBlockOptions = dataflowBlockOptions;
-                       this.compHelper = new CompletionHelper (dataflowBlockOptions);
-               }
-
-               public TransformManyBlock (Func<TInput, IEnumerable<TOutput>> transform)
-                       : this (transform, ExecutionDataflowBlockOptions.Default)
-               {
-               }
-
-               public TransformManyBlock (Func<TInput, IEnumerable<TOutput>> transform,
-                                          ExecutionDataflowBlockOptions dataflowBlockOptions)
-                       : this (dataflowBlockOptions)
-               {
-                       if (transform == null)
-                               throw new ArgumentNullException ("transform");
-
-                       this.transform = transform;
-                       this.messageBox = new ExecutingMessageBox<TInput> (this, messageQueue, compHelper,
-                               () => outgoing.IsCompleted, TransformProcess, () => outgoing.Complete (),
-                               dataflowBlockOptions);
-                       this.outgoing = new OutgoingQueue<TOutput> (this, compHelper,
-                               () => messageQueue.IsCompleted, messageBox.DecreaseCount,
-                               dataflowBlockOptions);
-               }
-
-               public TransformManyBlock (Func<TInput, Task<IEnumerable<TOutput>>> transform)
-                       : this (transform, ExecutionDataflowBlockOptions.Default)
-               {
-               }
-
-               public TransformManyBlock (Func<TInput, Task<IEnumerable<TOutput>>> transform,
-                                          ExecutionDataflowBlockOptions dataflowBlockOptions)
-                       : this (dataflowBlockOptions)
-               {
-                       if (transform == null)
-                               throw new ArgumentNullException ("transform");
-
-                       this.asyncTransform = transform;
-                       this.messageBox = new AsyncExecutingMessageBox<TInput, Task<IEnumerable<TOutput>>> (this, messageQueue, compHelper,
-                               () => outgoing.IsCompleted, AsyncTransformProcess, ProcessFinishedTask, () => outgoing.Complete (),
-                               dataflowBlockOptions);
-                       this.outgoing = new OutgoingQueue<TOutput> (this, compHelper,
-                               () => messageQueue.IsCompleted, messageBox.DecreaseCount,
-                               dataflowBlockOptions);                  
-               }
-
-               DataflowMessageStatus ITargetBlock<TInput>.OfferMessage (
-                       DataflowMessageHeader messageHeader, TInput messageValue,
-                       ISourceBlock<TInput> source, bool consumeToAccept)
-               {
-                       return messageBox.OfferMessage (messageHeader, messageValue, source, consumeToAccept);
-               }
-
-               public IDisposable LinkTo (ITargetBlock<TOutput> target, DataflowLinkOptions linkOptions)
-               {
-                       return outgoing.AddTarget (target, linkOptions);
-               }
-
-               TOutput ISourceBlock<TOutput>.ConsumeMessage (
-                       DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target,
-                       out bool messageConsumed)
-               {
-                       return outgoing.ConsumeMessage (messageHeader, target, out messageConsumed);
-               }
-
-               void ISourceBlock<TOutput>.ReleaseReservation (
-                       DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
-               {
-                       outgoing.ReleaseReservation (messageHeader, target);
-               }
-
-               bool ISourceBlock<TOutput>.ReserveMessage (
-                       DataflowMessageHeader messageHeader, ITargetBlock<TOutput> target)
-               {
-                       return outgoing.ReserveMessage (messageHeader, target);
-               }
-
-               public bool TryReceive (Predicate<TOutput> filter, out TOutput item)
-               {
-                       return outgoing.TryReceive (filter, out item);
-               }
-
-               public bool TryReceiveAll (out IList<TOutput> items)
-               {
-                       return outgoing.TryReceiveAll (out items);
-               }
-
-               /// <summary>
-               /// Transforms one item from the queue if the transform delegate is synchronous.
-               /// </summary>
-               /// <returns>Returns whether an item was processed. Returns <c>false</c> if the queue is empty.</returns>
-               bool TransformProcess ()
-               {
-                       TInput input;
-
-                       var dequeued = messageQueue.TryTake (out input);
-                       if (dequeued) {
-                               var result = transform (input);
-
-                               EnqueueTransformed (result);
-                       }
-
-                       return dequeued;
-               }
-
-               /// <summary>
-               /// Adds the transformed collection to the output queue.
-               /// </summary>
-               void EnqueueTransformed (IEnumerable<TOutput> transformed)
-               {
-                       bool first = true;
-                       if (transformed != null) {
-                               foreach (var item in transformed) {
-                                       if (first)
-                                               first = false;
-                                       else
-                                               messageBox.IncreaseCount ();
-                                       outgoing.AddData (item);
-                               }
-                       }
-                       if (first)
-                               messageBox.DecreaseCount ();
-               }
-
-               /// <summary>
-               /// Processes one item from the queue if the transform delegate is asynchronous.
-               /// </summary>
-               /// <param name="task">The Task that was returned by the synchronous part of the delegate.</param>
-               /// <returns>Returns whether an item was processed. Returns <c>false</c> if the queue was empty.</returns>
-               bool AsyncTransformProcess (out Task<IEnumerable<TOutput>> task)
-               {
-                       TInput input;
-
-                       var dequeued = messageQueue.TryTake (out input);
-                       if (dequeued)
-                               task = asyncTransform (input);
-                       else
-                               task = null;
-
-                       return dequeued;
-               }
-
-               /// <summary>
-               /// Process result of finished asynchronous transformation.
-               /// </summary>
-               void ProcessFinishedTask (Task<IEnumerable<TOutput>> task)
-               {
-                       if (task == null || task.IsCanceled)
-                               messageBox.DecreaseCount ();
-                       else
-                               EnqueueTransformed (task.Result);
-               }
-
-               public void Complete ()
-               {
-                       messageBox.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 InputCount {
-                       get { return messageQueue.Count; }
-               }
-
-               public override string ToString ()
-               {
-                       return NameHelper.GetName (this, dataflowBlockOptions);
-               }
-       }
-}
\ No newline at end of file
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/WriteOnceBlock.cs b/mcs/class/System.Threading.Tasks.Dataflow/System.Threading.Tasks.Dataflow/WriteOnceBlock.cs
deleted file mode 100644 (file)
index 6da7046..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-// WriteOnceBlock.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 WriteOnceBlock<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;
-               AtomicBooleanValue written;
-
-               public WriteOnceBlock (Func<T, T> cloningFunction)
-                       : this (cloningFunction, DataflowBlockOptions.Default)
-               {
-               }
-
-               public WriteOnceBlock (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,
-                               () => true, _ => BroadcastProcess (), dataflowBlockOptions,
-                               canAccept: () => written.TrySet ());
-                       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)
-               {
-                       var result = messageBox.OfferMessage (messageHeader, messageValue, source,
-                               consumeToAccept);
-                       if (result == DataflowMessageStatus.Accepted)
-                               messageQueue.CompleteAdding ();
-                       return result;
-               }
-
-               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)
-               {
-                       T message = outgoing.ConsumeMessage (
-                               messageHeader, target, out messageConsumed);
-                       if (messageConsumed && cloningFunction != null)
-                               message = cloningFunction (message);
-                       return message;
-               }
-
-               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)
-               {
-                       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 an item from the input queue to the output queue.
-               /// </summary>
-               void BroadcastProcess ()
-               {
-                       T item;
-                       if (messageQueue.TryTake (out item))
-                               outgoing.AddData (item);
-                       outgoing.DequeueItem ();
-               }
-
-               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);
-               }
-       }
-}
\ No newline at end of file
index a1d569fcb22e1ebd9a0c42dd6a6eb9d137691fee..f23cf2edc1fdd1b7c54c73873793a684caae3504 100644 (file)
@@ -2,7 +2,6 @@ TestScheduler.cs
 AssertEx.cs
 Blocks.cs
 System.Threading.Tasks.Dataflow/DataflowMessageHeaderTest.cs
-System.Threading.Tasks.Dataflow/CompletionHelperTest.cs
 System.Threading.Tasks.Dataflow/CompletionTest.cs
 System.Threading.Tasks.Dataflow/ReceivingTest.cs
 System.Threading.Tasks.Dataflow/OptionsTest.cs
@@ -26,5 +25,4 @@ System.Threading.Tasks.Dataflow/InvalidArgumentsTest.cs
 System.Threading.Tasks.Dataflow/OutputAvailableTest.cs
 System.Threading.Tasks.Dataflow/EncapsulateTest.cs
 System.Threading.Tasks.Dataflow/ChooseTest.cs
-../System.Threading.Tasks.Dataflow/CompletionHelper.cs
 ../../Mono.Parallel/Mono.Threading/AtomicBoolean.cs
diff --git a/mcs/class/System.Threading.Tasks.Dataflow/Test/System.Threading.Tasks.Dataflow/CompletionHelperTest.cs b/mcs/class/System.Threading.Tasks.Dataflow/Test/System.Threading.Tasks.Dataflow/CompletionHelperTest.cs
deleted file mode 100644 (file)
index ca8309e..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-// 
-// CompletionHelperTest.cs
-//  
-// Author:
-//       Jérémie "garuma" Laval <jeremie.laval@gmail.com>
-// 
-// 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.Linq;
-using System.Threading.Tasks;
-using System.Threading.Tasks.Dataflow;
-
-using NUnit.Framework;
-
-namespace MonoTests.System.Threading.Tasks.Dataflow
-{
-       [TestFixture]
-       public class CompletionHelperTest
-       {
-               CompletionHelper helper;
-
-               [SetUp]
-               public void Setup ()
-               {
-                       helper = CompletionHelper.GetNew (null);
-               }
-
-               [Test]
-               public void InitialStateTest ()
-               {
-                       Task completed = helper.Completion;
-
-                       Assert.IsNotNull (completed);
-                       Assert.IsFalse (completed.IsCompleted);
-               }
-
-               [Test]
-               public void FaultedTest ()
-               {
-                       Exception ex = new ApplicationException ("Foobar");
-                       helper.RequestFault (ex);
-                       Task completed = helper.Completion;
-
-                       Assert.IsNotNull (completed);
-                       Assert.IsTrue (completed.IsCompleted);
-                       Assert.AreEqual (TaskStatus.Faulted, completed.Status);
-                       Assert.AreEqual (ex, completed.Exception.InnerExceptions.First ());
-               }
-
-               [Test]
-               public void CompleteTest ()
-               {
-                       helper.Complete ();
-                       Task completed = helper.Completion;
-
-                       Assert.IsNotNull (completed);
-                       Assert.IsTrue (completed.IsCompleted);
-                       Assert.IsFalse (completed.IsFaulted);
-                       Assert.IsFalse (completed.IsCanceled);
-               }
-       }
-}