diff --git a/src/mongo/executor/network_interface_asio.cpp b/src/mongo/executor/network_interface_asio.cpp index d9f7ac41250..b122d76ab87 100644 --- a/src/mongo/executor/network_interface_asio.cpp +++ b/src/mongo/executor/network_interface_asio.cpp @@ -40,6 +40,8 @@ #include "mongo/stdx/memory.h" #include "mongo/util/log.h" #include "mongo/util/net/sock.h" +#include "mongo/util/net/ssl_manager.h" +#include "mongo/util/time_support.h" namespace mongo { namespace executor { @@ -141,7 +143,17 @@ void NetworkInterfaceASIO::cancelCommand(const TaskExecutor::CallbackHandle& cbH } void NetworkInterfaceASIO::setAlarm(Date_t when, const stdx::function& action) { - MONGO_UNREACHABLE; + // "alarm" must stay alive until it expires, hence the shared_ptr. + auto alarm = std::make_shared(_io_service, when - now()); + alarm->async_wait([alarm, this, action](std::error_code ec) { + if (!ec) { + return action(); + } else if (ec != asio::error::operation_aborted) { + // When the network interface is shut down, it will cancel all pending + // alarms, raising an "operation_aborted" error here, which we ignore. + warning() << "setAlarm() received an error: " << ec.message(); + } + }); }; bool NetworkInterfaceASIO::inShutdown() const { diff --git a/src/mongo/executor/network_interface_asio_test.cpp b/src/mongo/executor/network_interface_asio_test.cpp index 72784848bce..a1aeb324eab 100644 --- a/src/mongo/executor/network_interface_asio_test.cpp +++ b/src/mongo/executor/network_interface_asio_test.cpp @@ -60,7 +60,9 @@ public: } void tearDown() override { - _net->shutdown(); + if (!_net->inShutdown()) { + _net->shutdown(); + } } NetworkInterface& net() { @@ -222,6 +224,36 @@ TEST_F(NetworkInterfaceASIOTest, StartCommand) { ASSERT_EQ(res.metadata, expectedMetadata); } +TEST_F(NetworkInterfaceASIOTest, setAlarm) { + stdx::promise nearFuture; + stdx::future executed = nearFuture.get_future(); + + // set a first alarm, to execute after "expiration" + Date_t expiration = net().now() + Milliseconds(100); + net().setAlarm( + expiration, + [this, expiration, &nearFuture]() { nearFuture.set_value(net().now() >= expiration); }); + + // wait enough time for first alarm to execute + auto status = executed.wait_for(Milliseconds(5000)); + + // assert that not only did it execute, but executed after "expiration" + ASSERT(status == stdx::future_status::ready); + ASSERT(executed.get()); + + // set an alarm for the future, kill interface, ensure it didn't execute + stdx::promise farFuture; + stdx::future executed2 = farFuture.get_future(); + + expiration = net().now() + Milliseconds(99999999); + net().setAlarm(expiration, [this, &farFuture]() { farFuture.set_value(true); }); + + net().shutdown(); + + status = executed2.wait_for(Milliseconds(0)); + ASSERT(status == stdx::future_status::timeout); +} + } // namespace } // namespace executor } // namespace mongo