binder: Avoid potential deadlock when canceling AsyncSecurityPolicy futures#12283
binder: Avoid potential deadlock when canceling AsyncSecurityPolicy futures#12283jdcormie merged 7 commits intogrpc:masterfrom
Conversation
…utures Move future cancellation outside of synchronized block in BinderClientTransport.notifyTerminated() to prevent deadlock if AsyncSecurityPolicy uses directExecutor() for callbacks. Fixes grpc#12190
|
Help me understand the change here? All those cancel() calls still appear to come from inside the @GuardedBy("this") method notifyTerminated() method ... |
…o fix-binder-deadlock-12190 Signed-off-by: Hyunsang Han <gustkd3@gmail.com>
…ted() Move future cancellation to offloadExecutor to avoid deadlock when AsyncSecurityPolicy uses directExecutor() for callbacks. Fixes grpc#12190 Signed-off-by: Hyunsang Han <gustkd3@gmail.com>
OMG! Sorry. I realized that I missed committing the actual fix! @jdcormie Could you please check the latest commit? |
Extract future cancellation logic into cancelAsync method and only cancel futures that are not already done for better performance. Signed-off-by: Hyunsang Han <gustkd3@gmail.com>
Rename cancelAsync to cancelAsyncIfNeeded, move future cancellation next to readyTimeoutFuture, and remove unnecessary null assignments. Signed-off-by: Hyunsang Han <gustkd3@gmail.com>
|
@jdcormie |
|
Woke up this morning with a small new concern: Would this PR cause us to declare the Channel terminated before all work we've enqueued on the offload Executor is complete (or cancelled) ? Take a look at how |
I agree with your concern. That's a very good point. That said, while thinking about ways to improve the code, two questions came up 🤔 :
Could you elaborate a bit more on what you meant by "move into the shutdown path instead"? I want to make sure I understand your idea fully. |
|
Regarding (1), You're right that enqueuing it earlier, in shutdown() say, doesn't solve anything. My suggestion here wasn't a good one. Regarding (2), You're right that I do still think we should cancel any internally scheduled Runnables and any in-flight work behind our calls to The simplest way to achieve |
|
@jdcormie I’ve reviewed your commit and I agree this "collect with lock, cancel without lock" pattern is a much cleaner solution. It avoids both the race condition and the deadlock scenario while staying consistent with the existing cleanup logic. The only trade-off I see is that the logic might feel a bit less cohesive, but overall I think it’s the right direction :) |
|
Great! Can you update your PR to take that approach and I'll merge it? |
…ng locks Signed-off-by: Hyunsang Han <gustkd3@gmail.com>
…o fix-binder-deadlock-12190
|
@jdcormie |
Fixes BinderClientTransportTest which has been flaky since grpc#12283.
Fixes BinderClientTransportTest#testAsyncSecurityPolicyCancelledUponExternalTermination and others which have been flaky since #12283. @HyunsangHan
…utures (grpc#12283) Move future cancellation outside of synchronized block in `BinderClientTransport.notifyTerminated()` to prevent deadlock if `AsyncSecurityPolicy` uses `directExecutor()` for callbacks. Fixes grpc#12190 --------- Signed-off-by: Hyunsang Han <gustkd3@gmail.com>
Fixes BinderClientTransportTest#testAsyncSecurityPolicyCancelledUponExternalTermination and others which have been flaky since grpc#12283. @HyunsangHan
Move future cancellation outside of synchronized block in
BinderClientTransport.notifyTerminated()to prevent deadlock ifAsyncSecurityPolicyusesdirectExecutor()for callbacks.Fixes #12190