Esempio n. 1
0
        public async Task CreateAndRun(string rHome, string rhostDirectory = null, string rCommandLineArguments = null, int timeout = 3000, CancellationToken ct = default(CancellationToken))
        {
            await TaskUtilities.SwitchToBackgroundThread();

            rhostDirectory        = rhostDirectory ?? Path.GetDirectoryName(typeof(RHost).Assembly.GetAssemblyPath());
            rCommandLineArguments = rCommandLineArguments ?? string.Empty;

            string rhostExe = Path.Combine(rhostDirectory, RHostExe);
            string rBinPath = Path.Combine(rHome, RBinPathX64);

            if (!File.Exists(rhostExe))
            {
                throw new RHostBinaryMissingException();
            }

            // Grab an available port from the ephemeral port range (per RFC 6335 8.1.2) for the server socket.

            WebSocketServer server = null;
            var             rnd    = new Random();
            const int       ephemeralRangeStart = 49152;
            var             ports =
                from port in Enumerable.Range(ephemeralRangeStart, 0x10000 - ephemeralRangeStart)
                let pos = rnd.NextDouble()
                          orderby pos
                          select port;

            foreach (var port in ports)
            {
                ct.ThrowIfCancellationRequested();

                server = new WebSocketServer(port)
                {
                    ReuseAddress = false
                };
                server.AddWebSocketService("/", CreateWebSocketMessageTransport);

                try {
                    server.Start();
                    break;
                } catch (SocketException ex) {
                    if (ex.SocketErrorCode == SocketError.AddressAlreadyInUse)
                    {
                        server = null;
                    }
                    else
                    {
                        throw new MessageTransportException(ex);
                    }
                } catch (WebSocketException ex) {
                    throw new MessageTransportException(ex);
                }
            }

            if (server == null)
            {
                throw new MessageTransportException(new SocketException((int)SocketError.AddressAlreadyInUse));
            }

            var psi = new ProcessStartInfo {
                FileName        = rhostExe,
                UseShellExecute = false
            };

            psi.EnvironmentVariables["R_HOME"] = rHome;
            psi.EnvironmentVariables["PATH"]   = Environment.GetEnvironmentVariable("PATH") + ";" + rBinPath;

            if (_name != null)
            {
                psi.Arguments += " --rhost-name " + _name;
            }

            psi.Arguments += Invariant($" --rhost-connect ws://127.0.0.1:{server.Port}");

            if (!showConsole)
            {
                psi.CreateNoWindow = true;
            }

            if (!string.IsNullOrWhiteSpace(rCommandLineArguments))
            {
                psi.Arguments += Invariant($" {rCommandLineArguments}");
            }

            using (this)
                using (_process = Process.Start(psi)) {
                    _log.RHostProcessStarted(psi);
                    _process.EnableRaisingEvents = true;
                    _process.Exited += delegate { Dispose(); };

                    try {
                        ct = CancellationTokenSource.CreateLinkedTokenSource(ct, _cts.Token).Token;

                        // Timeout increased to allow more time in test and code coverage runs.
                        await Task.WhenAny(_transportTcs.Task, Task.Delay(timeout)).Unwrap();

                        if (!_transportTcs.Task.IsCompleted)
                        {
                            _log.FailedToConnectToRHost();
                            throw new RHostTimeoutException("Timed out waiting for R host process to connect");
                        }

                        await Run(null, ct);
                    } catch (Exception) {
                        // TODO: delete when we figure out why host occasionally times out in code coverage runs.
                        //await _log.WriteFormatAsync(MessageCategory.Error, "Exception running R Host: {0}", ex.Message);
                        throw;
                    } finally {
                        if (!_process.HasExited)
                        {
                            try {
                                _process.WaitForExit(500);
                                if (!_process.HasExited)
                                {
                                    _process.Kill();
                                    _process.WaitForExit();
                                }
                            } catch (InvalidOperationException) {
                            }
                        }
                        _log.RHostProcessExited();
                    }
                }
        }