Skip to content

Commit f835d13

Browse files
committed
Introduce DisposableObject base class to impl the dispose pattern
Implement the dispose pattern correctly for all IDisposable objects by introducing a common abstract `DisposableObject` class which 'does the correct thing'.
1 parent e355b12 commit f835d13

File tree

10 files changed

+128
-115
lines changed

10 files changed

+128
-115
lines changed

src/shared/GitHub/GitHubHostProvider.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public override string GetCredentialKey(InputArguments input)
6464

6565
public override async Task<ICredential> GenerateCredentialAsync(InputArguments input)
6666
{
67+
ThrowIfDisposed();
68+
6769
// We should not allow unencrypted communication and should inform the user
6870
if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http"))
6971
{
@@ -105,14 +107,10 @@ public override async Task<ICredential> GenerateCredentialAsync(InputArguments i
105107
throw new Exception($"Interactive logon for '{targetUri}' failed.");
106108
}
107109

108-
protected override void Dispose(bool disposing)
110+
protected override void ReleaseManagedResources()
109111
{
110-
if (disposing)
111-
{
112-
_gitHubApi.Dispose();
113-
}
114-
115-
base.Dispose(disposing);
112+
_gitHubApi.Dispose();
113+
base.ReleaseManagedResources();
116114
}
117115

118116
#region Private Methods

src/shared/Microsoft.AzureRepos/AzureReposHostProvider.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public override string GetCredentialKey(InputArguments input)
5454

5555
public override async Task<ICredential> GenerateCredentialAsync(InputArguments input)
5656
{
57+
ThrowIfDisposed();
58+
5759
// We should not allow unencrypted communication and should inform the user
5860
if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http"))
5961
{
@@ -94,14 +96,10 @@ public override async Task<ICredential> GenerateCredentialAsync(InputArguments i
9496
return new GitCredential(Constants.PersonalAccessTokenUserName, pat);
9597
}
9698

97-
protected override void Dispose(bool disposing)
99+
protected override void ReleaseManagedResources()
98100
{
99-
if (disposing)
100-
{
101-
_azDevOps.Dispose();
102-
}
103-
104-
base.Dispose(disposing);
101+
_azDevOps.Dispose();
102+
base.ReleaseManagedResources();
105103
}
106104

107105
#endregion

src/shared/Microsoft.Git.CredentialManager/CommandContext.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public interface ICommandContext : IDisposable
5454
/// <summary>
5555
/// Real command execution environment using the actual <see cref="Console"/>, file system calls and environment.
5656
/// </summary>
57-
public class CommandContext : ICommandContext
57+
public class CommandContext : DisposableObject, ICommandContext
5858
{
5959
private readonly IGit _git;
6060

@@ -111,10 +111,13 @@ public CommandContext()
111111

112112
#region IDisposable
113113

114-
public void Dispose()
114+
protected override void ReleaseManagedResources()
115115
{
116116
Settings?.Dispose();
117117
_git?.Dispose();
118+
Trace?.Dispose();
119+
120+
base.ReleaseManagedResources();
118121
}
119122

120123
#endregion
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
using System;
4+
5+
namespace Microsoft.Git.CredentialManager
6+
{
7+
/// <summary>
8+
/// An object that implements the <see cref="IDisposable"/> interface and the disposable pattern.
9+
/// </summary>
10+
public abstract class DisposableObject : IDisposable
11+
{
12+
private bool _isDisposed;
13+
14+
/// <summary>
15+
/// Throw an exception if the object has been disposed.
16+
/// </summary>
17+
/// <exception cref="ObjectDisposedException">Thrown if the object has been disposed.</exception>
18+
protected void ThrowIfDisposed()
19+
{
20+
if (_isDisposed)
21+
{
22+
throw new ObjectDisposedException(GetType().Name);
23+
}
24+
}
25+
26+
/// <summary>
27+
/// Called when unmanaged resources should be released and memory freed.
28+
/// </summary>
29+
protected virtual void ReleaseUnmanagedResources() { }
30+
31+
/// <summary>
32+
/// Called when managed resources should be released.
33+
/// </summary>
34+
protected virtual void ReleaseManagedResources() { }
35+
36+
/// <summary>
37+
/// Called when the application is being terminated. Clean up and release any resources.
38+
/// </summary>
39+
/// <param name="disposing">True if the instance is being disposed, false if being finalized.</param>
40+
private void Dispose(bool disposing)
41+
{
42+
if (_isDisposed)
43+
{
44+
return;
45+
}
46+
47+
ReleaseUnmanagedResources();
48+
49+
if (disposing)
50+
{
51+
ReleaseManagedResources();
52+
}
53+
54+
_isDisposed = true;
55+
}
56+
57+
public void Dispose()
58+
{
59+
Dispose(true);
60+
GC.SuppressFinalize(this);
61+
}
62+
63+
~DisposableObject()
64+
{
65+
Dispose(false);
66+
}
67+
}
68+
}

src/shared/Microsoft.Git.CredentialManager/GenericHostProvider.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ public override string GetCredentialKey(InputArguments input)
5353

5454
public override async Task<ICredential> GenerateCredentialAsync(InputArguments input)
5555
{
56+
ThrowIfDisposed();
57+
5658
Uri uri = GetUriFromInput(input);
5759

5860
// Determine the if the host supports Windows Integration Authentication (WIA)
@@ -115,10 +117,10 @@ private bool IsWindowsAuthAllowed
115117
}
116118
}
117119

118-
protected override void Dispose(bool disposing)
120+
protected override void ReleaseManagedResources()
119121
{
120122
_winAuth.Dispose();
121-
base.Dispose(disposing);
123+
base.ReleaseManagedResources();
122124
}
123125

124126
#endregion

src/shared/Microsoft.Git.CredentialManager/HostProvider.cs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public interface IHostProvider : IDisposable
5858
/// Represents a Git hosting provider where credentials can be stored and recalled in/from the Operating System's
5959
/// secure credential store.
6060
/// </summary>
61-
public abstract class HostProvider : IHostProvider
61+
public abstract class HostProvider : DisposableObject, IHostProvider
6262
{
6363
protected HostProvider(ICommandContext context)
6464
{
@@ -194,22 +194,5 @@ public virtual Task EraseCredentialAsync(InputArguments input)
194194

195195
return Task.CompletedTask;
196196
}
197-
198-
/// <summary>
199-
/// Called when the application is being terminated. Clean up and release any resources.
200-
/// </summary>
201-
/// <param name="disposing">True if the instance is being disposed, false if being finalized.</param>
202-
protected virtual void Dispose(bool disposing) { }
203-
204-
public void Dispose()
205-
{
206-
Dispose(true);
207-
GC.SuppressFinalize(this);
208-
}
209-
210-
~HostProvider()
211-
{
212-
Dispose(false);
213-
}
214197
}
215198
}

src/shared/Microsoft.Git.CredentialManager/Interop/LibGit2.cs

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace Microsoft.Git.CredentialManager.Interop
1010
{
11-
public class LibGit2 : IGit
11+
public class LibGit2 : DisposableObject, IGit
1212
{
1313
private readonly ITrace _trace;
1414

@@ -24,6 +24,8 @@ public LibGit2(ITrace trace)
2424

2525
public unsafe IGitConfiguration GetConfiguration(string repositoryPath)
2626
{
27+
ThrowIfDisposed();
28+
2729
_trace.WriteLine("Opening default Git configuration...");
2830

2931
// Open the default, non-repository-scoped configuration (progdata, system, xdg, global)
@@ -59,6 +61,8 @@ public unsafe IGitConfiguration GetConfiguration(string repositoryPath)
5961

6062
public string GetRepositoryPath(string path)
6163
{
64+
ThrowIfDisposed();
65+
6266
_trace.WriteLine($"Discovering repository from path '{path}'...");
6367

6468
var buf = new git_buf();
@@ -85,24 +89,14 @@ public string GetRepositoryPath(string path)
8589
}
8690
}
8791

88-
private void Dispose(bool disposing)
92+
protected override void ReleaseUnmanagedResources()
8993
{
9094
git_libgit2_shutdown();
91-
}
92-
93-
public void Dispose()
94-
{
95-
Dispose(true);
96-
GC.SuppressFinalize(this);
97-
}
98-
99-
~LibGit2()
100-
{
101-
Dispose(false);
95+
base.ReleaseUnmanagedResources();
10296
}
10397
}
10498

105-
internal class LibGit2Configuration : IGitConfiguration
99+
internal class LibGit2Configuration : DisposableObject, IGitConfiguration
106100
{
107101
private readonly ITrace _trace;
108102
private readonly unsafe git_config* _config;
@@ -122,6 +116,8 @@ internal unsafe LibGit2Configuration(ITrace trace, git_config* config)
122116

123117
public unsafe void Enumerate(GitConfigurationEnumerationCallback cb)
124118
{
119+
ThrowIfDisposed();
120+
125121
int native_cb(git_config_entry entry, void* payload)
126122
{
127123
if (!cb(entry.GetName(), entry.GetValue()))
@@ -149,6 +145,8 @@ int native_cb(git_config_entry entry, void* payload)
149145

150146
public unsafe bool TryGetValue(string name, out string value)
151147
{
148+
ThrowIfDisposed();
149+
152150
_trace.WriteLine($"Reading Git configuration entry '{name}'...");
153151
int result = git_config_get_string(out value, _snapshot, name);
154152

@@ -169,7 +167,7 @@ public unsafe bool TryGetValue(string name, out string value)
169167
return false;
170168
}
171169

172-
private void Dispose(bool disposing)
170+
protected override void ReleaseUnmanagedResources()
173171
{
174172
unsafe
175173
{
@@ -178,16 +176,5 @@ private void Dispose(bool disposing)
178176
git_config_free(_config);
179177
}
180178
}
181-
182-
public void Dispose()
183-
{
184-
Dispose(true);
185-
GC.SuppressFinalize(this);
186-
}
187-
188-
~LibGit2Configuration()
189-
{
190-
Dispose(false);
191-
}
192179
}
193180
}

src/shared/Microsoft.Git.CredentialManager/Interop/Posix/PosixFileDescriptor.cs

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@ namespace Microsoft.Git.CredentialManager.Interop.Posix
99
/// <summary>
1010
/// Represents a thin wrapper over a POSIX file descriptor.
1111
/// </summary>
12-
public class PosixFileDescriptor : IDisposable
12+
public class PosixFileDescriptor : DisposableObject
1313
{
1414
private readonly int _fd;
1515

16-
private bool _isDisposed;
17-
1816
private PosixFileDescriptor()
1917
{
2018
PlatformUtils.EnsurePosix();
@@ -72,27 +70,14 @@ public int Write(string str)
7270
return Write(buf, buf.Length);
7371
}
7472

75-
private void Dispose(bool disposing)
73+
protected override void ReleaseUnmanagedResources()
7674
{
77-
if (_isDisposed)
78-
{
79-
return;
80-
}
81-
8275
if (!IsInvalid)
8376
{
8477
Unistd.close(_fd);
8578
}
8679

87-
_isDisposed = true;
88-
}
89-
90-
private void ThrowIfDisposed()
91-
{
92-
if (_isDisposed)
93-
{
94-
throw new ObjectDisposedException(nameof(PosixFileDescriptor));
95-
}
80+
base.ReleaseUnmanagedResources();
9681
}
9782

9883
private void ThrowIfInvalid()
@@ -102,16 +87,5 @@ private void ThrowIfInvalid()
10287
throw new InvalidOperationException("File descriptor is invalid");
10388
}
10489
}
105-
106-
public void Dispose()
107-
{
108-
Dispose(true);
109-
GC.SuppressFinalize(this);
110-
}
111-
112-
~PosixFileDescriptor()
113-
{
114-
Dispose(false);
115-
}
11690
}
11791
}

0 commit comments

Comments
 (0)