-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
bpo-30966: Add multiprocessing.SimpleQueue.close() #2760
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
Conversation
Lib/multiprocessing/queues.py
Outdated
@@ -319,6 +319,13 @@ def __init__(self, *, ctx): | |||
else: | |||
self._wlock = ctx.Lock() | |||
|
|||
def __del__(self): |
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.
You shouldn't need that: the reader and writer already have a __del__
.
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.
Hum, maybe I gone too far. This change is part on a larger plan to add ResourceWarning everywhere.
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.
I removed del(), sorry this change was not directly related to this PR. It can be added later if needed (for ResourceWarning).
Lib/test/_test_multiprocessing.py
Outdated
queue = multiprocessing.SimpleQueue() | ||
queue.close() | ||
# closing a queue twice should not fail | ||
queue.close() |
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.
Should this also need explicit testing _reader
and _writer
's .closed
to be True
?
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.
I dislike testing private attributes in unit tests. Should I add a second @cpython_only
unit test testing private attributes?
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.
I added functional tests instead.
Lib/test/_test_multiprocessing.py
Outdated
queue = multiprocessing.SimpleQueue() | ||
queue.close() | ||
# closing a queue twice should not fail | ||
queue.close() |
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.
What happens if I call get() or put() on a closed queue?
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.
What happens if I call get() or put() on a closed queue?
No idea :-) Which behaviour do you expect/want?
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.
Usually, it would raise a ValueError.
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, let's do that.
Lib/multiprocessing/queues.py
Outdated
|
||
def get(self): | ||
self._check_closed() |
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.
How much overhead adds this?
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.
No idea. Does it matter? Correctness beats performance, no?
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.
The performance matters. What bug fixed by adding this 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.
@pitrou asked me to raise a ValueError.
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.
There's so much going on here (I/O, pickling) that I don't think a trivial method call is going to matter.
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.
There is a race condition here. AttributeError
is raised if the queue is closed after calling _check_closed()
. Maybe use locking in close()
and guard the check with a lock?
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.
SimpleQueue is supposed to be thread-safe? (I don't know the answer, it's a real question!) If we start to use the read lock, we also may have to use the write lock??
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.
Otherwise why it use the locks?
Doc/whatsnew/3.7.rst
Outdated
--------------- | ||
|
||
The :class:`multiprocessing.SimpleQueue` class has a new | ||
:meth:`~multiprocessing.SimpleQueue.close` method to explicitly closes the |
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.
I'm not sure, but seems there are errors in English here.
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.
yup, same typo as below: should be "to explicitly close the..."
I rewrote close() to close the writer even if closing the reader fails. It also clears _reader and _writer references to help the garbage collector. I chose to remove the _poll attribute to simplify the implementation. But I don't know the rationale of the _poll() method. Was it added to add a private _poll() method? Or is it to prevent issues with the garbage collector on Python finalization? SimpleQueue._poll attribute was added by the commit bdb1cf1 and http://bugs.python.org/issue12328:
So @pitrou: Is it safe to remove _poll? Or should I keep it? If we keep it, I would prefer to explain why we need it. |
@@ -0,0 +1,2 @@ | |||
Add a new close() method to multiprocessing.SimpleQueue to explicitly closes |
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.
should be "to explicitly close the..."
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.
oh right, fixed.
* Add a new close() method to multiprocessing.SimpleQueue to explicitly close the queue. * Operation on a closed queue raises a ValueError. * Remove the SimpleQueue._poll attribute: use self._reader.poll()
Ok, I think the current diff has gone too far :-) If raising ValueError cannot be provided without adding a lot of locking and complication in the implementation, then I'll happily lose the ValueError requirement so that the implementation remains simple and lightweight (it's called "SimpleQueue" for a reason :-)). |
The complicated code comes from my will of setting attributes to None. Maybe if we only call .close() without setting attributes to None, it would simplify the code, no? What do you think? |
I don't know, I'd like to see the code :-) |
Ok, let's try something completely different: PR #2776 is a simpler queue with closer() ;-) |
Oh, I removed the branch by mistake :-p |
Add a new close() method to multiprocessing.SimpleQueue to explicitly
closes the queue. This is called automatically when the queue is
garbage collected.