Example #1
0
        Run(FileInfo mainScriptFile, string[] argv,
            bool inspect, bool pauseDebuggerOnStart,
            IImmutableSet <string> inspectLoadSet,
            Func <CancellationToken, Task <string> > f,
            Process parentProcess,
            bool verbose)
        {
            var rootDir = mainScriptFile.Directory;

            Debug.Assert(rootDir != null);

            var settings = new V8Settings
            {
                EnableDebugging =
                    inspect || pauseDebuggerOnStart ||
                    inspectLoadSet.Any(),
                AwaitDebuggerAndPauseOnStart =
                    pauseDebuggerOnStart || inspectLoadSet.Any(),
            };

            using (var engine = new V8JsEngine(settings))
            {
                var scheduler = new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler;
                var console   = ConsoleService.Default;

                void Load(string module)
                {
                    var path   = Path.Combine(rootDir.FullName, module);
                    var source = File.ReadAllText(path);

                    if (inspectLoadSet.Contains(source))
                    {
                        source = "debugger;" + source;
                    }
                    engine.Execute(source, module);
                }

                using (var host = new Host(Load, console, scheduler))
                {
                    string FormatMessage(object sender, string message)
                    {
                        var senderName = sender is string s ? s : sender.GetType().Name;
                        var formatted  = $"{senderName}[{Thread.CurrentThread.ManagedThreadId}]: {message}";

                        return(formatted.FormatFoldedLines().TrimNewLineAtTail());
                    }

                    void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs args) =>
                    console.Error(FormatMessage(sender, args.Exception.ToString()));

                    TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;

                    var infoLog = !verbose ? null : new LogEventHandler((sender, message) =>
                                                                        host.Console.Info(FormatMessage(sender, message)));

                    var warnLog = !verbose ? null : new ErrorLogEventHandler((sender, e, message) =>
                                                                             host.Console.Warn(FormatMessage(sender, e == null ? message : string.IsNullOrEmpty(message) ? e.ToString() : message + Environment.NewLine + e)));

                    var errorLog = !verbose ? null : new ErrorLogEventHandler((sender, e, message) =>
                                                                              host.Console.Error(FormatMessage(sender, string.IsNullOrEmpty(message) ? e.ToString() : message + Environment.NewLine + e)));

                    foreach (var service in new ILogSource[] { host, host.Timer, host.Xhr })
                    {
                        service.InfoLog  = infoLog;
                        service.WarnLog  = warnLog;
                        service.ErrorLog = errorLog;
                    }

                    var tasks = new List <NamedTask>();

                    void AddTask(NamedTask task)
                    {
                        lock (tasks) tasks.Add(task);
                    }

                    void RemoveTask(NamedTask task)
                    {
                        lock (tasks) tasks.Remove(task);
                    }

                    host.TaskStarting  += (_, task) => AddTask(task);
                    host.TaskFinishing += (_, task) => RemoveTask(task);

                    if (verbose)
                    {
                        host.ServiceCreated += (_, service) =>
                        {
                            service.InfoLog  = infoLog;
                            service.WarnLog  = warnLog;
                            service.ErrorLog = errorLog;
                        };
                    }

                    engine.EmbedHostObject("host", host);
                    var     initScript = GetManifestResourceStream("init.js", typeof(Program)).ReadAsText();
                    dynamic init       = engine.Evaluate(initScript, "__init.js");
                    init(host, engine.Evaluate("this"), argv);

                    if (settings.AwaitDebuggerAndPauseOnStart)
                    {
                        console.Warn(FormatMessage(nameof(Program), "Will wait for debugger to attach."));
                    }

                    Load(mainScriptFile.Name);

                    void Schedule(string name, Action <AsyncTaskControl> action)
                    {
                        var task = AsyncTask.Create(name, thisTask =>
                        {
                            Exception error = null;
                            try
                            {
                                action(thisTask);
                            }
                            catch (Exception e)
                            {
                                error = e;
                            }
                            RemoveTask(thisTask);
                            switch (error)
                            {
                            case null:
                                thisTask.FlagSuccess();
                                break;

                            case OperationCanceledException e:
                                thisTask.FlagCanceled(e.CancellationToken);
                                break;

                            default:
                                errorLog?.Invoke(nameof(Program), error, null);
                                thisTask.FlagError(error);
                                break;
                            }
                        });

                        AddTask(task);
                        task.Start(scheduler);
                    }

                    var parentProcessTask = parentProcess.AsTask(dispose: true, p => p.ExitCode,
                                                                 _ => null,
                                                                 exit => exit);

                    while (true)
                    {
                        var readCommandTask = f(CancellationToken.None);
                        if (parentProcessTask != null)
                        {
                            if (parentProcessTask == await Task.WhenAny(readCommandTask, parentProcessTask))
                            {
                                break;
                            }
                        }
                        else
                        {
                            await readCommandTask;
                        }

                        var command = (await readCommandTask)?.Trim();

                        if (command == null)
                        {
                            break;
                        }

                        if (command.Length == 0)
                        {
                            continue;
                        }

                        const string ondata = "ondata";
                        Schedule(ondata, delegate
                        {
                            infoLog?.Invoke(nameof(Program), "STDIN: " + command);
                            engine.CallFunction(ondata, command);
                        });
                    }

                    const string onclose = "onclose";
                    Schedule(onclose, delegate
                    {
                        engine.Execute(@"if (typeof onclose === 'function') onclose();");
                    });

                    host.Timer.CancelAll();
                    host.Xhr.AbortAll();

                    infoLog?.Invoke(typeof(Program), "Shutting down...");

                    ImmutableArray <Task> tasksSnapshot;

                    lock (tasks)
                        tasksSnapshot = ImmutableArray.CreateRange(from t in tasks select t.Task);

                    if (await tasksSnapshot.WhenAll(TimeSpan.FromSeconds(30)))
                    {
                        Debug.Assert(tasks.Count == 0);
                    }
                    else
                    {
                        warnLog?.Invoke(typeof(Program), null, "Timed-out waiting for all tasks to end for a graceful shutdown!");
                    }

                    infoLog?.Invoke(typeof(Program), "Shutdown completed.");

                    TaskScheduler.UnobservedTaskException -= OnUnobservedTaskException;
                }
            }
        }
