From 2004f71290f2b378954c8fc8e9fcef13e9a16e73 Mon Sep 17 00:00:00 2001 From: Westin Miller Date: Mon, 9 Oct 2017 01:06:01 -0700 Subject: [PATCH] Patch manager state is static, registration methods are non-static. Patch manager tracks patch context ownership by assembly internally --- Torch.API/Torch.API.csproj | 4 ++ Torch/Managers/Event/EventManager.cs | 13 ++-- Torch/Managers/PatchManager/PatchContext.cs | 11 ++++ Torch/Managers/PatchManager/PatchManager.cs | 69 +++++++++++++++++---- Torch/Torch.csproj | 10 ++- 5 files changed, 86 insertions(+), 21 deletions(-) diff --git a/Torch.API/Torch.API.csproj b/Torch.API/Torch.API.csproj index bbf2e3b..c3d2823 100644 --- a/Torch.API/Torch.API.csproj +++ b/Torch.API/Torch.API.csproj @@ -163,10 +163,14 @@ + + + + diff --git a/Torch/Managers/Event/EventManager.cs b/Torch/Managers/Event/EventManager.cs index 2c54a4b..f6b0330 100644 --- a/Torch/Managers/Event/EventManager.cs +++ b/Torch/Managers/Event/EventManager.cs @@ -39,7 +39,12 @@ namespace Torch.Managers.Event } } - /// + + /// + /// Gets all event handler methods declared by the given type and its base types. + /// + /// Type to explore + /// All event handler methods private static IEnumerable EventHandlers(Type exploreType) { IEnumerable enumerable = exploreType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) @@ -149,9 +154,9 @@ namespace Torch.Managers.Event /// Unregisters all handlers owned by the given assembly. /// /// Assembly to unregister - /// Optional callback invoked for every registered handler in the assembly. Ignored if null + /// Optional callback invoked before a handler is unregistered. Ignored if null /// the number of handlers that were unregistered - internal int UnregisterAllHandlers(Assembly asm, Func callback = null) + internal int UnregisterAllHandlers(Assembly asm, Action callback = null) { lock (_registeredHandlers) { @@ -159,7 +164,7 @@ namespace Torch.Managers.Event return 0; foreach (IEventHandler k in handlers) { - callback?.Invoke(); + callback?.Invoke(k); UnregisterHandlerInternal(k); } int count = handlers.Count; diff --git a/Torch/Managers/PatchManager/PatchContext.cs b/Torch/Managers/PatchManager/PatchContext.cs index a213d99..66694a3 100644 --- a/Torch/Managers/PatchManager/PatchContext.cs +++ b/Torch/Managers/PatchManager/PatchContext.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Reflection; namespace Torch.Managers.PatchManager @@ -30,7 +31,17 @@ namespace Torch.Managers.PatchManager _rewritePatterns.Add(method, res); return res; } + + /// + /// Gets all methods that this context has patched + /// + internal IEnumerable PatchedMethods => _rewritePatterns + .Where(x => x.Value.Prefixes.Count > 0 || x.Value.Suffixes.Count > 0 || x.Value.Transpilers.Count > 0) + .Select(x => x.Key); + /// + /// Removes all patches in this context + /// internal void RemoveAll() { foreach (MethodRewritePattern pattern in _rewritePatterns.Values) diff --git a/Torch/Managers/PatchManager/PatchManager.cs b/Torch/Managers/PatchManager/PatchManager.cs index 86b0905..7e9a205 100644 --- a/Torch/Managers/PatchManager/PatchManager.cs +++ b/Torch/Managers/PatchManager/PatchManager.cs @@ -1,6 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Reflection; +using System.Runtime.CompilerServices; using Torch.API; +using Torch.Managers.PatchManager.Transpile; namespace Torch.Managers.PatchManager { @@ -17,8 +20,8 @@ namespace Torch.Managers.PatchManager { } - private readonly Dictionary _rewritePatterns = new Dictionary(); - private readonly HashSet _contexts = new HashSet(); + private static readonly Dictionary _rewritePatterns = new Dictionary(); + private readonly Dictionary> _contexts = new Dictionary>(); /// /// Gets the rewrite pattern for the given method, creating one if it doesn't exist. @@ -27,21 +30,31 @@ namespace Torch.Managers.PatchManager /// public MethodRewritePattern GetPattern(MethodBase method) { - if (_rewritePatterns.TryGetValue(method, out DecoratedMethod pattern)) - return pattern; - var res = new DecoratedMethod(method); - _rewritePatterns.Add(method, res); - return res; + lock (_rewritePatterns) + { + if (_rewritePatterns.TryGetValue(method, out DecoratedMethod pattern)) + return pattern; + var res = new DecoratedMethod(method); + _rewritePatterns.Add(method, res); + return res; + } } /// /// Creates a new used for tracking changes. A call to will apply the patches. /// + [MethodImpl(MethodImplOptions.NoInlining)] public PatchContext AcquireContext() { + Assembly assembly = Assembly.GetCallingAssembly(); var context = new PatchContext(this); - _contexts.Add(context); + lock (_contexts) + { + if (!_contexts.TryGetValue(assembly, out List localContexts)) + _contexts.Add(assembly, localContexts = new List()); + localContexts.Add(context); + } return context; } @@ -49,10 +62,43 @@ namespace Torch.Managers.PatchManager /// Frees the given context, and unregister all patches from it. A call to will apply the unpatching operation. /// /// Context to remove + [MethodImpl(MethodImplOptions.NoInlining)] public void FreeContext(PatchContext context) { + Assembly assembly = Assembly.GetCallingAssembly(); context.RemoveAll(); - _contexts.Remove(context); + lock (_contexts) + { + if (_contexts.TryGetValue(assembly, out List localContexts)) + localContexts.Remove(context); + } + } + + /// + /// Frees all contexts owned by the given assembly. A call to will apply the unpatching operation. + /// + /// Assembly to retrieve owned contexts for + /// Callback to run for before each context is freed, ignored if null. + /// number of contexts freed + internal int FreeAllContexts(Assembly assembly, Action callback = null) + { + List localContexts; + lock (_contexts) + { + if (!_contexts.TryGetValue(assembly, out localContexts)) + return 0; + _contexts.Remove(assembly); + } + if (localContexts == null) + return 0; + int count = localContexts.Count; + foreach (PatchContext k in localContexts) + { + callback?.Invoke(k); + k.RemoveAll(); + } + localContexts.Clear(); + return count; } /// @@ -80,7 +126,8 @@ namespace Torch.Managers.PatchManager foreach (DecoratedMethod m in _rewritePatterns.Values) m.Revert(); _rewritePatterns.Clear(); - _contexts.Clear(); + lock (_contexts) + _contexts.Clear(); } } } diff --git a/Torch/Torch.csproj b/Torch/Torch.csproj index 3fb3a01..a39572e 100644 --- a/Torch/Torch.csproj +++ b/Torch/Torch.csproj @@ -160,12 +160,10 @@ - - - - - - + + + +