GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — bpo-36607: Eliminate RuntimeError raised by asyncio.all_tasks() by asvetlov · Pull Request #13971 · python/cpython Conversation
|
| try: | ||
| tasks = list(_all_tasks) | ||
| except RuntimeError: | ||
| pass |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I don't like unchecked loops like that. There must be a retry count or something.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The number of iterations is the question.
What is good enough to prevent a collision? 10? 100? 1000?
I think the number depends on count of threads and a number of tasks inside every processed loop.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I'd say 1000 should be enough. It's high enough to give it enough attempts and it's low enough to abort early if something weird is going on.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
When you're done making the requested changes, leave the comment: |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| loop = events.get_running_loop() | ||
| # NB: set(_all_tasks) is required to protect | ||
| # NB: list(_all_tasks) is required to protect | ||
| # from https://bugs.python.org/issue34970 bug |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Can we write a proper comment here without referencing an issue on bpo? I mean a url is fine, but it requires the reader to navigate to it in order to understand what's going on.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| # NB: list(_all_tasks) is required to protect | ||
| # from https://bugs.python.org/issue34970 bug | ||
| return {t for t in list(_all_tasks) | ||
| # NB: Have to repeat on RuntimeError, other thread |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
There's no point in prefixing all comments with "NB"; it's just a regular comment. I'd suggest to write something along the following (as it took me sometime to understand why this change is necessary at all):
"Looping over a WeakSet (_all_tasks) isn't safe as it can be updated from another thread while we do so. Therefore we cast it to list prior to filtering. The list cast itself requires iteration, so we repeat it several times ignoring RuntimeErrors (which are not very likely to occur). See issues 34970 and 36607 for details."
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Thanks @asvetlov for the PR 🌮🎉.. I'm working now to backport this PR to: 3.7, 3.8. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
…onGH-13971) If internal tasks weak set is changed by another thread during iteration. https://bugs.python.org/issue36607 (cherry picked from commit 65aa64f) Co-authored-by: Andrew Svetlov <[email protected]>
|
GH-13975 is a backport of this pull request to the 3.8 branch. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
…onGH-13971) If internal tasks weak set is changed by another thread during iteration. https://bugs.python.org/issue36607 (cherry picked from commit 65aa64f) Co-authored-by: Andrew Svetlov <[email protected]>
|
GH-13976 is a backport of this pull request to the 3.7 branch. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
This approach feels very wrong to me as we aren't fixing the underlying problem of multiple threads mutating the same global variable without locks. Some more concrete issues:
In my opinion the only valid solution to this problem is to remove the possibility of two threads mutating the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
…3971) If internal tasks weak set is changed by another thread during iteration. https://bugs.python.org/issue36607 (cherry picked from commit 65aa64f) Co-authored-by: Andrew Svetlov <[email protected]>
|
Using a lock is problematic because of GC. Making all_tasks a loop property is a good idea. Andrew? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Yeah locks like be either impossible or almost impossible to implement correctly to solve this. I mentioned this on the issue as a complexity of trying to solve this using locks.
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
One downside to make this a loop attribute is that the attribute becomes part of the AbstractEventLoop api. Other implementations (uvloop, tokio) will need to implement it. Another option is to add an indirection: another mapping of loop -> all_tasks |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Ideally yes.
To protect the current try-again approach: yes, theoretically it still can fail, the PR just reduces a chance of failure by several orders of magnitude. Suppose we have a code: Local If |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Re adding new API: I think I addressed that in my last comment :) |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Yes. Sorry, I read your comment just after sending mine |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
I agree that we shouldn't try and support this because using loops across threads is not supported already.
Correct me if I am wrong as I am not very familiar with the C implementations but I feel like this type of invalid data is very different from the time of copy vs time of use problem you are describing:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
For reference we solved this in our backported version with this hack: https://gist.github.com/nickdavies/4a37c6cd9dcc7041fddd2d2a81cee383 the key difference is the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
We do support it.
Two copies are impossible IMHO. Anyway, we make a set again before returning from Valid tasks miss is possible if CPython switches threads in approximately every 5 ms (see sys.getswitchinterval()). So, another implementation can save second (rare enough) |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
I just don't understand how having code in the core CPython implementation that is correct most of the time is at all acceptable. It doesn't matter how unlikely it is, I expect the language itself to be correct 100% of the time. We should really aim for a deterministic fix. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
I think we should revert the change and implement it via the loop->all_tasks mapping. Or just revert it and don't fix if you have no time. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
@1st1 and I agree that the PR is not perfect. https://bugs.python.org/issue36607 is reverted to "open" state. Please feel to propose the PR which will be awesome! |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Yeah, I'm OK with this fix for 3.7. The main reason is that this allows us to not touch a bunch of low-level C code in a bugfix release. For 3.8 we'll do better. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
…3971) If internal tasks weak set is changed by another thread during iteration. https://bugs.python.org/issue36607 (cherry picked from commit 65aa64f) Co-authored-by: Andrew Svetlov <[email protected]>
|
@nickdavies your patch still has a low chance of failure. A probability of failure is very-very low but still not zero |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
…onGH-13971) If internal tasks weak set is changed by another thread during iteration. https://bugs.python.org/issue36607
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.