Exemple #1
0
        void QueueAction(T next)
        {
            var ct = CancelTokenSrc.Token;

            Task task = new Task(() =>
            {
                // If we are cancelled before we start, push the job back on the queue so it can be dealt with by another runner.
                if (ct.IsCancellationRequested)
                {
                    Queue.Enqueue(next);
                    return;
                }

                SafeAction(next);
            }, ct);

            task.ContinueWith((t) =>
            {
                byte val;
                RunningTasks.TryRemove(t, out val);

                WaitForTaskEvent.Set( );
            }, ct);

            RunningTasks.TryAdd(task, 0);

            task.Start( );
        }
        /// <summary>
        /// Return a linked CancelToken?
        /// </summary>
        /// <param name="token"></param>
        public void StopEventLoop(CancellationToken token = default)
        {
            isLooping = false;
            var clearOnDone = AwaitAllTasks().ContinueWith(_ => RunningTasks.Clear());

            RunningTasks.GetOrAdd(clearOnDone.Id, clearOnDone);
        }
Exemple #3
0
 public async Task <HttpResponseMessage> GetNewsAsync()
 {
     using (var cts = new CancellationTokenSource())
     {
         RunningTasks.Add(RemoteRequestAsync(redditApi.GetApi(Priority.UserInitiated).GetNews(cts.Token)).Id, cts);
         return(await RemoteRequestAsync(redditApi.GetApi(Priority.UserInitiated).GetNews(cts.Token)));
     }
 }
 public async Task <HttpResponseMessage> GetMakeUpsAsync(string brand)
 {
     using (var cts = new CancellationTokenSource())
     {
         Task <HttpResponseMessage> task = RemoteRequestAsync(makeUpApi.GetApi(Priority.UserInitiated).GetMakeUps(brand));
         RunningTasks.Add(task.Id, cts);
         return(await task);
     }
 }
        public async Task <int> AddMakeupLocalDataAsync(List <MakeUpModel> makeUpModelList)
        {
            using var cts = new CancellationTokenSource();
            Task <int> task = makeUpApi.InsertAll(AutoMapper.Map <List <MakeUp> >(makeUpModelList));

            RunningTasks.Add(task.Id, cts);

            return(await task);
        }
 Task AppendAwaitCompletion(Task theTask)
 {
     if (!theTask.IsCompleted)
     {
         var updatedTask = theTask.ContinueWith(t => RunningTasks.Remove(t.Id, out _));
         return(RunningTasks.AddOrUpdate(theTask.Id, updatedTask, (_, t) => updatedTask));
     }
     return(theTask);
 }
        public async Task <List <MakeUpModel> > GetMakeupLocalDataAsync()
        {
            using (var cts = new CancellationTokenSource())
            {
                Task <List <MakeUp> > task = makeUpApi.Get();
                RunningTasks.Add(task.Id, cts);

                var makeUpModels = Task.Run(() => AutoMapper.Map <List <MakeUpModel> >(task.Result));
                RunningTasks.Add(makeUpModels.Id, cts);

                return(await makeUpModels);
            }
        }
Exemple #8
0
        protected async Task RunImageLoadingTaskAsync(IImageLoaderTask pendingTask)
        {
            string keyRaw = pendingTask.KeyRaw;

            try
            {
                if (_imageLoader.VerbosePerformanceLogging)
                {
                    IPlatformPerformance performance = _imageLoader.Performance;

                    LogSchedulerStats(performance);
                    var stopwatch = Stopwatch.StartNew();

                    await pendingTask.RunAsync().ConfigureAwait(false);

                    stopwatch.Stop();

                    _imageLoader.Logger?.Debug(string.Format("[PERFORMANCE] RunAsync - NetManagedThreadId: {0}, NativeThreadId: {1}, Execution: {2} ms, Key: {3}",
                                                             performance.GetCurrentManagedThreadId(),
                                                             performance.GetCurrentSystemThreadId(),
                                                             stopwatch.Elapsed.Milliseconds,
                                                             pendingTask.Key));
                }
                else
                {
                    await pendingTask.RunAsync().ConfigureAwait(false);
                }
            }
            finally
            {
                lock (_lock)
                {
                    RunningTasks.Remove(keyRaw);

                    if (SimilarTasks.Count > 0)
                    {
                        SimilarTasks.RemoveAll(v => v == null || v.IsCompleted || v.IsCancelled);
                        var similarItems = SimilarTasks.Where(v => v.KeyRaw == keyRaw);
                        foreach (var similar in similarItems)
                        {
                            SimilarTasks.Remove(similar);

                            LoadImage(similar);
                        }
                    }
                }

                pendingTask.TryDispose();
                await TakeFromPendingTasksAndRunAsync().ConfigureAwait(false);
            }
        }
