-
Notifications
You must be signed in to change notification settings - Fork 7.3k
Refactor OkHttpCall to use CAS instead of synchronized (#4297) #4301
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
base: trunk
Are you sure you want to change the base?
Conversation
Replace synchronized blocks with atomic compare-and-swap logic. It prevent virtual thread pinning and reduce contention. See square#4297 for more details.
86cde28
to
91b4ebf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still think this needs some work. I would probably look to simplify things, such as merge the failure and call reference into one, and potentially even include the cancel state as new Object()
tombstone reference. I need to spend more time with this code before I can move forward with it.
rawCallRef.compareAndSet(null, newCall); | ||
return rawCallRef.get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rawCallRef.compareAndSet(null, newCall); | |
return rawCallRef.get(); | |
return rawCallRef.compareAndSet(null, newCall) | |
? newCall | |
: rawCallRef.get(); |
call = createRawCall(); | ||
rawCallRef.compareAndSet(null, call); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is now racy. Another thread could be performing the same operation in parallel and its okhttp3.Call
might "win" causing this instance to be a duplicate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. If you have some ideas, pls share it. I'll hope I can get back to this with more resources at the end of September. If it will be needed at that time.
if (call == null && failure == null) { | ||
try { | ||
call = createRawCall(); | ||
rawCallRef.compareAndSet(null, call); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Circling back on this in case it's still active. As @JakeWharton noted, another thread could enter this block and call createRawCall()
. Even though only one compareAndSet(...)
might succeed, the other still ends up creating a duplicate instance.
Hi @qwexter, just curious if this race condition was considered acceptable here, or if there's a plan to mitigate it without reintroducing locking?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree. I think I agree that the winner-takes-all approach mentioned in the discussion is too straightforward.
I didn't have any issues with my unit tests, but I might have missed something.
Replace synchronized blocks with atomic compare-and-swap logic.
It prevent virtual thread pinning and reduce contention.
See #4297 for more details.
CHANGELOG.md
's "Unreleased" section has been updated, if applicable.