Пример #1
0
 /// <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++;
             }
         }
     }
 }
Пример #2
0
        /// <summary>
        /// This method causes the reader and writer threads to start and create the shared memory structures
        /// </summary>
        void StartCommunicationThreads()
        {
            // The writer thread should be created before the
            // reader thread because some LocalCallDescriptors
            // assume the shared memory for the writer thread
            // has already been created. The method will both
            // instantiate the shared memory for the writer
            // thread and also start the writer thread itself.
            // We will verifyThrow in the method if the
            // sharedMemory was not created correctly.
            engineCallback.StartWriterThread(nodeNumber);

            // Create the shared memory buffer
            this.sharedMemory =
                new SharedMemory
                (
                    // Generate the name for the shared memory region
                    LocalNodeProviderGlobalNames.NodeInputMemoryName(nodeNumber),
                    SharedMemoryType.ReadOnly,
                    // Reuse an existing shared memory region as it should have already
                    // been created by the parent node side
                    true
                );

            ErrorUtilities.VerifyThrow(this.sharedMemory.IsUsable,
                                       "Failed to create shared memory for local node input.");

            // Start the thread that will be processing the calls from the parent engine
            ThreadStart threadState = new ThreadStart(this.SharedMemoryReaderThread);

            readerThread      = new Thread(threadState);
            readerThread.Name = "MSBuild Child<-Parent Reader";
            readerThread.Start();
        }
Пример #3
0
        /// <summary>
        /// This method creates the shared memory buffers for communicating with the node
        /// </summary>
        /// <returns>Was the shared memory created and is useable</returns>
        internal bool CreateSharedMemoryBuffers()
        {
            this.sharedMemoryToNode =
                new SharedMemory
                (
                    LocalNodeProviderGlobalNames.NodeInputMemoryName(this.nodeNumber),
                    SharedMemoryType.WriteOnly,
                    false
                );

            if (!this.sharedMemoryToNode.IsUsable)
            {
                return(false);
            }


            this.sharedMemoryFromNode =
                new SharedMemory
                (
                    LocalNodeProviderGlobalNames.NodeOutputMemoryName(this.nodeNumber),
                    SharedMemoryType.ReadOnly,
                    false
                );

            if (!this.sharedMemoryFromNode.IsUsable)
            {
                return(false);
            }

            return(true);
        }
Пример #4
0
        /// <summary>
        /// Create global events necessary for handshaking with the parent
        /// </summary>
        /// <param name="nodeNumber"></param>
        /// <returns>True if events created successfully and false otherwise</returns>
        private static bool CreateGlobalEvents(int nodeNumber)
        {
            bool createdNew;

            if (NativeMethods.IsUserAdministrator())
            {
                EventWaitHandleSecurity mSec = new EventWaitHandleSecurity();

                // Add a rule that grants the access only to admins and systems
                mSec.SetSecurityDescriptorSddlForm(NativeMethods.ADMINONLYSDDL);

                // Create an initiation event to allow the parent side  to prove to the child that we have the same level of privilege as it does.
                // this is done by having the parent set this event which means it needs to have administrative permissions to do so.
                globalInitiateActivationEvent = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeInitiateActivationEventName(nodeNumber), out createdNew, mSec);
            }
            else
            {
                // Create an initiation event to allow the parent side  to prove to the child that we have the same level of privilege as it does.
                // this is done by having the parent set this event which means it has atleast the same permissions as the child process
                globalInitiateActivationEvent = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeInitiateActivationEventName(nodeNumber), out createdNew);
            }

            // This process must be the creator of the event to prevent squating by a lower privilaged attacker
            if (!createdNew)
            {
                return(false);
            }

            // Informs the parent process that the child process has been created.
            globalNodeActive = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeActiveEventName(nodeNumber));
            globalNodeActive.Set();

            // Indicate to the parent process, this node is currently is ready to start to recieve requests
            globalNodeInUse = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeInUseEventName(nodeNumber));

            // Used by the parent process to inform the child process to shutdown due to the child process
            // not recieving the initialization command.
            globalNodeErrorShutdown = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeErrorShutdownEventName(nodeNumber));

            // Inform the parent process the node has started its communication threads.
            globalNodeActivate = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeActivedEventName(nodeNumber));

            return(true);
        }
        /// <summary>
        /// This function attempts to find out if there is currently a node running
        /// for a given index. The node is running if the global mutex with a
        /// "Node_" + nodeId + "_ActiveReady" as a name was created
        /// </summary>
        private static bool checkIfNodeActive(int nodeNumber)
        {
            bool            nodeIsActive     = false;
            EventWaitHandle nodeActiveHandle = null;

            try
            {
                nodeActiveHandle = EventWaitHandle.OpenExisting(LocalNodeProviderGlobalNames.NodeActiveEventName(nodeNumber));
                nodeIsActive     = true;
            }
            catch (WaitHandleCannotBeOpenedException)
            {
                // Assume that the node is not running
            }
            finally
            {
                nodeActiveHandle?.Close();
            }

            return(nodeIsActive);
        }
