core: make NameResolver not thread-safe - #5364
Conversation
| private final Stopwatch stopwatch; | ||
| private final SynchronizationContext syncContext; | ||
|
|
||
| // Following fields must be accessed from synContext |
| @GuardedBy("this") | ||
| private boolean resolving; | ||
| @GuardedBy("this") | ||
| private Listener listener; |
There was a problem hiding this comment.
It looks like this can be accessed outside of the syncContext; it's only changed in start().
There was a problem hiding this comment.
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.
I'm saying that listener can actually be accessed from any thread. It doesn't change.
| private final Resource<Executor> executorResource; | ||
| @GuardedBy("this") | ||
| private final long cacheTtlNanos; | ||
| private final Stopwatch stopwatch; |
There was a problem hiding this comment.
This should only be accessed from syncContext.
| * 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.
Independently of this PR, you should probably backport this to v1.19.x
| * {@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.
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.
remove the null. Its the default and surprising to see it.
| 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.
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.
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.
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.
Okay then, I think helper needs a checkNotNull too, then.
|
|
||
| @Override | ||
| public final synchronized void refresh() { | ||
| public final void refresh() { |
There was a problem hiding this comment.
This does not need to be final, since the class is final.
There was a problem hiding this comment.
Removed final on all methods like this.
| } | ||
| }); | ||
| if (cacheTtlNanos > 0) { | ||
| stopwatch.reset().start(); |
There was a problem hiding this comment.
Can stopwatch be accessed by multiple threads?
There was a problem hiding this comment.
Good catch. Moved into syncContext.
zhangkun83
left a comment
There was a problem hiding this comment.
Thanks for the review. PTAL
| * {@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.
Fixed. Blame muscle memory :P
| private final Resource<Executor> executorResource; | ||
| @GuardedBy("this") | ||
| private final long cacheTtlNanos; | ||
| private final Stopwatch stopwatch; |
| private final Stopwatch stopwatch; | ||
| private final SynchronizationContext syncContext; | ||
|
|
||
| // Following fields must be accessed from synContext |
| private final SynchronizationContext syncContext; | ||
|
|
||
| // Following fields must be accessed from synContext | ||
| private ResolutionResults cachedResolutionResults = null; |
| @GuardedBy("this") | ||
| private boolean resolving; | ||
| @GuardedBy("this") | ||
| private Listener listener; |
There was a problem hiding this comment.
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.
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.
|
|
||
| @Override | ||
| public final synchronized void refresh() { | ||
| public final void refresh() { |
There was a problem hiding this comment.
Removed final on all methods like this.
| } | ||
| }); | ||
| if (cacheTtlNanos > 0) { | ||
| stopwatch.reset().start(); |
There was a problem hiding this comment.
Good catch. Moved into syncContext.
| * The port number used in case the target or the underlying naming system doesn't provide a | ||
| * port number. | ||
| * | ||
| * @since 1.19.0 |
| private final SynchronizationContext syncContext; | ||
|
|
||
| // Must only be called from syncContext | ||
| private final Stopwatch stopwatch; |
There was a problem hiding this comment.
It seems better to include this in the "must be accessed from the syncContext" section.
zhangkun83
left a comment
There was a problem hiding this comment.
@carl-mastrangelo, PTAL
I'd like to include this change in my import this week.
carl-mastrangelo
left a comment
There was a problem hiding this comment.
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.
Okay then, I think helper needs a checkNotNull too, then.
Resolves #2649
As a prerequisite, added
getSynchronizationContext()toNameResolver.Helper.DnsNameResolverhas gone through a small refactor around theResolverunnable, which makes it a little simpler.