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

add sync::OnceExt and sync::OnceLockExt traits #4676

Merged
merged 10 commits into from
Nov 2, 2024

Conversation

davidhewitt
Copy link
Member

Related to #4513, #4671

It turns out that within our test suite the racy behaviour of GILOnceCell is already problematic on GIL-enabled builds, and is just waiting for additional GIL switches to bait out problems 😬. In particular in test_declarative_module we have a race which might attempt to create a module twice; we explicitly forbid this because we don't support subinterpreters.

This PR introduces pyo3::sync::OnceExt, which adds helper methods to std::sync::Once which avoid deadlocking with the GIL by releasing if necessary before blocking.

This solves the issue with test_declarative_module by using the Once to guarantee we only ever create the module once.

I think we should document this in the FAQ and freethreading guides, and probably this is good justification for getting #4513 and maybe also some OnceCellExt traits into 0.23. cc @ngoldbaum, I guess this would delay the release by a few days.

@davidhewitt davidhewitt changed the title add sync::OnceExt traitt add sync::OnceExt trait Oct 31, 2024
@ngoldbaum
Copy link
Contributor

ngoldbaum commented Oct 31, 2024

I guess this would delay the release by a few days

That's OK. Single-initialization comes up a lot so we should take our time and get it right.

I also want to add a section to the guide about how to use threads from both Python and rust and how free-threading changes things. I may expand the existing parallelism docs if needed. Having some time to do that is nice!

// Some other thread filled this Once, so we need to restore the GIL state.
unsafe { ffi::PyEval_RestoreThread(ts) };
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I wrote a test for this trait based on the standard library example for try_once_force and it crashed Python because the thread state was corrupted. I think this code won't restore the thread state correctly if a panic happens inside f() and the fix is to use the RAII guard pattern. Assuming that's right, I'll go ahead and push a fix along with the test.

Copy link
Member

Choose a reason for hiding this comment

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

Does the guard fix your problem? That'd be surprising to me, because I reviewed the previous implementation and believed it was sound.

Copy link
Member

Choose a reason for hiding this comment

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

Oh nevermind; I read the Once documentation and if it is poisoned it will panic before the closure is called.

@ngoldbaum
Copy link
Contributor

I looked at including OnceLockExt in this PR as well and I got a fair bit of the way through that (see ngoldbaum@f0eb7e0, feel free to pull that and use it if you want) but realized we don't have a way to conditionally compile based on the rust compiler version, so maybe we need to bump the MSRV? Or is there a way to account for the fact that the MSRV doesn't have OnceLock?

@mejrs
Copy link
Member

mejrs commented Nov 1, 2024

but realized we don't have a way to conditionally compile based on the rust compiler version

We do? https://github.com/PyO3/pyo3/blob/main/pyo3-build-config/src/lib.rs#L138

@ngoldbaum
Copy link
Contributor

Sorry, missed that! Thanks for the link.

@ngoldbaum ngoldbaum changed the title add sync::OnceExt trait add sync::OnceExt and sync::OnceLockExt traits Nov 1, 2024
@ngoldbaum
Copy link
Contributor

We do?

I think I did it right, but clippy complains that I'm using features that aren't in the MSRV. OK to ignore that or did I mess something up?

});
});
assert_eq!(cell.get(), Some(&12345));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestions welcome for a more interesting test

});
# }
```

Copy link
Contributor

Choose a reason for hiding this comment

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

I probably want to rewrite this example to use OnceLock

@mejrs
Copy link
Member

mejrs commented Nov 2, 2024

We do?

I think I did it right, but clippy complains that I'm using features that aren't in the MSRV. OK to ignore that or did I mess something up?

That's annoying. You can set it to allow here: https://github.com/PyO3/pyo3/blob/main/Cargo.toml#L153

@ngoldbaum
Copy link
Contributor

I just locally ignored it, I still think that clippy lint is useful and it doesn't look like we use features that aren't in the MSRV very often.

@davidhewitt
Copy link
Member Author

Thanks, this looks great to me! I simplified the Guard type a little (think the option no longer needed), and corrected a couple docs typos. Will merge!

@davidhewitt davidhewitt enabled auto-merge November 2, 2024 10:30
@ngoldbaum ngoldbaum disabled auto-merge November 2, 2024 11:11
@ngoldbaum ngoldbaum enabled auto-merge November 2, 2024 11:13
@ngoldbaum ngoldbaum added this pull request to the merge queue Nov 2, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Nov 2, 2024
@ngoldbaum ngoldbaum added this pull request to the merge queue Nov 2, 2024
@ngoldbaum ngoldbaum removed this pull request from the merge queue due to a manual request Nov 2, 2024
@ngoldbaum
Copy link
Contributor

The new tests that use threads need to be skipped on emscripten.

@ngoldbaum ngoldbaum enabled auto-merge November 2, 2024 14:27
@ngoldbaum ngoldbaum added this pull request to the merge queue Nov 2, 2024
Merged via the queue into PyO3:main with commit 55c9543 Nov 2, 2024
44 checks passed
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.

3 participants