-
-
Notifications
You must be signed in to change notification settings - Fork 30.8k
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
gh-111569: Implement Python critical section API #111571
Conversation
Critical sections are helpers to replace the global interpreter lock with finer grained locking. They provide similar guarantees to the GIL and avoid the deadlock risk that plain locking involves. Critical sections are implicitly ended whenever the GIL would be released. They are resumed when the GIL would be acquired. Nested critical sections behave as if the sections were interleaved.
@ericsnowcurrently I'd appreciate your review if you have time to look at this. This builds off of the |
}; | ||
assert(test_data.obj1 != NULL); | ||
assert(test_data.obj2 != NULL); | ||
assert(test_data.obj3 != NULL); |
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.
Just a drive-by comment, I would much prefer to have conditional returns from native test functions than asserts. When these fail, they can block test progress (on Windows it pops up a modal dialog by default), or may trigger error reporting or a debugger. It also shows up as a test crash, rather than a failure.
Perhaps a new macro is needed to print location information to the console and then fail?
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 agree that the failure case is unpleasant, but I don't think trying to route proper error returns in the C API tests is worthwhile. To the extent that there are bugs in the tested code, they are likely to show up as crashes, deadlocks, or other unpleasant behavior even if we make the asserts more nicely behaved (because this is testing C code, not Python). Additionally, it's challenging to route error messages from other threads in C since they don't always have a GIL context and might not be able to create Python error objects.
One consequence of this is that I think we should basically have a zero tolerance for flaky C API tests. If we are going to be bothered by an unpleasant failure, it better signify something important.
If crashes in the C API tests become an actual problem, then I think it would be better to invest in running these tests in a subprocess like we do with test_support.script_helper.assert_python_failure
.
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.
Yeah, that's reasonable. I'm mostly annoyed about it because some versions of MSVC have a compiler bug on ARM64 that causes some of the interlocked tests to fail, which means the entire test run is basically useless until all the machines get updated (as far as I can tell, the code is only ever tested and never actually used, so it doesn't need fixing for our releases - there's nothing we can do about it on our side anyway).
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.
At its conceptual core this is a relatively straight-forward addition (inasmuch as anything thread-related can be 😄). I've left comments for a few typos, some minor readability issues, and in places where I'd like some clarification.
In particular, how is reentrancy handled?
I'm also especially curious about critical sections involving many objects, not just one or two. See my comment in pycore_critical_section.h, line 45.
FWIW, it's also nice to see how a lot of the code paths you are adding gets exercised on regular builds (even if the fundamental logic is skipped there). That certainly gives me more confidence about maintainability. 😄
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 And if you don't make the requested changes, you will be poked with soft cushions! |
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.
Thanks for reviewing this Eric.
In particular, how is reentrancy handled?
Reentrancy is handled implicitly. Basically, if a thread tries to Py_BEGIN_CRITICAL_SECTION
the same object, it would block, calling _PyThreadState_Detach()
, which suspends the outer critical section and unlocks the object, allowing the most recent Py_BEGIN_CRITICAL_SECTION
to continue. We may want to handle this case more explicitly in the future for better efficiency.
I'm also especially curious about critical sections involving many objects, not just one or two.
This doesn't provide an API for locking more than two objects at at once. That hasn't been necessary in the nogil forks. (We could implement it, but my feeling is that if we have a design that wants to lock N objects simultaneously, we probably want to rethink that design.)
I have made the requested changes; please review again |
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.
Mostly LGTM. I just have some suggestions about wording/spelling in your big comment.
BTW, thanks for updating that comment. It's much clearer now.
Thanks @ericsnowcurrently. I've updated the comments. |
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.
LGTM
|
|
Looks like this PR is causing the WASI buildbots to time out on the new |
Oops. I'll put up a PR to fix WASI tomorrow morning. |
Critical sections are helpers to replace the global interpreter lock with finer grained locking. They provide similar guarantees to the GIL and avoid the deadlock risk that plain locking involves. Critical sections are implicitly ended whenever the GIL would be released. They are resumed when the GIL would be acquired. Nested critical sections behave as if the sections were interleaved.
Critical sections are helpers to replace the global interpreter lock with finer grained locking. They provide similar guarantees to the GIL and avoid the deadlock risk that plain locking involves. Critical sections are implicitly ended whenever the GIL would be released. They are resumed when the GIL would be acquired. Nested critical sections behave as if the sections were interleaved.
Critical sections are helpers to replace the global interpreter lock with finer grained locking. They provide similar guarantees to the GIL and avoid the deadlock risk that plain locking involves. Critical sections are implicitly ended whenever the GIL would be released. They are resumed when the GIL would be acquired. Nested critical sections behave as if the sections were interleaved.