예제 #1
0
파일: Pipes.cs 프로젝트: kittinap/kunnjae
        /// <summary>
        /// Creates a pipe in which the specified ends are inheritable by child processes.
        /// In order for the pipe to be closable by the child, the inherited end should be closed after process creation.
        /// </summary>
        /// <remarks>
        /// This implementation creates a named pipe instance and immediately connects it. This is effectively the same implementation
        /// as <c>CreatePipe</c> (see %SDXROOT%\minkernel\kernelbase\pipe.c), but we compute a random pipe named rather than using the special
        /// 'anonymous' naming case of opening \\.\pipe or \Device\NamedPipe\ (without a subsequent path).
        /// We choose to not use <c>CreatePipe</c> since it opens both sides for synchronous I/O. Instead, for running build tools we want the
        /// BuildXL side opened for overlapped I/O and the build tool side opened for synchronous I/O (if the build tool side acts as a redirected
        /// console handle; recall that synchronous read and write attempts will fail on an async handle, which motivates the <c>CreatePipe</c> defaults
        /// in the first place). This is significant since many build tool console pipes are forever silent, and with sync handles we'd need N * 3 threads
        /// (stdin, stdout, stderr for N concurrent processes) to pessimistically drain them.
        /// </remarks>
        public static void CreateInheritablePipe(PipeInheritance inheritance, PipeFlags flags, out SafeFileHandle readHandle, out SafeFileHandle writeHandle)
        {
            string pipeName = @"\\.\pipe\BuildXL-" + Guid.NewGuid().ToString("N");

            writeHandle = ProcessUtilities.CreateNamedPipe(
                pipeName,
                PipeOpenMode.PipeAccessOutbound | (((flags & PipeFlags.WriteSideAsync) != 0) ? PipeOpenMode.FileFlagOverlapped : 0),
                PipeMode.PipeTypeByte | PipeMode.PipeRejectRemoteClients,
                nMaxInstances: 1,
                nOutBufferSize: PipeBufferSize,
                nInBufferSize: PipeBufferSize,
                nDefaultTimeout: 0,
                lpSecurityAttributes: IntPtr.Zero);

            if (writeHandle.IsInvalid)
            {
                throw new NativeWin32Exception(Marshal.GetLastWin32Error(), "CreateNamedPipeW failed");
            }

            var readSideFlags = FileFlagsAndAttributes.SecurityAnonymous;

            if ((flags & PipeFlags.ReadSideAsync) != 0)
            {
                readSideFlags |= FileFlagsAndAttributes.FileFlagOverlapped;
            }

            OpenFileResult openReadSideResult = FileUtilities.TryCreateOrOpenFile(
                pipeName,
                FileDesiredAccess.GenericRead,
                FileShare.None,
                FileMode.Open,
                readSideFlags,
                out readHandle);

            if (!openReadSideResult.Succeeded)
            {
                throw openReadSideResult.CreateExceptionForError();
            }

            if ((inheritance & PipeInheritance.InheritRead) != 0)
            {
                SetInheritable(readHandle);
            }

            if ((inheritance & PipeInheritance.InheritWrite) != 0)
            {
                SetInheritable(writeHandle);
            }
        }
예제 #2
0
        /// <summary>
        /// Creates a pipe in which the specified ends are inheritable by child processes.
        /// In order for the pipe to be closable by the child, the inherited end should be closed after process creation.
        /// </summary>
        /// <remarks>
        /// This implementation creates a named pipe instance and immediately connects it. This is effectively the same implementation
        /// as <c>CreatePipe</c> (see %SDXROOT%\minkernel\kernelbase\pipe.c), but we compute a random pipe named rather than using the special
        /// 'anonymous' naming case of opening \\.\pipe or \Device\NamedPipe\ (without a subsequent path).
        /// We choose to not use <c>CreatePipe</c> since it opens both sides for synchronous I/O. Instead, for running build tools we want the
        /// BuildXL side opened for overlapped I/O and the build tool side opened for synchronous I/O (if the build tool side acts as a redirected
        /// console handle; recall that synchronous read and write attempts will fail on an async handle, which motivates the <c>CreatePipe</c> defaults
        /// in the first place). This is significant since many build tool console pipes are forever silent, and with sync handles we'd need N * 3 threads
        /// (stdin, stdout, stderr for N concurrent processes) to pessimistically drain them.
        /// </remarks>
        public static void CreateInheritablePipe(PipeInheritance inheritance, PipeFlags flags, out SafeFileHandle readHandle, out SafeFileHandle writeHandle)
        {
            string pipeName = @"\\.\pipe\BuildXL-" + Guid.NewGuid().ToString("N");

            writeHandle = ProcessUtilities.CreateNamedPipe(
                pipeName,
                PipeOpenMode.PipeAccessOutbound | (((flags & PipeFlags.WriteSideAsync) != 0) ? PipeOpenMode.FileFlagOverlapped : 0),
                PipeMode.PipeTypeByte | PipeMode.PipeRejectRemoteClients,
                nMaxInstances: 1,
                nOutBufferSize: PipeBufferSize,
                nInBufferSize: PipeBufferSize,
                nDefaultTimeout: 0,
                lpSecurityAttributes: IntPtr.Zero);

            if (writeHandle.IsInvalid)
            {
                throw new NativeWin32Exception(Marshal.GetLastWin32Error(), "CreateNamedPipeW failed");
            }

            var readSideFlags = FileFlagsAndAttributes.SecurityAnonymous;

            if ((flags & PipeFlags.ReadSideAsync) != 0)
            {
                readSideFlags |= FileFlagsAndAttributes.FileFlagOverlapped;
            }

            int maxRetry = 3;

            while (true)
            {
                OpenFileResult openReadSideResult = FileUtilities.TryCreateOrOpenFile(
                    pipeName,
                    FileDesiredAccess.GenericRead,
                    FileShare.None,
                    FileMode.Open,
                    readSideFlags,
                    out readHandle);

                if (openReadSideResult.Succeeded)
                {
                    break;
                }

                if (openReadSideResult.NativeErrorCode != NativeIOConstants.ErrorPipeBusy ||
                    maxRetry == 0)
                {
                    throw openReadSideResult.CreateExceptionForError();
                }

                bool success = false;

                // Wait for at most 5s.
                for (int i = 0; i < 10; ++i)
                {
                    success = ProcessUtilities.WaitNamedPipe(pipeName, 500);
                    if (success)
                    {
                        break;
                    }
                }

                if (!success)
                {
                    // After waiting for 5s, pipe is still not ready.
                    throw new NativeWin32Exception(Marshal.GetLastWin32Error(), "WaitNamedPipe");
                }

                --maxRetry;
            }

            if ((inheritance & PipeInheritance.InheritRead) != 0)
            {
                SetInheritable(readHandle);
            }

            if ((inheritance & PipeInheritance.InheritWrite) != 0)
            {
                SetInheritable(writeHandle);
            }
        }