Skip to content

Commit d45523c

Browse files
committed
Bring back LeaksContainer
It went away when removing the base safe handle, but we need it for the debug/CI builds.
1 parent c9c49b1 commit d45523c

File tree

3 files changed

+116
-5
lines changed

3 files changed

+116
-5
lines changed

LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ public virtual void Dispose()
234234
if (LeaksContainer.TypeNames.Any())
235235
{
236236
Assert.False(true, string.Format("Some handles of the following types haven't been properly released: {0}.{1}"
237-
+ "In order to get some help fixing those leaks, uncomment the define LEAKS_TRACKING in SafeHandleBase.cs{1}"
237+
+ "In order to get some help fixing those leaks, uncomment the define LEAKS_TRACKING in Libgit2Object.cs{1}"
238238
+ "and run the tests locally.", string.Join(", ", LeaksContainer.TypeNames), Environment.NewLine));
239239
}
240240
#endif

LibGit2Sharp/Core/Handles/Libgit2Object.cs

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,90 @@
1-
using System;
1+
// This activates a lightweight mode which will help put under the light
2+
// incorrectly released handles by outputing a warning message in the console.
3+
//
4+
// This should be activated when tests are being run of the CI server.
5+
//
6+
// Uncomment the line below or add a conditional symbol to activate this mode
7+
8+
// #define LEAKS_IDENTIFYING
9+
10+
// This activates a more throrough mode which will show the stack trace of the
11+
// allocation code path for each handle that has been improperly released.
12+
//
13+
// This should be manually activated when some warnings have been raised as
14+
// a result of LEAKS_IDENTIFYING mode activation.
15+
//
16+
// Uncomment the line below or add a conditional symbol to activate this mode
17+
18+
// #define LEAKS_TRACKING
19+
20+
using System;
21+
using System.Linq;
22+
using System.Diagnostics;
23+
using System.Globalization;
24+
using System.Collections.Generic;
25+
26+
#if LEAKS_IDENTIFYING
27+
namespace LibGit2Sharp.Core
28+
{
29+
/// <summary>
30+
/// Holds leaked handle type names reported by <see cref="Core.Handles.Libgit2Object"/>
31+
/// </summary>
32+
public static class LeaksContainer
33+
{
34+
private static readonly HashSet<string> _typeNames = new HashSet<string>();
35+
private static readonly object _lockpad = new object();
36+
37+
/// <summary>
38+
/// Report a new leaked handle type name
39+
/// </summary>
40+
/// <param name="typeName">Short name of the leaked handle type.</param>
41+
public static void Add(string typeName)
42+
{
43+
lock (_lockpad)
44+
{
45+
_typeNames.Add(typeName);
46+
}
47+
}
48+
49+
/// <summary>
50+
/// Removes all previously reported leaks.
51+
/// </summary>
52+
public static void Clear()
53+
{
54+
lock (_lockpad)
55+
{
56+
_typeNames.Clear();
57+
}
58+
}
59+
60+
/// <summary>
61+
/// Returns all reported leaked handle type names.
62+
/// </summary>
63+
public static IEnumerable<string> TypeNames
64+
{
65+
get
66+
{
67+
string[] result = null;
68+
lock (_lockpad)
69+
{
70+
result = _typeNames.ToArray();
71+
}
72+
return result;
73+
}
74+
}
75+
}
76+
}
77+
#endif
278

379
namespace LibGit2Sharp.Core.Handles
480
{
581
internal unsafe abstract class Libgit2Object : IDisposable
682
{
83+
#if LEAKS_TRACKING
84+
private readonly string trace;
85+
private readonly Guid id;
86+
#endif
87+
788
protected void* ptr;
889

990
internal void* Handle
@@ -21,12 +102,17 @@ internal unsafe Libgit2Object(void* handle, bool owned)
21102
{
22103
this.ptr = handle;
23104
this.owned = owned;
105+
106+
#if LEAKS_TRACKING
107+
id = Guid.NewGuid();
108+
Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "Allocating {0} handle ({1})", GetType().Name, id));
109+
trace = new StackTrace(2, true).ToString();
110+
#endif
24111
}
25112

26113
internal unsafe Libgit2Object(IntPtr ptr, bool owned)
114+
: this(ptr.ToPointer(), owned)
27115
{
28-
this.ptr = ptr.ToPointer();
29-
this.owned = owned;
30116
}
31117

32118
~Libgit2Object()
@@ -51,6 +137,15 @@ internal IntPtr AsIntPtr()
51137

52138
void Dispose(bool disposing)
53139
{
140+
#if LEAKS_IDENTIFYING
141+
bool leaked = !disposing && ptr != null;
142+
143+
if (leaked)
144+
{
145+
LeaksContainer.Add(GetType().Name);
146+
}
147+
#endif
148+
54149
if (!disposed)
55150
{
56151
if (owned)
@@ -62,6 +157,19 @@ void Dispose(bool disposing)
62157
}
63158

64159
disposed = true;
160+
161+
#if LEAKS_TRACKING
162+
if (!leaked)
163+
{
164+
Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "Disposing {0} handle ({1})", GetType().Name, id));
165+
}
166+
else
167+
{
168+
Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "Unexpected finalization of {0} handle ({1})", GetType().Name, id));
169+
Trace.WriteLine(trace);
170+
Trace.WriteLine("");
171+
}
172+
#endif
65173
}
66174

67175
public void Dispose()

LibGit2Sharp/Tree.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ internal string Path
7777

7878
unsafe TreeEntry byIndex(ObjectSafeWrapper obj, uint i, ObjectId parentTreeId, Repository repo, FilePath parentPath)
7979
{
80-
return new TreeEntry(Proxy.git_tree_entry_byindex(obj.ObjectPtr, i), parentTreeId, repo, parentPath);
80+
using (var entryHandle = Proxy.git_tree_entry_byindex(obj.ObjectPtr, i))
81+
{
82+
return new TreeEntry(entryHandle, parentTreeId, repo, parentPath);
83+
}
8184
}
8285

8386
/// <summary>

0 commit comments

Comments
 (0)