Skip to content

FIX a number of race conditions in threadpoolctl's OpenBLAS-pthreads ownership - #300

Open
itamarst wants to merge 24 commits into
joblib:masterfrom
itamarst:299-overlapping-restores
Open

itamarst wants to merge 24 commits into
joblib:masterfrom
itamarst:299-overlapping-restores

Conversation

@itamarst

@itamarst itamarst commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #299

@itamarst
itamarst marked this pull request as ready for review November 5, 2026 20:38
@itamarst

itamarst commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

Plausibly there's a second half missing, where the top-level restore of threadpoolctl, the one where it relinquishes control, should always be to the original level of the process, because when it grabs the original level it might grab the wrong one as a race condition.

@itamarst

itamarst commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

Or, another way of saying it, if thread A stores values to restore to at a time when thread B is the controlling thread, the restore value is wrong, and should instead be "all cores" (zero? or a level stored at startup). Will do that or equivalent tomorrow.

@itamarst

itamarst commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

The above is a real race condition (I have implemented a test and fix) but it is still not enough to fix the joblib race condition reproducer!

@itamarst

itamarst commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

Will continue trying to figure out the joblib race condition next week.

@itamarst itamarst changed the title FIX Don't restore the limits if setting them was a no-op FIX a number of race conditions in threadpoolctl's OpenBLAS-pthreads ownership Nov 9, 2026
@itamarst

itamarst commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

@ogrisel ok I think this is ready for review.

@ogrisel

This comment was marked as resolved.

@ogrisel ogrisel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Some more feedback, this time about API design concerns.

Comment thread threadpoolctl.py Resolved
@itamarst

Copy link
Copy Markdown
Contributor Author

The reproducer seems like basically the known limitation, that using the imperative API doesn't participate in automatic process? Last time it came up we decided to just document it. And it's why joblib has to increment/decrement manually itself.

@ogrisel

ogrisel commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

Yeah sorry, I forgot about that. Let's ignore this part of my review. The second part on the public API could be fixed though. What do you think?

@itamarst

Copy link
Copy Markdown
Contributor Author

Yep, on it.

@itamarst

Copy link
Copy Markdown
Contributor Author

Note that CI was failing because ubuntu-latest is now 26.04, and LLVM apt repo doesn't LLVM 18. In order to continue testing with LLVM 18 (which is presumably still pretty common) I switched to ubuntu-24.04 explicitly, but could alternatively switch to newer LLVM instead (21, 22, or 23).

@itamarst
itamarst requested a review from ogrisel November 10, 2026 15:35
@itamarst

Copy link
Copy Markdown
Contributor Author

@ogrisel ok back to you

@ogrisel ogrisel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @itamarst for the follow-up. The fix itself LGTM, an maybe is the correct one for a 3.8.1 bug fix release.

However, I have the feeling that our API design makes OpenBLASController leak its peculiarities in too many places:

I think this is a sign we should consider exposing ownership control semantics more explicitly in controllers. WDYT?

Comment thread threadpoolctl.py Resolved
Co-authored-by: Olivier Grisel <olivier.grisel@ensta.org>
@itamarst
itamarst force-pushed the 299-overlapping-restores branch from 975865d to 11eaef5 Compare November 12, 2026 12:18
@itamarst

Copy link
Copy Markdown
Contributor Author

On the one hand it is getting more complicated, yeah. On the other hand this only makes a difference when:

  • Using nested Python thread pools.
  • The parent thread pool is less than the number of cores.

Which isn't that common outside of scikit-learn, probably?

And then add in the fact that half the time OpenBLAS parallelism slows things down and plausibly a lot of more sophisticated performance-sensitive users will just set the limit to 1 and call it a day (or use MKL if they have complex parallelism).

@itamarst

Copy link
Copy Markdown
Contributor Author

Switching to isinstance(..., OpenBLASController) maybe made a bunch of tests fail with FlexiBLAS, or maybe it was just random failures unrelated to that, as there is still one test failing when I reverted.

Probably because with FlexiBLAS you actually set limits twice, once directly on OpenBLAS and once indirectly with FlexiBLAS and they now have conflicting semantics. So will think about it and hopefully fix that today.

@itamarst

Copy link
Copy Markdown
Contributor Author

I made the failing test more constrained, so it should hopefully pass even on FlexiBLAS, and just left FlexiBLAS in its previous state of 🤷.

@itamarst

Copy link
Copy Markdown
Contributor Author

My basic thought is, to summarize: none of this is perfect but it's probably better than what we have now, and the edge cases (which are real!) probably don't affect that many people. And edge cases are probably no worse than the status quo before 3.8.0.

This branch has not been deployed

No deployments
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.

OpenBLAS controlling thread logic needs to be smarter when thre are overlapping set/restore pairs in different threads

2 participants