Exemple #9
0
 private void WaitForAvailableThread()
 {
     System.DateTime dtStart = System.DateTime.Now;
     while (!disposing && MaximumConcurrency <= RunningTasks.Count())
     {
         System.Threading.Thread.Sleep(10);
         if (System.DateTime.Now - dtStart >= TimeOut)
         {
             throw new System.TimeoutException(
                       string.Format(IO.FileParser.Properties.Resources.ResourceManager.GetString("ThreadNotAvailable"),
                                     TimeOut.ToString("HH:mm:ss.fff")));
         }
     }
 }
Exemple #10
0
        protected async Task TakeFromPendingTasksAndRunAsync()
        {
            Dictionary <string, PendingTask> tasksToRun = null;

            lock (_pendingTasksLock)
            {
                if (RunningTasks.Count >= MaxParallelTasks)
                {
                    return;
                }

                int numberOfTasks = MaxParallelTasks - RunningTasks.Count;
                tasksToRun = new Dictionary <string, PendingTask>();

                foreach (var task in PendingTasks.Where(t => !t.ImageLoadingTask.IsCancelled && !t.ImageLoadingTask.IsCompleted)
                         .OrderByDescending(t => t.ImageLoadingTask.Parameters.Priority ?? GetDefaultPriority(t.ImageLoadingTask.Parameters.Source))
                         .ThenBy(t => t.Position))
                {
                    // We don't want to load, at the same time, images that have same key or same raw key at the same time
                    // This way we prevent concurrent downloads and benefit from caches

                    string rawKey = task.ImageLoadingTask.KeyRaw;
                    if (RunningTasks.ContainsKey(rawKey) || tasksToRun.ContainsKey(rawKey))
                    {
                        continue;
                    }

                    tasksToRun.Add(rawKey, task);

                    if (tasksToRun.Count == numberOfTasks)
                    {
                        break;
                    }
                }
            }

            if (tasksToRun != null && tasksToRun.Count > 0)
            {
                if (tasksToRun.Count == 1)
                {
                    await RunImageLoadingTaskAsync(tasksToRun.Values.First(), false).ConfigureAwait(false);
                }
                else
                {
                    var tasks = tasksToRun.Select(p => RunImageLoadingTaskAsync(p.Value, true));
                    await Task.WhenAny(tasks).ConfigureAwait(false);
                }
            }
        }
Exemple #11
0
        protected async Task RunImageLoadingTaskAsync(PendingTask pendingTask)
        {
            var key = pendingTask.ImageLoadingTask.Key;

            lock (_pendingTasksLock)
            {
                if (RunningTasks.ContainsKey(key))
                    return;

                RunningTasks.Add(key, pendingTask);
                Interlocked.Increment(ref _statsTotalRunning);
            }

            try
            {
                if (Configuration.VerbosePerformanceLogging)
                {
                    LogSchedulerStats();
                    var stopwatch = Stopwatch.StartNew();

                    await Task.Run(pendingTask.ImageLoadingTask.RunAsync).ConfigureAwait(false);

                    stopwatch.Stop();

                    Logger.Debug(
                        string.Format(
                            "[PERFORMANCE] RunAsync - NetManagedThreadId: {0}, NativeThreadId: {1}, Execution: {2} ms, Key: {3}",
                            Performance.GetCurrentManagedThreadId(),
                            Performance.GetCurrentSystemThreadId(),
                            stopwatch.Elapsed.Milliseconds,
                            key));
                }
                else
                {
                    await Task.Run(pendingTask.ImageLoadingTask.RunAsync).ConfigureAwait(false);
                }
            }
            finally
            {
                lock (_pendingTasksLock)
                {
                    RunningTasks.Remove(key);
                }
                pendingTask?.ImageLoadingTask?.Dispose();

                await TakeFromPendingTasksAndRunAsync().ConfigureAwait(false);
            }
        }
