Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Add async File.ReadAll*, File.AppendAll* and File.WriteAll #15372

Merged
merged 1 commit into from
Feb 2, 2017

Conversation

JonHanna
Copy link
Contributor

Fixes #11220

@JonHanna JonHanna force-pushed the fix_11220_redux branch 4 times, most recently from dcdd69d to da20360 Compare January 22, 2017 00:01

// UTF-8 without BOM and with error detection.
// Same as the default encoding for StreamWriter.
private static Encoding UTF8NoBOM => _UTF8NoBOM ?? (_UTF8NoBOM = new UTF8Encoding(false, true));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Would named parameters help here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, they wouldn't hurt.

path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize,
FileOptions.Asynchronous | FileOptions.SequentialScan);

return new StreamReader(stream, encoding, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would named parameters help here?

cancellationToken.ThrowIfCancellationRequested();
StringBuilder sb = new StringBuilder();
int bufferSize = sr.CurrentEncoding.GetMaxCharCount(DefaultBufferSize);
char[] buffer = new char[bufferSize];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe StringBuilderCache.Acquire() and ArrayPool<char>.Shared.Rent() respectively could be used here.


return cancellationToken.IsCancellationRequested
? Task.FromCanceled(cancellationToken)
: InternalWriteAllTextAsync(AsyncStreamWriter(path, false, encoding), contents, cancellationToken);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Named parameters

using (fs)
{
int index = 0;
byte[] bytes = new byte[count];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use ArrayPool<byte>.Shared.Rent(count)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not so useful here, as that array is returned back to the client.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I missed that.


return cancellationToken.IsCancellationRequested
? Task.FromCanceled(cancellationToken)
: InternalWriteAllLinesAsync(AsyncStreamWriter(path, false, encoding), contents, cancellationToken);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Named parameters

: InternalWriteAllLinesAsync(AsyncStreamWriter(path, false, encoding), contents, cancellationToken);
}

private static async Task InternalWriteAllLinesAsync(TextWriter writer, IEnumerable<String> contents, CancellationToken cancellationToken)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: string

if (!string.IsNullOrEmpty(contents))
{
cancellationToken.ThrowIfCancellationRequested();
char[] buffer = new char[DefaultBufferSize];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ArrayPool? Same for the other allocations happening.


return cancellationToken.IsCancellationRequested
? Task.FromCanceled(cancellationToken)
: InternalWriteAllTextAsync(AsyncStreamWriter(path, true, encoding), contents, cancellationToken);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Named parameters? Same for all of the other places with true, false, null literals being passed directly.

@JonHanna
Copy link
Contributor Author

Thanks @jamesqo. I'm now making use of shared buffers in those cases that don't return the array to the client. I'm using the length of the buffer obtained rather than the size asked for with reading but not for writing. With reading it's up to the stream implementation whether it uses the full size or smaller, but with writing it has to use all of it, so that is more likely to be sub-optimal.

int read = await sr.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
if (read == 0)
{
ArrayPool<char>.Shared.Return(buffer);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to use a try/finally pattern with ArrayPool so the buffer is returned in the case of an exception. That is the general usage from what I've seen, at least.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering about that. It's non-fateful but less than ideal for such a thing to happen. I think I'll replace the using with a single try/finally and then there's no further impact.

using (fs)
{
int index = 0;
byte[] bytes = new byte[count];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I missed that.

foreach (string line in contents)
{
cancellationToken.ThrowIfCancellationRequested();
// Note that this working depends on the fix to #8662, and cannot be ported without
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#8662 seems to be a closed issue

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is. This is a note in case it's ported to desktop. If this is ported and the fix for #8662 isn't then this will have a bug.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Darn it. Not 8662, but #14563. 8662 was a related PR number in coreclr.


namespace System.IO
{
// Class for creating FileStream objects, and some basic file management
// routines such as Delete, etc.
public static class File
{
private static Encoding _UTF8NoBOM;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static fields should be prefixed with s_

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed they should. Fixed.

@JonHanna JonHanna force-pushed the fix_11220_redux branch 2 times, most recently from 2a49b66 to 241fc4e Compare January 23, 2017 10:14
File.Delete(path);
}

public Task AlreadyCanceledAsync()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forgot a [Fact] on this one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also forgot to cancel the token!

public async Task NullParametersAsync()
{
string path = GetTestFilePath();
await Assert.ThrowsAsync<ArgumentNullException>(async () => await WriteAsync(null, "Text"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does ThrowsAsync allow assertion of parameterNames on Argument*Exceptions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, thanks.

@danmoseley
Copy link
Member

@ianhays is this ready for another review?

Copy link
Contributor

@ianhays ianhays left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took another look and this LGTM. Thanks Jon!

@ianhays ianhays merged commit 2b4e1e4 into dotnet:master Feb 2, 2017
@JonHanna JonHanna deleted the fix_11220_redux branch February 2, 2017 18:59
@khellang
Copy link
Member

khellang commented Feb 2, 2017

Nice! Will this be part of .NET Core/Standard/Framework vNext?

@karelz
Copy link
Member

karelz commented Feb 2, 2017

It will be in .NET Core 2.0.
I won't be part of .NET Standard 2.0 ... we will have to wait for other platforms (Desktop Framework, Xamarin) to adopt it first.

@karelz
Copy link
Member

karelz commented Feb 2, 2017

Thanks @JonHanna for your help here!

@karelz karelz added this to the 2.0.0 milestone Feb 2, 2017
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
Add async File.ReadAll*, File.AppendAll* and File.WriteAll

Commit migrated from dotnet/corefx@2b4e1e4
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants