-
-
Notifications
You must be signed in to change notification settings - Fork 699
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
Fix Issue 24165 - Failed readf leaves File in inconsistent state #8826
base: master
Are you sure you want to change the base?
Conversation
Thanks for your pull request and interest in making D better, @pbackus! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla references
Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub run digger -- build "master + phobos#8826" |
Given that Don't we just need to ensure that only one legit instance is valid at a time? What about adding a copy constructor that hollows out the original? |
Previously, a failed call to readf resulted in multiple copies of the same LockingTextReader being destroyed. Since LockingTextReader's destructor calls ungetc on the last-read character, this caused that character to appear multiple times in subsequent reads from the File. This change ensures that the destructor in question is only run once by making LockingTextReader a reference-counted type. Ideally, to avoid unnecessary overhead, this issue would have been fixed by making LockingTextReader non-copyable. However, non-copyable ranges are poorly-supported by Phobos, and this approach would have required extensive changes to several other modules, including changes to the interfaces of some public symbols.
Whoops, my bad, this affects |
Seems like this could break code that currently works (e.g., anything that makes a copy and then calls |
The question is, what is intended to be supported? Typically with input-only ranges (not forward ranges), only one instance should be used. Not preventing copying on input ranges was a design mistake, but we have to live with it now. even if you don't hollow it out, what about removing the character to put back so it's not put back twice? That also seems like a kludge as well. |
Isn't that functionally the same as hollowing it out? I guess I could add a |
Actually never mind, |
Attempted the hollow-out approach anyway and it turns out that replacing |
Another idea -- since |
Another idea -- upon creation of the LockingTextReader, store a bool in the File impl that indicates whether the ungtc has been called once. Then on destruction, if the hasChar is true, only do ungetc if that hasn't been done yet. |
Couldn't something like this work @pbackus ? |
The problem with that suggestion is that it calls If that sounds unrealistic, keep in mind that passing by value creates and destroys a copy. For example, a function like the following would trigger the failure mode described above: // Note: takes range by value
void enforceNonEmpty(Range)(Range range)
if (isInputRange!Range)
{
enforce(!r.empty, "Expected non-empty range");
} Now, maybe our official position is that such code is not supported—if you copy a range, and then read from the original, you get what you get and there are no guarantees, even if you never read from the copy. But that seems like unnecessarily hostile API design to me. |
We can have 3 states:
We already have a We need a second boolean, which can be stored in the main File refcounted struct, which is set to true when What I'm trying to avoid is yet another allocation for something that could easily be short-lived. We already have the pointer stored in the You can even encapsulate this functionality in 2 new private functions inside the File: I think this works, but I haven't tried it out or thought of all the details. The main goal should be to avoid putting back data that was never on the stream in the first place. Even if this is at the expense of "just don't do that" type warnings, fine. The secondary goal is to try and avoid reading data that isn't there. I think we can do both, without resorting to an extra indirection and an extra allocation. |
Previously, a failed call to readf resulted in multiple copies of the
same LockingTextReader being destroyed. Since LockingTextReader's
destructor calls ungetc on the last-read character, this caused that
character to appear multiple times in subsequent reads from the File.
This change ensures that the destructor in question is only run once by
making LockingTextReader a reference-counted type.
Ideally, to avoid unnecessary overhead, this issue would have been fixed
by making LockingTextReader non-copyable. However, non-copyable ranges
are poorly-supported by Phobos, and this approach would have required
extensive changes to several other modules, including changes to the
interfaces of some public symbols.