forked from libgit2/libgit2sharp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSafeHandleBase.cs
More file actions
116 lines (104 loc) · 3.9 KB
/
SafeHandleBase.cs
File metadata and controls
116 lines (104 loc) · 3.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using Interlocked = System.Threading.Interlocked;
namespace LibGit2Sharp.Core.Handles
{
internal abstract class SafeHandleBase : SafeHandle
{
#if LEAKS
private readonly string trace;
#endif
/// <summary>
/// This is set to non-zero when <see cref="NativeMethods.AddHandle"/> has
/// been called for this handle, but <see cref="NativeMethods.RemoveHandle"/>
/// has not yet been called.
/// </summary>
private int registered;
protected SafeHandleBase()
: base(IntPtr.Zero, true)
{
NativeMethods.AddHandle();
registered = 1;
#if LEAKS
trace = new StackTrace(2, true).ToString();
#endif
}
#if DEBUG
protected override void Dispose(bool disposing)
{
if (!disposing && !IsInvalid)
{
Trace.WriteLine(string.Format(CultureInfo.InvariantCulture, "A {0} handle wrapper has not been properly disposed.", GetType().Name));
#if LEAKS
Trace.WriteLine(trace);
#endif
Trace.WriteLine("");
}
base.Dispose(disposing);
}
#endif
// Prevent the debugger from evaluating this property because it has side effects
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public override sealed bool IsInvalid
{
get
{
bool invalid = IsInvalidImpl();
if (invalid && Interlocked.CompareExchange(ref registered, 0, 1) == 1)
{
/* Unregister the handle because we know ReleaseHandle won't be called
* to do it for us.
*/
NativeMethods.RemoveHandle();
}
return invalid;
}
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
protected virtual bool IsInvalidImpl()
{
return handle == IntPtr.Zero;
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
protected abstract bool ReleaseHandleImpl();
protected override sealed bool ReleaseHandle()
{
bool result;
try
{
result = ReleaseHandleImpl();
}
finally
{
if (Interlocked.CompareExchange(ref registered, 0, 1) == 1)
{
// if the handle is still registered at this point, we definitely
// want to unregister it
NativeMethods.RemoveHandle();
}
else
{
/* For this to be called, the following sequence of events must occur:
*
* 1. The handle is created
* 2. The IsInvalid property is evaluated, and the result is false
* 3. The IsInvalid property is evaluated by the runtime to determine if
* finalization is necessary, and the result is now true
*
* This can only happen if the value of `handle` is manipulated in an unexpected
* way (through the Reflection API or by a specially-crafted derived type that
* does not currently exist). The only safe course of action at this point in
* the shutdown process is returning false, which will trigger the
* releaseHandleFailed MDA but have no other impact on the CLR state.
* http://msdn.microsoft.com/en-us/library/85eak4a0.aspx
*/
result = false;
}
}
return result;
}
}
}