Unindent LoadAllPlugins by converting it to an async function, fixing task wait problems meanwhile (#966)

This commit is contained in:
kizer 2022-08-23 21:03:22 +09:00 committed by GitHub
parent 039dc1b447
commit c46cb36549
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 106 deletions

View file

@ -376,6 +376,7 @@ namespace Dalamud.Game
try try
{ {
File.WriteAllText(this.cacheFile.FullName, JsonConvert.SerializeObject(this.textCache)); File.WriteAllText(this.cacheFile.FullName, JsonConvert.SerializeObject(this.textCache));
Log.Information("Saved cache to {CachePath}", this.cacheFile);
} }
catch (Exception e) catch (Exception e)
{ {

View file

@ -334,7 +334,8 @@ internal partial class PluginManager : IDisposable, IServiceType
/// <remarks> /// <remarks>
/// This should only be called during Dalamud startup. /// This should only be called during Dalamud startup.
/// </remarks> /// </remarks>
public void LoadAllPlugins() /// <returns>The task.</returns>
public async Task LoadAllPlugins()
{ {
var pluginDefs = new List<PluginDef>(); var pluginDefs = new List<PluginDef>();
var devPluginDefs = new List<PluginDef>(); var devPluginDefs = new List<PluginDef>();
@ -405,8 +406,9 @@ internal partial class PluginManager : IDisposable, IServiceType
// Dev plugins should load first. // Dev plugins should load first.
pluginDefs.InsertRange(0, devPluginDefs); 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")) using (Timings.Start($"{pluginDef.DllFile.Name}: {logPrefix}Boot"))
{ {
try try
@ -429,24 +431,30 @@ internal partial class PluginManager : IDisposable, IServiceType
} }
} }
async Task LoadPluginsSync(string logPrefix, IEnumerable<PluginDef> pluginDefsList) async Task LoadPluginsSync(string logPrefix, IEnumerable<PluginDef> pluginDefsList, CancellationToken token)
{ {
Log.Information("============= LoadPluginsSync START ============="); Log.Information($"============= LoadPluginsSync({logPrefix}) START =============");
foreach (var pluginDef in pluginDefsList) 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<PluginDef> pluginDefsList) async Task LoadPluginsAsync(string logPrefix, IEnumerable<PluginDef> pluginDefsList, CancellationToken token)
{ {
Log.Information("============= LoadPluginsAsync START ============="); Log.Information($"============= LoadPluginsAsync({logPrefix}) START =============");
return Task.WhenAll(
await Task.WhenAll(
pluginDefsList pluginDefsList
.Select(pluginDef => Task.Run(Timings.AttachTimingHandle( .Select(pluginDef =>
() => LoadPluginOnBoot(logPrefix, pluginDef)))) Task.Run(
.ToArray()).ContinueWith(t => Log.Information($"============= LoadPluginsAsync END {t.IsCompletedSuccessfully} =============")); 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(); 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)); var tokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(5));
// Load plugins that can be loaded anytime // Load plugins that can be loaded anytime
LoadPluginsSync( await LoadPluginsSync(
"AnytimeSync", "AnytimeSync",
syncPlugins.Where(def => def.Manifest?.LoadRequiredState == 2)).GetAwaiter().GetResult(); syncPlugins.Where(def => def.Manifest?.LoadRequiredState == 2),
loadTasks.Add( tokenSource.Token);
Task.Run( loadTasks.Add(LoadPluginsAsync(
() => LoadPluginsAsync( "AnytimeAsync",
"AnytimeAsync", asyncPlugins.Where(def => def.Manifest?.LoadRequiredState == 2),
asyncPlugins.Where(def => def.Manifest?.LoadRequiredState == 2)), tokenSource.Token));
tokenSource.Token));
// Load plugins that want to be loaded during Framework.Tick // Pass the rest of plugin loading to another thread(task)
loadTasks.Add( _ = Task.Run(
Task.Run( async () =>
() => Service<Framework> {
.GetAsync() // Load plugins that want to be loaded during Framework.Tick
.ContinueWith( var framework = await Service<Framework>.GetAsync().ConfigureAwait(false);
x => x.Result.RunOnTick( await framework.RunOnTick(
() => LoadPluginsSync( () => LoadPluginsSync(
"FrameworkTickSync", "FrameworkTickSync",
syncPlugins.Where(def => def.Manifest?.LoadRequiredState == 1)), syncPlugins.Where(def => def.Manifest?.LoadRequiredState == 1),
cancellationToken: tokenSource.Token), tokenSource.Token),
tokenSource.Token, cancellationToken: tokenSource.Token).ConfigureAwait(false);
TaskContinuationOptions.RunContinuationsAsynchronously, loadTasks.Add(LoadPluginsAsync(
TaskScheduler.Default) "FrameworkTickAsync",
.Unwrap() asyncPlugins.Where(def => def.Manifest?.LoadRequiredState == 1),
.ContinueWith( tokenSource.Token));
_ => LoadPluginsAsync(
"FrameworkTickAsync",
asyncPlugins.Where(def => def.Manifest?.LoadRequiredState == 1)),
tokenSource.Token,
TaskContinuationOptions.RunContinuationsAsynchronously,
TaskScheduler.Default)
.Unwrap(),
tokenSource.Token));
// Load plugins that want to be loaded during Framework.Tick, when drawing facilities are available // Load plugins that want to be loaded during Framework.Tick, when drawing facilities are available
loadTasks.Add( _ = await Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync().ConfigureAwait(false);
Task.Run( await framework.RunOnTick(
() => Service<InterfaceManager.InterfaceManagerWithScene> () => LoadPluginsSync(
.GetAsync() "DrawAvailableSync",
.ContinueWith( syncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null),
_ => Service<Framework>.Get().RunOnTick( tokenSource.Token),
() => LoadPluginsSync( cancellationToken: tokenSource.Token);
"DrawAvailableSync", loadTasks.Add(LoadPluginsAsync(
syncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null)),
cancellationToken: tokenSource.Token),
tokenSource.Token)
.Unwrap()
.ContinueWith(
_ => LoadPluginsAsync(
"DrawAvailableAsync", "DrawAvailableAsync",
asyncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null)), asyncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null),
tokenSource.Token) tokenSource.Token));
.Unwrap(),
tokenSource.Token));
// Save signatures when all plugins are done loading, successful or not. // Save signatures when all plugins are done loading, successful or not.
_ = Task try
.WhenAll(loadTasks)
.ContinueWith(
t =>
{ {
Log.Information("Task.WhenAll continuing"); await Task.WhenAll(loadTasks).ConfigureAwait(false);
if (!t.IsCompletedSuccessfully) Log.Information("Loaded plugins on boot");
{ }
foreach (var loadTask in loadTasks) catch (Exception e)
{
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<SigScanner>.GetAsync();
},
tokenSource.Token,
TaskContinuationOptions.RunContinuationsAsynchronously,
TaskScheduler.Default)
.Unwrap()
.ContinueWith(
sigScannerTask =>
{ {
Log.Information("sigScannerTask continuing"); Log.Error(e, "Failed to load at least one plugin");
}
this.PluginsReady = true; var sigScanner = await Service<SigScanner>.GetAsync().ConfigureAwait(false);
this.NotifyInstalledPluginsChanged(); this.PluginsReady = true;
this.NotifyInstalledPluginsChanged();
sigScannerTask.Result.Save(); sigScanner.Save();
}, },
tokenSource.Token, tokenSource.Token);
TaskContinuationOptions.RunContinuationsAsynchronously,
TaskScheduler.Default)
.ConfigureAwait(false);
} }
/// <summary> /// <summary>

View file

@ -35,7 +35,7 @@ public class StartupPluginLoader : IServiceType
using (Timings.Start("PM Load Sync Plugins")) using (Timings.Start("PM Load Sync Plugins"))
{ {
pluginManager.LoadAllPlugins(); pluginManager.LoadAllPlugins().Wait();
Log.Information("[T3] PML OK!"); Log.Information("[T3] PML OK!");
} }