Skip to content
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

Override all Read methods in NullTextReader and NullStreamReader #64301

Merged
merged 9 commits into from
Jan 27, 2022

Conversation

bgrainger
Copy link
Contributor

This is a fast-follow to #61898 (comment):

There are a few other TextReader-derived types in dotnet/runtime, e.g. a couple in System.Console, and NullTextReader in corelib. We should likely override the new overloads on those as well. (I'm not sure why NullTextReader doesn't already override most of the virtuals, but it seems it should, for perf.) Same goes for NullStreamReader.

CC @adamsitnik

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Jan 25, 2022
@ghost
Copy link

ghost commented Jan 25, 2022

Tagging subscribers to this area: @dotnet/area-system-io
See info in area-owners.md if you want to be subscribed.

Issue Details

This is a fast-follow to #61898 (comment):

There are a few other TextReader-derived types in dotnet/runtime, e.g. a couple in System.Console, and NullTextReader in corelib. We should likely override the new overloads on those as well. (I'm not sure why NullTextReader doesn't already override most of the virtuals, but it seems it should, for perf.) Same goes for NullStreamReader.

CC @adamsitnik

Author: bgrainger
Assignees: -
Labels:

area-System.IO, community-contribution

Milestone: -

@@ -342,15 +342,41 @@ private sealed class NullTextReader : TextReader
{
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we even need NullTextReader? Seems like public static readonly TextReader Null = new NullTextReader(); could be changed to instead construct a NullStreamReader. It's possible there's some reason I'm missing not to do that, but it'd be nice to avoid the duplication if possible. @adamsitnik, thoughts?

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 wondered this, too, as I refactored the classes to be almost identical; the only thing I can think of changing/breaking is code that (essentially) performs TextReader.Null is TextReader and not StreamReader (or similar switch/case or if/else).

If setting TextReader.Null to a NullStreamReader (and deleting NullTextReader) is desirable, let me know and I'm happy to make the change.

Copy link
Member

Choose a reason for hiding this comment

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

The scenario provided by @bgrainger is valid, but I hope that nobody does that.

I've taken a look at how these two properties are typically used and I think that it's relatively safe to reduce the code duplication and remove NullTextReader.

Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

Thank you for your contribution @bgrainger ! PTAL at my comments.

src/libraries/System.IO/tests/Stream/Stream.NullTests.cs Outdated Show resolved Hide resolved
@@ -342,15 +342,41 @@ private sealed class NullTextReader : TextReader
{
Copy link
Member

Choose a reason for hiding this comment

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

The scenario provided by @bgrainger is valid, but I hope that nobody does that.

I've taken a look at how these two properties are typically used and I think that it's relatively safe to reduce the code duplication and remove NullTextReader.

@bgrainger bgrainger requested a review from adamsitnik January 26, 2022 15:09
@@ -19,7 +19,7 @@ namespace System.IO
// There are methods on the Stream class for reading bytes.
public abstract partial class TextReader : MarshalByRefObject, IDisposable
{
public static readonly TextReader Null = new NullTextReader();
public static readonly TextReader Null = StreamReader.Null;
Copy link
Member

@adamsitnik adamsitnik Jan 26, 2022

Choose a reason for hiding this comment

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

Overall the code looks good, but for some reason the tests are failing on mono (the CI has not finished so I am not 100% sure that it's mono bug):

  Discovering: System.IO.Tests (method display = ClassAndMethod, method display options = None)
  Discovered:  System.IO.Tests (found 694 of 700 test cases)
  Starting:    System.IO.Tests (parallel test collections = on, max threads = 6)
    System.IO.Tests.NullTests.TestCanceledNullTextReaderAsync(input: null) [FAIL]
      Assert.Throws() Failure
      Expected: typeof(System.OperationCanceledException)
      Actual:   typeof(System.NullReferenceException): Object reference not set to an instance of an object
      ---- System.NullReferenceException : Object reference not set to an instance of an object
      Stack Trace:
        /_/src/libraries/System.IO/tests/Stream/Stream.NullTests.cs(184,0): at System.IO.Tests.NullTests.<>c__DisplayClass11_0.<<TestCanceledNullTextReaderAsync>b__0>d.MoveNext()
        --- End of stack trace from previous location ---
        ----- Inner Stack Trace -----
        /_/src/libraries/System.IO/tests/Stream/Stream.NullTests.cs(184,0): at System.IO.Tests.NullTests.<>c__DisplayClass11_0.<<TestCanceledNullTextReaderAsync>b__0>d.MoveNext()
        --- End of stack trace from previous location ---
      Assert.Throws() Failure

Is there any chance you could build it and try to repro locally? https://github.com/dotnet/runtime/blob/main/docs/workflow/building/mono/README.md

I have no idea why it fails only on Mono, but I guess it's caused by the fact that two out of three arugments provided by NullReaders refer to the same object instance and somewhere in our stack (not in your PR) there is a bug.

public static IEnumerable<object[]> NullReaders
{
get
{
yield return new object[] { TextReader.Null };
yield return new object[] { StreamReader.Null };
yield return new object[] { StringReader.Null };

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 believe it's actually 3 out of 3 now. StringReader.Null always inherited from the base class, so it was the same object as TextReader.Null. But now they all are the same object.

I could throw on a .Distinct() to ensure that all the properties are read, but that only unique objects are tested, but I'm not sure if that would stop null being passed as the input to the test method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just had a thought that perhaps this is a static field initialisation problem (only on mono)? If the base class static fields have to be initialised before the derived class, then TextReader.Null would be set to null, which would cause the test failure. The fix would be to make NullStreamReader internal and simply construct a second instance in TextReader.Null. (StreamReader.Null technically wouldn't need to be a second instance, but could be = (StreamReader) TextReader.Null but that feels unnecessarily brittle to me.)

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 could throw on a .Distinct()

That didn't fix the broken builds; I'll move ahead with the static field theory. (The Distinct() is still useful to avoid unnecessary testing.)

@bgrainger
Copy link
Contributor Author

Changing the static field initialisation has resolved the "Libraries Test Run release mono" build failures.

@bgrainger bgrainger requested a review from adamsitnik January 26, 2022 20:30
Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

LGTM, thank you @bgrainger !

@adamsitnik adamsitnik merged commit bab1a20 into dotnet:main Jan 27, 2022
@adamsitnik adamsitnik added this to the 7.0.0 milestone Jan 27, 2022
@bgrainger bgrainger deleted the nulltextreader branch January 27, 2022 16:39
@ghost ghost locked as resolved and limited conversation to collaborators Feb 26, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.IO community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants