Skip to content

Commit 9ccf55c

Browse files
committed
Initial commit
1 parent 5330bbb commit 9ccf55c

File tree

5 files changed

+259
-0
lines changed

5 files changed

+259
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace GitVersion
6+
{
7+
public static class GitVersionCoreDefaults
8+
{
9+
public const int LockTimeoutInMilliseconds = 1000 * 15;
10+
public const string LockFileNameWithExtensions = "GitVersion.lock";
11+
}
12+
}

src/GitVersionCore/GitVersionCoreModule.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
using System;
2+
using System.IO;
23
using GitVersion.BuildAgents;
34
using GitVersion.Common;
45
using GitVersion.Configuration;
56
using GitVersion.Configuration.Init;
67
using GitVersion.Extensions;
8+
using GitVersion.Helpers;
9+
using GitVersion.Helpers.Abstractions;
710
using GitVersion.Logging;
811
using GitVersion.VersionCalculation;
912
using GitVersion.VersionCalculation.Cache;
@@ -29,6 +32,15 @@ public void RegisterTypes(IServiceCollection services)
2932
services.AddSingleton<IConsole, ConsoleAdapter>();
3033
services.AddSingleton<IGitVersionCache, GitVersionCache>();
3134

35+
services.AddSingleton<IFileLock>((serviceProvider) => {
36+
var gitVersionCache = serviceProvider.GetRequiredService<IGitVersionCache>();
37+
var cacheDirectory = gitVersionCache.GetCacheDirectory();
38+
var lockFilePath = Path.Combine(cacheDirectory, GitVersionCoreDefaults.LockFileNameWithExtensions);
39+
var fileStream = LockFile.WaitUntilAcquired(lockFilePath, GitVersionCoreDefaults.LockTimeoutInMilliseconds);
40+
var fileLock = new FileLock(fileStream);
41+
return fileLock;
42+
});
43+
3244
services.AddSingleton<IGitVersionCacheKeyFactory, GitVersionCacheKeyFactory>();
3345
services.AddSingleton<IGitVersionContextFactory, GitVersionContextFactory>();
3446
services.AddSingleton<IConfigFileLocatorFactory, ConfigFileLocatorFactory>();
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
using System.IO;
3+
4+
namespace GitVersion.Helpers.Abstractions
5+
{
6+
public interface IFileLock : IDisposable
7+
{
8+
FileStream FileStream { get; }
9+
}
10+
}
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/*
2+
MIT License
3+
4+
Copyright 2020 Teroneko
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights to
9+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10+
of the Software, and to permit persons to whom the Software is furnished to do
11+
so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22+
*/
23+
using System;
24+
using System.IO;
25+
using System.Threading;
26+
27+
namespace GitVersion.Helpers
28+
{
29+
/// <summary>
30+
/// This helper class can lock files.
31+
/// </summary>
32+
public static class LockFile
33+
{
34+
public const FileMode DefaultFileMode = FileMode.OpenOrCreate;
35+
public const FileAccess DefaultFileAccess = FileAccess.ReadWrite;
36+
public const FileShare DefaultFileShare = FileShare.None;
37+
public const int DefaultTimeoutInMilliseconds = Timeout.Infinite;
38+
39+
/// <summary>
40+
/// Try to acquire lock on file but only as long the file stream is opened.
41+
/// </summary>
42+
/// <param name="filePath">The path to file that get locked.</param>
43+
/// <param name="fileStream">The locked file as file stream.</param>
44+
/// <param name="fileMode">The file mode when opening file.</param>
45+
/// <param name="fileAccess">The file access when opening file.</param>
46+
/// <param name="fileShare">The file share when opening file</param>
47+
/// <returns>If true the lock acquirement was successful.</returns>
48+
public static bool TryAcquire(string filePath, out FileStream? fileStream, FileMode fileMode = DefaultFileMode,
49+
FileAccess fileAccess = DefaultFileAccess, FileShare fileShare = DefaultFileShare)
50+
{
51+
filePath = filePath ?? throw new ArgumentNullException(nameof(filePath));
52+
53+
try
54+
{
55+
fileStream = File.Open(filePath, fileMode, fileAccess, fileShare);
56+
return true;
57+
}
58+
catch (Exception error) when (error.GetType() == typeof(IOException))
59+
{
60+
fileStream = null;
61+
return false;
62+
}
63+
}
64+
65+
/// <summary>
66+
/// Try to acquire lock on file but only as long the file stream is opened.
67+
/// </summary>
68+
/// <param name="filePath">The path to file that get locked.</param>
69+
/// <param name="fileMode">The file mode when opening file.</param>
70+
/// <param name="fileAccess">The file access when opening file.</param>
71+
/// <param name="fileShare">The file share when opening file</param>
72+
/// <returns>If not null the lock acquirement was successful.</returns>
73+
public static FileStream? TryAcquire(string filePath, FileMode fileMode = DefaultFileMode,
74+
FileAccess fileAccess = DefaultFileAccess, FileShare fileShare = DefaultFileShare)
75+
{
76+
TryAcquire(filePath, out var fileStream, fileMode: fileMode,
77+
fileAccess: fileAccess, fileShare: fileShare);
78+
79+
return fileStream;
80+
}
81+
82+
private static bool waitUntilAcquired(string filePath, out FileStream? fileStream, FileMode fileMode,
83+
FileAccess fileAccess, FileShare fileShare, int timeoutInMilliseconds, bool throwOnTimeout)
84+
{
85+
FileStream spinningFileStream = null;
86+
87+
var spinHasBeenFinished = SpinWait.SpinUntil(() => {
88+
return TryAcquire(filePath, out spinningFileStream, fileMode: fileMode, fileAccess: fileAccess, fileShare: fileShare);
89+
}, timeoutInMilliseconds);
90+
91+
if (spinHasBeenFinished)
92+
{
93+
fileStream = spinningFileStream ?? throw new ArgumentNullException(nameof(spinningFileStream));
94+
return true;
95+
}
96+
else
97+
{
98+
if (throwOnTimeout)
99+
{
100+
throw new TimeoutException($"Waiting until file got acquired failed.");
101+
}
102+
103+
fileStream = null;
104+
return false;
105+
}
106+
}
107+
108+
private static FileStream? waitUntilAcquired(string filePath, FileMode fileMode,
109+
FileAccess fileAccess, FileShare fileShare, int timeoutInMilliseconds, bool noThrowOnTimeout)
110+
{
111+
waitUntilAcquired(filePath, out var fileStream, fileMode, fileAccess, fileShare, timeoutInMilliseconds, !noThrowOnTimeout);
112+
return fileStream;
113+
}
114+
115+
/// <summary>
116+
/// Wait until file gets acquired lock but only as long the file stream is opened.
117+
/// </summary>
118+
/// <param name="filePath">The path to file that get locked.</param>
119+
/// <param name="fileStream">The locked file as file stream.</param>
120+
/// <param name="fileMode">The file mode when opening file.</param>
121+
/// <param name="fileAccess">The file access when opening file.</param>
122+
/// <param name="fileShare">The file share when opening file</param>
123+
/// <returns>If true the lock acquirement was successful.</returns>
124+
public static bool WaitUntilAcquired(string filePath, out FileStream? fileStream, FileMode fileMode = DefaultFileMode,
125+
FileAccess fileAccess = DefaultFileAccess, FileShare fileShare = DefaultFileShare, bool throwOnTimeout = false)
126+
{
127+
var timeoutInMilliseconds = DefaultTimeoutInMilliseconds;
128+
return waitUntilAcquired(filePath, out fileStream, fileMode, fileAccess, fileShare, timeoutInMilliseconds, throwOnTimeout);
129+
}
130+
131+
/// <summary>
132+
/// Wait until file gets acquired lock but only as long the file stream is opened.
133+
/// </summary>
134+
/// <param name="filePath">The path to file that get locked.</param>
135+
/// <param name="fileMode">The file mode when opening file.</param>
136+
/// <param name="fileAccess">The file access when opening file.</param>
137+
/// <param name="fileShare">The file share when opening file</param>
138+
/// <returns>If not null the lock acquirement was successful.</returns>
139+
public static FileStream? WaitUntilAcquired(string filePath, FileMode fileMode = DefaultFileMode,
140+
FileAccess fileAccess = DefaultFileAccess, FileShare fileShare = DefaultFileShare, bool noThrowOnTimeout = false)
141+
{
142+
var timeoutInMilliseconds = DefaultTimeoutInMilliseconds;
143+
return waitUntilAcquired(filePath, fileMode, fileAccess, fileShare, timeoutInMilliseconds, noThrowOnTimeout);
144+
}
145+
146+
/// <summary>
147+
/// Wait until file gets acquired lock but only as long the file stream is opened.
148+
/// </summary>
149+
/// <param name="filePath">The path to file that get locked.</param>
150+
/// <param name="timeoutInMilliseconds">The timeout in milliseconds.</param>
151+
/// <param name="fileStream">The locked file as file stream.</param>
152+
/// <param name="fileMode">The file mode when opening file.</param>
153+
/// <param name="fileAccess">The file access when opening file.</param>
154+
/// <param name="fileShare">The file share when opening file</param>
155+
/// <returns>If true the lock acquirement was successful.</returns>
156+
public static bool WaitUntilAcquired(string filePath, int timeoutInMilliseconds, out FileStream? fileStream, FileMode fileMode = DefaultFileMode,
157+
FileAccess fileAccess = DefaultFileAccess, FileShare fileShare = DefaultFileShare, bool throwOnTimeout = false) =>
158+
waitUntilAcquired(filePath, out fileStream, fileMode, fileAccess, fileShare, timeoutInMilliseconds, throwOnTimeout);
159+
160+
/// <summary>
161+
/// Wait until file gets acquired lock but only as long the file stream is opened.
162+
/// </summary>
163+
/// <param name="filePath">The path to file that get locked.</param>
164+
/// <param name="timeoutInMilliseconds">The timeout in milliseconds.</param>
165+
/// <param name="fileStream">The locked file as file stream.</param>
166+
/// <param name="fileMode">The file mode when opening file.</param>
167+
/// <param name="fileAccess">The file access when opening file.</param>
168+
/// <param name="fileShare">The file share when opening file</param>
169+
/// <returns>If not null the lock acquirement was successful.</returns>
170+
public static FileStream? WaitUntilAcquired(string filePath, int timeoutInMilliseconds, FileMode fileMode = DefaultFileMode,
171+
FileAccess fileAccess = DefaultFileAccess, FileShare fileShare = DefaultFileShare, bool noThrowOnTimeout = false) =>
172+
waitUntilAcquired(filePath, fileMode, fileAccess, fileShare, timeoutInMilliseconds, noThrowOnTimeout);
173+
174+
/// <summary>
175+
/// Wait until file gets acquired lock but only as long the file stream is opened.
176+
/// </summary>
177+
/// <param name="filePath">The path to file that get locked.</param>
178+
/// <param name="timeout">The timeout specified as <see cref="TimeSpan"/>.</param>
179+
/// <param name="fileStream">The locked file as file stream.</param>
180+
/// <param name="fileMode">The file mode when opening file.</param>
181+
/// <param name="fileAccess">The file access when opening file.</param>
182+
/// <param name="fileShare">The file share when opening file</param>
183+
/// <returns>If true the lock acquirement was successful.</returns>
184+
public static bool WaitUntilAcquired(string filePath, TimeSpan timeout, out FileStream? fileStream, FileMode fileMode = DefaultFileMode,
185+
FileAccess fileAccess = DefaultFileAccess, FileShare fileShare = DefaultFileShare, bool throwOnTimeout = false)
186+
{
187+
var timeoutInMilliseconds = Convert.ToInt32(timeout.TotalMilliseconds);
188+
return waitUntilAcquired(filePath, out fileStream, fileMode, fileAccess, fileShare, timeoutInMilliseconds, throwOnTimeout);
189+
}
190+
191+
/// <summary>
192+
/// Wait until file gets acquired lock but only as long the file stream is opened.
193+
/// </summary>
194+
/// <param name="filePath">The path to file that get locked.</param>
195+
/// <param name="timeout">The timeout specified as <see cref="TimeSpan"/>.</param>
196+
/// <param name="fileStream">The locked file as file stream.</param>
197+
/// <param name="fileMode">The file mode when opening file.</param>
198+
/// <param name="fileAccess">The file access when opening file.</param>
199+
/// <param name="fileShare">The file share when opening file</param>
200+
/// <returns>If ont null lock acquirement was successful.</returns>
201+
public static FileStream? WaitUntilAcquired(string filePath, TimeSpan timeout, FileMode fileMode = DefaultFileMode,
202+
FileAccess fileAccess = DefaultFileAccess, FileShare fileShare = DefaultFileShare, bool noThrowOnTimeout = false)
203+
{
204+
var timeoutInMilliseconds = Convert.ToInt32(timeout.TotalMilliseconds);
205+
return waitUntilAcquired(filePath, fileMode, fileAccess, fileShare, timeoutInMilliseconds, noThrowOnTimeout);
206+
}
207+
}
208+
}

src/GitVersionCore/Model/FileLock.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using GitVersion.Helpers.Abstractions;
2+
using System;
3+
using System.IO;
4+
5+
namespace GitVersion.Helpers
6+
{
7+
public class FileLock : IFileLock
8+
{
9+
public FileStream FileStream { get; }
10+
11+
public FileLock(FileStream fileStream) =>
12+
fileStream = fileStream ?? throw new ArgumentNullException(nameof(fileStream));
13+
14+
public void Dispose() =>
15+
FileStream.Dispose();
16+
}
17+
}

0 commit comments

Comments
 (0)