Пример #6
0
        internal void StartWriterThread(int nodeNumber)
        {
            this.writerThreadHasExited = false;

            this.sharedMemory =
                new SharedMemory
                (
                    LocalNodeProviderGlobalNames.NodeOutputMemoryName(nodeNumber),
                    SharedMemoryType.WriteOnly,
                    true
                );

            ErrorUtilities.VerifyThrow(this.sharedMemory.IsUsable,
                                       "Failed to create shared memory for engine callback.");

            // Start the thread that will be processing the calls to the parent engine
            ThreadStart threadState = new ThreadStart(this.SharedMemoryWriterThread);

            writerThread      = new Thread(threadState);
            writerThread.Name = "MSBuild Child->Parent Writer";
            writerThread.Start();
        }
Пример #7
0
        /// <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();
        }
Пример #8
0
        /// <summary>
        /// This function launches a new node given a node index
        /// </summary>
        private void LaunchNode(int nodeIndex)
        {
            EventWaitHandle nodeReadyEvent = null;

            string msbuildLocation = Path.Combine(locationOfMSBuildExe, "MSBuild.exe");

            ErrorUtilities.VerifyThrow(File.Exists(msbuildLocation), "Msbuild.exe cannot be found at: " + msbuildLocation);

            bool exitedDueToError = true;

            try
            {
                NativeMethods.STARTUPINFO startInfo = new NativeMethods.STARTUPINFO();
                startInfo.cb = Marshal.SizeOf(startInfo);
                uint dwCreationFlags = NativeMethods.NORMAL_PRIORITY_CLASS;
                if (!Engine.debugMode)
                {
                    startInfo.hStdError  = NativeMethods.InvalidHandle;
                    startInfo.hStdInput  = NativeMethods.InvalidHandle;
                    startInfo.hStdOutput = NativeMethods.InvalidHandle;
                    startInfo.dwFlags    = NativeMethods.STARTF_USESTDHANDLES;
                    dwCreationFlags      = dwCreationFlags | NativeMethods.CREATE_NO_WINDOW;
                }

                NativeMethods.SECURITY_ATTRIBUTES pSec = new NativeMethods.SECURITY_ATTRIBUTES();
                NativeMethods.SECURITY_ATTRIBUTES tSec = new NativeMethods.SECURITY_ATTRIBUTES();
                pSec.nLength = Marshal.SizeOf(pSec);
                tSec.nLength = Marshal.SizeOf(tSec);

                NativeMethods.PROCESS_INFORMATION pInfo = new NativeMethods.PROCESS_INFORMATION();

                string appName = msbuildLocation;
                // Repeat the executable name as the first token of the command line because the command line
                // parser logic expects it and will otherwise skip the first argument
                string cmdLine = msbuildLocation + " /nologo /oldom /nodemode:" + nodeData[nodeIndex].NodeNumber;
                NativeMethods.CreateProcess(appName, cmdLine,
                                            ref pSec, ref tSec,
                                            false, dwCreationFlags,
                                            NativeMethods.NullPtr, null, ref startInfo, out pInfo);

                nodeReadyEvent = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeActiveEventName(nodeData[nodeIndex].NodeNumber));

                // Wait until the node is ready to process the requests
                if (nodeReadyEvent.WaitOne(launchTimeout, false))
                {
                    exitedDueToError = false;
                }
            }
            finally
            {
                // Dispose before losing scope
                if (nodeReadyEvent != null)
                {
                    nodeReadyEvent.Close();
                }

                if (exitedDueToError)
                {
                    nodeData[nodeIndex].CommunicationFailed = true;
                }
            }
        }
