示例#1
0
 /// <summary>
 /// Executes a parallel task using the correct <see cref="ITargetLogger"/>
 /// </summary>
 /// <param name="task">The task to execute</param>
 /// <param name="cacophony">Whether or not cacophany logging is requested</param>
 /// <param name="state">The concurrency state used to synchonize the threads</param>
 /// <param name="index">The index of this thread in the grand scheme of things</param>
 private void ExecuteWithCorrectLogging(ParallelTask task, Boolean cacophony, ConcurrencyState state, Int32 index)
 {
     (task as ParallelTask).loggerFromParent = this.Logger;
     this.ExecuteWithCorrectLogging(
         task.Execute,
         logger =>
     {
         (task as ParallelTask).loggerFromParent = logger;
         task.Execute();
     },
         cacophony,
         state,
         index);
 }
示例#2
0
        /// <summary>
        /// Executes something using the correct <see cref="ITargetLogger"/>
        /// </summary>
        /// <param name="something">The action to perform when there is no special logging to be done</param>
        /// <param name="newLoggerAction">The action to be performed when a new logger is to be used</param>
        /// <param name="cacophony">Whether or not cacophany logging is requested</param>
        /// <param name="state">The concurrency state used to synchonize the threads</param>
        /// <param name="index">The index of this thread in the grand scheme of things</param>
        private void ExecuteWithCorrectLogging(
            Action something,
            Action <ITargetLogger> newLoggerAction,
            Boolean cacophony,
            ConcurrencyState state,
            Int32 index)
        {
            if (cacophony)
            {
                something();
            }
            else
            {
                var logger = new BufferingTargetLogger(this.Logger);

                lock (state)
                {
                    state.Loggers[index] = logger;

                    // If this is the first item, or if all other items before this one have completed (rare, but it can happen)
                    // then we want to immediately dismantle this buffer
                    if (index == 0 || state.CompletedList.Take(index).All(z => z))
                    {
                        logger.FlushAndDismantle();
                    }
                }

                try
                {
                    if (newLoggerAction != null)
                    {
                        newLoggerAction(logger);
                    }
                    else
                    {
                        something();
                    }
                }
                finally
                {
                    lock (state)
                    {
                        state.CompletedList[index] = true;
                        if (logger.IsDismantled)
                        {
                            // If our logger has been dismanted, then we are the thread that the others are waiting on to complete
                            // Dismantle the next logger, assuming there is one.  Keep dismantling until we find one that belongs to
                            // A still-running task
                            for (var i = index + 1; i < state.Loggers.Length; i++)
                            {
                                if (state.Loggers[i] == null || state.Loggers[i].IsDismantled)
                                {
                                    break;
                                }

                                state.Loggers[i].FlushAndDismantle();

                                if (!state.CompletedList[i])
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
示例#3
0
 /// <summary>
 /// Executes a parallel target using the correct <see cref="ITargetLogger"/>
 /// </summary>
 /// <param name="pcall">The target</param>
 /// <param name="cacophony">Whether or not cacophany logging is requested</param>
 /// <param name="state">The concurrency state used to synchonize the threads</param>
 /// <param name="index">The index of this thread in the grand scheme of things</param>
 private void ExecuteWithCorrectLogging(ParallelTarget pcall, Boolean cacophony, ConcurrencyState state, Int32 index)
 {
     try
     {
         this.ExecuteWithCorrectLogging(
             () => this.Project.Execute(pcall.TargetName, pcall.CascadeDependencies, this, this.CloneCallStack(), this.Logger, pcall.Arguments),
             logger => this.Project.Execute(pcall.TargetName, pcall.CascadeDependencies, this, this.CloneCallStack(), logger, pcall.Arguments),
             cacophony,
             state,
             index);
     }
     catch (ArgumentException e)
     {
         throw new BuildException(e.Message, this.Location);
     }
 }
示例#4
0
        /// <summary>
        /// Executes this task
        /// </summary>
        protected override void ExecuteTask()
        {
            this.Log(Level.Info, "Begining " + (this.RunInSerial ? "sequential" : "parallel") + " execution [" + this.Name + " : " + this.Description + "]");
            this.ValidateContext();
            this.Logger.Indent();

            var cacophony = this.Cacophony;

            // Targets can be either the ParallelTargets or SequenceTasks or ParallelTasks
            var targets = this.Children.Where(t => t is ParallelTask || (t as ParallelTarget).IfDefined && !(t as ParallelTarget).UnlessDefined).ToArray();

            var concurrencyState = new ConcurrencyState()
            {
                Loggers       = new BufferingTargetLogger[targets.Length],
                CompletedList = new bool[targets.Length]
            };

            // Sequential execution is simple
            if (this.RunInSerial || this.Project.ForceSequential)
            {
                for (var i = 0; i < targets.Length; i++)
                {
                    var element = targets[i];

                    // If it's a pcall, execute the target
                    if (element is ParallelTarget)
                    {
                        var targetElement = element as ParallelTarget;

                        this.Logger.Unindent();
                        this.Log(Level.Info, "Executing \"" + targetElement.TargetName + "\" in sequence.");
                        this.Logger.Indent();
                        try
                        {
                            this.Project.Execute(targetElement.TargetName, targetElement.CascadeDependencies, this, this.CloneCallStack(), this.Logger, targetElement.Arguments);
                        }
                        catch (ArgumentException e)
                        {
                            throw new BuildException(e.Message, this.Location);
                        }
                    }
                    else
                    {
                        // otherwise execute the tasks as is
                        (element as ParallelTask).Execute();
                    }
                }
            }
            else  // Parallel execution
            {
                try
                {
                    Parallel.ForEach(targets, (element, state) =>
                    {
                        try
                        {
                            // If it's a pcall, execute the target
                            if (element is ParallelTarget)
                            {
                                var targetElement = element as ParallelTarget;
                                this.Log(Level.Info, "Executing \"" + targetElement.TargetName + "\" in parallel.");

                                if (!state.IsExceptional && !state.IsStopped)
                                {
                                    this.ExecuteWithCorrectLogging(
                                        targetElement,
                                        cacophony,
                                        concurrencyState,
                                        Array.IndexOf(targets, element));
                                }
                            }
                            else
                            {
                                // otherwise execute the tasks as is
                                this.ExecuteWithCorrectLogging(element as ParallelTask, cacophony, concurrencyState, Array.IndexOf(targets, element));
                            }
                        }
                        catch (Exception e) // This catch is inside the foreach and so we do some first-chance handling here
                        {
                            // Try to stop other threads from starting.
                            state.Stop();
                            var targetElement = element as ParallelTarget;

                            // Only log if this is not another parallel task
                            if (targetElement != null)
                            {
                                lock (this) // Prevent ourself from jumbling our logging, at least
                                {
                                    this.Logger.Unindent();
                                    var message = "ERROR: An exception has been thrown by the \"" + targetElement.TargetName + "\" target of the parallel.  Any currently executing targets will run to completion but the build will fail.";

                                    this.Log(Level.Error, new string('=', message.Length));
                                    this.Log(Level.Error, message);
                                    this.Log(Level.Error, e.Message);
                                    this.Log(Level.Error, new string('=', message.Length) + "\r\n");
                                    this.Logger.Indent();
                                    throw;
                                }
                            }
                            else
                            {
                                throw;
                            }
                        }
                    });
                }
                catch (AggregateException agge) // This catch is outside the foreach so we only have aggregate exceptions but we know for sure that everything is stopped now.
                {
                    throw new BuildException(
                              String.Concat(
                                  "The following build errors were encountered during the parallel execution of targets:",
                                  Environment.NewLine,
                                  Environment.NewLine,
                                  String.Join(
                                      String.Concat(
                                          Environment.NewLine,
                                          Environment.NewLine),
                                      agge.InnerExceptions.Select(ex => ex.Message)),
                                  Environment.NewLine,
                                  "Please look earlier in the log to see the contexts in which these errors occurred"));
                }
            }

            this.Logger.Unindent();
        }