Example #2
0
        public Host(Action <string> loader,
                    ConsoleService console,
                    TaskScheduler taskScheduler)
        {
            _loader = loader ?? throw new ArgumentNullException(nameof(loader));

            if (taskScheduler == null)
            {
                throw new ArgumentNullException(nameof(taskScheduler));
            }

            var scheduler = _scheduler = Schedule;

            Console = console;
            Timer   = new TimerService(scheduler);
            Xhr     = new XhrService(scheduler);

            void Schedule(ILogSource source, string name, CancellationToken cancellationToken,
                          Func <CancellationToken, Task> onSchedule,
                          Action <Exception> onError,
                          Action <OperationCanceledException> onCancel,
                          Action onFinally)
            {
                var task =
                    AsyncTask.Create(name,
                                     async thisTask =>
                {
                    try
                    {
                        await onSchedule(cancellationToken);
                        TaskFinishing?.Invoke(this, thisTask);
                        thisTask.FlagSuccess();
                    }
                    catch (OperationCanceledException e)
                    {
                        try
                        {
                            source.WarnLog?.Invoke(source, e, name);
                            onCancel?.Invoke(e);
                        }
                        finally
                        {
                            TaskFinishing?.Invoke(this, thisTask);
                            thisTask.FlagCanceled(e.CancellationToken);
                        }
                    }
                    catch (Exception e)
                    {
                        try
                        {
                            source.ErrorLog?.Invoke(source, e, name);
                            onError?.Invoke(e);
                        }
                        finally
                        {
                            TaskFinishing?.Invoke(this, thisTask);
                            thisTask.FlagError(e);
                        }
                    }
                    finally
                    {
                        try
                        {
                            onFinally?.Invoke();
                        }
                        finally
                        {
                            TaskFinished?.Invoke(this, thisTask);
                        }
                    }
                });

                TaskStarting?.Invoke(this, task);
                task.Start(taskScheduler);
            }
        }