Exemple #12
0
 /// <summary>
 /// Starts the <see cref="AbortableTask">Task</see> using <see cref="TaskManager.TaskFactory">TaskFactory</see>
 /// and adds it to the <see cref="TaskManager.Tasks">Tasks</see> collection or the
 /// <see cref="TaskManager.PendingTasks">PendingTasks</see> collection if no threads are available.
 /// </summary>
 /// <param name="task"></param>
 public void StartTask(AbortableTask task)
 {
     if (MaximumConcurrency <= RunningTasks.Count())
     {
         PendingTasks.Add(task);
     }
     else
     {
         Tasks.Add(task);
         if (TaskFactory.Scheduler == null)
         {
             task.Task.Start();
         }
         else
         {
             task.Task.Start(TaskFactory.Scheduler);
         }
     }
 }
Exemple #13
0
    public async Task RemeberWithTestAsync()
    {
        AbandonedTasks processingEvents = new();

        using (RunningTasks.RememberWith(processingEvents))
        {
            Assert.Equal(1, processingEvents.Count);
        }

        Assert.Equal(0, processingEvents.Count);

        using (RunningTasks.RememberWith(processingEvents))
        {
            Assert.Equal(1, processingEvents.Count);
        }

        Assert.Equal(0, processingEvents.Count);
        await processingEvents.WhenAllAsync();
    }
        public async Task <List <MakeUpModel> > GetMakeupLocalUsingEFDataAsync()
        {
            using (var cts = new CancellationTokenSource())
            {
                var makeUp = new MakeUp()
                {
                    Brand    = "xxxxx",
                    Category = "xxxxxxx"
                };

                MakeUp value = UnitOfWork.GenericHandler <MakeUp>().Insert(makeUp);
                UnitOfWork.Save();

                Task <List <MakeUp> > task = makeUpApi.Get();
                RunningTasks.Add(task.Id, cts);

                var makeUpModels = Task.Run(() => AutoMapper.Map <List <MakeUpModel> >(task.Result));
                RunningTasks.Add(makeUpModels.Id, cts);

                return(await makeUpModels);
            }
        }
Exemple #15
0
 public void SubmitTask(Task task)
 {
     RunningTasks.Add(task);
 }
Exemple #16
0
        protected async Task TakeFromPendingTasksAndRunAsync()
        {
            Dictionary<string, PendingTask> tasksToRun = null;

            lock (_pendingTasksLock)
            {
                var preloadOrUrlTasksCount = 0;
                var urlTasksCount = 0;
                var preloadTasksCount = 0;

                if (RunningTasks.Count >= MaxParallelTasks)
                {
                    urlTasksCount =
                        RunningTasks.Count(
                            v =>
                                (v.Value?.ImageLoadingTask != null) && !v.Value.ImageLoadingTask.Parameters.Preload &&
                                (v.Value.ImageLoadingTask.Parameters.Source == ImageSource.Url));

                    preloadTasksCount = RunningTasks.Count(v => (v.Value?.ImageLoadingTask != null)
                                                                && v.Value.ImageLoadingTask.Parameters.Preload);
                    preloadOrUrlTasksCount = preloadTasksCount + urlTasksCount;

                    if ((preloadOrUrlTasksCount == 0) || (preloadOrUrlTasksCount != MaxParallelTasks))
                        return;

                    // Allow only half of MaxParallelTasks as additional allowed tasks when preloading occurs to prevent starvation
                    if (RunningTasks.Count - Math.Max(1, Math.Min(preloadOrUrlTasksCount, MaxParallelTasks/2)) >=
                        MaxParallelTasks)
                        return;
                }

                var numberOfTasks = MaxParallelTasks - RunningTasks.Count +
                                    Math.Min(preloadOrUrlTasksCount, MaxParallelTasks/2);
                tasksToRun = new Dictionary<string, PendingTask>();

                foreach (
                    var task in
                    PendingTasks.Where(t => !t.ImageLoadingTask.IsCancelled && !t.ImageLoadingTask.IsCompleted)
                        .OrderByDescending(
                            t =>
                                t.ImageLoadingTask.Parameters.Priority ??
                                GetDefaultPriority(t.ImageLoadingTask.Parameters.Source))
                        .ThenBy(t => t.Position))
                {
                    // We don't want to load, at the same time, images that have same key or same raw key at the same time
                    // This way we prevent concurrent downloads and benefit from caches

                    var rawKey = task.ImageLoadingTask.KeyRaw;
                    if (RunningTasks.ContainsKey(rawKey) || tasksToRun.ContainsKey(rawKey))
                        continue;

                    if (preloadOrUrlTasksCount != 0)
                    {
                        if (!task.ImageLoadingTask.Parameters.Preload &&
                            ((urlTasksCount == 0) || (task.ImageLoadingTask.Parameters.Source != ImageSource.Url)))
                            tasksToRun.Add(rawKey, task);
                    }
                    else
                    {
                        tasksToRun.Add(rawKey, task);
                    }

                    if (tasksToRun.Count == numberOfTasks)
                        break;
                }
            }

            if ((tasksToRun != null) && (tasksToRun.Count > 0))
                if (tasksToRun.Count == 1)
                {
                    await RunImageLoadingTaskAsync(tasksToRun.Values.First()).ConfigureAwait(false);
                }
                else
                {
                    var tasks = tasksToRun.Select(p => RunImageLoadingTaskAsync(p.Value));
                    await Task.WhenAll(tasks).ConfigureAwait(false);
                }
        }
