Skip to content

Commit 72a4e94

Browse files
committed
Cache GetFileInformationByHandleEx (Length) when FileShare does not allow other processes to modify it
1 parent 60eedb4 commit 72a4e94

File tree

4 files changed

+28
-3
lines changed

4 files changed

+28
-3
lines changed

src/libraries/System.Private.CoreLib/src/System/IO/AsyncWindowsFileStreamStrategy.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory<byte> source, Cancella
271271
// touch the file pointer location at all. We will adjust it
272272
// ourselves, but only in memory. This isn't threadsafe.
273273
_filePosition += source.Length;
274+
UpdateLengthOnChangePosition();
274275
}
275276

276277
// queue an async WriteFile operation and pass in a packed overlapped

src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace System.IO
1212
public class FileStream : Stream
1313
{
1414
internal const int DefaultBufferSize = 4096;
15-
private const FileShare DefaultShare = FileShare.Read;
15+
internal const FileShare DefaultShare = FileShare.Read;
1616
private const bool DefaultIsAsync = false;
1717

1818
/// <summary>Caches whether Serialization Guard has been disabled for file writes</summary>

src/libraries/System.Private.CoreLib/src/System/IO/SyncWindowsFileStreamStrategy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ private unsafe void WriteSpan(ReadOnlySpan<byte> source)
159159
}
160160
Debug.Assert(r >= 0, "FileStream's WriteCore is likely broken.");
161161
_filePosition += r;
162-
return;
162+
UpdateLengthOnChangePosition();
163163
}
164164

165165
private NativeOverlapped GetNativeOverlappedForCurrentPosition()

src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ internal abstract class WindowsFileStreamStrategy : FileStreamStrategy
2323
/// <summary>Whether the file is opened for reading, writing, or both.</summary>
2424
private readonly FileAccess _access;
2525

26+
private readonly FileShare _share;
27+
2628
/// <summary>The path to the opened file.</summary>
2729
protected readonly string? _path;
2830

@@ -32,6 +34,7 @@ internal abstract class WindowsFileStreamStrategy : FileStreamStrategy
3234
private readonly bool _isPipe; // Whether to disable async buffering code.
3335

3436
private long _appendStart; // When appending, prevent overwriting file.
37+
private long? _length;
3538

3639
internal WindowsFileStreamStrategy(SafeFileHandle handle, FileAccess access)
3740
{
@@ -40,6 +43,7 @@ internal WindowsFileStreamStrategy(SafeFileHandle handle, FileAccess access)
4043
// Note: Cleaner to set the following fields in ValidateAndInitFromHandle,
4144
// but we can't as they're readonly.
4245
_access = access;
46+
_share = FileStream.DefaultShare;
4347

4448
// As the handle was passed in, we must set the handle field at the very end to
4549
// avoid the finalizer closing the handle when we throw errors.
@@ -52,6 +56,7 @@ internal WindowsFileStreamStrategy(string path, FileMode mode, FileAccess access
5256

5357
_path = fullPath;
5458
_access = access;
59+
_share = share;
5560

5661
_fileHandle = FileStreamHelpers.OpenHandle(fullPath, mode, access, share, options);
5762

@@ -77,7 +82,25 @@ internal WindowsFileStreamStrategy(string path, FileMode mode, FileAccess access
7782

7883
public sealed override bool CanWrite => !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0;
7984

80-
public unsafe sealed override long Length => FileStreamHelpers.GetFileLength(_fileHandle, _path);
85+
public unsafe sealed override long Length => _share > FileShare.Read ?
86+
FileStreamHelpers.GetFileLength(_fileHandle, _path) :
87+
_length ??= FileStreamHelpers.GetFileLength(_fileHandle, _path);
88+
89+
protected void UpdateLengthOnChangePosition()
90+
{
91+
// Do not update the cached length if the file can be written somewhere else
92+
// or if the length has not been queried.
93+
if (_share > FileShare.Read || _length is null)
94+
{
95+
Debug.Assert(_length is null);
96+
return;
97+
}
98+
99+
if (_filePosition > _length)
100+
{
101+
_length = _filePosition;
102+
}
103+
}
81104

82105
/// <summary>Gets or sets the position within the current stream</summary>
83106
public override long Position
@@ -256,6 +279,7 @@ protected unsafe void SetLengthCore(long value)
256279
Debug.Assert(value >= 0, "value >= 0");
257280

258281
FileStreamHelpers.SetLength(_fileHandle, _path, value);
282+
_length = value;
259283

260284
if (_filePosition > value)
261285
{

0 commit comments

Comments
 (0)