1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-08-08 09:42:06 +03:00

Socket lifecycle observer

Summary:
Adds `QuicSocket::LifecycleObserver`, an observer that is notified when a socket is closed and destroyed.

- Can be used by instrumentation that ties its lifetime to that of the socket.
- Multiple observer can be registered, but a `folly::small_vector` is used to minimize memory overhead in the common case of 0 - 2 being registered.
- `folly::AsyncSocket` has a matching interface being added (D21613750), so instrumentation can follow the same paradigm for both QUIC and TCP.
- In the future, will extend to also be triggered when a transport becomes connected, similar to what we have for `AsyncSocket`.

Reviewed By: mjoras

Differential Revision: D21653651

fbshipit-source-id: 0aa374cc0dd9b9c11d81b49597326bd53264531d
This commit is contained in:
Brandon Schlinker
2020-07-16 10:23:35 -07:00
committed by Facebook GitHub Bot
parent 095eee20e6
commit 98e0e4dc5d
9 changed files with 387 additions and 9 deletions

View File

@@ -2786,5 +2786,187 @@ TEST_F(QuicTransportImplTest, StreamWriteCallbackUnregister) {
evb->loopOnce();
}
TEST_F(QuicTransportImplTest, LifecycleObserverAttachRemove) {
auto cb = std::make_unique<StrictMock<MockLifecycleObserver>>();
EXPECT_CALL(*cb, observerAttach(transport.get()));
transport->addLifecycleObserver(cb.get());
EXPECT_THAT(
transport->getLifecycleObservers(), UnorderedElementsAre(cb.get()));
EXPECT_CALL(*cb, observerDetach(transport.get()));
EXPECT_TRUE(transport->removeLifecycleObserver(cb.get()));
Mock::VerifyAndClearExpectations(cb.get());
EXPECT_THAT(transport->getLifecycleObservers(), IsEmpty());
}
TEST_F(QuicTransportImplTest, LifecycleObserverRemoveMissing) {
auto cb = std::make_unique<StrictMock<MockLifecycleObserver>>();
EXPECT_FALSE(transport->removeLifecycleObserver(cb.get()));
EXPECT_THAT(transport->getLifecycleObservers(), IsEmpty());
}
TEST_F(QuicTransportImplTest, LifecycleObserverDestroyTransport) {
auto cb = std::make_unique<StrictMock<MockLifecycleObserver>>();
EXPECT_CALL(*cb, observerAttach(transport.get()));
transport->addLifecycleObserver(cb.get());
EXPECT_THAT(
transport->getLifecycleObservers(), UnorderedElementsAre(cb.get()));
InSequence s;
EXPECT_CALL(*cb, close(transport.get(), _)).Times(2);
EXPECT_CALL(*cb, destroy(transport.get()));
transport = nullptr;
Mock::VerifyAndClearExpectations(cb.get());
}
TEST_F(
QuicTransportImplTest,
LifecycleObserverCloseNoErrorThenDestroyTransport) {
auto cb = std::make_unique<StrictMock<MockLifecycleObserver>>();
EXPECT_CALL(*cb, observerAttach(transport.get()));
transport->addLifecycleObserver(cb.get());
EXPECT_THAT(
transport->getLifecycleObservers(), UnorderedElementsAre(cb.get()));
const std::pair<QuicErrorCode, std::string> defaultError = std::make_pair(
GenericApplicationErrorCode::NO_ERROR,
toString(GenericApplicationErrorCode::NO_ERROR));
EXPECT_CALL(*cb, close(transport.get(), folly::Optional(defaultError)));
transport->close(folly::none);
Mock::VerifyAndClearExpectations(cb.get());
InSequence s;
EXPECT_CALL(*cb, close(transport.get(), _)).Times(2);
EXPECT_CALL(*cb, destroy(transport.get()));
transport = nullptr;
Mock::VerifyAndClearExpectations(cb.get());
}
TEST_F(
QuicTransportImplTest,
LifecycleObserverCloseWithErrorThenDestroyTransport) {
auto cb = std::make_unique<StrictMock<MockLifecycleObserver>>();
EXPECT_CALL(*cb, observerAttach(transport.get()));
transport->addLifecycleObserver(cb.get());
EXPECT_THAT(
transport->getLifecycleObservers(), UnorderedElementsAre(cb.get()));
const auto testError = std::make_pair(
QuicErrorCode(LocalErrorCode::CONNECTION_RESET),
std::string("testError"));
EXPECT_CALL(*cb, close(transport.get(), folly::Optional(testError)));
transport->close(testError);
Mock::VerifyAndClearExpectations(cb.get());
InSequence s;
EXPECT_CALL(*cb, close(transport.get(), _)).Times(2);
EXPECT_CALL(*cb, destroy(transport.get()));
transport = nullptr;
Mock::VerifyAndClearExpectations(cb.get());
}
TEST_F(QuicTransportImplTest, LifecycleObserverDetachObserverImmediately) {
auto cb = std::make_unique<StrictMock<MockLifecycleObserver>>();
EXPECT_CALL(*cb, observerAttach(transport.get()));
transport->addLifecycleObserver(cb.get());
EXPECT_THAT(
transport->getLifecycleObservers(), UnorderedElementsAre(cb.get()));
EXPECT_CALL(*cb, observerDetach(transport.get()));
EXPECT_TRUE(transport->removeLifecycleObserver(cb.get()));
Mock::VerifyAndClearExpectations(cb.get());
EXPECT_THAT(transport->getLifecycleObservers(), IsEmpty());
}
TEST_F(
QuicTransportImplTest,
LifecycleObserverDetachObserverAfterTransportClose) {
auto cb = std::make_unique<StrictMock<MockLifecycleObserver>>();
EXPECT_CALL(*cb, observerAttach(transport.get()));
transport->addLifecycleObserver(cb.get());
EXPECT_THAT(
transport->getLifecycleObservers(), UnorderedElementsAre(cb.get()));
EXPECT_CALL(*cb, close(transport.get(), _));
transport->close(folly::none);
Mock::VerifyAndClearExpectations(cb.get());
EXPECT_CALL(*cb, observerDetach(transport.get()));
EXPECT_TRUE(transport->removeLifecycleObserver(cb.get()));
Mock::VerifyAndClearExpectations(cb.get());
EXPECT_THAT(transport->getLifecycleObservers(), IsEmpty());
}
TEST_F(
QuicTransportImplTest,
LifecycleObserverDetachObserverOnCloseDuringTransportDestroy) {
auto cb = std::make_unique<StrictMock<MockLifecycleObserver>>();
EXPECT_CALL(*cb, observerAttach(transport.get()));
transport->addLifecycleObserver(cb.get());
EXPECT_THAT(
transport->getLifecycleObservers(), UnorderedElementsAre(cb.get()));
InSequence s;
EXPECT_CALL(*cb, close(transport.get(), _))
.WillOnce(Invoke([&cb](auto callbackTransport, auto /* errorOpt */) {
EXPECT_TRUE(callbackTransport->removeLifecycleObserver(cb.get()));
}));
EXPECT_CALL(*cb, observerDetach(transport.get()));
transport = nullptr;
Mock::VerifyAndClearExpectations(cb.get());
}
TEST_F(QuicTransportImplTest, LifecycleObserverMultipleAttachRemove) {
auto cb1 = std::make_unique<StrictMock<MockLifecycleObserver>>();
EXPECT_CALL(*cb1, observerAttach(transport.get()));
transport->addLifecycleObserver(cb1.get());
EXPECT_THAT(
transport->getLifecycleObservers(), UnorderedElementsAre(cb1.get()));
auto cb2 = std::make_unique<StrictMock<MockLifecycleObserver>>();
EXPECT_CALL(*cb2, observerAttach(transport.get()));
transport->addLifecycleObserver(cb2.get());
EXPECT_THAT(
transport->getLifecycleObservers(),
UnorderedElementsAre(cb1.get(), cb2.get()));
EXPECT_CALL(*cb2, observerDetach(transport.get()));
EXPECT_TRUE(transport->removeLifecycleObserver(cb2.get()));
EXPECT_THAT(
transport->getLifecycleObservers(), UnorderedElementsAre(cb1.get()));
Mock::VerifyAndClearExpectations(cb1.get());
Mock::VerifyAndClearExpectations(cb2.get());
EXPECT_CALL(*cb1, observerDetach(transport.get()));
EXPECT_TRUE(transport->removeLifecycleObserver(cb1.get()));
EXPECT_THAT(transport->getLifecycleObservers(), IsEmpty());
Mock::VerifyAndClearExpectations(cb1.get());
Mock::VerifyAndClearExpectations(cb2.get());
transport = nullptr;
}
TEST_F(QuicTransportImplTest, LifecycleObserverMultipleAttachDestroyTransport) {
auto cb1 = std::make_unique<StrictMock<MockLifecycleObserver>>();
EXPECT_CALL(*cb1, observerAttach(transport.get()));
transport->addLifecycleObserver(cb1.get());
EXPECT_THAT(
transport->getLifecycleObservers(), UnorderedElementsAre(cb1.get()));
auto cb2 = std::make_unique<StrictMock<MockLifecycleObserver>>();
EXPECT_CALL(*cb2, observerAttach(transport.get()));
transport->addLifecycleObserver(cb2.get());
EXPECT_THAT(
transport->getLifecycleObservers(),
UnorderedElementsAre(cb1.get(), cb2.get()));
InSequence s;
EXPECT_CALL(*cb1, close(transport.get(), _));
EXPECT_CALL(*cb2, close(transport.get(), _));
EXPECT_CALL(*cb1, close(transport.get(), _));
EXPECT_CALL(*cb2, close(transport.get(), _));
EXPECT_CALL(*cb1, destroy(transport.get()));
EXPECT_CALL(*cb2, destroy(transport.get()));
transport = nullptr;
Mock::VerifyAndClearExpectations(cb1.get());
Mock::VerifyAndClearExpectations(cb2.get());
}
} // namespace test
} // namespace quic