Skip to content

Commit 38ee45f

Browse files
committed
LockFile API is now mockable; FileLocker implements IFileLocker and can produce FileLockUses; FileLock wraps FileLockUse and implements IDisposable for being cleaned when disposing ServiceProvider
1 parent 9ccf55c commit 38ee45f

14 files changed

+616
-43
lines changed

src/GitVersionCore/Core/Abstractions/IFileSystem.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public interface IFileSystem
1414
void WriteAllText(string file, string fileContents);
1515
void WriteAllText(string file, string fileContents, Encoding encoding);
1616
IEnumerable<string> DirectoryEnumerateFiles(string directory, string searchPattern, SearchOption searchOption);
17+
FileStream Open(string path, FileMode mode, FileAccess access, FileShare share);
1718
Stream OpenWrite(string path);
1819
Stream OpenRead(string path);
1920
void CreateDirectory(string path);

src/GitVersionCore/Core/FileSystem.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ public IEnumerable<string> DirectoryEnumerateFiles(string directory, string sear
5151
return Directory.EnumerateFiles(directory, searchPattern, searchOption);
5252
}
5353

54+
public FileStream Open(string path, FileMode mode, FileAccess access, FileShare share)
55+
{
56+
return File.Open(path, mode, access, share);
57+
}
58+
5459
public Stream OpenWrite(string path)
5560
{
5661
return File.OpenWrite(path);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
using System;
2+
3+
namespace GitVersion.FileLocking.Abstractions
4+
{
5+
public interface IFileLock : IDisposable
6+
{ }
7+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
namespace GitVersion.FileLocking.Abstractions
24+
{
25+
public interface IFileLocker
26+
{
27+
FileLockUse WaitUntilAcquired();
28+
}
29+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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.Diagnostics;
25+
using System.IO;
26+
using System.Threading;
27+
28+
namespace GitVersion.FileLocking
29+
{
30+
internal class FileLockContext
31+
{
32+
public FileStream FileStream { get; set; }
33+
public Exception Error { get; set; }
34+
public ManualResetEvent ErrorUnlockDone { get; set; }
35+
36+
private readonly FileLocker fileLocker;
37+
private object decreaseLockUseLocker;
38+
39+
public FileLockContext(FileLocker fileLocker, object decreaseLockUseLocker)
40+
{
41+
this.fileLocker = fileLocker;
42+
this.decreaseLockUseLocker = decreaseLockUseLocker;
43+
this.decreaseLockUseLocker = decreaseLockUseLocker;
44+
}
45+
46+
public void DecreaseLockUse(bool decreaseToZero, string lockId)
47+
{
48+
var decreaseLockUseLocker = this.decreaseLockUseLocker;
49+
50+
if (decreaseLockUseLocker == null)
51+
return;
52+
53+
// Why surround by lock?
54+
// There is a race condition, when number of file lock uses
55+
// is decrased to 0. It may not have invalidated the file
56+
// stream yet. Now it can happen that the number of file lock
57+
// uses is increased to 1 due to file lock, but right after another
58+
// file unlock is about to decrease the number again to 0.
59+
// There is the possiblity that the actual file lock gets released
60+
// two times accidentally.
61+
lock (decreaseLockUseLocker)
62+
{
63+
if (!(FileStream.CanRead || FileStream.CanWrite))
64+
{
65+
Trace.WriteLine($"{FileLocker.CurrentThreadWithLockIdPrefix(lockId)} Lock use has been invalidated before. Skip decreasing lock use.", FileLocker.TraceCategory);
66+
return;
67+
}
68+
69+
var locksInUse = fileLocker.DecreaseLockUse(decreaseToZero, lockId);
70+
71+
if (0 == locksInUse)
72+
this.decreaseLockUseLocker = null;
73+
}
74+
}
75+
}
76+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
namespace GitVersion.FileLocking
24+
{
25+
internal static class FileLockContextExtensions
26+
{
27+
public static bool IsErroneous(this FileLockContext fileLockContext)
28+
{
29+
if (fileLockContext?.Error is null)
30+
return false;
31+
32+
return true;
33+
}
34+
}
35+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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.ComponentModel;
25+
using System.IO;
26+
27+
namespace GitVersion.FileLocking
28+
{
29+
public struct FileLockUse : IDisposable
30+
{
31+
public FileStream FileStream => fileLockContext.FileStream;
32+
33+
private readonly FileLockContext fileLockContext;
34+
[EditorBrowsable(EditorBrowsableState.Never)]
35+
public readonly string LockId;
36+
37+
internal FileLockUse(FileLockContext fileLockContext, string LockId)
38+
{
39+
this.fileLockContext = fileLockContext;
40+
this.LockId = LockId;
41+
}
42+
43+
public void Dispose()
44+
{
45+
// When stream not closed, we can decrease lock use.
46+
fileLockContext.DecreaseLockUse(false, LockId);
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)