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
{
File.WriteAllText(this.cacheFile.FullName, JsonConvert.SerializeObject(this.textCache));
Log.Information("Saved cache to {CachePath}", this.cacheFile);
}
catch (Exception e)
{

View file

@ -334,7 +334,8 @@ internal partial class PluginManager : IDisposable, IServiceType
/// <remarks>
/// This should only be called during Dalamud startup.
/// </remarks>
public void LoadAllPlugins()
/// <returns>The task.</returns>
public async Task LoadAllPlugins()
{
var pluginDefs = new List<PluginDef>();
var devPluginDefs = new List<PluginDef>();
@ -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<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)
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 =============");
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<Framework>
.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<Framework>.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<InterfaceManager.InterfaceManagerWithScene>
.GetAsync()
.ContinueWith(
_ => Service<Framework>.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<InterfaceManager.InterfaceManagerWithScene>.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<SigScanner>.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<SigScanner>.GetAsync().ConfigureAwait(false);
this.PluginsReady = true;
this.NotifyInstalledPluginsChanged();
sigScanner.Save();
},
tokenSource.Token);
}
/// <summary>

View file

@ -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!");
}