diff --git a/Dalamud/Game/SigScanner.cs b/Dalamud/Game/SigScanner.cs index ffc7188f4..9f5a8e33c 100644 --- a/Dalamud/Game/SigScanner.cs +++ b/Dalamud/Game/SigScanner.cs @@ -376,6 +376,7 @@ namespace Dalamud.Game try { File.WriteAllText(this.cacheFile.FullName, JsonConvert.SerializeObject(this.textCache)); + Log.Information("Saved cache to {CachePath}", this.cacheFile); } catch (Exception e) { diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs index 52059da7e..3d66c89ff 100644 --- a/Dalamud/Plugin/Internal/PluginManager.cs +++ b/Dalamud/Plugin/Internal/PluginManager.cs @@ -334,7 +334,8 @@ internal partial class PluginManager : IDisposable, IServiceType /// /// This should only be called during Dalamud startup. /// - public void LoadAllPlugins() + /// The task. + public async Task LoadAllPlugins() { var pluginDefs = new List(); var devPluginDefs = new List(); @@ -405,8 +406,9 @@ internal partial class PluginManager : IDisposable, IServiceType // Dev plugins should load first. pluginDefs.InsertRange(0, devPluginDefs); - async Task LoadPluginOnBoot(string logPrefix, PluginDef pluginDef) + async Task LoadPluginOnBoot(string logPrefix, PluginDef pluginDef, CancellationToken token) { + token.ThrowIfCancellationRequested(); using (Timings.Start($"{pluginDef.DllFile.Name}: {logPrefix}Boot")) { try @@ -429,24 +431,30 @@ internal partial class PluginManager : IDisposable, IServiceType } } - async Task LoadPluginsSync(string logPrefix, IEnumerable pluginDefsList) + async Task LoadPluginsSync(string logPrefix, IEnumerable pluginDefsList, CancellationToken token) { - Log.Information("============= LoadPluginsSync START ============="); + Log.Information($"============= LoadPluginsSync({logPrefix}) START ============="); foreach (var pluginDef in pluginDefsList) - await LoadPluginOnBoot(logPrefix, pluginDef); + await LoadPluginOnBoot(logPrefix, pluginDef, token).ConfigureAwait(false); - Log.Information("============= LoadPluginsSync END ============="); + Log.Information($"============= LoadPluginsSync({logPrefix}) END ============="); } - Task LoadPluginsAsync(string logPrefix, IEnumerable pluginDefsList) + async Task LoadPluginsAsync(string logPrefix, IEnumerable pluginDefsList, CancellationToken token) { - Log.Information("============= LoadPluginsAsync START ============="); - return Task.WhenAll( + Log.Information($"============= LoadPluginsAsync({logPrefix}) START ============="); + + await Task.WhenAll( pluginDefsList - .Select(pluginDef => Task.Run(Timings.AttachTimingHandle( - () => LoadPluginOnBoot(logPrefix, pluginDef)))) - .ToArray()).ContinueWith(t => Log.Information($"============= LoadPluginsAsync END {t.IsCompletedSuccessfully} =============")); + .Select(pluginDef => + Task.Run( + Timings.AttachTimingHandle( + () => LoadPluginOnBoot(logPrefix, pluginDef, token)), + token)) + .ToArray()).ConfigureAwait(false); + + Log.Information($"============= LoadPluginsAsync({logPrefix}) END ============="); } var syncPlugins = pluginDefs.Where(def => def.Manifest?.LoadSync == true).ToList(); @@ -456,107 +464,62 @@ internal partial class PluginManager : IDisposable, IServiceType var tokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(5)); // Load plugins that can be loaded anytime - LoadPluginsSync( + await LoadPluginsSync( "AnytimeSync", - syncPlugins.Where(def => def.Manifest?.LoadRequiredState == 2)).GetAwaiter().GetResult(); - loadTasks.Add( - Task.Run( - () => LoadPluginsAsync( - "AnytimeAsync", - asyncPlugins.Where(def => def.Manifest?.LoadRequiredState == 2)), - tokenSource.Token)); + syncPlugins.Where(def => def.Manifest?.LoadRequiredState == 2), + tokenSource.Token); + loadTasks.Add(LoadPluginsAsync( + "AnytimeAsync", + asyncPlugins.Where(def => def.Manifest?.LoadRequiredState == 2), + tokenSource.Token)); - // Load plugins that want to be loaded during Framework.Tick - loadTasks.Add( - Task.Run( - () => Service - .GetAsync() - .ContinueWith( - x => x.Result.RunOnTick( - () => LoadPluginsSync( - "FrameworkTickSync", - syncPlugins.Where(def => def.Manifest?.LoadRequiredState == 1)), - cancellationToken: tokenSource.Token), - tokenSource.Token, - TaskContinuationOptions.RunContinuationsAsynchronously, - TaskScheduler.Default) - .Unwrap() - .ContinueWith( - _ => LoadPluginsAsync( - "FrameworkTickAsync", - asyncPlugins.Where(def => def.Manifest?.LoadRequiredState == 1)), - tokenSource.Token, - TaskContinuationOptions.RunContinuationsAsynchronously, - TaskScheduler.Default) - .Unwrap(), - tokenSource.Token)); + // Pass the rest of plugin loading to another thread(task) + _ = Task.Run( + async () => + { + // Load plugins that want to be loaded during Framework.Tick + var framework = await Service.GetAsync().ConfigureAwait(false); + await framework.RunOnTick( + () => LoadPluginsSync( + "FrameworkTickSync", + syncPlugins.Where(def => def.Manifest?.LoadRequiredState == 1), + tokenSource.Token), + cancellationToken: tokenSource.Token).ConfigureAwait(false); + loadTasks.Add(LoadPluginsAsync( + "FrameworkTickAsync", + asyncPlugins.Where(def => def.Manifest?.LoadRequiredState == 1), + tokenSource.Token)); - // Load plugins that want to be loaded during Framework.Tick, when drawing facilities are available - loadTasks.Add( - Task.Run( - () => Service - .GetAsync() - .ContinueWith( - _ => Service.Get().RunOnTick( - () => LoadPluginsSync( - "DrawAvailableSync", - syncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null)), - cancellationToken: tokenSource.Token), - tokenSource.Token) - .Unwrap() - .ContinueWith( - _ => LoadPluginsAsync( + // Load plugins that want to be loaded during Framework.Tick, when drawing facilities are available + _ = await Service.GetAsync().ConfigureAwait(false); + await framework.RunOnTick( + () => LoadPluginsSync( + "DrawAvailableSync", + syncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null), + tokenSource.Token), + cancellationToken: tokenSource.Token); + loadTasks.Add(LoadPluginsAsync( "DrawAvailableAsync", - asyncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null)), - tokenSource.Token) - .Unwrap(), - tokenSource.Token)); + asyncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null), + tokenSource.Token)); - // Save signatures when all plugins are done loading, successful or not. - _ = Task - .WhenAll(loadTasks) - .ContinueWith( - t => + // Save signatures when all plugins are done loading, successful or not. + try { - Log.Information("Task.WhenAll continuing"); - if (!t.IsCompletedSuccessfully) - { - foreach (var loadTask in loadTasks) - { - if (!loadTask.IsCompletedSuccessfully) - { - if (loadTask.Exception != null) - { - Log.Error(loadTask.Exception, " => Exception during load"); - } - else - { - Log.Error($" => Task failed, canceled: {t.IsCanceled} faulted: {t.IsFaulted}"); - } - } - } - } - - return Service.GetAsync(); - }, - tokenSource.Token, - TaskContinuationOptions.RunContinuationsAsynchronously, - TaskScheduler.Default) - .Unwrap() - .ContinueWith( - sigScannerTask => + await Task.WhenAll(loadTasks).ConfigureAwait(false); + Log.Information("Loaded plugins on boot"); + } + catch (Exception e) { - Log.Information("sigScannerTask continuing"); + Log.Error(e, "Failed to load at least one plugin"); + } - this.PluginsReady = true; - this.NotifyInstalledPluginsChanged(); - - sigScannerTask.Result.Save(); - }, - tokenSource.Token, - TaskContinuationOptions.RunContinuationsAsynchronously, - TaskScheduler.Default) - .ConfigureAwait(false); + var sigScanner = await Service.GetAsync().ConfigureAwait(false); + this.PluginsReady = true; + this.NotifyInstalledPluginsChanged(); + sigScanner.Save(); + }, + tokenSource.Token); } /// diff --git a/Dalamud/Plugin/Internal/StartupPluginLoader.cs b/Dalamud/Plugin/Internal/StartupPluginLoader.cs index a95e25ace..3c1b80363 100644 --- a/Dalamud/Plugin/Internal/StartupPluginLoader.cs +++ b/Dalamud/Plugin/Internal/StartupPluginLoader.cs @@ -35,7 +35,7 @@ public class StartupPluginLoader : IServiceType using (Timings.Start("PM Load Sync Plugins")) { - pluginManager.LoadAllPlugins(); + pluginManager.LoadAllPlugins().Wait(); Log.Information("[T3] PML OK!"); }