Exemple #17
0
        /// <summary>
        /// Asynchronously runs <paramref name="delegateFunction"/> for every item in the given collection
        /// </summary>
        /// <typeparam name="T">Type of the collection item</typeparam>
        /// <param name="delegateFunction">The function to asynchronously run</param>
        /// <param name="collection">The collection to be enumerated</param>
        /// <exception cref="InvalidOperationException">Thrown if execution starts before the end of another operation</exception>
        public async Task RunForEach <T>(IEnumerable <T> collection, ForEachItemAsync <T> delegateFunction)
        {
            if (RunningTasks.Count > 0)
            {
                throw new InvalidOperationException(Properties.Resources.AsyncFor_ErrorNoConcurrentExecution);
            }

            var exceptions    = new List <Exception>();
            var taskItemQueue = new Queue <T>();

            foreach (var item in collection)
            {
                taskItemQueue.Enqueue(item);
            }

            TotalTasks = taskItemQueue.Count;

            // While there's either more tasks to start or while there's still tasks running
            while (taskItemQueue.Count > 0 || (taskItemQueue.Count == 0 && RunningTasks.Count > 0))
            {
                if ((BatchSize < 1 || RunningTasks.Count < BatchSize) && taskItemQueue.Count > 0)
                {
                    // We can run more tasks

                    // Get the next task item to run
                    var item = taskItemQueue.Dequeue(); // The item in Collection to process

                    // Start the task
                    var tTask = Task.Run(async() =>
                    {
                        await delegateFunction(item).ConfigureAwait(false);
                        IncrementCompletedTasks();
                    });

                    // Either wait for it or move on
                    if (RunSynchronously)
                    {
                        await tTask.ConfigureAwait(false);
                    }
                    else
                    {
                        RunningTasks.Add(tTask);
                    }
                }
                else
                {
                    if (RunningTasks.Count > 0)
                    {
                        // We can't start any more tasks, so we have to wait on one.
                        await Task.WhenAny(RunningTasks).ConfigureAwait(false);

                        // Remove completed tasks
                        for (var count = RunningTasks.Count - 1; count >= 0; count--)
                        {
                            if (RunningTasks[count].Exception != null)
                            {
                                var ex = RunningTasks[count].Exception;
                                if (ex is AggregateException aggregateEx && aggregateEx.InnerExceptions.Count == 1)
                                {
                                    exceptions.Add(aggregateEx.InnerExceptions.First());
                                }
                                else
                                {
                                    exceptions.Add(RunningTasks[count].Exception);
                                }

                                RunningTasks.RemoveAt(count);
                            }
                            else if (RunningTasks[count].IsCompleted)
                            {
                                RunningTasks.RemoveAt(count);
                            }
                        }
                    }
Exemple #18
0
        public override async Task ExecuteAsync(object parameter)
        {
            _shell.StatusText = $"Open Async task {Thread.CurrentThread.ManagedThreadId } of {RunningTasks.Count()} ";

            await Task.Delay(2000);

            _shell.StatusText = $"Completed Open Async task {Thread.CurrentThread.ManagedThreadId } of {RunningTasks.Count()} ";
        }
Exemple #19
0
        protected async Task TakeFromPendingTasksAndRunAsync()
        {
            if (PendingTasks.Count == 0)
            {
                return;
            }

            Dictionary <string, IImageLoaderTask>?tasksToRun = null;

            int urlTasksCount = 0;

            lock (_lock)
            {
                if (RunningTasks.Count >= MaxParallelTasks)
                {
                    urlTasksCount = RunningTasks.Count(v => v.Value != null && v.Value.ImageSource.Source is UriSource);

                    if (urlTasksCount == 0 || urlTasksCount != MaxParallelTasks)
                    {
                        return;
                    }

                    // Allow only half of MaxParallelTasks as additional allowed tasks when preloading occurs to prevent starvation
                    if (RunningTasks.Count - Math.Max(1, Math.Min(urlTasksCount, MaxParallelTasks / 2)) >= MaxParallelTasks)
                    {
                        return;
                    }
                }

                int numberOfTasks = MaxParallelTasks - RunningTasks.Count + Math.Min(urlTasksCount, MaxParallelTasks / 2);
                tasksToRun = new Dictionary <string, IImageLoaderTask>();

                while (tasksToRun.Count < numberOfTasks && PendingTasks.TryDequeue(out IImageLoaderTask? task))
                {
                    if (task == null || task.IsCancelled || task.IsCompleted)
                    {
                        continue;
                    }

                    // We don't want to load, at the same time, images that have same key or same raw key at the same time
                    // This way we prevent concurrent downloads and benefit from caches
                    string rawKey = task.KeyRaw;
                    if (RunningTasks.ContainsKey(rawKey) || tasksToRun.ContainsKey(rawKey))
                    {
                        SimilarTasks.Add(task);
                        continue;
                    }

                    if (urlTasksCount != 0)
                    {
                        if (!(task.ImageSource.Source is UriSource))
                        {
                            tasksToRun.Add(rawKey, task);
                        }
                        else
                        {
                            Enqueue(task);
                            break;
                        }
                    }
                    else
                    {
                        tasksToRun.Add(rawKey, task);
                    }
                }

                foreach (var item in tasksToRun)
                {
                    RunningTasks.Add(item.Key, item.Value);
                    Interlocked.Increment(ref _statsTotalRunning);
                }
            }

            if (tasksToRun != null && tasksToRun.Count > 0)
            {
                var tasks = tasksToRun.Select(async p =>
                {
                    await Task.Factory.StartNew(async() =>
                    {
                        try
                        {
                            await RunImageLoadingTaskAsync(p.Value).ConfigureAwait(false);
                        }
                        catch (Exception ex)
                        {
                            _imageLoader.Logger?.Error("TakeFromPendingTasksAndRun exception", ex);
                        }
                    }, CancellationToken.None, TaskCreationOptions.PreferFairness | TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler, TaskScheduler.Default).ConfigureAwait(false);
                });
                await Task.WhenAll(tasks).ConfigureAwait(false);
            }
        }
        protected async Task TakeFromPendingTasksAndRunAsync()
        {
            if (PendingTasks.Count == 0)
            {
                return;
            }

            Dictionary <string, IImageLoaderTask> tasksToRun = null;

            int preloadOrUrlTasksCount = 0;
            int urlTasksCount          = 0;
            int preloadTasksCount      = 0;

            lock (_lock)
            {
                if (RunningTasks.Count >= MaxParallelTasks)
                {
                    urlTasksCount          = RunningTasks.Count(v => v.Value != null && (!v.Value.Parameters.Preload && v.Value.Parameters.Source == ImageSource.Url));
                    preloadTasksCount      = RunningTasks.Count(v => v.Value != null && v.Value.Parameters.Preload);
                    preloadOrUrlTasksCount = preloadTasksCount + urlTasksCount;

                    if (preloadOrUrlTasksCount == 0 || preloadOrUrlTasksCount != MaxParallelTasks)
                    {
                        return;
                    }

                    // Allow only half of MaxParallelTasks as additional allowed tasks when preloading occurs to prevent starvation
                    if (RunningTasks.Count - Math.Max(1, Math.Min(preloadOrUrlTasksCount, MaxParallelTasks / 2)) >= MaxParallelTasks)
                    {
                        return;
                    }
                }

                int numberOfTasks = MaxParallelTasks - RunningTasks.Count + Math.Min(preloadOrUrlTasksCount, MaxParallelTasks / 2);
                tasksToRun = new Dictionary <string, IImageLoaderTask>();
                IImageLoaderTask task = null;

                while (tasksToRun.Count < numberOfTasks && PendingTasks.TryDequeue(out task))
                {
                    if (task == null || task.IsCancelled || task.IsCompleted)
                    {
                        continue;
                    }

                    // We don't want to load, at the same time, images that have same key or same raw key at the same time
                    // This way we prevent concurrent downloads and benefit from caches
                    string rawKey = task.KeyRaw;
                    if (RunningTasks.ContainsKey(rawKey) || tasksToRun.ContainsKey(rawKey))
                    {
                        SimilarTasks.Add(task);
                        continue;
                    }

                    if (preloadOrUrlTasksCount != 0)
                    {
                        if (!task.Parameters.Preload && (urlTasksCount == 0 || task.Parameters.Source != ImageSource.Url))
                        {
                            tasksToRun.Add(rawKey, task);
                        }
                        else
                        {
                            Enqueue(task);
                            break;
                        }
                    }
                    else
                    {
                        tasksToRun.Add(rawKey, task);
                    }
                }

                foreach (var item in tasksToRun)
                {
                    RunningTasks.Add(item.Key, item.Value);
                    Interlocked.Increment(ref _statsTotalRunning);
                }
            }

            if (tasksToRun != null && tasksToRun.Count > 0)
            {
                var tasks = tasksToRun.Select(p => RunImageLoadingTaskAsync(p.Value));
                await Task.WhenAll(tasks).ConfigureAwait(false);
            }
        }
Exemple #21
0
    static void _Main(string[] args)
    {
        //Debug_.PrintLoadedAssemblies(true, !true);

        AppDomain.CurrentDomain.UnhandledException += _UnhandledException;
        process.ThisThreadSetComApartment_(ApartmentState.STA);
        process.thisProcessCultureIsInvariant = true;
        DebugTraceListener.Setup(usePrint: true);
        Directory.SetCurrentDirectory(folders.ThisApp);                     //it is c:\windows\system32 when restarted as admin
        Api.SetSearchPathMode(Api.BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE); //let SearchPath search in current directory after system directories
        Api.SetErrorMode(Api.GetErrorMode() | Api.SEM_FAILCRITICALERRORS);  //disable some error message boxes, eg when removable media not found; MSDN recommends too.
        _SetThisAppDocuments();

        if (CommandLine.ProgramStarted2(args))
        {
            return;
        }

        PrintServer = new print.Server(true)
        {
            NoNewline = true
        };
        PrintServer.Start();
#if TRACE
        print.qm2.use = !true;
        //timer.after(1, _ => perf.nw());
#endif

        perf.next('o');
        Settings = AppSettings.Load();         //the slowest part, >50 ms. Loads many dlls.
        //Debug_.PrintLoadedAssemblies(true, !true);
        perf.next('s');
        UserGuid = Settings.user; if (UserGuid == null)
        {
            Settings.user = UserGuid = Guid.NewGuid().ToString();
        }

        AssemblyLoadContext.Default.Resolving             += _Assembly_Resolving;
        AssemblyLoadContext.Default.ResolvingUnmanagedDll += _UnmanagedDll_Resolving;

        Tasks = new RunningTasks();
        perf.next('t');

        script.editor.IconNameToXaml_ = (s, what) => DIcons.GetIconString(s, what);
        FilesModel.LoadWorkspace(CommandLine.WorkspaceDirectory);
        perf.next('W');
        CommandLine.ProgramLoaded();
        perf.next('c');
        Loaded = EProgramState.LoadedWorkspace;

        timer.every(1000, t => _TimerProc(t));
        //note: timer can make Process Hacker/Explorer show CPU usage, even if we do nothing. Eg 0.02 if 250, 0.01 if 500, <0.01 if 1000.
        //Timer1s += () => print.it("1 s");
        //Timer1sOr025s += () => print.it("0.25 s");

        TrayIcon.Update_();
        perf.next('i');

        _app = new() {
            ShutdownMode = ShutdownMode.OnExplicitShutdown             //will set OnMainWindowClose when creating main window. If now, would exit if a startup script shows/closes a WPF window.
        };
        _app.Dispatcher.InvokeAsync(() => Model.RunStartupScripts());
        if (!Settings.runHidden || CommandLine.StartVisible)
        {
            _app.Dispatcher.Invoke(() => ShowWindow());
        }
        try {
            _app.Run();
            //Hidden app should start as fast as possible, because usually starts with Windows.
            //Tested with native message loop. Faster by 70 ms (240 vs 310 without the .NET startup time).
            //	But then problems. Eg cannot auto-create main window synchronously, because need to exit native loop and start WPF loop.
        }
        finally {
            Loaded = EProgramState.Unloading;
            var fm = Model; Model = null;
            fm.Dispose();             //stops tasks etc
            Loaded = EProgramState.Unloaded;

            PrintServer.Stop();
        }
    }