mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
fix: Attempt to better handle hook disposal (#1803)
- Use a Weak Concurrent Collection to track scoped hooks - Make `Hook`s remove themselves from the Tracked Hook list.
This commit is contained in:
parent
203d80c602
commit
a2124bb73d
8 changed files with 67 additions and 21 deletions
|
|
@ -20,6 +20,8 @@ public sealed class AsmHook : IDisposable, IDalamudHook
|
||||||
private bool isEnabled = false;
|
private bool isEnabled = false;
|
||||||
|
|
||||||
private DynamicMethod statsMethod;
|
private DynamicMethod statsMethod;
|
||||||
|
|
||||||
|
private Guid hookId = Guid.NewGuid();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="AsmHook"/> class.
|
/// Initializes a new instance of the <see cref="AsmHook"/> class.
|
||||||
|
|
@ -44,7 +46,7 @@ public sealed class AsmHook : IDisposable, IDalamudHook
|
||||||
this.statsMethod.GetILGenerator().Emit(OpCodes.Ret);
|
this.statsMethod.GetILGenerator().Emit(OpCodes.Ret);
|
||||||
var dele = this.statsMethod.CreateDelegate(typeof(Action));
|
var dele = this.statsMethod.CreateDelegate(typeof(Action));
|
||||||
|
|
||||||
HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, dele, Assembly.GetCallingAssembly()));
|
HookManager.TrackedHooks.TryAdd(this.hookId, new HookInfo(this, dele, Assembly.GetCallingAssembly()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -70,7 +72,7 @@ public sealed class AsmHook : IDisposable, IDalamudHook
|
||||||
this.statsMethod.GetILGenerator().Emit(OpCodes.Ret);
|
this.statsMethod.GetILGenerator().Emit(OpCodes.Ret);
|
||||||
var dele = this.statsMethod.CreateDelegate(typeof(Action));
|
var dele = this.statsMethod.CreateDelegate(typeof(Action));
|
||||||
|
|
||||||
HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, dele, Assembly.GetCallingAssembly()));
|
HookManager.TrackedHooks.TryAdd(this.hookId, new HookInfo(this, dele, Assembly.GetCallingAssembly()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -116,6 +118,8 @@ public sealed class AsmHook : IDisposable, IDalamudHook
|
||||||
|
|
||||||
this.IsDisposed = true;
|
this.IsDisposed = true;
|
||||||
|
|
||||||
|
HookManager.TrackedHooks.TryRemove(this.hookId, out _);
|
||||||
|
|
||||||
if (this.isEnabled)
|
if (this.isEnabled)
|
||||||
{
|
{
|
||||||
this.isEnabled = false;
|
this.isEnabled = false;
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,11 @@ public abstract class Hook<T> : IDalamudHook where T : Delegate
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual string BackendName => throw new NotImplementedException();
|
public virtual string BackendName => throw new NotImplementedException();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the unique GUID for this hook.
|
||||||
|
/// </summary>
|
||||||
|
protected Guid HookId { get; } = Guid.NewGuid();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove a hook from the current process.
|
/// Remove a hook from the current process.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ internal class FunctionPointerVariableHook<T> : Hook<T>
|
||||||
|
|
||||||
unhooker.TrimAfterHook();
|
unhooker.TrimAfterHook();
|
||||||
|
|
||||||
HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly));
|
HookManager.TrackedHooks.TryAdd(this.HookId, new HookInfo(this, detour, callingAssembly));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,6 +137,8 @@ internal class FunctionPointerVariableHook<T> : Hook<T>
|
||||||
|
|
||||||
this.Disable();
|
this.Disable();
|
||||||
|
|
||||||
|
HookManager.TrackedHooks.TryRemove(this.HookId, out _);
|
||||||
|
|
||||||
var index = HookManager.MultiHookTracker[this.Address].IndexOf(this);
|
var index = HookManager.MultiHookTracker[this.Address].IndexOf(this);
|
||||||
HookManager.MultiHookTracker[this.Address][index] = null;
|
HookManager.MultiHookTracker[this.Address][index] = null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Collections.Concurrent;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
|
|
@ -7,6 +6,7 @@ using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Plugin.Internal.Types;
|
using Dalamud.Plugin.Internal.Types;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Dalamud.Utility;
|
||||||
using Dalamud.Utility.Signatures;
|
using Dalamud.Utility.Signatures;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ internal class GameInteropProviderPluginScoped : IGameInteropProvider, IInternal
|
||||||
private readonly LocalPlugin plugin;
|
private readonly LocalPlugin plugin;
|
||||||
private readonly SigScanner scanner;
|
private readonly SigScanner scanner;
|
||||||
|
|
||||||
private readonly ConcurrentBag<IDalamudHook> trackedHooks = new();
|
private readonly WeakConcurrentCollection<IDalamudHook> trackedHooks = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="GameInteropProviderPluginScoped"/> class.
|
/// Initializes a new instance of the <see cref="GameInteropProviderPluginScoped"/> class.
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ internal class MinHookHook<T> : Hook<T> where T : Delegate
|
||||||
|
|
||||||
unhooker.TrimAfterHook();
|
unhooker.TrimAfterHook();
|
||||||
|
|
||||||
HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly));
|
HookManager.TrackedHooks.TryAdd(this.HookId, new HookInfo(this, detour, callingAssembly));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,6 +76,8 @@ internal class MinHookHook<T> : Hook<T> where T : Delegate
|
||||||
HookManager.MultiHookTracker[this.Address][index] = null;
|
HookManager.MultiHookTracker[this.Address][index] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HookManager.TrackedHooks.TryRemove(this.HookId, out _);
|
||||||
|
|
||||||
base.Dispose();
|
base.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ internal class ReloadedHook<T> : Hook<T> where T : Delegate
|
||||||
|
|
||||||
unhooker.TrimAfterHook();
|
unhooker.TrimAfterHook();
|
||||||
|
|
||||||
HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly));
|
HookManager.TrackedHooks.TryAdd(this.HookId, new HookInfo(this, detour, callingAssembly));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,6 +63,8 @@ internal class ReloadedHook<T> : Hook<T> where T : Delegate
|
||||||
if (this.IsDisposed)
|
if (this.IsDisposed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
HookManager.TrackedHooks.TryRemove(this.HookId, out _);
|
||||||
|
|
||||||
this.Disable();
|
this.Disable();
|
||||||
|
|
||||||
base.Dispose();
|
base.Dispose();
|
||||||
|
|
|
||||||
|
|
@ -258,8 +258,6 @@ internal class PluginStatWindow : Window
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
var toRemove = new List<Guid>();
|
|
||||||
|
|
||||||
if (ImGui.BeginTabItem("Hooks"))
|
if (ImGui.BeginTabItem("Hooks"))
|
||||||
{
|
{
|
||||||
ImGui.Checkbox("Show Dalamud Hooks", ref this.showDalamudHooks);
|
ImGui.Checkbox("Show Dalamud Hooks", ref this.showDalamudHooks);
|
||||||
|
|
@ -291,9 +289,6 @@ internal class PluginStatWindow : Window
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (trackedHook.Hook.IsDisposed)
|
|
||||||
toRemove.Add(guid);
|
|
||||||
|
|
||||||
if (!this.showDalamudHooks && trackedHook.Assembly == Assembly.GetExecutingAssembly())
|
if (!this.showDalamudHooks && trackedHook.Assembly == Assembly.GetExecutingAssembly())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -355,14 +350,6 @@ internal class PluginStatWindow : Window
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui.IsWindowAppearing())
|
|
||||||
{
|
|
||||||
foreach (var guid in toRemove)
|
|
||||||
{
|
|
||||||
HookManager.TrackedHooks.TryRemove(guid, out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndTabBar();
|
ImGui.EndTabBar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
44
Dalamud/Utility/WeakConcurrentCollection.cs
Normal file
44
Dalamud/Utility/WeakConcurrentCollection.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Dalamud.Utility;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An implementation of a weak concurrent set based on a <see cref="ConditionalWeakTable{TKey,TValue}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of object that we're tracking.</typeparam>
|
||||||
|
public class WeakConcurrentCollection<T> : ICollection<T> where T : class
|
||||||
|
{
|
||||||
|
private readonly ConditionalWeakTable<T, object> cwt = new();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int Count => this.cwt.Count();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool IsReadOnly => false;
|
||||||
|
|
||||||
|
private IEnumerable<T> Keys => this.cwt.Select(pair => pair.Key);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IEnumerator<T> GetEnumerator() => this.cwt.Select(pair => pair.Key).GetEnumerator();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => this.cwt.Select(pair => pair.Key).GetEnumerator();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Add(T item) => this.cwt.AddOrUpdate(item, null);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Clear() => this.cwt.Clear();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool Contains(T item) => this.Keys.Contains(item);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void CopyTo(T[] array, int arrayIndex) => this.Keys.ToArray().CopyTo(array, arrayIndex);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool Remove(T item) => this.cwt.Remove(item);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue