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

bpo-31976: Fix race condition when flushing a file is slow. #4331

Merged
merged 4 commits into from Nov 10, 2017

Conversation

benfogle
Copy link
Contributor

@benfogle benfogle commented Nov 8, 2017

  • Move IS_CLOSED check to after the acquiring the lock
  • Add a check for self->buffer == NULL in IS_CLOSED
  • Make _pyio.BufferedWriter close the same way the C version does
  • Add unit tests

https://bugs.python.org/issue31976

* Move IS_CLOSED check to after the acquiring the lock
* Add a check for self->buffer == NULL in IS_CLOSED
* Make _pyio.BufferedWriter close the same way the C version does
* Add unit tests
Copy link
Member

@pitrou pitrou 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 spotting this and proposing this fix!
Before this goes in, you should address the comments and also a NEWS entry. For the latter, you can use the "blurb" utility.

Lib/_pyio.py Outdated
with self._write_lock:
if self.raw is None or self.closed:
return
try:
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't look right. The close() method should take the lock once, not take it three times in a row (one of them implicit in flush()). Note _flush_unlocked() can help you 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.

That made a bunch of tests (test_flush_error_on_close, etc.) fail since close() no longer calls flush() if the file object is subclassed or fileobj.flush is set to something.

This is actually the same behavior as buffered_close in the C implementation. I have added a comment to that effect.


if (!ENTER_BUFFERED(self))
return NULL;

if (IS_CLOSED(self)) {
Copy link
Member

Choose a reason for hiding this comment

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

Can you add a comment and a reference to the bug entry here?

@@ -1726,6 +1737,16 @@ def bad_write(b):
self.assertRaises(OSError, b.close) # exception not swallowed
self.assertTrue(b.closed)

def test_slow_close_from_thread(self):
rawio = self.SlowFlushRawIO()
Copy link
Member

Choose a reason for hiding this comment

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

Can you add a comment pointing to the bug entry here?

Copy link
Member

Choose a reason for hiding this comment

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

It looks like you forgot to address this comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, whoops. Thank you.

bufio = self.tp(rawio, 8)
t = threading.Thread(target=bufio.close)
t.start()
time.sleep(0.1) # Long enough that t is sleeping in bufio.close()
Copy link
Member

Choose a reason for hiding this comment

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

You could probably use an Event that you would set in SlowFlushRawIO.flush() instead.

@bedevere-bot
Copy link

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@benfogle
Copy link
Contributor Author

I have made the requested changes; please review again

@bedevere-bot
Copy link

Thanks for making the requested changes!

@pitrou: please review the changes made to this pull request.

@benfogle
Copy link
Contributor Author

I have made the requested changes; please review again.

@bedevere-bot
Copy link

Thanks for making the requested changes!

@pitrou: please review the changes made to this pull request.

@pitrou
Copy link
Member

pitrou commented Nov 10, 2017

Thanks. I'm inclined to merge this after CI approval. I'm not sure I want to backport it to 3.6, though, because there is a tiny but non-zero chance that this might break some odd usage somewhere and I don't want that to happen in a bugfix release.

@pitrou pitrou merged commit 9703f09 into python:master Nov 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants