Index: project/core/core.csproj =================================================================== --- project/core/core.csproj (revision 3352) +++ project/core/core.csproj (working copy) @@ -649,6 +649,7 @@ Code + Code @@ -688,6 +689,7 @@ Code + Code Index: project/core/IntegrationResult.cs =================================================================== --- project/core/IntegrationResult.cs (revision 3352) +++ project/core/IntegrationResult.cs (working copy) @@ -245,7 +245,10 @@ public void AddTaskResult(ITaskResult result) { - taskResults.Add(result); + lock (taskResults.SyncRoot) + { + taskResults.Add(result); + } if (! (Failed || Status == IntegrationStatus.Exception)) Status = result.Succeeded() ? IntegrationStatus.Success : IntegrationStatus.Failure; } @@ -388,4 +391,4 @@ return string.Format("Project: {0}, Status: {1}, Label: {2}, StartTime: {3}", ProjectName, Status, Label, StartTime); } } -} \ No newline at end of file +} Index: project/core/tasks/ParallelTask.cs =================================================================== --- project/core/tasks/ParallelTask.cs (revision 0) +++ project/core/tasks/ParallelTask.cs (revision 0) @@ -0,0 +1,45 @@ +using System.Collections; +using System.IO; +using System.Threading; +using Exortech.NetReflector; +using ThoughtWorks.CruiseControl.Core.Util; + +namespace ThoughtWorks.CruiseControl.Core.Tasks +{ + [ReflectorType("parallel")] + public class ParallelTask : ITask + { + private ITask[] tasks = new ITask[] { new NullTask() }; + + + [ReflectorArray("tasks", Required = false)] + public ITask[] Tasks + { + get { return tasks; } + set { tasks = value; } + } + + public void Run(IIntegrationResult result) + { + Thread[] threads = new Thread[tasks.Length]; + for(int i = 0; i < threads.Length; i++) + { + object[] objs = { tasks[i], result }; + threads[i] = new Thread((ParameterizedThreadStart)ThreadStub); + threads[i].Start(objs); + } + foreach (Thread t in threads) + { + t.Join(); + } + } + + public void ThreadStub(object o) + { + object[] objs = (object[])o; + ITask task = (ITask)objs[0]; + IIntegrationResult res = (IIntegrationResult)objs[1]; + task.Run(res); + } + } +} Index: project/core/tasks/SerialTask.cs =================================================================== --- project/core/tasks/SerialTask.cs (revision 0) +++ project/core/tasks/SerialTask.cs (revision 0) @@ -0,0 +1,28 @@ +using System.Collections; +using System.IO; +using Exortech.NetReflector; +using ThoughtWorks.CruiseControl.Core.Util; + +namespace ThoughtWorks.CruiseControl.Core.Tasks +{ + [ReflectorType("serial")] + public class SerialTask : ITask + { + private ITask[] tasks = new ITask[] { new NullTask() }; + + [ReflectorArray("tasks", Required = false)] + public ITask[] Tasks + { + get { return tasks; } + set { tasks = value; } + } + + public void Run(IIntegrationResult result) + { + foreach (ITask task in tasks) + { + task.Run(result); + } + } + } +} Index: project/UnitTests/Core/Tasks/ParallelTaskTest.cs =================================================================== --- project/UnitTests/Core/Tasks/ParallelTaskTest.cs (revision 0) +++ project/UnitTests/Core/Tasks/ParallelTaskTest.cs (revision 0) @@ -0,0 +1,62 @@ +using NUnit.Framework; +using Exortech.NetReflector; +using ThoughtWorks.CruiseControl.Core; +using ThoughtWorks.CruiseControl.Core.Tasks; +using ThoughtWorks.CruiseControl.Core.Util; + +namespace ThoughtWorks.CruiseControl.UnitTests.Core.Tasks +{ + [TestFixture] + public class ParallelTaskTest + { + private ParallelTask task; + + [SetUp] + public void Setup() + { + task = new ParallelTask(); + } + + [Test] + public void CanRunOneNullTask() + { + IntegrationResult result = new IntegrationResult(); + task.Tasks = new ITask[1]; + task.Tasks[0] = new NullTask(); + task.Run(result); + Assert.IsTrue(result.Succeeded); + } + + [Test] + public void CanRunTwoNullTasks() + { + IntegrationResult result = new IntegrationResult(); + task.Tasks = new ITask[2]; + task.Tasks[0] = new NullTask(); + task.Tasks[1] = new NullTask(); + task.Run(result); + Assert.IsTrue(result.Succeeded); + } + + [Test] + public void PopulateFromReflector() + { + string xml = @" + + + + mybatchfile.bat + C:\ + myarg1 myarg2 + 123 + + + + "; + + task = (ParallelTask)NetReflector.Read(xml); + Assert.IsInstanceOfType(typeof(ExecutableTask), task.Tasks[0]); + Assert.IsInstanceOfType(typeof(NullTask), task.Tasks[1]); + } + } +} Index: project/UnitTests/Core/Tasks/SerialTaskTest.cs =================================================================== --- project/UnitTests/Core/Tasks/SerialTaskTest.cs (revision 0) +++ project/UnitTests/Core/Tasks/SerialTaskTest.cs (revision 0) @@ -0,0 +1,62 @@ +using NUnit.Framework; +using Exortech.NetReflector; +using ThoughtWorks.CruiseControl.Core; +using ThoughtWorks.CruiseControl.Core.Tasks; +using ThoughtWorks.CruiseControl.Core.Util; + +namespace ThoughtWorks.CruiseControl.UnitTests.Core.Tasks +{ + [TestFixture] + public class SerialTaskTest + { + private SerialTask task; + + [SetUp] + public void Setup() + { + task = new SerialTask(); + } + + [Test] + public void CanRunOneNullTask() + { + IntegrationResult result = new IntegrationResult(); + task.Tasks = new ITask[1]; + task.Tasks[0] = new NullTask(); + task.Run(result); + Assert.IsTrue(result.Succeeded); + } + + [Test] + public void CanRunTwoNullTasks() + { + IntegrationResult result = new IntegrationResult(); + task.Tasks = new ITask[2]; + task.Tasks[0] = new NullTask(); + task.Tasks[1] = new NullTask(); + task.Run(result); + Assert.IsTrue(result.Succeeded); + } + + [Test] + public void PopulateFromReflector() + { + string xml = @" + + + + mybatchfile.bat + C:\ + myarg1 myarg2 + 123 + + + + "; + + task = (SerialTask)NetReflector.Read(xml); + Assert.IsInstanceOfType(typeof(ExecutableTask), task.Tasks[0]); + Assert.IsInstanceOfType(typeof(NullTask), task.Tasks[1]); + } + } +} Index: project/UnitTests/UnitTests.csproj =================================================================== --- project/UnitTests/UnitTests.csproj (revision 3352) +++ project/UnitTests/UnitTests.csproj (working copy) @@ -673,6 +673,7 @@ Code + Code @@ -715,6 +716,7 @@ Code + Code