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

Fix java context handle #1389

Merged
merged 1 commit into from
Dec 21, 2023
Merged

Fix java context handle #1389

merged 1 commit into from
Dec 21, 2023

Conversation

batiati
Copy link
Contributor

@batiati batiati commented Dec 21, 2023

The problem

The contextHandle can be in use by submit while the deinit function is called.
We synchronize acquire_packet to prevent new packets from being acquired during shutdown, but we didn't guard the contextHandle itself, causing the JVM to crash when acquire_packet was called with a disposed context.

Solution

Guard the contextHandle and a reference counter behind atomics. It's cheaper than a lock.

Undo the temp fix introduced by #1375

Comment on lines +66 to +83
if (contextHandle.getAcquire() != 0L) {
synchronized (this) {
if (contextHandle != 0L) {
// Deinit and signalize that this client is closed by setting the handles to 0.
clientDeinit(contextHandle);
this.contextHandle = 0L;
final var handle = contextHandle.getAcquire();
if (handle != 0L) {
// Signalize that this client is closed by setting the handler to 0,
// and spin wait until all references that might be using the old handle could
// be released.
contextHandle.setRelease(0L);
while (contextHandleReferences.getAcquire() > 0L) {
// Thread::onSpinWait method to give JVM a hint that the following code is
// in a spin loop. This has no side-effect and only provides a hint to
// optimize spin loops in a processor specific manner.
Thread.onSpinWait();
}

// This function waits until all submited requests are completed, and no more
// packets can be acquired after that.
clientDeinit(handle);
Copy link
Member

Choose a reason for hiding this comment

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

Heh, overall, I can't say I am too happy about this:

  • abstractly, nesting synchronized and a spinloop (well, and spinloop by itself https://matklad.github.io/2020/01/02/spinlocks-considered-harmful.html :D) doesn't feel right. Ideally, I'd love to see something more principled here
  • concretely, I think a flaw in this implementation is that, with many concurrent calls to close, only one would actually wait for the stuff to get close. That is, it can be the case that a .close() call returns, but there's still some outstanding work.

However, if I got this on the sync correctly, we are going to nix this code anyway soon, so I guess that's fine! Certainly better than an outright race we have at the moment!

Copy link
Contributor Author

@batiati batiati Dec 21, 2023

Choose a reason for hiding this comment

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

Yeah, my first thought was to encapsulate the context on a thread-safe class controlling all aspects more elegantly, but as you said, we are going to nuke this entire logic very soon.

About the close call, I don't think it can have side effects; the semantics are the same: only the first call to close will wait, no matter if concurrent or sequential calls. If we want to solve that, we can just remove the first verification before the lock.

@batiati batiati added this pull request to the merge queue Dec 21, 2023
Merged via the queue into main with commit 614e7d0 Dec 21, 2023
27 checks passed
@batiati batiati deleted the batiati-java-close-fix branch December 21, 2023 16:31
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.

None yet

3 participants