Merge pull request #2236 from akoeplinger/add-dataflow
authorAlexander Köplinger <alex.koeplinger@outlook.com>
Tue, 17 Nov 2015 21:03:53 +0000 (22:03 +0100)
committerAlexander Köplinger <alex.koeplinger@outlook.com>
Tue, 17 Nov 2015 21:03:53 +0000 (22:03 +0100)
[System.Threading.Tasks.Dataflow] Replace implementation with CoreFx version

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);
-               }
-       }
-}