1 // Copyright (c) Microsoft. All rights reserved. 2 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 //----------------------------------------------------------------------- 4 // </copyright> 5 // <summary>Contains a set of data used for legacy threading semantics</summary> 6 //----------------------------------------------------------------------- 7 8 using System; 9 using System.Threading; 10 using Microsoft.Build.BackEnd; 11 using Microsoft.Build.Shared; 12 using System.Collections.Generic; 13 using System.Threading.Tasks; 14 15 namespace Microsoft.Build.Execution 16 { 17 /// <summary> 18 /// This class represents the data which is used for legacy threading semantics for the build 19 /// </summary> 20 internal class LegacyThreadingData 21 { 22 #region Fields 23 /// <summary> 24 /// Store the pair of start/end events used by a particular submission to track their ownership 25 /// of the legacy thread. 26 /// Item1: Start event, tracks when the submission has permission to start building. 27 /// Item2: End event, signalled when that submission is no longer using the legacy thread. 28 /// </summary> 29 private IDictionary<int, Tuple<AutoResetEvent, ManualResetEvent>> _legacyThreadingEventsById = new Dictionary<int, Tuple<AutoResetEvent, ManualResetEvent>>(); 30 31 /// <summary> 32 /// The current submission id building on the main thread, if any. 33 /// </summary> 34 private int _mainThreadSubmissionId = -1; 35 36 /// <summary> 37 /// The instance to be used when the new request builder is started on the main thread. 38 /// </summary> 39 private RequestBuilder _instanceForMainThread; 40 41 /// <summary> 42 /// Lock object for startNewRequestBuilderMainThreadEventsById, since it's possible for multiple submissions to be 43 /// submitted at the same time. 44 /// </summary> 45 private Object _legacyThreadingEventsLock = new Object(); 46 #endregion 47 48 #region Properties 49 50 /// <summary> 51 /// The instance to be used when the new request builder is started on the main thread. 52 /// </summary> 53 internal RequestBuilder InstanceForMainThread 54 { 55 get 56 { 57 return _instanceForMainThread; 58 } 59 60 set 61 { 62 ErrorUtilities.VerifyThrow(_instanceForMainThread == null || (_instanceForMainThread != null && value == null) || (_instanceForMainThread == value), "Should not assign to instanceForMainThread twice without cleaning it"); 63 _instanceForMainThread = value; 64 } 65 } 66 67 /// <summary> 68 /// The current submission id building on the main thread, if any. 69 /// </summary> 70 internal int MainThreadSubmissionId 71 { 72 get 73 { 74 return _mainThreadSubmissionId; 75 } 76 77 set 78 { 79 if (value == -1) 80 { 81 _instanceForMainThread = null; 82 } 83 84 _mainThreadSubmissionId = value; 85 } 86 } 87 #endregion 88 89 /// <summary> 90 /// Given a submission ID, assign it "start" and "finish" events to track its use of 91 /// the legacy thread. 92 /// </summary> RegisterSubmissionForLegacyThread(int submissionId)93 internal void RegisterSubmissionForLegacyThread(int submissionId) 94 { 95 lock (_legacyThreadingEventsLock) 96 { 97 ErrorUtilities.VerifyThrow(!_legacyThreadingEventsById.ContainsKey(submissionId), "Submission {0} should not already be registered with LegacyThreadingData", submissionId); 98 99 _legacyThreadingEventsById[submissionId] = new Tuple<AutoResetEvent, ManualResetEvent> 100 ( 101 new AutoResetEvent(false), 102 new ManualResetEvent(false) 103 ); 104 } 105 } 106 107 /// <summary> 108 /// This submission is completely done with the legacy thread, so unregister it 109 /// from the dictionary so that we don't leave random events lying around. 110 /// </summary> UnregisterSubmissionForLegacyThread(int submissionId)111 internal void UnregisterSubmissionForLegacyThread(int submissionId) 112 { 113 lock (_legacyThreadingEventsLock) 114 { 115 ErrorUtilities.VerifyThrow(_legacyThreadingEventsById.ContainsKey(submissionId), "Submission {0} should have been previously registered with LegacyThreadingData", submissionId); 116 117 if (_legacyThreadingEventsById.ContainsKey(submissionId)) 118 { 119 _legacyThreadingEventsById.Remove(submissionId); 120 } 121 } 122 } 123 124 /// <summary> 125 /// Given a submission ID, return the event being used to track when that submission is ready 126 /// to be executed on the legacy thread. 127 /// </summary> GetStartRequestBuilderMainThreadEventForSubmission(int submissionId)128 internal WaitHandle GetStartRequestBuilderMainThreadEventForSubmission(int submissionId) 129 { 130 Tuple<AutoResetEvent, ManualResetEvent> legacyThreadingEvents = null; 131 132 lock (_legacyThreadingEventsLock) 133 { 134 _legacyThreadingEventsById.TryGetValue(submissionId, out legacyThreadingEvents); 135 } 136 137 ErrorUtilities.VerifyThrow(legacyThreadingEvents != null, "We're trying to wait on the legacy thread for submission {0}, but that submission has not been registered.", submissionId); 138 139 return legacyThreadingEvents.Item1; 140 } 141 142 /// <summary> 143 /// Given a submission ID, return the event being used to track when that submission is ready 144 /// to be executed on the legacy thread. 145 /// </summary> GetLegacyThreadInactiveTask(int submissionId)146 internal Task GetLegacyThreadInactiveTask(int submissionId) 147 { 148 Tuple<AutoResetEvent, ManualResetEvent> legacyThreadingEvents = null; 149 150 lock (_legacyThreadingEventsLock) 151 { 152 _legacyThreadingEventsById.TryGetValue(submissionId, out legacyThreadingEvents); 153 } 154 155 ErrorUtilities.VerifyThrow(legacyThreadingEvents != null, "We're trying to track when the legacy thread for submission {0} goes inactive, but that submission has not been registered.", submissionId); 156 157 return legacyThreadingEvents.Item2.ToTask(); 158 } 159 160 /// <summary> 161 /// Signal that the legacy thread is starting work. 162 /// </summary> SignalLegacyThreadStart(RequestBuilder instance)163 internal void SignalLegacyThreadStart(RequestBuilder instance) 164 { 165 ErrorUtilities.VerifyThrow 166 ( 167 instance != null && 168 instance.RequestEntry != null && 169 instance.RequestEntry.Request != null, 170 "Cannot signal legacy thread start for a RequestBuilder without a request" 171 ); 172 173 int submissionId = instance.RequestEntry.Request.SubmissionId; 174 this.InstanceForMainThread = instance; 175 176 Tuple<AutoResetEvent, ManualResetEvent> legacyThreadingEvents = null; 177 lock (_legacyThreadingEventsLock) 178 { 179 _legacyThreadingEventsById.TryGetValue(submissionId, out legacyThreadingEvents); 180 } 181 182 ErrorUtilities.VerifyThrow(legacyThreadingEvents != null, "We're trying to signal that the legacy thread is ready for submission {0} to execute, but that submission has not been registered", submissionId); 183 184 // signal that this submission is currently controlling the legacy thread 185 legacyThreadingEvents.Item1.Set(); 186 187 // signal that the legacy thread is not currently idle 188 legacyThreadingEvents.Item2.Reset(); 189 } 190 191 /// <summary> 192 /// Signal that the legacy thread has finished its work. 193 /// </summary> SignalLegacyThreadEnd(int submissionId)194 internal void SignalLegacyThreadEnd(int submissionId) 195 { 196 this.MainThreadSubmissionId = -1; 197 198 Tuple<AutoResetEvent, ManualResetEvent> legacyThreadingEvents = null; 199 lock (_legacyThreadingEventsLock) 200 { 201 _legacyThreadingEventsById.TryGetValue(submissionId, out legacyThreadingEvents); 202 } 203 204 ErrorUtilities.VerifyThrow(legacyThreadingEvents != null, "We're trying to signal that submission {0} is done with the legacy thread, but that submission has not been registered", submissionId); 205 206 // The legacy thread is now idle 207 legacyThreadingEvents.Item2.Set(); 208 } 209 } 210 } 211