示例#1
0
 internal BuildClient(RequestLanguage language, CompileFunc compileFunc, CreateServerFunc createServerFunc = null, int?timeoutOverride = null)
 {
     _language         = language;
     _compileFunc      = compileFunc;
     _createServerFunc = createServerFunc ?? BuildServerConnection.TryCreateServerCore;
     _timeoutOverride  = timeoutOverride;
 }
示例#2
0
        internal static async Task <BuildResponse> RunServerCompilationCoreAsync(
            Guid requestId,
            RequestLanguage language,
            List <string> arguments,
            BuildPathsAlt buildPaths,
            string?pipeName,
            string?keepAlive,
            string?libDirectory,
            int?timeoutOverride,
            CreateServerFunc createServerFunc,
            ICompilerServerLogger logger,
            CancellationToken cancellationToken)
        {
            if (pipeName is null)
            {
                throw new ArgumentException(nameof(pipeName));
            }

            if (buildPaths.TempDirectory == null)
            {
                throw new ArgumentException(nameof(buildPaths));
            }

            // early check for the build hash. If we can't find it something is wrong; no point even trying to go to the server
            if (string.IsNullOrWhiteSpace(BuildProtocolConstants.GetCommitHash()))
            {
                return(new IncorrectHashBuildResponse());
            }

            var pipeTask = tryConnectToServer(pipeName, buildPaths, timeoutOverride, createServerFunc, logger, cancellationToken);

            if (pipeTask is null)
            {
                return(new RejectedBuildResponse("Failed to connect to server"));
            }
            else
            {
                using var pipe = await pipeTask.ConfigureAwait(false);

                if (pipe is null)
                {
                    return(new RejectedBuildResponse("Failed to connect to server"));
                }
                else
                {
                    var request = BuildRequest.Create(language,
                                                      arguments,
                                                      workingDirectory: buildPaths.WorkingDirectory,
                                                      tempDirectory: buildPaths.TempDirectory,
                                                      compilerHash: BuildProtocolConstants.GetCommitHash() ?? "",
                                                      requestId: requestId,
                                                      keepAlive: keepAlive,
                                                      libDirectory: libDirectory);

                    return(await TryCompileAsync(pipe, request, logger, cancellationToken).ConfigureAwait(false));
                }
            }
示例#3
0
 private BuildClient CreateClient(
     RequestLanguage?language          = null,
     CompileFunc compileFunc           = null,
     CreateServerFunc createServerFunc = null)
 {
     language ??= RequestLanguage.CSharpCompile;
     compileFunc ??= delegate { return(0); };
     createServerFunc ??= ((_, pipeName) => TryCreateServer(pipeName));
     return(new BuildClient(language.Value, compileFunc, createServerFunc));
 }
示例#4
0
        internal static async Task <BuildResponse> RunServerCompilationCore(
            RequestLanguage language,
            List <string> arguments,
            BuildPathsAlt buildPaths,
            string pipeName,
            string keepAlive,
            string libEnvVariable,
            int?timeoutOverride,
            CreateServerFunc createServerFunc,
            CancellationToken cancellationToken)
        {
            if (pipeName == null)
            {
                return(new RejectedBuildResponse());
            }

            if (buildPaths.TempDirectory == null)
            {
                return(new RejectedBuildResponse());
            }

            // early check for the build hash. If we can't find it something is wrong; no point even trying to go to the server
            if (string.IsNullOrWhiteSpace(BuildProtocolConstants.GetCommitHash()))
            {
                return(new IncorrectHashBuildResponse());
            }

            var pipeTask = tryConnectToServer(pipeName, buildPaths, timeoutOverride, createServerFunc, cancellationToken);

            if (pipeTask is null)
            {
                return(new RejectedBuildResponse());
            }
            else
            {
                var pipe = await pipeTask.ConfigureAwait(false);

                if (pipe is null)
                {
                    return(new RejectedBuildResponse());
                }
                else
                {
                    var request = BuildRequest.Create(language,
                                                      buildPaths.WorkingDirectory,
                                                      buildPaths.TempDirectory,
                                                      BuildProtocolConstants.GetCommitHash(),
                                                      arguments,
                                                      keepAlive,
                                                      libEnvVariable);

                    return(await TryCompile(pipe, request, cancellationToken).ConfigureAwait(false));
                }
            }
示例#5
0
            // This code uses a Mutex.WaitOne / ReleaseMutex pairing. Both of these calls must occur on the same thread
            // or an exception will be thrown. This code lives in a separate non-async function to help ensure this
            // invariant doesn't get invalidated in the future by an `await` being inserted.
            static Task <NamedPipeClientStream> tryConnectToServer(
                string pipeName,
                BuildPathsAlt buildPaths,
                int?timeoutOverride,
                CreateServerFunc createServerFunc,
                CancellationToken cancellationToken)
            {
                var originalThreadId                  = Thread.CurrentThread.ManagedThreadId;
                var clientDir                         = buildPaths.ClientDirectory;
                var timeoutNewProcess                 = timeoutOverride ?? TimeOutMsNewProcess;
                var timeoutExistingProcess            = timeoutOverride ?? TimeOutMsExistingProcess;
                Task <NamedPipeClientStream> pipeTask = null;
                IServerMutex clientMutex              = null;

                try
                {
                    var holdsMutex = false;
                    try
                    {
                        var clientMutexName = GetClientMutexName(pipeName);
                        clientMutex = OpenOrCreateMutex(clientMutexName, out holdsMutex);
                    }
                    catch
                    {
                        // The Mutex constructor can throw in certain cases. One specific example is docker containers
                        // where the /tmp directory is restricted. In those cases there is no reliable way to execute
                        // the server and we need to fall back to the command line.
                        //
                        // Example: https://github.com/dotnet/roslyn/issues/24124
                        return(null);
                    }

                    if (!holdsMutex)
                    {
                        try
                        {
                            holdsMutex = clientMutex.TryLock(timeoutNewProcess);

                            if (!holdsMutex)
                            {
                                return(null);
                            }
                        }
                        catch (AbandonedMutexException)
                        {
                            holdsMutex = true;
                        }
                    }

                    // Check for an already running server
                    var  serverMutexName  = GetServerMutexName(pipeName);
                    bool wasServerRunning = WasServerMutexOpen(serverMutexName);
                    var  timeout          = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess;

                    if (wasServerRunning || createServerFunc(clientDir, pipeName))
                    {
                        pipeTask = TryConnectToServerAsync(pipeName, timeout, cancellationToken);
                    }

                    return(pipeTask);
                }
                finally
                {
                    try
                    {
                        clientMutex?.Dispose();
                    }
                    catch (ApplicationException e)
                    {
                        var releaseThreadId = Thread.CurrentThread.ManagedThreadId;
                        var message         = $"ReleaseMutex failed. WaitOne Id: {originalThreadId} Release Id: {releaseThreadId}";
                        throw new Exception(message, e);
                    }
                }
            }
        internal static async Task <BuildResponse> RunServerCompilationCore(
            RequestLanguage language,
            List <string> arguments,
            BuildPathsAlt buildPaths,
            string pipeName,
            string keepAlive,
            string libEnvVariable,
            int?timeoutOverride,
            CreateServerFunc createServerFunc,
            CancellationToken cancellationToken)
        {
            if (pipeName == null)
            {
                return(new RejectedBuildResponse());
            }

            if (buildPaths.TempDirectory == null)
            {
                return(new RejectedBuildResponse());
            }

            // early check for the build hash. If we can't find it something is wrong; no point even trying to go to the server
            if (string.IsNullOrWhiteSpace(BuildProtocolConstants.GetCommitHash()))
            {
                return(new IncorrectHashBuildResponse());
            }

            var clientDir                         = buildPaths.ClientDirectory;
            var timeoutNewProcess                 = timeoutOverride ?? TimeOutMsNewProcess;
            var timeoutExistingProcess            = timeoutOverride ?? TimeOutMsExistingProcess;
            Task <NamedPipeClientStream> pipeTask = null;
            IServerMutex clientMutex              = null;

            try
            {
                var holdsMutex = false;
                try
                {
                    var clientMutexName = GetClientMutexName(pipeName);
                    clientMutex = OpenOrCreateMutex(clientMutexName, out holdsMutex);
                }
                catch
                {
                    // The Mutex constructor can throw in certain cases. One specific example is docker containers
                    // where the /tmp directory is restricted. In those cases there is no reliable way to execute
                    // the server and we need to fall back to the command line.
                    //
                    // Example: https://github.com/dotnet/roslyn/issues/24124
                    return(new RejectedBuildResponse());
                }

                if (!holdsMutex)
                {
                    try
                    {
                        holdsMutex = clientMutex.TryLock(timeoutNewProcess);

                        if (!holdsMutex)
                        {
                            return(new RejectedBuildResponse());
                        }
                    }
                    catch (AbandonedMutexException)
                    {
                        holdsMutex = true;
                    }
                }

                // Check for an already running server
                var  serverMutexName  = GetServerMutexName(pipeName);
                bool wasServerRunning = WasServerMutexOpen(serverMutexName);
                var  timeout          = wasServerRunning ? timeoutExistingProcess : timeoutNewProcess;

                if (wasServerRunning || createServerFunc(clientDir, pipeName))
                {
                    pipeTask = TryConnectToServerAsync(pipeName, timeout, cancellationToken);
                }
            }
            finally
            {
                clientMutex?.Dispose();
            }

            if (pipeTask != null)
            {
                var pipe = await pipeTask.ConfigureAwait(false);

                if (pipe != null)
                {
                    var request = BuildRequest.Create(language,
                                                      buildPaths.WorkingDirectory,
                                                      buildPaths.TempDirectory,
                                                      BuildProtocolConstants.GetCommitHash(),
                                                      arguments,
                                                      keepAlive,
                                                      libEnvVariable);

                    return(await TryCompile(pipe, request, cancellationToken).ConfigureAwait(false));
                }
            }

            return(new RejectedBuildResponse());
        }