mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Merge branch 'net5' of ssh://github.com/goatcorp/Dalamud into net5
This commit is contained in:
commit
29dee596c4
19 changed files with 402 additions and 246 deletions
|
|
@ -1,7 +1,5 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -9,24 +7,15 @@ using Dalamud.Configuration.Internal;
|
|||
using Dalamud.Data;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Game.Gui.Internal;
|
||||
using Dalamud.Game.Internal;
|
||||
using Dalamud.Game.Network;
|
||||
using Dalamud.Game.Network.Internal;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Hooking.Internal;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Plugin.Internal;
|
||||
using Dalamud.Plugin.Ipc.Internal;
|
||||
using Dalamud.Support;
|
||||
using Dalamud.Utility;
|
||||
using Dalamud.Utility.Timing;
|
||||
using Serilog;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
|
|
@ -74,9 +63,7 @@ namespace Dalamud
|
|||
|
||||
SerilogEventSink.Instance.LogLine += SerilogOnLogLine;
|
||||
|
||||
Service<Dalamud>.Provide(this);
|
||||
Service<DalamudStartInfo>.Provide(info);
|
||||
Service<DalamudConfiguration>.Provide(configuration);
|
||||
ServiceManager.InitializeProvidedServicesAndClientStructs(this, info, configuration);
|
||||
|
||||
if (!configuration.IsResumeGameAfterPluginLoad)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ namespace Dalamud.Game.Gui.Dtr
|
|||
private uint runningNodeIds = BaseNodeId;
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private DtrBar(DalamudConfiguration configuration)
|
||||
private DtrBar(DalamudConfiguration configuration, Framework framework)
|
||||
{
|
||||
Service<Framework>.Get().Update += this.Update;
|
||||
framework.Update += this.Update;
|
||||
|
||||
configuration.DtrOrder ??= new List<string>();
|
||||
configuration.DtrIgnore ??= new List<string>();
|
||||
|
|
|
|||
|
|
@ -423,13 +423,13 @@ namespace Dalamud.Game.Gui
|
|||
/// </summary>
|
||||
public void Enable()
|
||||
{
|
||||
Service<ChatGui>.Get().Enable();
|
||||
Service<ToastGui>.Get().Enable();
|
||||
Service<FlyTextGui>.Get().Enable();
|
||||
Service<PartyFinderGui>.Get().Enable();
|
||||
Service<ChatGui>.GetAsync().ContinueWith(x => x.Result.Enable());
|
||||
Service<ToastGui>.GetAsync().ContinueWith(x => x.Result.Enable());
|
||||
Service<FlyTextGui>.GetAsync().ContinueWith(x => x.Result.Enable());
|
||||
Service<PartyFinderGui>.GetAsync().ContinueWith(x => x.Result.Enable());
|
||||
|
||||
if (EnvironmentConfiguration.DalamudDoContextMenu)
|
||||
Service<ContextMenu>.Get().Enable();
|
||||
Service<ContextMenu>.GetAsync().ContinueWith(x => x.Result.Enable());
|
||||
|
||||
this.setGlobalBgmHook.Enable();
|
||||
this.handleItemHoverHook.Enable();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ using System.Runtime.InteropServices;
|
|||
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Utility.Timing;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
|
||||
|
|
@ -20,7 +19,6 @@ namespace Dalamud.Game
|
|||
/// </summary>
|
||||
[PluginInterface]
|
||||
[InterfaceVersion("1.0")]
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
public class SigScanner : IDisposable
|
||||
{
|
||||
private readonly FileInfo? cacheFile;
|
||||
|
|
@ -30,38 +28,6 @@ namespace Dalamud.Game
|
|||
|
||||
private ConcurrentDictionary<string, long>? textCache;
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private SigScanner(DalamudStartInfo startInfo)
|
||||
{
|
||||
// Initialize the process information.
|
||||
var cacheDir = new DirectoryInfo(Path.Combine(startInfo.WorkingDirectory!, "cachedSigs"));
|
||||
if (!cacheDir.Exists)
|
||||
cacheDir.Create();
|
||||
|
||||
this.cacheFile = new FileInfo(Path.Combine(cacheDir.FullName, $"{startInfo.GameVersion}.json"));
|
||||
this.Module = Process.GetCurrentProcess().MainModule!;
|
||||
this.Is32BitProcess = !Environment.Is64BitProcess;
|
||||
this.IsCopy = true;
|
||||
|
||||
// Limit the search space to .text section.
|
||||
this.SetupSearchSpace(this.Module);
|
||||
|
||||
if (this.IsCopy)
|
||||
this.SetupCopiedSegments();
|
||||
|
||||
Log.Verbose($"Module base: 0x{this.TextSectionBase.ToInt64():X}");
|
||||
Log.Verbose($"Module size: 0x{this.TextSectionSize:X}");
|
||||
|
||||
if (this.cacheFile != null)
|
||||
this.Load();
|
||||
|
||||
// Initialize FFXIVClientStructs function resolver
|
||||
using (Timings.Start("CS Resolver Init"))
|
||||
{
|
||||
FFXIVClientStructs.Resolver.InitializeParallel(new FileInfo(Path.Combine(cacheDir.FullName, $"{startInfo.GameVersion}_cs.json")));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SigScanner"/> class using the main module of the current process.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ namespace Dalamud.Interface.GameFonts
|
|||
.Select(x => x.Glyphs.Select(y => y.TextureFileIndex).Max())
|
||||
.Max())
|
||||
.Select(x => dataManager.GetFile<TexFile>($"common/font/font{x}.tex")!)
|
||||
.Select(x => new Task<byte[]>(() => x.ImageData!))
|
||||
.Select(x => new Task<byte[]>(Timings.AttachTimingHandle(() => x.ImageData!)))
|
||||
.ToArray();
|
||||
foreach (var task in texTasks)
|
||||
task.Start();
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ namespace Dalamud.Interface.Internal
|
|||
|
||||
try
|
||||
{
|
||||
Service<PluginManager>.Get().ReloadAllPlugins();
|
||||
Service<PluginManager>.Get().ReloadAllPluginsAsync().Wait();
|
||||
chatGui.Print("OK");
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace Dalamud.Interface.Internal
|
|||
/// <summary>
|
||||
/// This plugin implements all of the Dalamud interface separately, to allow for reloading of the interface and rapid prototyping.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
[ServiceManager.AfterDrawingEarlyLoadedService]
|
||||
internal class DalamudInterface : IDisposable
|
||||
{
|
||||
private static readonly ModuleLog Log = new("DUI");
|
||||
|
|
@ -714,7 +714,7 @@ namespace Dalamud.Interface.Internal
|
|||
{
|
||||
try
|
||||
{
|
||||
pluginManager.ReloadAllPlugins();
|
||||
pluginManager.ReloadAllPluginsAsync().Wait();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -60,8 +60,6 @@ namespace Dalamud.Interface.Internal.Windows
|
|||
var dalamud = Service<Dalamud>.Get();
|
||||
var interfaceManager = Service<InterfaceManager>.Get();
|
||||
|
||||
interfaceManager.SceneInitializeTask.Wait();
|
||||
|
||||
this.DefaultIcon = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "defaultIcon.png"))!;
|
||||
this.TroubleIcon = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "troubleIcon.png"))!;
|
||||
this.UpdateIcon = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "updateIcon.png"))!;
|
||||
|
|
|
|||
|
|
@ -1735,7 +1735,7 @@ namespace Dalamud.Interface.Internal.Windows.PluginInstaller
|
|||
if (!enableTask.Result)
|
||||
return;
|
||||
|
||||
var loadTask = Task.Run(() => plugin.Load(PluginLoadReason.Installer))
|
||||
var loadTask = Task.Run(() => plugin.LoadAsync(PluginLoadReason.Installer))
|
||||
.ContinueWith(this.DisplayErrorContinuation, Locs.ErrorModal_LoadFail(plugin.Name));
|
||||
|
||||
loadTask.Wait();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface.Colors;
|
||||
|
|
@ -13,25 +14,35 @@ public class ProfilerWindow : Window
|
|||
{
|
||||
private double min;
|
||||
private double max;
|
||||
private List<List<Tuple<double, double>>> occupied = new();
|
||||
|
||||
public ProfilerWindow() : base("Profiler", forceMainWindow: true) { }
|
||||
|
||||
public override void OnOpen()
|
||||
{
|
||||
this.min = Timings.AllTimings.Min(x => x.StartTime);
|
||||
this.max = Timings.AllTimings.Max(x => x.EndTime);
|
||||
this.min = Timings.AllTimings.Keys.Min(x => x.StartTime);
|
||||
this.max = Timings.AllTimings.Keys.Max(x => x.EndTime);
|
||||
}
|
||||
|
||||
private class RectInfo
|
||||
{
|
||||
internal TimingHandle Timing;
|
||||
internal Vector2 MinPos;
|
||||
internal Vector2 MaxPos;
|
||||
internal Vector4 RectColor;
|
||||
internal bool Hover;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Draw()
|
||||
{
|
||||
var width = ImGui.GetWindowWidth();
|
||||
var actualMin = Timings.AllTimings.Min(x => x.StartTime);
|
||||
var actualMax = Timings.AllTimings.Max(x => x.EndTime);
|
||||
var actualMin = Timings.AllTimings.Keys.Min(x => x.StartTime);
|
||||
var actualMax = Timings.AllTimings.Keys.Max(x => x.EndTime);
|
||||
|
||||
ImGui.Text("Timings");
|
||||
|
||||
const int childHeight = 300;
|
||||
var childHeight = Math.Max(300, 20 * (2 + this.occupied.Count));
|
||||
|
||||
if (ImGui.BeginChild("Timings", new Vector2(0, childHeight), true))
|
||||
{
|
||||
|
|
@ -68,48 +79,113 @@ public class ProfilerWindow : Window
|
|||
|
||||
uint maxRectDept = 0;
|
||||
|
||||
foreach (var timingHandle in Timings.AllTimings)
|
||||
foreach (var l in this.occupied)
|
||||
l.Clear();
|
||||
|
||||
var parentDepthDict = new Dictionary<long, int>();
|
||||
var rects = new Dictionary<long, RectInfo>();
|
||||
var mousePos = ImGui.GetMousePos();
|
||||
foreach (var timingHandle in Timings.AllTimings.Keys)
|
||||
{
|
||||
var startX = (timingHandle.StartTime - this.min) / (this.max - this.min) * width;
|
||||
var endX = (timingHandle.EndTime - this.min) / (this.max - this.min) * width;
|
||||
var depth = timingHandle.IdChain.Length < 2 ? 0 : parentDepthDict.GetValueOrDefault(timingHandle.IdChain[^2]);
|
||||
for (; depth < this.occupied.Count; depth++)
|
||||
{
|
||||
var acceptable = true;
|
||||
foreach (var (x1, x2) in this.occupied[depth])
|
||||
{
|
||||
if (x2 <= timingHandle.StartTime || x1 >= timingHandle.EndTime)
|
||||
continue;
|
||||
acceptable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (acceptable)
|
||||
break;
|
||||
}
|
||||
|
||||
if (depth == this.occupied.Count)
|
||||
this.occupied.Add(new());
|
||||
this.occupied[depth].Add(Tuple.Create(timingHandle.StartTime, timingHandle.EndTime));
|
||||
parentDepthDict[timingHandle.Id] = depth;
|
||||
|
||||
startX = Math.Max(startX, 0);
|
||||
endX = Math.Max(endX, 0);
|
||||
|
||||
var rectColor = timingHandle.IsMainThread ? ImGuiColors.ParsedBlue : ImGuiColors.ParsedPurple;
|
||||
rectColor.X -= timingHandle.Depth * 0.12f;
|
||||
rectColor.Y -= timingHandle.Depth * 0.12f;
|
||||
rectColor.Z -= timingHandle.Depth * 0.12f;
|
||||
Vector4 rectColor;
|
||||
if (this.occupied[depth].Count % 2 == 0)
|
||||
rectColor = timingHandle.IsMainThread ? ImGuiColors.ParsedBlue : ImGuiColors.ParsedOrange;
|
||||
else
|
||||
rectColor = timingHandle.IsMainThread ? ImGuiColors.ParsedPurple : ImGuiColors.ParsedGold;
|
||||
rectColor.X -= timingHandle.IdChain.Length * 0.12f;
|
||||
rectColor.Y -= timingHandle.IdChain.Length * 0.12f;
|
||||
rectColor.Z -= timingHandle.IdChain.Length * 0.12f;
|
||||
|
||||
if (maxRectDept < timingHandle.Depth)
|
||||
maxRectDept = timingHandle.Depth;
|
||||
if (maxRectDept < depth)
|
||||
maxRectDept = (uint)depth;
|
||||
|
||||
if (startX == endX)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var minPos = pos + new Vector2((uint)startX, 20 * timingHandle.Depth);
|
||||
var maxPos = pos + new Vector2((uint)endX, 20 * (timingHandle.Depth + 1));
|
||||
var minPos = pos + new Vector2((uint)startX, 20 * depth);
|
||||
var maxPos = pos + new Vector2((uint)endX, 20 * (depth + 1));
|
||||
|
||||
rects[timingHandle.Id] = new RectInfo
|
||||
{
|
||||
Hover = mousePos.X >= minPos.X && mousePos.X < maxPos.X &&
|
||||
mousePos.Y >= minPos.Y && mousePos.Y < maxPos.Y,
|
||||
Timing = timingHandle,
|
||||
MinPos = minPos,
|
||||
MaxPos = maxPos,
|
||||
RectColor = rectColor,
|
||||
};
|
||||
}
|
||||
|
||||
foreach (var hoveredItem in rects.Values.Where(x => x.Hover))
|
||||
{
|
||||
foreach (var rectInfo in rects.Values)
|
||||
{
|
||||
if (rectInfo == hoveredItem)
|
||||
rectInfo.RectColor = new Vector4(255, 255, 255, 255);
|
||||
else if (rectInfo.Timing.IdChain.Contains(hoveredItem.Timing.Id))
|
||||
rectInfo.RectColor = ImGuiColors.ParsedGreen;
|
||||
else if (hoveredItem.Timing.IdChain.Contains(rectInfo.Timing.Id))
|
||||
rectInfo.RectColor = ImGuiColors.ParsedPink;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var rectInfo in rects.Values)
|
||||
{
|
||||
ImGui.GetWindowDrawList().AddRectFilled(
|
||||
minPos,
|
||||
maxPos,
|
||||
ImGui.GetColorU32(rectColor));
|
||||
rectInfo.MinPos,
|
||||
rectInfo.MaxPos,
|
||||
ImGui.GetColorU32(rectInfo.RectColor));
|
||||
|
||||
ImGui.GetWindowDrawList().AddTextClippedEx(minPos, maxPos, timingHandle.Name, null, Vector2.Zero, null);
|
||||
if (rectInfo.Hover)
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0, 0, 0, 255));
|
||||
ImGui.GetWindowDrawList().AddTextClippedEx(
|
||||
rectInfo.MinPos,
|
||||
rectInfo.MaxPos,
|
||||
rectInfo.Timing.Name,
|
||||
null,
|
||||
Vector2.Zero,
|
||||
null);
|
||||
if (rectInfo.Hover)
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
// Show tooltip when hovered
|
||||
var mousePos = ImGui.GetMousePos();
|
||||
if (mousePos.X > pos.X + startX && mousePos.X < pos.X + endX &&
|
||||
mousePos.Y > pos.Y + (20 * timingHandle.Depth) &&
|
||||
mousePos.Y < pos.Y + (20 * (timingHandle.Depth + 1)))
|
||||
if (rectInfo.Hover)
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.Text(timingHandle.Name);
|
||||
ImGui.Text(timingHandle.MemberName);
|
||||
ImGui.Text($"{timingHandle.FileName}:{timingHandle.LineNumber}");
|
||||
ImGui.Text($"Duration: {timingHandle.Duration}ms");
|
||||
ImGui.TextUnformatted(rectInfo.Timing.Name);
|
||||
ImGui.TextUnformatted(rectInfo.Timing.MemberName);
|
||||
ImGui.TextUnformatted($"{rectInfo.Timing.FileName}:{rectInfo.Timing.LineNumber}");
|
||||
ImGui.TextUnformatted($"Duration: {rectInfo.Timing.Duration}ms");
|
||||
if (rectInfo.Timing.Parent != null)
|
||||
ImGui.TextUnformatted($"Parent: {rectInfo.Timing.Parent.Name}");
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Reflection;
|
|||
using System.Runtime.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Utility.Timing;
|
||||
|
||||
namespace Dalamud.IoC.Internal
|
||||
{
|
||||
|
|
@ -60,7 +61,7 @@ namespace Dalamud.IoC.Internal
|
|||
var parameterType = p.ParameterType;
|
||||
var requiredVersion = p.GetCustomAttribute(typeof(RequiredVersionAttribute)) as RequiredVersionAttribute;
|
||||
return (parameterType, requiredVersion);
|
||||
});
|
||||
}).ToList();
|
||||
|
||||
var versionCheck = parameters.All(p => CheckInterfaceVersion(p.requiredVersion, p.parameterType));
|
||||
|
||||
|
|
|
|||
|
|
@ -314,13 +314,13 @@ internal partial class PluginManager : IDisposable
|
|||
// Dev plugins should load first.
|
||||
pluginDefs.InsertRange(0, devPluginDefs);
|
||||
|
||||
Task LoadPluginOnBoot(string logPrefix, PluginDef pluginDef)
|
||||
async Task LoadPluginOnBoot(string logPrefix, PluginDef pluginDef)
|
||||
{
|
||||
using (Timings.Start($"{pluginDef.DllFile.Name}: {logPrefix}Boot"))
|
||||
{
|
||||
try
|
||||
{
|
||||
return this.LoadPluginAsync(
|
||||
await this.LoadPluginAsync(
|
||||
pluginDef.DllFile,
|
||||
pluginDef.Manifest,
|
||||
PluginLoadReason.Boot,
|
||||
|
|
@ -336,8 +336,6 @@ internal partial class PluginManager : IDisposable
|
|||
Log.Error(ex, "{0}: During boot plugin load, an unexpected error occurred", logPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
void LoadPluginsSync(string logPrefix, IEnumerable<PluginDef> pluginDefsList)
|
||||
|
|
@ -350,7 +348,8 @@ internal partial class PluginManager : IDisposable
|
|||
{
|
||||
return Task.WhenAll(
|
||||
pluginDefsList
|
||||
.Select(pluginDef => LoadPluginOnBoot(logPrefix, pluginDef))
|
||||
.Select(pluginDef => Task.Run(Timings.AttachTimingHandle(
|
||||
() => LoadPluginOnBoot(logPrefix, pluginDef))))
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
|
|
@ -394,15 +393,15 @@ internal partial class PluginManager : IDisposable
|
|||
TaskContinuationOptions.RunContinuationsAsynchronously)
|
||||
.Unwrap()
|
||||
.ContinueWith(
|
||||
_ => Service<Framework>.Get().RunOnTick(() =>
|
||||
{
|
||||
LoadPluginsSync(
|
||||
_ => Service<Framework>.Get().RunOnTick(
|
||||
() => LoadPluginsSync(
|
||||
"DrawAvailableSync",
|
||||
syncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null));
|
||||
return LoadPluginsAsync(
|
||||
"DrawAvailableAsync",
|
||||
asyncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null));
|
||||
}))
|
||||
syncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null))))
|
||||
.Unwrap()
|
||||
.ContinueWith(
|
||||
_ => LoadPluginsAsync(
|
||||
"DrawAvailableAsync",
|
||||
asyncPlugins.Where(def => def.Manifest?.LoadRequiredState is 0 or null)))
|
||||
.Unwrap());
|
||||
|
||||
// Save signatures when all plugins are done loading, successful or not.
|
||||
|
|
@ -427,33 +426,16 @@ internal partial class PluginManager : IDisposable
|
|||
/// <summary>
|
||||
/// Reload all loaded plugins.
|
||||
/// </summary>
|
||||
public void ReloadAllPlugins()
|
||||
/// <returns>A task.</returns>
|
||||
public Task ReloadAllPluginsAsync()
|
||||
{
|
||||
var aggregate = new List<Exception>();
|
||||
|
||||
lock (this.pluginListLock)
|
||||
{
|
||||
foreach (var plugin in this.InstalledPlugins)
|
||||
{
|
||||
if (plugin.IsLoaded)
|
||||
{
|
||||
try
|
||||
{
|
||||
plugin.Reload();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error during reload all");
|
||||
|
||||
aggregate.Add(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aggregate.Any())
|
||||
{
|
||||
throw new AggregateException(aggregate);
|
||||
return Task.WhenAll(this.InstalledPlugins
|
||||
.Where(x => x.IsLoaded)
|
||||
.ToList()
|
||||
.Select(x => Task.Run(async () => await x.ReloadAsync()))
|
||||
.ToList());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -706,10 +688,7 @@ internal partial class PluginManager : IDisposable
|
|||
if (plugin.IsDisabled)
|
||||
plugin.Enable();
|
||||
|
||||
// Await for things that plugin just require
|
||||
_ = await Service<SigScanner>.GetAsync();
|
||||
|
||||
plugin.Load(reason);
|
||||
await plugin.LoadAsync(reason);
|
||||
}
|
||||
catch (InvalidPluginException)
|
||||
{
|
||||
|
|
@ -1004,7 +983,7 @@ internal partial class PluginManager : IDisposable
|
|||
Thread.Sleep(500);
|
||||
|
||||
// Let's indicate "installer" here since this is supposed to be a fresh install
|
||||
plugin.Load(PluginLoadReason.Installer);
|
||||
plugin.LoadAsync(PluginLoadReason.Installer).Wait();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ internal class LocalDevPlugin : LocalPlugin, IDisposable
|
|||
var current = Interlocked.Increment(ref this.reloadCounter);
|
||||
|
||||
Task.Delay(500).ContinueWith(
|
||||
_ =>
|
||||
async _ =>
|
||||
{
|
||||
if (this.fileWatcherTokenSource.IsCancellationRequested)
|
||||
{
|
||||
|
|
@ -148,7 +148,7 @@ internal class LocalDevPlugin : LocalPlugin, IDisposable
|
|||
|
||||
try
|
||||
{
|
||||
this.Reload();
|
||||
await this.ReloadAsync();
|
||||
notificationManager.AddNotification($"The DevPlugin '{this.Name} was reloaded successfully.", "Plugin reloaded!", NotificationType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ using System;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.Gui.Dtr;
|
||||
|
|
@ -160,7 +160,7 @@ internal class LocalPlugin : IDisposable
|
|||
public PluginState State { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the AssemblyName plugin, populated during <see cref="Load(PluginLoadReason, bool)"/>.
|
||||
/// Gets the AssemblyName plugin, populated during <see cref="LoadAsync"/>.
|
||||
/// </summary>
|
||||
/// <returns>Plugin type.</returns>
|
||||
public AssemblyName? AssemblyName { get; private set; }
|
||||
|
|
@ -225,7 +225,8 @@ internal class LocalPlugin : IDisposable
|
|||
/// </summary>
|
||||
/// <param name="reason">The reason why this plugin is being loaded.</param>
|
||||
/// <param name="reloading">Load while reloading.</param>
|
||||
public void Load(PluginLoadReason reason, bool reloading = false)
|
||||
/// <returns>A task.</returns>
|
||||
public async Task LoadAsync(PluginLoadReason reason, bool reloading = false)
|
||||
{
|
||||
var startInfo = Service<DalamudStartInfo>.Get();
|
||||
var configuration = Service<DalamudConfiguration>.Get();
|
||||
|
|
@ -334,7 +335,7 @@ internal class LocalPlugin : IDisposable
|
|||
this.DalamudInterface = new DalamudPluginInterface(this.pluginAssembly.GetName().Name!, this.DllFile, reason, this.IsDev);
|
||||
|
||||
var ioc = Service<ServiceContainer>.Get();
|
||||
this.instance = ioc.CreateAsync(this.pluginType, this.DalamudInterface).GetAwaiter().GetResult() as IDalamudPlugin;
|
||||
this.instance = await ioc.CreateAsync(this.pluginType, this.DalamudInterface) as IDalamudPlugin;
|
||||
if (this.instance == null)
|
||||
{
|
||||
this.State = PluginState.LoadError;
|
||||
|
|
@ -423,7 +424,8 @@ internal class LocalPlugin : IDisposable
|
|||
/// <summary>
|
||||
/// Reload this plugin.
|
||||
/// </summary>
|
||||
public void Reload()
|
||||
/// <returns>A task.</returns>
|
||||
public async Task ReloadAsync()
|
||||
{
|
||||
this.Unload(true);
|
||||
|
||||
|
|
@ -431,7 +433,7 @@ internal class LocalPlugin : IDisposable
|
|||
var dtr = Service<DtrBar>.Get();
|
||||
dtr.HandleRemovedNodes();
|
||||
|
||||
this.Load(PluginLoadReason.Reload, true);
|
||||
await this.LoadAsync(PluginLoadReason.Reload, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Utility.Timing;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Dalamud
|
||||
|
|
@ -26,16 +31,43 @@ namespace Dalamud
|
|||
/// </summary>
|
||||
public static Task BlockingResolved { get; } = BlockingServicesLoadedTaskCompletionSource.Task;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes Provided Services and FFXIVClientStructs.
|
||||
/// </summary>
|
||||
/// <param name="dalamud">Instance of <see cref="Dalamud"/>.</param>
|
||||
/// <param name="startInfo">Instance of <see cref="DalamudStartInfo"/>.</param>
|
||||
/// <param name="configuration">Instance of <see cref="DalamudConfiguration"/>.</param>
|
||||
public static void InitializeProvidedServicesAndClientStructs(Dalamud dalamud, DalamudStartInfo startInfo, DalamudConfiguration configuration)
|
||||
{
|
||||
Service<Dalamud>.Provide(dalamud);
|
||||
Service<DalamudStartInfo>.Provide(startInfo);
|
||||
Service<DalamudConfiguration>.Provide(configuration);
|
||||
Service<ServiceContainer>.Provide(new ServiceContainer());
|
||||
|
||||
// Initialize the process information.
|
||||
var cacheDir = new DirectoryInfo(Path.Combine(startInfo.WorkingDirectory!, "cachedSigs"));
|
||||
if (!cacheDir.Exists)
|
||||
cacheDir.Create();
|
||||
Service<SigScanner>.Provide(new SigScanner(true, new FileInfo(Path.Combine(cacheDir.FullName, $"{startInfo.GameVersion}.json"))));
|
||||
|
||||
using (Timings.Start("CS Resolver Init"))
|
||||
{
|
||||
FFXIVClientStructs.Resolver.InitializeParallel(new FileInfo(Path.Combine(cacheDir.FullName, $"{startInfo.GameVersion}_cs.json")));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kicks off construction of services that can handle early loading.
|
||||
/// </summary>
|
||||
/// <returns>Task for initializing all services.</returns>
|
||||
public static async Task InitializeEarlyLoadableServices()
|
||||
{
|
||||
Service<ServiceContainer>.Provide(new ServiceContainer());
|
||||
|
||||
using var serviceInitializeTimings = Timings.Start("Services Init");
|
||||
var service = typeof(Service<>);
|
||||
var blockingEarlyLoadingServices = new List<Task>();
|
||||
|
||||
var earlyLoadingServices = new HashSet<Type>();
|
||||
var blockingEarlyLoadingServices = new HashSet<Type>();
|
||||
var afterDrawingEarlyLoadedServices = new HashSet<Type>();
|
||||
|
||||
var dependencyServicesMap = new Dictionary<Type, List<Type>>();
|
||||
var getAsyncTaskMap = new Dictionary<Type, Task>();
|
||||
|
|
@ -52,10 +84,19 @@ namespace Dalamud
|
|||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
if (attr.IsAssignableTo(typeof(BlockingEarlyLoadedService)))
|
||||
{
|
||||
getAsyncTaskMap[serviceType] = getTask;
|
||||
blockingEarlyLoadingServices.Add(getTask);
|
||||
blockingEarlyLoadingServices.Add(serviceType);
|
||||
}
|
||||
else if (attr.IsAssignableTo(typeof(AfterDrawingEarlyLoadedService)))
|
||||
{
|
||||
afterDrawingEarlyLoadedServices.Add(serviceType);
|
||||
}
|
||||
else
|
||||
{
|
||||
earlyLoadingServices.Add(serviceType);
|
||||
}
|
||||
|
||||
dependencyServicesMap[serviceType] =
|
||||
|
|
@ -69,50 +110,70 @@ namespace Dalamud
|
|||
null);
|
||||
}
|
||||
|
||||
_ = Task.WhenAll(blockingEarlyLoadingServices).ContinueWith(x =>
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (x.IsFaulted)
|
||||
BlockingServicesLoadedTaskCompletionSource.SetException(x.Exception!);
|
||||
else
|
||||
BlockingServicesLoadedTaskCompletionSource.SetResult();
|
||||
using var blockingServiceInitializeTimings = Timings.Start("BlockingServices Init");
|
||||
await Task.WhenAll(blockingEarlyLoadingServices.Select(x => getAsyncTaskMap[x]));
|
||||
BlockingServicesLoadedTaskCompletionSource.SetResult();
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception e)
|
||||
{
|
||||
// don't care, as this means task result/exception has already been set
|
||||
BlockingServicesLoadedTaskCompletionSource.SetException(e);
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
var tasks = new List<Task>();
|
||||
while (dependencyServicesMap.Any())
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
tasks.Clear();
|
||||
foreach (var (serviceType, dependencies) in dependencyServicesMap.ToList())
|
||||
var tasks = new List<Task>();
|
||||
var servicesToLoad = new HashSet<Type>();
|
||||
if (i == 0)
|
||||
{
|
||||
if (!dependencies.All(
|
||||
x => !getAsyncTaskMap.ContainsKey(x) || getAsyncTaskMap[x].IsCompleted))
|
||||
continue;
|
||||
|
||||
tasks.Add((Task)service.MakeGenericType(serviceType).InvokeMember(
|
||||
"StartLoader",
|
||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
||||
null,
|
||||
null,
|
||||
null));
|
||||
dependencyServicesMap.Remove(serviceType);
|
||||
servicesToLoad.UnionWith(earlyLoadingServices);
|
||||
servicesToLoad.UnionWith(blockingEarlyLoadingServices);
|
||||
}
|
||||
else
|
||||
{
|
||||
servicesToLoad.UnionWith(afterDrawingEarlyLoadedServices);
|
||||
await (await Service<InterfaceManager>.GetAsync()).SceneInitializeTask;
|
||||
}
|
||||
|
||||
if (!tasks.Any())
|
||||
throw new InvalidOperationException("Unresolvable dependency cycle detected");
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
foreach (var task in tasks)
|
||||
while (servicesToLoad.Any())
|
||||
{
|
||||
if (task.IsFaulted)
|
||||
throw task.Exception!;
|
||||
foreach (var serviceType in servicesToLoad)
|
||||
{
|
||||
if (dependencyServicesMap[serviceType].Any(
|
||||
x => getAsyncTaskMap.GetValueOrDefault(x)?.IsCompleted == false))
|
||||
continue;
|
||||
|
||||
tasks.Add((Task)service.MakeGenericType(serviceType).InvokeMember(
|
||||
"StartLoader",
|
||||
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
||||
null,
|
||||
null,
|
||||
null));
|
||||
servicesToLoad.Remove(serviceType);
|
||||
}
|
||||
|
||||
if (!tasks.Any())
|
||||
throw new InvalidOperationException("Unresolvable dependency cycle detected");
|
||||
|
||||
if (servicesToLoad.Any())
|
||||
{
|
||||
await Task.WhenAny(tasks);
|
||||
var faultedTasks = tasks.Where(x => x.IsFaulted).Select(x => (Exception)x.Exception!).ToArray();
|
||||
if (faultedTasks.Any())
|
||||
throw new AggregateException(faultedTasks);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
tasks.RemoveAll(x => x.IsCompleted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -173,5 +234,14 @@ namespace Dalamud
|
|||
public class BlockingEarlyLoadedService : EarlyLoadedService
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the class is a service, and will be instantiated automatically on startup,
|
||||
/// when drawing becomes available.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class AfterDrawingEarlyLoadedService : EarlyLoadedService
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@ namespace Dalamud
|
|||
// ReSharper disable once StaticMemberInGenericType
|
||||
private static readonly TaskCompletionSource<T> InstanceTcs = new();
|
||||
|
||||
// ReSharper disable once StaticMemberInGenericType
|
||||
private static bool startLoaderInvoked = false;
|
||||
|
||||
static Service()
|
||||
{
|
||||
var exposeToPlugins = typeof(T).GetCustomAttribute<PluginInterfaceAttribute>() != null;
|
||||
|
|
@ -45,37 +42,29 @@ namespace Dalamud
|
|||
[UsedImplicitly]
|
||||
public static Task<T> StartLoader()
|
||||
{
|
||||
if (startLoaderInvoked)
|
||||
throw new InvalidOperationException("StartLoader has already been called.");
|
||||
|
||||
var attr = typeof(T).GetCustomAttribute<ServiceManager.Service>(true)?.GetType();
|
||||
if (attr?.IsAssignableTo(typeof(ServiceManager.EarlyLoadedService)) != true)
|
||||
throw new InvalidOperationException($"{typeof(T).Name} is not an EarlyLoadedService");
|
||||
|
||||
startLoaderInvoked = true;
|
||||
return Task.Run(async () =>
|
||||
return Task.Run(Timings.AttachTimingHandle(async () =>
|
||||
{
|
||||
using (Timings.Start($"{typeof(T).Namespace} Enable"))
|
||||
ServiceManager.Log.Debug("Service<{0}>: Begin construction", typeof(T).Name);
|
||||
try
|
||||
{
|
||||
var x = await ConstructObject();
|
||||
if (attr?.IsAssignableTo(typeof(ServiceManager.BlockingEarlyLoadedService)) == true)
|
||||
ServiceManager.Log.Debug("Service<{0}>: Begin construction", typeof(T).Name);
|
||||
try
|
||||
{
|
||||
var x = await ConstructObject();
|
||||
if (attr?.IsAssignableTo(typeof(ServiceManager.BlockingEarlyLoadedService)) == true)
|
||||
ServiceManager.Log.Debug("Service<{0}>: Construction complete", typeof(T).Name);
|
||||
InstanceTcs.SetResult(x);
|
||||
return x;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
InstanceTcs.SetException(e);
|
||||
if (attr?.IsAssignableTo(typeof(ServiceManager.BlockingEarlyLoadedService)) == true)
|
||||
ServiceManager.Log.Error(e, "Service<{0}>: Construction failure", typeof(T).Name);
|
||||
throw;
|
||||
}
|
||||
ServiceManager.Log.Debug("Service<{0}>: Construction complete", typeof(T).Name);
|
||||
InstanceTcs.SetResult(x);
|
||||
return x;
|
||||
}
|
||||
});
|
||||
catch (Exception e)
|
||||
{
|
||||
InstanceTcs.SetException(e);
|
||||
if (attr?.IsAssignableTo(typeof(ServiceManager.BlockingEarlyLoadedService)) == true)
|
||||
ServiceManager.Log.Error(e, "Service<{0}>: Construction failure", typeof(T).Name);
|
||||
throw;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -165,7 +154,10 @@ namespace Dalamud
|
|||
var ctor = GetServiceConstructor();
|
||||
var args = await Task.WhenAll(
|
||||
ctor.GetParameters().Select(x => GetServiceObjectConstructArgument(x.ParameterType)));
|
||||
return (T)ctor.Invoke(args)!;
|
||||
using (Timings.Start($"{typeof(T).Name} Construct"))
|
||||
{
|
||||
return (T)ctor.Invoke(args)!;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,27 @@
|
|||
using System.Threading;
|
||||
|
||||
namespace Dalamud.Utility.Timing;
|
||||
|
||||
public class TimingEvent
|
||||
{
|
||||
private static long IdCounter = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Id of this timing event.
|
||||
/// </summary>
|
||||
public readonly long Id = Interlocked.Increment(ref IdCounter);
|
||||
|
||||
internal TimingEvent(string name)
|
||||
{
|
||||
this.Name = name;
|
||||
this.StartTime = Timings.Stopwatch.Elapsed.TotalMilliseconds;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time this timing started.
|
||||
/// </summary>
|
||||
public double StartTime { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the timing.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
|
|
@ -8,7 +9,7 @@ namespace Dalamud.Utility.Timing;
|
|||
/// Class used for tracking a time interval taken.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Name} - {Duration}")]
|
||||
public sealed class TimingHandle : TimingEvent, IDisposable
|
||||
public sealed class TimingHandle : TimingEvent, IDisposable, IComparable<TimingHandle>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TimingHandle"/> class.
|
||||
|
|
@ -16,28 +17,33 @@ public sealed class TimingHandle : TimingEvent, IDisposable
|
|||
/// <param name="name">The name of this timing.</param>
|
||||
internal TimingHandle(string name) : base(name)
|
||||
{
|
||||
this.Parent = Timings.Current.Value;
|
||||
Timings.Current.Value = this;
|
||||
this.Stack = Timings.TaskTimingHandles;
|
||||
|
||||
lock (Timings.AllTimings)
|
||||
this.Parent = this.Stack.LastOrDefault();
|
||||
|
||||
if (this.Parent != null)
|
||||
{
|
||||
if (this.Parent != null)
|
||||
{
|
||||
this.ChildCount++;
|
||||
}
|
||||
|
||||
this.EndTime = this.StartTime;
|
||||
this.IsMainThread = ThreadSafety.IsMainThread;
|
||||
|
||||
if (Timings.ActiveTimings.Count > 0)
|
||||
{
|
||||
this.Depth = Timings.ActiveTimings.Max(x => x.Depth) + 1;
|
||||
}
|
||||
|
||||
Timings.ActiveTimings.Add(this);
|
||||
this.Parent.ChildCount++;
|
||||
this.IdChain = new long[this.Parent.IdChain.Length + 1];
|
||||
Array.Copy(this.Parent.IdChain, this.IdChain, this.Parent.IdChain.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.IdChain = new long[1];
|
||||
}
|
||||
|
||||
this.IdChain[^1] = this.Id;
|
||||
this.EndTime = this.StartTime;
|
||||
this.IsMainThread = ThreadSafety.IsMainThread;
|
||||
|
||||
this.Stack.Add(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the id chain.
|
||||
/// </summary>
|
||||
public long[] IdChain { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time this timing ended.
|
||||
/// </summary>
|
||||
|
|
@ -48,16 +54,16 @@ public sealed class TimingHandle : TimingEvent, IDisposable
|
|||
/// </summary>
|
||||
public double Duration => Math.Floor(this.EndTime - this.StartTime);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attached timing handle stack.
|
||||
/// </summary>
|
||||
public List<TimingHandle> Stack { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent timing.
|
||||
/// </summary>
|
||||
public TimingHandle? Parent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether or not this timing has already returned to its parent.
|
||||
/// </summary>
|
||||
public bool Returned { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether or not this timing was started on the main thread.
|
||||
/// </summary>
|
||||
|
|
@ -68,26 +74,39 @@ public sealed class TimingHandle : TimingEvent, IDisposable
|
|||
/// </summary>
|
||||
public uint ChildCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the depth of this timing.
|
||||
/// </summary>
|
||||
public uint Depth { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.EndTime = Timings.Stopwatch.Elapsed.TotalMilliseconds;
|
||||
Timings.Current.Value = this.Parent;
|
||||
|
||||
lock (Timings.AllTimings)
|
||||
this.Stack.Remove(this);
|
||||
if (this.Duration > 1 || this.ChildCount > 0)
|
||||
{
|
||||
if (this.Duration > 1 || this.ChildCount > 0)
|
||||
lock (Timings.AllTimings)
|
||||
{
|
||||
Timings.AllTimings.Add(this);
|
||||
this.Returned = this.Parent != null && Timings.ActiveTimings.Contains(this.Parent);
|
||||
Timings.AllTimings.Add(this, this);
|
||||
}
|
||||
|
||||
Timings.ActiveTimings.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int CompareTo(TimingHandle? other)
|
||||
{
|
||||
if (other == null)
|
||||
return -1;
|
||||
|
||||
var i = 0;
|
||||
for (; i < this.IdChain.Length && i < other.IdChain.Length; i++)
|
||||
{
|
||||
if (this.IdChain[i] < other.IdChain[i])
|
||||
return -1;
|
||||
if (this.IdChain[i] > other.IdChain[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (this.IdChain.Length < other.IdChain.Length)
|
||||
return -1;
|
||||
if (this.IdChain.Length > other.IdChain.Length)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Utility.Timing;
|
||||
|
||||
|
|
@ -19,19 +20,75 @@ public static class Timings
|
|||
/// <summary>
|
||||
/// All concluded timings.
|
||||
/// </summary>
|
||||
internal static readonly List<TimingHandle> AllTimings = new();
|
||||
internal static readonly SortedList<TimingHandle, TimingHandle> AllTimings = new();
|
||||
|
||||
/// <summary>
|
||||
/// All active timings.
|
||||
/// </summary>
|
||||
internal static readonly List<TimingHandle> ActiveTimings = new();
|
||||
|
||||
internal static readonly List<TimingEvent> Events = new();
|
||||
|
||||
private static readonly AsyncLocal<Tuple<int?, List<TimingHandle>>> taskTimingHandleStorage = new();
|
||||
|
||||
/// <summary>
|
||||
/// Current active timing entry.
|
||||
/// Gets or sets all active timings of current thread.
|
||||
/// </summary>
|
||||
internal static readonly AsyncLocal<TimingHandle> Current = new();
|
||||
internal static List<TimingHandle> TaskTimingHandles
|
||||
{
|
||||
get
|
||||
{
|
||||
if (taskTimingHandleStorage.Value == null || taskTimingHandleStorage.Value.Item1 != Task.CurrentId)
|
||||
taskTimingHandleStorage.Value = Tuple.Create<int?, List<TimingHandle>>(Task.CurrentId, new());
|
||||
return taskTimingHandleStorage.Value!.Item2!;
|
||||
}
|
||||
set => taskTimingHandleStorage.Value = Tuple.Create(Task.CurrentId, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches timing handle to a Func{T}.
|
||||
/// </summary>
|
||||
/// <param name="task">Task to attach.</param>
|
||||
/// <typeparam name="T">Return type.</typeparam>
|
||||
/// <returns>Attached task.</returns>
|
||||
public static Func<T> AttachTimingHandle<T>(Func<T> task)
|
||||
{
|
||||
var outerTimingHandle = TaskTimingHandles;
|
||||
return () =>
|
||||
{
|
||||
T res = default(T);
|
||||
var prev = TaskTimingHandles;
|
||||
TaskTimingHandles = outerTimingHandle;
|
||||
try
|
||||
{
|
||||
res = task();
|
||||
}
|
||||
finally
|
||||
{
|
||||
TaskTimingHandles = prev;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches timing handle to an Action.
|
||||
/// </summary>
|
||||
/// <param name="task">Task to attach.</param>
|
||||
/// <returns>Attached task.</returns>
|
||||
public static Action AttachTimingHandle(Action task)
|
||||
{
|
||||
var outerTimingHandle = TaskTimingHandles;
|
||||
return () =>
|
||||
{
|
||||
var prev = TaskTimingHandles;
|
||||
TaskTimingHandles = outerTimingHandle;
|
||||
try
|
||||
{
|
||||
task();
|
||||
}
|
||||
finally
|
||||
{
|
||||
TaskTimingHandles = prev;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start a new timing.
|
||||
|
|
@ -50,7 +107,7 @@ public static class Timings
|
|||
LineNumber = sourceLineNumber,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Record a one-time event.
|
||||
/// </summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue