Improve a safety check when writing data into StatBuffer
[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                 ~BuildNodeManager ()
48                 {
49                         run_loop = false;
50                         queue_wait_handle.Set ();
51                 }
52                 
53                 public BuildManager BuildManager { get; private set; }
54                 
55                 List<BuildNode> in_proc_nodes = new List<BuildNode> ();
56                 List<BuildNode> out_proc_nodes = new List<BuildNode> ();
57                 AutoResetEvent queue_wait_handle = new AutoResetEvent (false);
58                 ConcurrentQueue<BuildSubmission> queued_builds = new ConcurrentQueue<BuildSubmission> ();
59                 // FIXME: currently it is not in use but it should be stored somewhere for cancellation.
60                 Dictionary<BuildSubmission,Task> ongoing_builds = new Dictionary<BuildSubmission, Task> ();
61                 bool run_loop = true;
62
63                 readonly TaskFactory<BuildResult> task_factory = new TaskFactory<BuildResult> ();
64                 internal TaskFactory<BuildResult> ThreadTaskFactory {
65                         get { return task_factory; }
66                 }
67                 
68                 void RunLoop ()
69                 {
70                         while (run_loop) {
71                                 try {
72                                         if (queued_builds.Count == 0)
73                                                 queue_wait_handle.WaitOne ();
74                                         if (!run_loop)
75                                                 break;
76                                         BuildSubmission build;
77                                         if (!queued_builds.TryDequeue (out build))
78                                                 continue;
79                                         StartOneBuild (build);
80                                 } catch (Exception ex) {
81                                         // FIXME: I guess INodeLogger should be used instead.
82                                         Console.Error.WriteLine ("Uncaught build node exception occured");
83                                         Console.Error.WriteLine (ex);
84                                 }
85                         }
86                 }
87
88                 public void Stop ()
89                 {
90                         run_loop = false;
91                         queue_wait_handle.Set ();
92                 }
93
94                 public void ResetCaches ()
95                 {
96                         in_proc_nodes.Clear ();
97                         out_proc_nodes.Clear ();
98                 }
99                 
100                 public void Enqueue (BuildSubmission build)
101                 {
102                         queued_builds.Enqueue (build);
103                         queue_wait_handle.Set ();
104                 }
105                 
106                 void StartOneBuild (BuildSubmission build)
107                 {
108                         var node = TakeNode (build);
109                         // FIXME: Task (non-generic) here causes NotImplementedException in somewhere in Interlocked. It does not make sense.
110                         ongoing_builds [build] = task_factory.StartNew (node.ExecuteBuild);
111                         //new Thread (() => { node.ExecuteBuild (); }).Start ();
112                 }
113                 
114                 void EndOneBuild (BuildNode node)
115                 {
116                         var task = ongoing_builds [node.Build];
117                         ongoing_builds [node.Build] = null;
118                         node.Release ();
119                 }
120                 
121                 // FIXME: take max nodes into account here, and get throttling working.
122                 BuildNode TakeNode (BuildSubmission build)
123                 {
124                         var host = BuildManager.OngoingBuildParameters.HostServices;
125                         NodeAffinity affinity;
126                         if (host == null)
127                                 affinity = NodeAffinity.Any;
128                         else
129                                 affinity = host.GetNodeAffinity (build.BuildRequest.ProjectFullPath);
130                         BuildNode n = GetReusableNode (affinity);
131                         if (n != null)
132                                 n.Assign (build);
133                         else {
134                                 n = new BuildNode (this, affinity == NodeAffinity.Any ? NodeAffinity.InProc : affinity);
135                                 n.Assign (build);
136                                 if (n.Affinity == NodeAffinity.InProc)
137                                         in_proc_nodes.Add (n);
138                                 else
139                                         out_proc_nodes.Add (n);
140                         }
141                         return n;
142                 }
143                 
144                 BuildNode GetReusableNode (NodeAffinity affinity)
145                 {
146                         if (!BuildManager.OngoingBuildParameters.EnableNodeReuse)
147                                 return null;
148                         
149                         if (affinity != NodeAffinity.OutOfProc)
150                                 foreach (var n in in_proc_nodes)
151                                         if (n.IsAvailable && (n.Affinity & affinity) != 0)
152                                                 return n;
153                         if (affinity != NodeAffinity.InProc)
154                                 foreach (var n in out_proc_nodes)
155                                         if (n.IsAvailable && (n.Affinity & affinity) != 0)
156                                                 return n;
157                         return null;
158                 }
159         
160                 internal class BuildNode
161                 {
162                         static Random rnd = new Random ();
163                         
164                         public BuildNode (BuildNodeManager manager, NodeAffinity affinity)
165                         {
166                                 Manager = manager;
167                                 Affinity = affinity;
168                                 Id = rnd.Next ();
169                         }
170                         
171                         public bool IsAvailable { get; private set; }
172                         public int Id { get; private set; }
173                         public BuildNodeManager Manager { get; set; }
174                         public NodeAffinity Affinity { get; private set; }
175                         public BuildSubmission Build { get; private set; }
176                         
177                         public void Assign (BuildSubmission build)
178                         {
179                                 IsAvailable = false;
180                                 Build = build;
181                         }
182                         
183                         public void Release ()
184                         {
185                                 Build = null;
186                                 IsAvailable = true;
187                         }
188                         
189                         public BuildResult ExecuteBuild ()
190                         {
191                                 BuildResult result;
192                                 try {
193                                         // FIXME: depending on NodeAffinity, build it through another MSBuild process.
194                                         if (Affinity == NodeAffinity.OutOfProc)
195                                                 throw new NotImplementedException ();
196                                         result = Build.InternalExecute ();
197                                 } catch (Exception ex) {
198                                         // FIXME: I guess INodeLogger should be used instead.
199                                         Console.Error.WriteLine ("Uncaught build node exception occured");
200                                         Console.Error.WriteLine (ex);
201                                         result = null;
202                                 } finally {
203                                         Manager.EndOneBuild (this);
204                                 }
205                                 return result;
206                         }
207                 }
208         }
209 }