// This activates a lightweight mode which will help put under the light // incorrectly released handles by outputing a warning message in the console. // // This should be activated when tests are being run on the CI server. // // Uncomment the line below or add a conditional symbol to activate this mode // #define LEAKS_IDENTIFYING // This activates a more throrough mode which will show the stack trace of the // allocation code path for each handle that has been improperly released. // // This should be manually activated when some warnings have been raised as // a result of LEAKS_IDENTIFYING mode activation. // // Uncomment the line below or add a conditional symbol to activate this mode // #define LEAKS_TRACKING using System; using System.Linq; using System.Diagnostics; using System.Globalization; using System.Collections.Generic; #if LEAKS_IDENTIFYING namespace LibGit2Sharp.Core { /// /// Holds leaked handle type names reported by /// public static class LeaksContainer { private static readonly HashSet _typeNames = new HashSet(); private static readonly object _lockpad = new object(); /// /// Report a new leaked handle type name /// /// Short name of the leaked handle type. public static void Add(string typeName) { lock (_lockpad) { _typeNames.Add(typeName); } } /// /// Removes all previously reported leaks. /// public static void Clear() { lock (_lockpad) { _typeNames.Clear(); } } /// /// Returns all reported leaked handle type names. /// public static IEnumerable TypeNames { get { string[] result = null; lock (_lockpad) { result = _typeNames.ToArray(); } return result; } } } } #endif namespace LibGit2Sharp.Core.Handles { internal unsafe abstract class Libgit2Object : IDisposable { #if LEAKS_TRACKING private readonly string trace; private readonly Guid id; #endif protected void* ptr; internal void* Handle { get { return ptr; } } bool owned; bool disposed; internal unsafe Libgit2Object(void* handle, bool owned) { this.ptr = handle; this.owned = owned; #if LEAKS_TRACKING id = Guid.NewGuid(); Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "Allocating {0} handle ({1})", GetType().Name, id)); trace = new StackTrace(2, true).ToString(); #endif } internal unsafe Libgit2Object(IntPtr ptr, bool owned) : this(ptr.ToPointer(), owned) { } ~Libgit2Object() { Dispose(false); } internal bool IsNull { get { return ptr == null; } } internal IntPtr AsIntPtr() { return new IntPtr(ptr); } public abstract void Free(); void Dispose(bool disposing) { #if LEAKS_IDENTIFYING bool leaked = !disposing && ptr != null; if (leaked) { LeaksContainer.Add(GetType().Name); } #endif if (!disposed) { if (owned) { Free(); } ptr = null; } disposed = true; #if LEAKS_TRACKING if (!leaked) { Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "Disposing {0} handle ({1})", GetType().Name, id)); } else { Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "Unexpected finalization of {0} handle ({1})", GetType().Name, id)); Trace.WriteLine(trace); Trace.WriteLine(""); } #endif } public void Dispose() { Dispose(true); } } }