Fixes #2548 - Possible deadlock failing HTTP/2 stream creation.

Now failing the callback outside of the synchronized block.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2018-05-16 22:18:47 +02:00
parent 01f7aecc4e
commit 16b7359ae5
1 changed files with 49 additions and 43 deletions

View File

@ -471,6 +471,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
@Override @Override
public void newStream(HeadersFrame frame, Promise<Stream> promise, Stream.Listener listener) public void newStream(HeadersFrame frame, Promise<Stream> promise, Stream.Listener listener)
{
try
{ {
// Synchronization is necessary to atomically create // Synchronization is necessary to atomically create
// the stream id and enqueue the frame to be sent. // the stream id and enqueue the frame to be sent.
@ -486,9 +488,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
priority.getWeight(), priority.isExclusive()); priority.getWeight(), priority.isExclusive());
frame = new HeadersFrame(streamId, frame.getMetaData(), priority, frame.isEndStream()); frame = new HeadersFrame(streamId, frame.getMetaData(), priority, frame.isEndStream());
} }
final IStream stream = createLocalStream(streamId, promise); IStream stream = createLocalStream(streamId);
if (stream == null)
return;
stream.setListener(listener); stream.setListener(listener);
ControlEntry entry = new ControlEntry(frame, stream, new PromiseCallback<>(promise, stream)); ControlEntry entry = new ControlEntry(frame, stream, new PromiseCallback<>(promise, stream));
@ -498,6 +498,11 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
if (queued) if (queued)
flusher.iterate(); flusher.iterate();
} }
catch (Throwable x)
{
promise.failed(x);
}
}
@Override @Override
public int priority(PriorityFrame frame, Callback callback) public int priority(PriorityFrame frame, Callback callback)
@ -516,6 +521,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
@Override @Override
public void push(IStream stream, Promise<Stream> promise, PushPromiseFrame frame, Stream.Listener listener) public void push(IStream stream, Promise<Stream> promise, PushPromiseFrame frame, Stream.Listener listener)
{
try
{ {
// Synchronization is necessary to atomically create // Synchronization is necessary to atomically create
// the stream id and enqueue the frame to be sent. // the stream id and enqueue the frame to be sent.
@ -525,9 +532,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
int streamId = streamIds.getAndAdd(2); int streamId = streamIds.getAndAdd(2);
frame = new PushPromiseFrame(frame.getStreamId(), streamId, frame.getMetaData()); frame = new PushPromiseFrame(frame.getStreamId(), streamId, frame.getMetaData());
final IStream pushStream = createLocalStream(streamId, promise); IStream pushStream = createLocalStream(streamId);
if (pushStream == null)
return;
pushStream.setListener(listener); pushStream.setListener(listener);
ControlEntry entry = new ControlEntry(frame, pushStream, new PromiseCallback<>(promise, pushStream)); ControlEntry entry = new ControlEntry(frame, pushStream, new PromiseCallback<>(promise, pushStream));
@ -537,6 +542,11 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
if (queued) if (queued)
flusher.iterate(); flusher.iterate();
} }
catch (Throwable x)
{
promise.failed(x);
}
}
@Override @Override
public void settings(SettingsFrame frame, Callback callback) public void settings(SettingsFrame frame, Callback callback)
@ -676,17 +686,14 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
} }
} }
protected IStream createLocalStream(int streamId, Promise<Stream> promise) protected IStream createLocalStream(int streamId)
{ {
while (true) while (true)
{ {
int localCount = localStreamCount.get(); int localCount = localStreamCount.get();
int maxCount = getMaxLocalStreams(); int maxCount = getMaxLocalStreams();
if (maxCount >= 0 && localCount >= maxCount) if (maxCount >= 0 && localCount >= maxCount)
{ throw new IllegalStateException("Max local stream count " + maxCount + " exceeded");
promise.failed(new IllegalStateException("Max local stream count " + maxCount + " exceeded"));
return null;
}
if (localStreamCount.compareAndSet(localCount, localCount + 1)) if (localStreamCount.compareAndSet(localCount, localCount + 1))
break; break;
} }
@ -702,8 +709,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
} }
else else
{ {
promise.failed(new IllegalStateException("Duplicate stream " + streamId)); throw new IllegalStateException("Duplicate stream " + streamId);
return null;
} }
} }