Пример #9
0
        /// <summary>
        /// This function establishes communication with a node given an index. If a node
        /// is not running it is launched.
        /// </summary>
        private void InitializeNode(int nodeIndex)
        {
            bool nodeConnected = false;
            int  restartCount  = 0;

            try
            {
                IncreaseActiveNodeCount();

                while (!nodeConnected && restartCount < maximumNodeRestartCount)
                {
                    if (!checkIfNodeActive(nodeData[nodeIndex].NodeNumber))
                    {
                        // Attempt to launch a new node process
                        LaunchNode(nodeIndex);
                        // If we could not launch the node there is no reason to continue
                        if (nodeData[nodeIndex].CommunicationFailed)
                        {
                            break;
                        }
                    }

                    if (checkIfNodeActive(nodeData[nodeIndex].NodeNumber))
                    {
                        nodeData[nodeIndex].SharedMemoryToNode.Reset();
                        nodeData[nodeIndex].SharedMemoryFromNode.Reset();

                        // Activate the initiation event to prove to the child that we have the same level of privilege as it does. This operation will not fail because each privilege level creates
                        // events in different namespaces
                        EventWaitHandle nodeInitiateActivationEvent = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeInitiateActivationEventName(nodeData[nodeIndex].NodeNumber));
                        nodeInitiateActivationEvent.Set();
                        nodeInitiateActivationEvent.Close();

                        // Wait for node to indicate that it is activated
                        EventWaitHandle nodeActivatedEvent = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeActivedEventName(nodeData[nodeIndex].NodeNumber));
                        nodeActivatedEvent.WaitOne(initializationTimeout, false);
                        nodeActivatedEvent.Close();

                        // Looked in Environment.cs the IDictionary is a HashTable
                        IDictionary variableDictionary        = Environment.GetEnvironmentVariables();
                        Hashtable   environmentVariablesTable = new Hashtable(variableDictionary);

                        LocalCallDescriptorForInitializeNode callDescriptorInit =
                            new LocalCallDescriptorForInitializeNode(environmentVariablesTable, nodeLoggers.ToArray(), nodeData[nodeIndex].NodeId, parentGlobalProperties, toolsetSearchLocations, Process.GetCurrentProcess().Id, startupDirectory);
                        nodeData[nodeIndex].NodeCommandQueue.Enqueue(callDescriptorInit);

                        EventWaitHandle nodeInUseEvent = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeInUseEventName(nodeData[nodeIndex].NodeNumber));

                        // Wait for node to indicate that it is ready. The node may time out and exit, between
                        // when we check that it is active and before the initialization messages reaches it.
                        // In that rare case we have to restart the node.
                        if (nodeInUseEvent.WaitOne(initializationTimeout, false))
                        {
                            UpdateSettings(nodeIndex);
                            nodeConnected = true;
                        }
                        nodeInUseEvent.Close();

                        // If the node is still active and has not replied to the initialization message it must
                        // be in bad state - try to get that node to exit
                        if (!nodeConnected && checkIfNodeActive(nodeData[nodeIndex].NodeNumber))
                        {
                            EventWaitHandle nodeShutdownEvent = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeErrorShutdownEventName(nodeData[nodeIndex].NodeNumber));
                            nodeShutdownEvent.Set();
                            nodeShutdownEvent.Close();

                            restartCount = maximumNodeRestartCount;
                        }

                        restartCount++;
                    }
                }
            }
            finally
            {
                // Make sure to decrement the active node count if the communication has failed
                if (nodeConnected != true)
                {
                    DecreaseActiveNodeCount(nodeData[nodeIndex].NodeId);
                    nodeData[nodeIndex].CommunicationFailed = true;
                }
            }
        }