LoadProject() should add the loaded project to the collection.
[mono.git] / mcs / class / Microsoft.Build / Microsoft.Build.Internal / BuildNodeManager.cs
1 //
2 // BuildNodeManager.cs
3 //
4 // Author:
5 //   Atsushi Enomoto (atsushi@xamarin.com)
6 //
7 // Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.Linq;
31 using Microsoft.Build.Execution;
32 using Microsoft.Build.Framework;
33 using System.Threading.Tasks;
34 using System.Threading;
35 using System.Collections.Concurrent;
36
37 namespace Microsoft.Build.Internal
38 {
39         class BuildNodeManager
40         {
41                 public BuildNodeManager (BuildManager buildManager)
42                 {
43                         BuildManager = buildManager;
44                         new Thread (RunLoop).Start ();
45                 }
46                 
47                 public BuildManager BuildManager { get; private set; }
48                 
49                 List<BuildNode> in_proc_nodes = new List<BuildNode> ();
50                 List<BuildNode> out_proc_nodes = new List<BuildNode> ();
51                 AutoResetEvent queue_wait_handle = new AutoResetEvent (false);
52                 ConcurrentQueue<BuildSubmission> queued_builds = new ConcurrentQueue<BuildSubmission> ();
53                 // FIXME: currently it is not in use but it should be stored somewhere for cancellation.
54                 Dictionary<BuildSubmission,Task> ongoing_builds = new Dictionary<BuildSubmission, Task> ();
55                 bool run_loop = true;
56
57                 readonly TaskFactory<BuildResult> task_factory = new TaskFactory<BuildResult> ();
58                 internal TaskFactory<BuildResult> ThreadTaskFactory {
59                         get { return task_factory; }
60                 }
61                 
62                 void RunLoop ()
63                 {
64                         while (run_loop) {
65                                 try {
66                                         if (queued_builds.Count == 0) {
67                                                 queue_wait_handle.WaitOne ();
68                                         }
69                                         if (!run_loop)
70                                                 break;
71                                         BuildSubmission build;
72                                         if (!queued_builds.TryDequeue (out build))
73                                                 continue;
74                                         StartOneBuild (build);
75                                 } catch (Exception ex) {
76                                         Console.Error.WriteLine ("Uncaught build node exception occured");
77                                         Console.Error.WriteLine (ex);
78                                 }
79                         }
80                 }
81
82                 public void Stop ()
83                 {
84                         run_loop = false;
85                         queue_wait_handle.Set ();
86                 }
87
88                 public void ResetCaches ()
89                 {
90                         in_proc_nodes.Clear ();
91                         out_proc_nodes.Clear ();
92                 }
93                 
94                 public void Enqueue (BuildSubmission build)
95                 {
96                         queued_builds.Enqueue (build);
97                         queue_wait_handle.Set ();
98                 }
99                 
100                 void StartOneBuild (BuildSubmission build)
101                 {
102                         var node = TakeNode (build);
103                         // FIXME: Task here causes NotImplementedException in somewhere in Interlocked. It does not make sense.
104                         //ongoing_builds [build] = task_factory.StartNew (node.ExecuteBuild);
105                         new Thread (() => { node.ExecuteBuild (); }).Start ();
106                 }
107                 
108                 // FIXME: take max nodes into account here, and get throttling working.
109                 BuildNode TakeNode (BuildSubmission build)
110                 {
111                         var host = BuildManager.OngoingBuildParameters.HostServices;
112                         NodeAffinity affinity;
113                         if (host == null)
114                                 affinity = NodeAffinity.Any;
115                         else
116                                 affinity = host.GetNodeAffinity (build.BuildRequest.ProjectFullPath);
117                         BuildNode n = GetReusableNode (affinity);
118                         if (n != null)
119                                 n.Assign (build);
120                         else {
121                                 n = new BuildNode (this, affinity == NodeAffinity.Any ? NodeAffinity.InProc : affinity);
122                                 n.Assign (build);
123                                 if (n.Affinity == NodeAffinity.InProc)
124                                         in_proc_nodes.Add (n);
125                                 else
126                                         out_proc_nodes.Add (n);
127                         }
128                         return n;
129                 }
130                 
131                 BuildNode GetReusableNode (NodeAffinity affinity)
132                 {
133                         if (!BuildManager.OngoingBuildParameters.EnableNodeReuse)
134                                 return null;
135                         
136                         if (affinity != NodeAffinity.OutOfProc)
137                                 foreach (var n in in_proc_nodes)
138                                         if (n.IsAvailable && (n.Affinity & affinity) != 0)
139                                                 return n;
140                         if (affinity != NodeAffinity.InProc)
141                                 foreach (var n in out_proc_nodes)
142                                         if (n.IsAvailable && (n.Affinity & affinity) != 0)
143                                                 return n;
144                         return null;
145                 }
146         
147                 internal class BuildNode
148                 {
149                         static Random rnd = new Random ();
150                         
151                         public BuildNode (BuildNodeManager manager, NodeAffinity affinity)
152                         {
153                                 Manager = manager;
154                                 Affinity = affinity;
155                                 Id = rnd.Next ();
156                         }
157                         
158                         public bool IsAvailable { get; private set; }
159                         public int Id { get; private set; }
160                         public BuildNodeManager Manager { get; set; }
161                         public NodeAffinity Affinity { get; private set; }
162                         public BuildSubmission Build { get; private set; }
163                         
164                         public void Assign (BuildSubmission build)
165                         {
166                                 IsAvailable = false;
167                                 Build = build;
168                         }
169                         
170                         public BuildResult ExecuteBuild ()
171                         {
172                                 // FIXME: depending on NodeAffinity, build it through another MSBuild process.
173                                 if (Affinity == NodeAffinity.OutOfProc)
174                                         throw new NotImplementedException ();
175                                 return Build.Execute ();
176                         }
177                 }
178         }
179 }