-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reading beyond EOF should return 0 for FILE_FLAG_NO_BUFFERING #63625
Changes from all commits
96dc41a
b44bac6
ad4dc75
a453066
50e1de2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,6 @@ | |
using System.Diagnostics; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using System.Threading.Tasks.Sources; | ||
using Microsoft.Win32.SafeHandles; | ||
|
||
namespace System.IO.Strategies | ||
|
@@ -16,9 +15,7 @@ internal abstract class OSFileStreamStrategy : FileStreamStrategy | |
private readonly FileAccess _access; // What file was opened for. | ||
|
||
protected long _filePosition; | ||
private long _length = -1; // negative means that hasn't been fetched. | ||
private long _appendStart; // When appending, prevent overwriting file. | ||
private bool _lengthCanBeCached; // SafeFileHandle hasn't been exposed, file has been opened for reading and not shared for writing. | ||
|
||
internal OSFileStreamStrategy(SafeFileHandle handle, FileAccess access) | ||
{ | ||
|
@@ -45,7 +42,6 @@ internal OSFileStreamStrategy(string path, FileMode mode, FileAccess access, Fil | |
string fullPath = Path.GetFullPath(path); | ||
|
||
_access = access; | ||
_lengthCanBeCached = (share & FileShare.Write) == 0 && (access & FileAccess.Write) == 0; | ||
|
||
_fileHandle = SafeFileHandle.Open(fullPath, mode, access, share, options, preallocationSize); | ||
|
||
|
@@ -78,34 +74,13 @@ internal OSFileStreamStrategy(string path, FileMode mode, FileAccess access, Fil | |
|
||
public sealed override bool CanWrite => !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; | ||
|
||
public unsafe sealed override long Length | ||
{ | ||
get | ||
{ | ||
if (!LengthCachingSupported) | ||
{ | ||
return RandomAccess.GetFileLength(_fileHandle); | ||
} | ||
|
||
// On Windows, when the file is locked for writes we can cache file length | ||
// in memory and avoid subsequent native calls which are expensive. | ||
|
||
if (_length < 0) | ||
{ | ||
_length = RandomAccess.GetFileLength(_fileHandle); | ||
} | ||
|
||
return _length; | ||
} | ||
} | ||
public unsafe sealed override long Length => _fileHandle.GetFileLength(); | ||
|
||
// in case of concurrent incomplete reads, there can be multiple threads trying to update the position | ||
// at the same time. That is why we are using Interlocked here. | ||
internal void OnIncompleteOperation(int expectedBytesTransferred, int actualBytesTransferred) | ||
=> Interlocked.Add(ref _filePosition, actualBytesTransferred - expectedBytesTransferred); | ||
|
||
private bool LengthCachingSupported => OperatingSystem.IsWindows() && _lengthCanBeCached; | ||
|
||
/// <summary>Gets or sets the position within the current stream</summary> | ||
public sealed override long Position | ||
{ | ||
|
@@ -129,9 +104,6 @@ internal sealed override SafeFileHandle SafeFileHandle | |
FileStreamHelpers.Seek(_fileHandle, _filePosition, SeekOrigin.Begin); | ||
} | ||
|
||
_lengthCanBeCached = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When we have introduced file length caching for the first time, we were caching it also when the file was opened for writing. We have tried to update the cached length in appropriate places, but at the end we have decided that it's not worth it (mostly due to the fact that users can start multiple concurrent write operations). File caching is allowed only on Windows, when the file has not been opened for writing and when it has not been shared with others for writing. Since it's impossible to use such file handle to modify the file, I've decided to remove the file length cache invalidaiton from |
||
_length = -1; // invalidate cached length | ||
|
||
return _fileHandle; | ||
} | ||
} | ||
|
@@ -224,10 +196,7 @@ protected unsafe void SetLengthCore(long value) | |
Debug.Assert(value >= 0, "value >= 0"); | ||
|
||
FileStreamHelpers.SetFileLength(_fileHandle, value); | ||
if (LengthCachingSupported) | ||
{ | ||
_length = value; | ||
} | ||
Debug.Assert(!_fileHandle.LengthCanBeCached, "If length can be cached (file opened for reading, not shared for writing), it should be impossible to modify file length"); | ||
|
||
if (_filePosition > value) | ||
{ | ||
|
@@ -314,7 +283,7 @@ public sealed override ValueTask<int> ReadAsync(Memory<byte> destination, Cancel | |
return RandomAccess.ReadAtOffsetAsync(_fileHandle, destination, fileOffset: -1, cancellationToken); | ||
} | ||
|
||
if (LengthCachingSupported && _length >= 0 && Volatile.Read(ref _filePosition) >= _length) | ||
if (_fileHandle.HasCachedFileLength && Volatile.Read(ref _filePosition) >= _fileHandle.GetFileLength()) | ||
{ | ||
// We know for sure that the file length can be safely cached and it has already been obtained. | ||
// If we have reached EOF we just return here and avoid a sys-call. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit:
FileLength
property rather thanGetFileLength
method?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Properties might be seen as something that is cheap to use. Since we can't cache file length on Unix and not always on Windows, I would prefer to keep it as a method. I hope that the callers are going to recognize it as something that is not for free.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, it's not a big deal (but FileStream.Length is a property).