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

core: Avoid locks in SynchronizationContext #5504

Merged
merged 1 commit into from Mar 26, 2019

Conversation

njhill
Copy link
Contributor

@njhill njhill commented Mar 26, 2019

Similar to what's done in SerializingExecutor, it's easy to make SynchronizationContext non-blocking.

Similar to what's done in SerializingExecutor, it's easy to make SynchronizationContext non-blocking.
Copy link
Contributor

@carl-mastrangelo carl-mastrangelo left a comment

Choose a reason for hiding this comment

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

LGTM

@zhangkun83 PTAL

I know this isn't super performance critical, but this has the nice benefit that the app thread can no longer block the network thread.

@dapengzhang0 dapengzhang0 added the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label Mar 26, 2019
@grpc-kokoro grpc-kokoro removed the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label Mar 26, 2019
synchronized (lock) {
queue.add(checkNotNull(runnable, "runnable is null"));
}
queue.add(checkNotNull(runnable, "runnable is null"));
Copy link
Member

Choose a reason for hiding this comment

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

This may change the current behavior because the ordering of the runnables may be changed. There seems no harm for the ordering change (May need confirm from @zhangkun83 ). This change seems better, because executeLater becomes always not blocking after the change.

Copy link
Member

Choose a reason for hiding this comment

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

Discussed offline, the behavior is more or less the same. So no concern here.

}
}
} finally {
drainingThread.set(null);
Copy link
Contributor

Choose a reason for hiding this comment

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

There seems to be a race:

T1: call drain(): set drainingThread. Finish all tasks, exit loop
T2: call executeLater(): add a new task
T2: call drain(): try to set drainingThread, but is still set, give up
T1: still in drain(): reset drainingThread, then exit

End result: the task added by T2 will not be run unless someone calls drain() again.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nm, I see you have another loop to catch that.

Copy link
Member

Choose a reason for hiding this comment

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

T1: still in drain(): reset drainingThread, then exit

For T1, because there is while (!queue.isEmpty()) in the end, it will drain again, not exit.

Copy link
Member

Choose a reason for hiding this comment

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

I agree without lock it's harder to prove the correctness and easier to break the correctness in the future though. But this seems to have the same complexity as the SerializingExecutor.

@njhill
Copy link
Contributor Author

njhill commented Mar 26, 2019

Thanks @carl-mastrangelo @dapengzhang0 @zhangkun83. A possible variation would be to use an AtomicBoolean and ThreadLocal<Boolean> instead of the AtomicReference<Thread>, i.e. no need to use Thread.currentThread(). This would make throwIfNotInThisSynchronizationContext cheaper - just a TL instead of volatile read. But as @carl-mastrangelo said, not on request path so I guess not that perf-critical.

Copy link
Contributor

@zhangkun83 zhangkun83 left a comment

Choose a reason for hiding this comment

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

LGTM

@zhangkun83 zhangkun83 merged commit 8e41f6e into grpc:master Mar 26, 2019
@njhill njhill deleted the nolock-sync-cxt branch March 26, 2019 21:28
@lock lock bot locked as resolved and limited conversation to collaborators Jun 24, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants