Don't hold a lock in DelayedStream when calling realStream#1526
Don't hold a lock in DelayedStream when calling realStream#1526ejona86 merged 1 commit intogrpc:masterfrom
Conversation
| } else { | ||
| realStream.setAuthority(authority); | ||
| List<Runnable> toRun = new ArrayList<Runnable>(); | ||
| while (true) { |
There was a problem hiding this comment.
This part is pretty tricky! It's worth a comment to explain why the loop is necessary. IIUC, it's because 1) runnable has to be run outside the lock; 2) passThrough can only be set to true when pendingCalls is empty, and this must be done inside the lock. So while runnable are running outside the lock, new runnables may be added to pendingCalls.
2cd1b3a to
a5fa38c
Compare
|
@zhangkun83, PTAL; now with some more comments |
| if (!cancelNow) { | ||
| error = reason; | ||
| listenerToClose = listener; | ||
| // TODO(ejona): Close InputStreams? |
There was a problem hiding this comment.
Do other stream implementations do so?
There was a problem hiding this comment.
Currently MessageFramer doesn't. At present that doesn't hurt much, but we will need to fix places eventually.
0bcdd7b to
cde9fb8
Compare
|
@zhangkun83, @carl-mastrangelo, now with immediate calling of setDecompressor |
| startedRealStream.setDecompressor(decompressor); | ||
| public void setDecompressor(final Decompressor decompressor) { | ||
| checkNotNull(decompressor, "decompressor"); | ||
| checkState(realStream != null, "How did we receive a reply before the request is sent?"); |
There was a problem hiding this comment.
Here realStream is accessed from a thread different from what it is set. Shouldn't realStream be volatile?
Or check passThrough first?
There was a problem hiding this comment.
This is only expected to be called after setStream, since this is for processing responses. If setDecompressor is called in the same thread as setStream, then there are no memory concerns. If it is called in a different thread then that means there was some lock/volatile after setStream in order to transfer work to that other thread.
There was a problem hiding this comment.
Please correct me if I misunderstood Java memory model. My understanding is, when a work is transferred across threads locally, there is always some lock/volatile which form memory barrier between the write on the first thread and the read on the second thread. In this case, although logically setStream() "happens before" setDecompressor(), there is a network round-trip in the middle. I doubt Java memory model could form a memory barrier for events across network.
There was a problem hiding this comment.
... Good point. I can't find much to define what happens due to I/O. I've added an empty synchronized block to trigger happens-before.
|
LGTM |
| if (passThrough) { | ||
| runNow = true; | ||
| } else { | ||
| pendingCalls.add(runnable); |
There was a problem hiding this comment.
Why not just return after this point, and not check runNow at the end of this func?
|
@carl-mastrangelo, PTAL |
| delayOrExecute(new Runnable() { | ||
| @Override | ||
| public void run() { | ||
| realStream.flush(); |
There was a problem hiding this comment.
This loses the original codes intent. Flushes were coalesced previously. Are you certain that is the behavior you want?
There was a problem hiding this comment.
I was okay with replaying each flush. I wasn't too concerned about that level of performance in the slow case. It should be possible to coalesce flushes, but is slightly non-trivial because of interactions with other methods like halfClose() and cancel(). I'm don't have much problem calling optimizing flush as future work, but I do have some trouble caring about the case at present because it will be no worse than when DelayedStream is not being used.
There was a problem hiding this comment.
Ok, just wanted to make sure this was intentional, not a side effect.
|
@carl-mastrangelo, thanks for the comments. PTAL |
|
@ejona86 LGTM |
Our current lock ordering rules prohibit holding a lock when calling the channel and stream. This change avoids the lock for both DelayedClientTransport and DelayedStream. It is effectively a rewrite of DelayedStream. The fixes to ClientCallImpl were to ensure sane state in DelayedStream. Fixes grpc#1510
7ac08c4 to
eccd231
Compare
Our currently lock ordering rules prohibit holding a lock when calling
the channel and stream. This change avoids the lock for both
DelayedClientTransport and DelayedStream. It is effectively a rewrite of
DelayedStream.
The fixes to ClientCallImpl were to ensure sane state in DelayedStream.