mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-31 12:53:41 +01:00
Add IInternal/PublicDisposableService (#1696)
* Add IInternal/PublicDisposableService Plugins are exposed interfaces that are not inherited from `IDisposable`, but services implementing plugin interfaces often implement `IDisposable`. Some plugins may try to call `IDisposable.Dispose` on everything provided, and it also is possible to use `using` clause too eagerly while working on Dalamud itself, such as writing `using var smth = await Service<SomeService>.GetAsync();`. Such behaviors often lead to a difficult-to-debug errors, and making those services either not an `IDisposable` or making `IDisposable.Dispose` do nothing if the object has been loaded would prevent such errors. As `ServiceManager` must be the only class dealing with construction and disposal of services, `IInternalDisposableService` has been added to limit who can dispose the object. `IPublicDisposableService` also has been added to classes that can be constructed and accessed directly by plugins; for those, `Dispose` will be ignored if the instance is a service instance, and only `DisposeService` will respond. In addition, `DalamudPluginInterface` and `UiBuilder` also have been changed so that their `IDisposable.Dispose` no longer respond, and instead, internal functions have been added to only allow disposal from Dalamud. * Cleanup * Postmerge fixes * More explanation on RunOnFrameworkThread(ClearHooks) * Mark ReliableFileStorage public ctor obsolete --------- Co-authored-by: goat <16760685+goaaats@users.noreply.github.com>
This commit is contained in:
parent
dcec076ca7
commit
87b9edb448
62 changed files with 441 additions and 381 deletions
|
|
@ -5,6 +5,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Configuration.Internal;
|
||||
|
|
@ -51,7 +52,7 @@ namespace Dalamud.Interface.Internal;
|
|||
/// This class manages interaction with the ImGui interface.
|
||||
/// </summary>
|
||||
[ServiceManager.BlockingEarlyLoadedService]
|
||||
internal class InterfaceManager : IDisposable, IServiceType
|
||||
internal class InterfaceManager : IInternalDisposableService
|
||||
{
|
||||
/// <summary>
|
||||
/// The default font size, in points.
|
||||
|
|
@ -69,10 +70,13 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
[ServiceManager.ServiceDependency]
|
||||
private readonly WndProcHookManager wndProcHookManager = Service<WndProcHookManager>.Get();
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly Framework framework = Service<Framework>.Get();
|
||||
|
||||
private readonly SwapChainVtableResolver address = new();
|
||||
private readonly Hook<SetCursorDelegate> setCursorHook;
|
||||
private RawDX11Scene? scene;
|
||||
|
||||
private Hook<SetCursorDelegate>? setCursorHook;
|
||||
private Hook<PresentDelegate>? presentHook;
|
||||
private Hook<ResizeBuffersDelegate>? resizeBuffersHook;
|
||||
|
||||
|
|
@ -87,8 +91,6 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
[ServiceManager.ServiceConstructor]
|
||||
private InterfaceManager()
|
||||
{
|
||||
this.setCursorHook = Hook<SetCursorDelegate>.FromImport(
|
||||
null, "user32.dll", "SetCursor", 0, this.SetCursorDetour);
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
|
|
@ -233,25 +235,45 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
/// <summary>
|
||||
/// Dispose of managed and unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
void IInternalDisposableService.DisposeService()
|
||||
{
|
||||
if (Service<Framework>.GetNullable() is { } framework)
|
||||
framework.RunOnFrameworkThread(Disposer).Wait();
|
||||
else
|
||||
Disposer();
|
||||
// Unload hooks from the framework thread if possible.
|
||||
// We're currently off the framework thread, as this function can only be called from
|
||||
// ServiceManager.UnloadAllServices, which is called from EntryPoint.RunThread.
|
||||
// The functions being unhooked are mostly called from the main thread, so unhooking from the main thread when
|
||||
// possible would avoid any chance of unhooking a function that currently is being called.
|
||||
// If unloading is initiated from "Unload Dalamud" /xldev menu, then the framework would still be running, as
|
||||
// Framework.Destroy has never been called and thus Framework.IsFrameworkUnloading cannot be true, and this
|
||||
// function will actually run the destroy from the framework thread.
|
||||
// Otherwise, as Framework.IsFrameworkUnloading should have been set, this code should run immediately.
|
||||
this.framework.RunOnFrameworkThread(ClearHooks).Wait();
|
||||
|
||||
// Below this point, hooks are guaranteed to be no longer called.
|
||||
|
||||
// A font resource lock outlives the parent handle and the owner atlas. It should be disposed.
|
||||
Interlocked.Exchange(ref this.defaultFontResourceLock, null)?.Dispose();
|
||||
|
||||
// Font handles become invalid after disposing the atlas, but just to be safe.
|
||||
this.DefaultFontHandle?.Dispose();
|
||||
this.DefaultFontHandle = null;
|
||||
|
||||
this.MonoFontHandle?.Dispose();
|
||||
this.MonoFontHandle = null;
|
||||
|
||||
this.IconFontHandle?.Dispose();
|
||||
this.IconFontHandle = null;
|
||||
|
||||
Interlocked.Exchange(ref this.dalamudAtlas, null)?.Dispose();
|
||||
Interlocked.Exchange(ref this.scene, null)?.Dispose();
|
||||
|
||||
this.wndProcHookManager.PreWndProc -= this.WndProcHookManagerOnPreWndProc;
|
||||
this.defaultFontResourceLock?.Dispose(); // lock outlives handle and atlas
|
||||
this.defaultFontResourceLock = null;
|
||||
this.dalamudAtlas?.Dispose();
|
||||
this.scene?.Dispose();
|
||||
return;
|
||||
|
||||
void Disposer()
|
||||
void ClearHooks()
|
||||
{
|
||||
this.setCursorHook.Dispose();
|
||||
this.presentHook?.Dispose();
|
||||
this.resizeBuffersHook?.Dispose();
|
||||
this.wndProcHookManager.PreWndProc -= this.WndProcHookManagerOnPreWndProc;
|
||||
Interlocked.Exchange(ref this.setCursorHook, null)?.Dispose();
|
||||
Interlocked.Exchange(ref this.presentHook, null)?.Dispose();
|
||||
Interlocked.Exchange(ref this.resizeBuffersHook, null)?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -693,7 +715,6 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
"InterfaceManager accepts event registration and stuff even when the game window is not ready.")]
|
||||
private void ContinueConstruction(
|
||||
TargetSigScanner sigScanner,
|
||||
Framework framework,
|
||||
FontAtlasFactory fontAtlasFactory)
|
||||
{
|
||||
this.dalamudAtlas = fontAtlasFactory
|
||||
|
|
@ -731,7 +752,7 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
this.DefaultFontHandle.ImFontChanged += (_, font) =>
|
||||
{
|
||||
var fontLocked = font.NewRef();
|
||||
Service<Framework>.Get().RunOnFrameworkThread(
|
||||
this.framework.RunOnFrameworkThread(
|
||||
() =>
|
||||
{
|
||||
// Update the ImGui default font.
|
||||
|
|
@ -765,6 +786,7 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
Log.Error(ex, "Could not enable immersive mode");
|
||||
}
|
||||
|
||||
this.setCursorHook = Hook<SetCursorDelegate>.FromImport(null, "user32.dll", "SetCursor", 0, this.SetCursorDetour);
|
||||
this.presentHook = Hook<PresentDelegate>.FromAddress(this.address.Present, this.PresentDetour);
|
||||
this.resizeBuffersHook = Hook<ResizeBuffersDelegate>.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
|
||||
|
||||
|
|
@ -808,7 +830,7 @@ internal class InterfaceManager : IDisposable, IServiceType
|
|||
if (this.lastWantCapture && (!this.scene?.IsImGuiCursor(hCursor) ?? false) && this.OverrideGameCursor)
|
||||
return IntPtr.Zero;
|
||||
|
||||
return this.setCursorHook.IsDisposed
|
||||
return this.setCursorHook?.IsDisposed is not false
|
||||
? User32.SetCursor(new(hCursor, false)).DangerousGetHandle()
|
||||
: this.setCursorHook.Original(hCursor);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue