diff options
Diffstat (limited to 'mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildNodeManager.cs')
-rw-r--r-- | mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildNodeManager.cs | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildNodeManager.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildNodeManager.cs new file mode 100644 index 0000000000..ebfad5ba75 --- /dev/null +++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildNodeManager.cs @@ -0,0 +1,209 @@ +// +// BuildNodeManager.cs +// +// Author: +// Atsushi Enomoto (atsushi@xamarin.com) +// +// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com) +// +// 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.Collections.Generic; +using System.Linq; +using Microsoft.Build.Execution; +using Microsoft.Build.Framework; +using System.Threading.Tasks; +using System.Threading; +using System.Collections.Concurrent; + +namespace Microsoft.Build.Internal +{ + class BuildNodeManager + { + public BuildNodeManager (BuildManager buildManager) + { + BuildManager = buildManager; + new Thread (RunLoop).Start (); + } + + ~BuildNodeManager () + { + run_loop = false; + queue_wait_handle.Set (); + } + + public BuildManager BuildManager { get; private set; } + + List<BuildNode> in_proc_nodes = new List<BuildNode> (); + List<BuildNode> out_proc_nodes = new List<BuildNode> (); + AutoResetEvent queue_wait_handle = new AutoResetEvent (false); + ConcurrentQueue<BuildSubmission> queued_builds = new ConcurrentQueue<BuildSubmission> (); + // FIXME: currently it is not in use but it should be stored somewhere for cancellation. + Dictionary<BuildSubmission,Task> ongoing_builds = new Dictionary<BuildSubmission, Task> (); + bool run_loop = true; + + readonly TaskFactory<BuildResult> task_factory = new TaskFactory<BuildResult> (); + internal TaskFactory<BuildResult> ThreadTaskFactory { + get { return task_factory; } + } + + void RunLoop () + { + while (run_loop) { + try { + if (queued_builds.Count == 0) + queue_wait_handle.WaitOne (); + if (!run_loop) + break; + BuildSubmission build; + if (!queued_builds.TryDequeue (out build)) + continue; + StartOneBuild (build); + } catch (Exception ex) { + // FIXME: I guess INodeLogger should be used instead. + Console.Error.WriteLine ("Uncaught build node exception occured"); + Console.Error.WriteLine (ex); + } + } + } + + public void Stop () + { + run_loop = false; + queue_wait_handle.Set (); + } + + public void ResetCaches () + { + in_proc_nodes.Clear (); + out_proc_nodes.Clear (); + } + + public void Enqueue (BuildSubmission build) + { + queued_builds.Enqueue (build); + queue_wait_handle.Set (); + } + + void StartOneBuild (BuildSubmission build) + { + var node = TakeNode (build); + // FIXME: Task (non-generic) here causes NotImplementedException in somewhere in Interlocked. It does not make sense. + ongoing_builds [build] = task_factory.StartNew (node.ExecuteBuild); + //new Thread (() => { node.ExecuteBuild (); }).Start (); + } + + void EndOneBuild (BuildNode node) + { + var task = ongoing_builds [node.Build]; + ongoing_builds [node.Build] = null; + node.Release (); + } + + // FIXME: take max nodes into account here, and get throttling working. + BuildNode TakeNode (BuildSubmission build) + { + var host = BuildManager.OngoingBuildParameters.HostServices; + NodeAffinity affinity; + if (host == null) + affinity = NodeAffinity.Any; + else + affinity = host.GetNodeAffinity (build.BuildRequest.ProjectFullPath); + BuildNode n = GetReusableNode (affinity); + if (n != null) + n.Assign (build); + else { + n = new BuildNode (this, affinity == NodeAffinity.Any ? NodeAffinity.InProc : affinity); + n.Assign (build); + if (n.Affinity == NodeAffinity.InProc) + in_proc_nodes.Add (n); + else + out_proc_nodes.Add (n); + } + return n; + } + + BuildNode GetReusableNode (NodeAffinity affinity) + { + if (!BuildManager.OngoingBuildParameters.EnableNodeReuse) + return null; + + if (affinity != NodeAffinity.OutOfProc) + foreach (var n in in_proc_nodes) + if (n.IsAvailable && (n.Affinity & affinity) != 0) + return n; + if (affinity != NodeAffinity.InProc) + foreach (var n in out_proc_nodes) + if (n.IsAvailable && (n.Affinity & affinity) != 0) + return n; + return null; + } + + internal class BuildNode + { + static Random rnd = new Random (); + + public BuildNode (BuildNodeManager manager, NodeAffinity affinity) + { + Manager = manager; + Affinity = affinity; + Id = rnd.Next (); + } + + public bool IsAvailable { get; private set; } + public int Id { get; private set; } + public BuildNodeManager Manager { get; set; } + public NodeAffinity Affinity { get; private set; } + public BuildSubmission Build { get; private set; } + + public void Assign (BuildSubmission build) + { + IsAvailable = false; + Build = build; + } + + public void Release () + { + Build = null; + IsAvailable = true; + } + + public BuildResult ExecuteBuild () + { + BuildResult result; + try { + // FIXME: depending on NodeAffinity, build it through another MSBuild process. + if (Affinity == NodeAffinity.OutOfProc) + throw new NotImplementedException (); + result = Build.InternalExecute (); + } catch (Exception ex) { + // FIXME: I guess INodeLogger should be used instead. + Console.Error.WriteLine ("Uncaught build node exception occured"); + Console.Error.WriteLine (ex); + result = null; + } finally { + Manager.EndOneBuild (this); + } + return result; + } + } + } +} |