-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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: make NameResolver not thread-safe #5364
Conversation
private final Stopwatch stopwatch; | ||
private final SynchronizationContext syncContext; | ||
|
||
// Following fields must be accessed from synContext |
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.
s/synContext/syncContext/
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.
Done.
private boolean resolving; | ||
@GuardedBy("this") | ||
private Listener listener; |
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.
It looks like this can be accessed outside of the syncContext; it's only changed in start().
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.
The field will only be accessed from syncContext, while it can be called from any thread. I have made it more clear in the comments.
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'm saying that listener can actually be accessed from any thread. It doesn't change.
@@ -138,17 +138,17 @@ | |||
private final String host; | |||
private final int port; | |||
private final Resource<Executor> executorResource; | |||
@GuardedBy("this") | |||
private final long cacheTtlNanos; | |||
private final Stopwatch stopwatch; |
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 should only be accessed from syncContext.
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.
Done.
@@ -214,13 +218,27 @@ void onAddresses( | |||
/** | |||
* The port number used in case the target or the underlying naming system doesn't provide a | |||
* port number. | |||
* | |||
* @since 1.19.0 |
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.
Independently of this PR, you should probably backport this to v1.19.x
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.
Sure: #5367
@@ -37,10 +37,14 @@ | |||
* {@link Listener} is responsible for eventually (after an appropriate backoff period) invoking | |||
* {@link #refresh()}. | |||
* | |||
* <p>Implementations <string>don't need to be thread-safe</strong>. All methods are guaranteed to |
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.
s/string/strong/
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.
Fixed. Blame muscle memory :P
private final SynchronizationContext syncContext; | ||
|
||
// Following fields must be accessed from synContext | ||
private ResolutionResults cachedResolutionResults = null; |
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.
remove the null. Its the default and surprising to see it.
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.
Done.
this.resolveRunnable = new Resolve(this, stopwatch, getNetworkAddressCacheTtlNanos(isAndroid)); | ||
this.cacheTtlNanos = getNetworkAddressCacheTtlNanos(isAndroid); | ||
this.stopwatch = Preconditions.checkNotNull(stopwatch, "stopwatch"); | ||
this.syncContext = |
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 is a little uncomfortable to me. It would be better if the ctor was dumber, and the DNRP constructed the dependencies for this class, rather than DNR constructing its own. Same with proxyDetector.
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 am not sure what you are suggesting. Are you suggesting instead of passing Helper
to the ctor, passing the defaultPort, proxyDetector and syncContext? I don't see much benefit of it rather than a longer argument list. There will be more stuff on Helper
that DNR needs to look at. Flatting them out doesn't seem favorable.
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 new form was much clearer for me, because they are the same for every resolver. Having them in the resolver is harder to reason about because I then have to check if they change. I'd actually prefer if Listener was removed from Resolver as well.
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.
Okay then, I think helper needs a checkNotNull too, then.
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.
Done.
Preconditions.checkState(this.listener == null, "already started"); | ||
executor = SharedResourceHolder.get(executorResource); | ||
this.listener = Preconditions.checkNotNull(listener, "listener"); | ||
resolve(); | ||
} | ||
|
||
@Override | ||
public final synchronized void refresh() { | ||
public final void refresh() { |
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 does not need to be final, since the class is final.
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.
Removed final on all methods like this.
public void run() { | ||
cachedResolutionResults = results; | ||
} | ||
}); | ||
if (cacheTtlNanos > 0) { | ||
stopwatch.reset().start(); |
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.
Can stopwatch be accessed by multiple threads?
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.
Good catch. Moved into syncContext.
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.
Thanks for the review. PTAL
@@ -37,10 +37,14 @@ | |||
* {@link Listener} is responsible for eventually (after an appropriate backoff period) invoking | |||
* {@link #refresh()}. | |||
* | |||
* <p>Implementations <string>don't need to be thread-safe</strong>. All methods are guaranteed to |
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.
Fixed. Blame muscle memory :P
@@ -138,17 +138,17 @@ | |||
private final String host; | |||
private final int port; | |||
private final Resource<Executor> executorResource; | |||
@GuardedBy("this") | |||
private final long cacheTtlNanos; | |||
private final Stopwatch stopwatch; |
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.
Done.
private final Stopwatch stopwatch; | ||
private final SynchronizationContext syncContext; | ||
|
||
// Following fields must be accessed from synContext |
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.
Done.
private final SynchronizationContext syncContext; | ||
|
||
// Following fields must be accessed from synContext | ||
private ResolutionResults cachedResolutionResults = null; |
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.
Done.
private boolean resolving; | ||
@GuardedBy("this") | ||
private Listener listener; |
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.
The field will only be accessed from syncContext, while it can be called from any thread. I have made it more clear in the comments.
this.resolveRunnable = new Resolve(this, stopwatch, getNetworkAddressCacheTtlNanos(isAndroid)); | ||
this.cacheTtlNanos = getNetworkAddressCacheTtlNanos(isAndroid); | ||
this.stopwatch = Preconditions.checkNotNull(stopwatch, "stopwatch"); | ||
this.syncContext = |
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 am not sure what you are suggesting. Are you suggesting instead of passing Helper
to the ctor, passing the defaultPort, proxyDetector and syncContext? I don't see much benefit of it rather than a longer argument list. There will be more stuff on Helper
that DNR needs to look at. Flatting them out doesn't seem favorable.
Preconditions.checkState(this.listener == null, "already started"); | ||
executor = SharedResourceHolder.get(executorResource); | ||
this.listener = Preconditions.checkNotNull(listener, "listener"); | ||
resolve(); | ||
} | ||
|
||
@Override | ||
public final synchronized void refresh() { | ||
public final void refresh() { |
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.
Removed final on all methods like this.
public void run() { | ||
cachedResolutionResults = results; | ||
} | ||
}); | ||
if (cacheTtlNanos > 0) { | ||
stopwatch.reset().start(); |
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.
Good catch. Moved into syncContext.
@@ -214,13 +218,27 @@ void onAddresses( | |||
/** | |||
* The port number used in case the target or the underlying naming system doesn't provide a | |||
* port number. | |||
* | |||
* @since 1.19.0 |
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.
Sure: #5367
private final SynchronizationContext syncContext; | ||
|
||
// Must only be called from syncContext | ||
private final Stopwatch stopwatch; |
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.
It seems better to include this in the "must be accessed from the syncContext" section.
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.
Done.
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.
@carl-mastrangelo, PTAL
I'd like to include this change in my import this week.
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.
One comment but LGTM
this.resolveRunnable = new Resolve(this, stopwatch, getNetworkAddressCacheTtlNanos(isAndroid)); | ||
this.cacheTtlNanos = getNetworkAddressCacheTtlNanos(isAndroid); | ||
this.stopwatch = Preconditions.checkNotNull(stopwatch, "stopwatch"); | ||
this.syncContext = |
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.
Okay then, I think helper needs a checkNotNull too, then.
Resolves #2649
As a prerequisite, added
getSynchronizationContext()
toNameResolver.Helper
.DnsNameResolver
has gone through a small refactor around theResolve
runnable, which makes it a little simpler.