diff --git a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs
index d3d0fcebe..5d121bea4 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs
@@ -19,13 +19,13 @@ namespace Dalamud.Game.Addon.Lifecycle;
[ServiceManager.EarlyLoadedService]
internal unsafe class AddonLifecycle : IInternalDisposableService
{
+ ///
+ /// Gets a list of all allocated addon virtual tables.
+ ///
+ public static readonly List AllocatedTables = [];
+
private static readonly ModuleLog Log = new("AddonLifecycle");
- [ServiceManager.ServiceDependency]
- private readonly Framework framework = Service.Get();
-
- private readonly Dictionary modifiedTables = [];
-
private Hook? onInitializeAddonHook;
[ServiceManager.ServiceConstructor]
@@ -47,13 +47,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
this.onInitializeAddonHook?.Dispose();
this.onInitializeAddonHook = null;
- this.framework.RunOnFrameworkThread(() =>
- {
- foreach (var virtualTable in this.modifiedTables.Values)
- {
- virtualTable.Dispose();
- }
- });
+ AllocatedTables.ForEach(entry => entry.Dispose());
+ AllocatedTables.Clear();
}
///
@@ -141,18 +136,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
{
this.LogInitialize(addon->NameString);
- if (!this.modifiedTables.ContainsKey(addon->NameString))
- {
- // AddonVirtualTable class handles creating the virtual table, and overriding each of the tracked virtual functions
- var managedVirtualTableEntry = new AddonVirtualTable(addon, this)
- {
- // This event is invoked when the game itself has disposed of an addon
- // We can use this to know when to remove our virtual table entry
- OnAddonFinalized = () => this.modifiedTables.Remove(addon->NameString),
- };
-
- this.modifiedTables.Add(addon->NameString, managedVirtualTableEntry);
- }
+ // AddonVirtualTable class handles creating the virtual table, and overriding each of the tracked virtual functions
+ AllocatedTables.Add(new AddonVirtualTable(addon, this));
}
catch (Exception e)
{
diff --git a/Dalamud/Game/Addon/Lifecycle/AddonVirtualTable.cs b/Dalamud/Game/Addon/Lifecycle/AddonVirtualTable.cs
index db698e626..d91cd648f 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonVirtualTable.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonVirtualTable.cs
@@ -1,6 +1,7 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Threading;
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Logging.Internal;
@@ -108,17 +109,11 @@ internal unsafe class AddonVirtualTable : IDisposable
this.modifiedVirtualTable->Hide = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(this.hideFunction);
}
- ///
- /// Gets an event that is invoked when this addon's Finalize method is called from native.
- ///
- public required Action OnAddonFinalized { get; init; }
-
- ///
- /// WARNING! This should not be called at any time except during dalamud unload.
- ///
+ ///
public void Dispose()
{
- this.atkUnitBase->VirtualTable = this.originalVirtualTable;
+ // Ensure restoration is done atomically.
+ Interlocked.Exchange(ref *(nint*)&this.atkUnitBase->VirtualTable, (nint)this.originalVirtualTable);
IMemorySpace.Free(this.modifiedVirtualTable, 0x8 * VirtualTableEntryCount);
}
@@ -131,7 +126,7 @@ internal unsafe class AddonVirtualTable : IDisposable
if ((freeFlags & 1) == 1)
{
IMemorySpace.Free(this.modifiedVirtualTable, 0x8 * VirtualTableEntryCount);
- this.OnAddonFinalized();
+ AddonLifecycle.AllocatedTables.Remove(this);
}
return result;