//-----------------------------------------------------------------------------------
// Creates and begins execution of a new spooling task. Runs asynchronously.
//
// Arguments:
// groupState - values for inter-task communication
// partitions - the producer enumerators
// channels - the producer-consumer channels
// taskScheduler - the task manager on which to execute
//
internal static void SpoolPipeline <TInputOutput, TIgnoreKey>(
QueryTaskGroupState groupState, PartitionedStream <TInputOutput, TIgnoreKey> partitions,
AsynchronousChannel <TInputOutput>[] channels, TaskScheduler taskScheduler)
{
Contract.Requires(partitions.PartitionCount == channels.Length);
Contract.Requires(groupState != null);
// Ensure all tasks in this query are parented under a common root. Because this
// is a pipelined query, we detach it from the parent (to avoid blocking the calling
// thread), and run the query on a separate thread.
Task rootTask = new Task(
() =>
{
// Create tasks that will enumerate the partitions in parallel. Because we're pipelining,
// we will begin running these tasks in parallel and then return.
for (int i = 0; i < partitions.PartitionCount; i++)
{
TraceHelpers.TraceInfo("SpoolingTask::Spool: Running partition[{0}] asynchronously", i);
QueryTask asyncTask = new PipelineSpoolingTask <TInputOutput, TIgnoreKey>(i, groupState, partitions[i], channels[i]);
asyncTask.RunAsynchronously(taskScheduler);
}
});
// Begin the query on the calling thread.
groupState.QueryBegin(rootTask);
// And schedule it for execution. This is done after beginning to ensure no thread tries to
// end the query before its root task has been recorded properly.
rootTask.Start(taskScheduler);
// We don't call QueryEnd here; when we return, the query is still executing, and the
// last enumerator to be disposed of will call QueryEnd for us.
}