Merge pull request #3938 from eclipse/jetty-9.4.x-3929-http2_deadlock_stopping_server

Fixes #3929 - Deadlock between new HTTP2Connection() and Server.stop().
This commit is contained in:
Simone Bordet 2019-08-07 12:34:15 +03:00 committed by GitHub
commit fa0e7850be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 125 additions and 30 deletions

View File

@ -106,18 +106,20 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
switch (b._managed)
{
case MANAGED:
if (!l.isRunning())
if (l.isStopped() || l.isFailed())
start(l);
break;
case AUTO:
if (l.isRunning())
unmanage(b);
else
if (l.isStopped())
{
manage(b);
start(l);
}
else
{
unmanage(b);
}
break;
default:
@ -142,7 +144,7 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
{
try
{
l.stop();
stop(l);
}
catch (Throwable cause2)
{

View File

@ -23,6 +23,7 @@ import java.io.IOException;
import java.io.StringReader;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.util.TypeUtil;
@ -129,10 +130,7 @@ public class ContainerLifeCycleTest
container.stop();
container.destroy();
assertThrows(IllegalStateException.class, () ->
{
container.start();
});
assertThrows(IllegalStateException.class, container::start);
}
@Test
@ -210,13 +208,13 @@ public class ContainerLifeCycleTest
{
ContainerLifeCycle a0 = new ContainerLifeCycle();
String dump = trim(a0.dump());
dump = check(dump, "ContainerLifeCycl");
check(dump, "ContainerLifeCycl");
ContainerLifeCycle aa0 = new ContainerLifeCycle();
a0.addBean(aa0);
dump = trim(a0.dump());
dump = check(dump, "ContainerLifeCycl");
dump = check(dump, "+? ContainerLife");
check(dump, "+? ContainerLife");
ContainerLifeCycle aa1 = new ContainerLifeCycle();
a0.addBean(aa1);
@ -224,7 +222,7 @@ public class ContainerLifeCycleTest
dump = check(dump, "ContainerLifeCycl");
dump = check(dump, "+? ContainerLife");
dump = check(dump, "+? ContainerLife");
dump = check(dump, "");
check(dump, "");
ContainerLifeCycle aa2 = new ContainerLifeCycle();
a0.addBean(aa2, false);
@ -233,7 +231,7 @@ public class ContainerLifeCycleTest
dump = check(dump, "+? ContainerLife");
dump = check(dump, "+? ContainerLife");
dump = check(dump, "+~ ContainerLife");
dump = check(dump, "");
check(dump, "");
aa1.start();
a0.start();
@ -242,7 +240,7 @@ public class ContainerLifeCycleTest
dump = check(dump, "+= ContainerLife");
dump = check(dump, "+~ ContainerLife");
dump = check(dump, "+~ ContainerLife");
dump = check(dump, "");
check(dump, "");
a0.manage(aa1);
a0.removeBean(aa2);
@ -250,7 +248,7 @@ public class ContainerLifeCycleTest
dump = check(dump, "ContainerLifeCycl");
dump = check(dump, "+= ContainerLife");
dump = check(dump, "+= ContainerLife");
dump = check(dump, "");
check(dump, "");
ContainerLifeCycle aaa0 = new ContainerLifeCycle();
aa0.addBean(aaa0);
@ -259,7 +257,7 @@ public class ContainerLifeCycleTest
dump = check(dump, "+= ContainerLife");
dump = check(dump, "| +~ Container");
dump = check(dump, "+= ContainerLife");
dump = check(dump, "");
check(dump, "");
ContainerLifeCycle aa10 = new ContainerLifeCycle();
aa1.addBean(aa10, true);
@ -269,7 +267,7 @@ public class ContainerLifeCycleTest
dump = check(dump, "| +~ Container");
dump = check(dump, "+= ContainerLife");
dump = check(dump, " += Container");
dump = check(dump, "");
check(dump, "");
final ContainerLifeCycle a1 = new ContainerLifeCycle();
final ContainerLifeCycle a2 = new ContainerLifeCycle();
@ -301,7 +299,7 @@ public class ContainerLifeCycleTest
dump = check(dump, " +> java.util.Arrays$ArrayList");
dump = check(dump, " +: ContainerLifeCycle");
dump = check(dump, " +: ContainerLifeCycle");
dump = check(dump, "");
check(dump, "");
a2.addBean(aa0, true);
dump = trim(a0.dump());
@ -319,7 +317,7 @@ public class ContainerLifeCycleTest
dump = check(dump, " +> java.util.Arrays$ArrayList");
dump = check(dump, " +: ContainerLifeCycle");
dump = check(dump, " +: ContainerLifeCycle");
dump = check(dump, "");
check(dump, "");
a2.unmanage(aa0);
dump = trim(a0.dump());
@ -336,7 +334,7 @@ public class ContainerLifeCycleTest
dump = check(dump, " +> java.util.Arrays$ArrayList");
dump = check(dump, " +: ContainerLifeCycle");
dump = check(dump, " +: ContainerLifeCycle");
dump = check(dump, "");
check(dump, "");
a0.unmanage(aa);
dump = trim(a0.dump());
@ -346,7 +344,7 @@ public class ContainerLifeCycleTest
dump = check(dump, "+= ContainerLife");
dump = check(dump, "| += Container");
dump = check(dump, "+~ ContainerLife");
dump = check(dump, "");
check(dump, "");
}
@Test
@ -504,7 +502,7 @@ public class ContainerLifeCycleTest
assertEquals(c00, child.poll());
}
private final class InheritedListenerLifeCycle extends AbstractLifeCycle implements Container.InheritedListener
private static final class InheritedListenerLifeCycle extends AbstractLifeCycle implements Container.InheritedListener
{
@Override
public void beanRemoved(Container p, Object c)
@ -627,7 +625,7 @@ public class ContainerLifeCycleTest
}
@Test
public void testGetBeans() throws Exception
public void testGetBeans()
{
TestContainerLifeCycle root = new TestContainerLifeCycle();
TestContainerLifeCycle left = new TestContainerLifeCycle();
@ -637,19 +635,114 @@ public class ContainerLifeCycleTest
TestContainerLifeCycle leaf = new TestContainerLifeCycle();
right.addBean(leaf);
root.addBean(Integer.valueOf(0));
root.addBean(Integer.valueOf(1));
left.addBean(Integer.valueOf(2));
right.addBean(Integer.valueOf(3));
leaf.addBean(Integer.valueOf(4));
Integer zero = 0;
Integer one = 1;
Integer two = 2;
Integer three = 3;
Integer four = 4;
root.addBean(zero);
root.addBean(one);
left.addBean(two);
right.addBean(three);
leaf.addBean(four);
leaf.addBean("leaf");
assertThat(root.getBeans(Container.class), containsInAnyOrder(left, right));
assertThat(root.getBeans(Integer.class), containsInAnyOrder(Integer.valueOf(0), Integer.valueOf(1)));
assertThat(root.getBeans(Integer.class), containsInAnyOrder(zero, one));
assertThat(root.getBeans(String.class), containsInAnyOrder());
assertThat(root.getContainedBeans(Container.class), containsInAnyOrder(left, right, leaf));
assertThat(root.getContainedBeans(Integer.class), containsInAnyOrder(Integer.valueOf(0), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4)));
assertThat(root.getContainedBeans(Integer.class), containsInAnyOrder(zero, one, two, three, four));
assertThat(root.getContainedBeans(String.class), containsInAnyOrder("leaf"));
}
@Test
public void testBeanStoppingAddedToStartingBean() throws Exception
{
ContainerLifeCycle longLived = new ContainerLifeCycle()
{
@Override
protected void doStop() throws Exception
{
super.doStop();
ContainerLifeCycle shortLived = new ContainerLifeCycle();
shortLived.addBean(this);
shortLived.start();
assertTrue(shortLived.isStarted());
assertTrue(isStopping());
assertFalse(shortLived.isManaged(this));
}
};
longLived.start();
longLived.stop();
}
@Test
public void testFailedManagedBeanCanBeRestarted() throws Exception
{
AtomicBoolean fail = new AtomicBoolean();
ContainerLifeCycle container = new ContainerLifeCycle();
ContainerLifeCycle bean1 = new ContainerLifeCycle();
ContainerLifeCycle bean2 = new ContainerLifeCycle()
{
@Override
protected void doStart() throws Exception
{
super.doStart();
// Fail only the first time.
if (fail.compareAndSet(false, true))
throw new RuntimeException();
}
};
ContainerLifeCycle bean3 = new ContainerLifeCycle();
container.addBean(bean1);
container.addBean(bean2);
container.addBean(bean3);
// Start the first time, it should fail.
assertThrows(RuntimeException.class, container::start);
assertTrue(container.isFailed());
assertTrue(bean1.isStopped());
assertTrue(bean2.isFailed());
assertTrue(bean3.isStopped());
// Re-start, it should succeed.
container.start();
assertTrue(container.isStarted());
assertTrue(bean1.isStarted());
assertTrue(bean2.isStarted());
assertTrue(bean3.isStarted());
}
@Test
public void testFailedAutoBeanIsNotRestarted() throws Exception
{
AtomicBoolean fail = new AtomicBoolean();
ContainerLifeCycle bean = new ContainerLifeCycle()
{
@Override
protected void doStart() throws Exception
{
super.doStart();
// Fail only the first time.
if (fail.compareAndSet(false, true))
throw new RuntimeException();
}
};
// The bean is started externally and fails.
assertThrows(RuntimeException.class, bean::start);
// The same bean now becomes part of a container.
ContainerLifeCycle container = new ContainerLifeCycle();
container.addBean(bean);
assertTrue(container.isAuto(bean));
// Start the container, the bean must not be managed.
container.start();
assertTrue(container.isStarted());
assertTrue(bean.isFailed());
assertTrue(container.isUnmanaged(bean));
}
}