/// <summary> /// This function attempts to find out a node number for which /// the event named Node_x_ProviderMutex doesn't exist. The existance /// of the event indicates that some other node provider is using the node. /// </summary> private void ReserveNextAvailableNodeNumber(int currentNodeNumber) { while (nodeReserveHandle == null) { bool createdNew; nodeReserveHandle = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeReserveEventName(currentNodeNumber), out createdNew); if (!createdNew) { nodeReserveHandle.Close(); nodeReserveHandle = null; currentNodeNumber++; } else { nodeNumber = currentNodeNumber; // Create the shared memory resources if (!CreateSharedMemoryBuffers()) { nodeReserveHandle.Close(); nodeReserveHandle = null; currentNodeNumber++; } } } }
/// <summary> /// This function starts local node when process is launched and shuts it down on time out /// Called by msbuild.exe. /// </summary> public static void StartLocalNodeServer(int nodeNumber) { // Create global events necessary for handshaking with the parent if (!CreateGlobalEvents(nodeNumber)) { return; } LocalNode localNode = new LocalNode(nodeNumber); WaitHandle[] waitHandles = new WaitHandle[4]; waitHandles[0] = shutdownEvent; waitHandles[1] = globalNodeErrorShutdown; waitHandles[2] = inUseEvent; waitHandles[3] = globalInitiateActivationEvent; // This is necessary to make build.exe finish promptly. Dont remove. if (!Engine.debugMode) { // Create null streams for the current input/output/error streams Console.SetOut(new StreamWriter(Stream.Null)); Console.SetError(new StreamWriter(Stream.Null)); Console.SetIn(new StreamReader(Stream.Null)); } bool continueRunning = true; while (continueRunning) { int eventType = WaitHandle.WaitAny(waitHandles, inactivityTimeout, false); if (eventType == 0 || eventType == 1 || eventType == WaitHandle.WaitTimeout) { continueRunning = false; localNode.ShutdownNode(eventType != 1 ? Node.NodeShutdownLevel.PoliteShutdown : Node.NodeShutdownLevel.ErrorShutdown, true, true); } else if (eventType == 2) { // reset the event as we do not want it to go into this state again when we are done with this if statement. inUseEvent.Reset(); // The parent knows at this point the child process has been launched globalNodeActivate.Reset(); // Set the global inuse event so other parent processes know this node is now initialized globalNodeInUse.Set(); // Make a copy of the parents handle to protect ourselves in case the parent dies, // this is to prevent a parent from reserving a node another parent is trying to use. globalNodeReserveHandle = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeReserveEventName(nodeNumber)); WaitHandle[] waitHandlesActive = new WaitHandle[3]; waitHandlesActive[0] = shutdownEvent; waitHandlesActive[1] = globalNodeErrorShutdown; waitHandlesActive[2] = notInUseEvent; eventType = WaitHandle.WaitTimeout; while (eventType == WaitHandle.WaitTimeout && continueRunning) { eventType = WaitHandle.WaitAny(waitHandlesActive, parentCheckInterval, false); if (eventType == 0 || /* nice shutdown due to shutdownEvent */ eventType == 1 || /* error shutdown due to globalNodeErrorShutdown */ (eventType == WaitHandle.WaitTimeout && !localNode.IsParentProcessAlive())) { continueRunning = false; // If the exit is not triggered by running of shutdown method if (eventType != 0) { localNode.ShutdownNode(Node.NodeShutdownLevel.ErrorShutdown, true, true); } } else if (eventType == 2) { // Trigger a collection before the node goes idle to insure that // the memory is released to the system as soon as possible GC.Collect(); // Change the current directory to a safe one so that the directory // last used by the build can be safely deleted. We must have read // access to the safe directory so use SystemDirectory for this purpose. Directory.SetCurrentDirectory(Environment.SystemDirectory); notInUseEvent.Reset(); globalNodeInUse.Reset(); } } ErrorUtilities.VerifyThrow(localNode.node == null, "Expected either node to be null or continueRunning to be false."); // Stop the communication threads and release the shared memory object so that the next parent can create it localNode.StopCommunicationThreads(); // Close the local copy of the reservation handle (this allows another parent to reserve // the node) globalNodeReserveHandle.Close(); globalNodeReserveHandle = null; } else if (eventType == 3) { globalInitiateActivationEvent.Reset(); localNode.StartCommunicationThreads(); globalNodeActivate.Set(); } } // Stop the communication threads and release the shared memory object so that the next parent can create it localNode.StopCommunicationThreads(); globalNodeActive.Close(); globalNodeInUse.Close(); }