mirror of
https://github.com/FairRootGroup/FairMQ.git
synced 2025-10-13 08:41:16 +00:00
SDK: Implement asio-compliant asynchronous operation helpers
This commit is contained in:
parent
1dec059104
commit
73af0ed78b
211
fairmq/sdk/AsioAsyncOp.h
Normal file
211
fairmq/sdk/AsioAsyncOp.h
Normal file
|
@ -0,0 +1,211 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_SDK_ASIOASYNCOP_H
|
||||
#define FAIR_MQ_SDK_ASIOASYNCOP_H
|
||||
|
||||
#include <asio/associated_allocator.hpp>
|
||||
#include <asio/associated_executor.hpp>
|
||||
#include <asio/executor_work_guard.hpp>
|
||||
#include <asio/system_executor.hpp>
|
||||
#include <chrono>
|
||||
#include <fairmq/sdk/Exceptions.h>
|
||||
#include <fairmq/sdk/Traits.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace fair {
|
||||
namespace mq {
|
||||
namespace sdk {
|
||||
|
||||
template<typename... SignatureArgTypes>
|
||||
struct AsioAsyncOpImplBase
|
||||
{
|
||||
virtual auto Complete(std::error_code, SignatureArgTypes&&...) -> void = 0;
|
||||
virtual auto IsCompleted() const -> bool = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @tparam Executor1 Associated I/O executor, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.associated_i_o_executor
|
||||
* @tparam Allocator1 Default allocation strategy, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.allocation_of_intermediate_storage
|
||||
*/
|
||||
template<typename Executor1, typename Allocator1, typename Handler, typename... SignatureArgTypes>
|
||||
struct AsioAsyncOpImpl : AsioAsyncOpImplBase<SignatureArgTypes...>
|
||||
{
|
||||
/// See https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.allocation_of_intermediate_storage
|
||||
using Allocator2 = typename asio::associated_allocator<Handler, Allocator1>::type;
|
||||
|
||||
/// See https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.associated_completion_handler_executor
|
||||
using Executor2 = typename asio::associated_executor<Handler, Executor1>::type;
|
||||
|
||||
/// Ctor
|
||||
AsioAsyncOpImpl(const Executor1& ex1, Allocator1&& alloc1, Handler&& handler)
|
||||
: fWork1(ex1)
|
||||
, fWork2(asio::get_associated_executor(handler, ex1))
|
||||
, fHandler(std::move(handler))
|
||||
, fAlloc1(std::move(alloc1))
|
||||
{}
|
||||
|
||||
auto GetAlloc2() const -> Allocator2 { return asio::get_associated_allocator(fHandler, fAlloc1); }
|
||||
auto GetEx2() const -> Executor2 { return asio::get_associated_executor(fWork2); }
|
||||
|
||||
auto Complete(std::error_code ec, SignatureArgTypes&&... args) -> void override
|
||||
{
|
||||
if (IsCompleted()) {
|
||||
throw RuntimeError("Async operation already completed");
|
||||
}
|
||||
|
||||
GetEx2().dispatch(
|
||||
[=, handler = std::move(fHandler)]() mutable {
|
||||
handler(ec, std::forward<SignatureArgTypes>(args)...);
|
||||
},
|
||||
GetAlloc2());
|
||||
|
||||
fWork1.reset();
|
||||
fWork2.reset();
|
||||
}
|
||||
|
||||
auto IsCompleted() const -> bool override
|
||||
{
|
||||
return !fWork1.owns_work() && !fWork2.owns_work();
|
||||
}
|
||||
|
||||
private:
|
||||
/// See https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.outstanding_work
|
||||
asio::executor_work_guard<Executor1> fWork1;
|
||||
asio::executor_work_guard<Executor2> fWork2;
|
||||
Handler fHandler;
|
||||
Allocator1 fAlloc1;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class AsioAsyncOp AsioAsyncOp.h <fairmq/sdk/AsioAsyncOp.h>
|
||||
* @tparam Executor Associated I/O executor, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.associated_i_o_executor
|
||||
* @tparam Allocator Default allocation strategy, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.allocation_of_intermediate_storage
|
||||
* @tparam CompletionSignature
|
||||
* @brief Interface for Asio-compliant asynchronous operation, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html
|
||||
*
|
||||
* @par Thread Safety
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe.
|
||||
*
|
||||
* primary template
|
||||
*/
|
||||
template<typename Executor, typename Allocator, typename CompletionSignature>
|
||||
struct AsioAsyncOp
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @tparam Executor See primary template
|
||||
* @tparam Allocator See primary template
|
||||
* @tparam SignatureReturnType Return type of CompletionSignature, see primary template
|
||||
* @tparam SignatureFirstArgType Type of first argument of CompletionSignature, see primary template
|
||||
* @tparam SignatureArgTypes Types of the rest of arguments of CompletionSignature
|
||||
*
|
||||
* partial specialization to deconstruct CompletionSignature
|
||||
*/
|
||||
template<typename Executor,
|
||||
typename Allocator,
|
||||
typename SignatureReturnType,
|
||||
typename SignatureFirstArgType,
|
||||
typename... SignatureArgTypes>
|
||||
struct AsioAsyncOp<Executor,
|
||||
Allocator,
|
||||
SignatureReturnType(SignatureFirstArgType, SignatureArgTypes...)>
|
||||
{
|
||||
static_assert(std::is_void<SignatureReturnType>::value,
|
||||
"return value of CompletionSignature must be void");
|
||||
static_assert(std::is_same<SignatureFirstArgType, std::error_code>::value,
|
||||
"first argument of CompletionSignature must be std::error_code");
|
||||
using Duration = std::chrono::milliseconds;
|
||||
|
||||
private:
|
||||
using Impl = AsioAsyncOpImplBase<SignatureArgTypes...>;
|
||||
using ImplPtr = std::unique_ptr<Impl, std::function<void(Impl*)>>;
|
||||
ImplPtr fImpl;
|
||||
|
||||
public:
|
||||
/// Default Ctor
|
||||
AsioAsyncOp()
|
||||
: fImpl(nullptr)
|
||||
{}
|
||||
|
||||
/// Ctor with handler
|
||||
template<typename Handler>
|
||||
AsioAsyncOp(Executor&& ex1, Allocator&& alloc1, Handler&& handler)
|
||||
: AsioAsyncOp()
|
||||
{
|
||||
// Async operation type to be allocated and constructed
|
||||
using Op = AsioAsyncOpImpl<Executor, Allocator, Handler, SignatureArgTypes...>;
|
||||
|
||||
// Create allocator for concrete op type
|
||||
// Allocator2, see https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.allocation_of_intermediate_storage
|
||||
using OpAllocator =
|
||||
typename std::allocator_traits<typename Op::Allocator2>::template rebind_alloc<Op>;
|
||||
OpAllocator opAlloc;
|
||||
|
||||
// Allocate memory
|
||||
auto mem(std::allocator_traits<OpAllocator>::allocate(opAlloc, 1));
|
||||
|
||||
// Construct object
|
||||
auto ptr(new (mem) Op(std::forward<Executor>(ex1),
|
||||
std::forward<Allocator>(alloc1),
|
||||
std::forward<Handler>(handler)));
|
||||
|
||||
// Assign ownership to this object
|
||||
fImpl = ImplPtr(ptr, [opAlloc](Impl* p) mutable {
|
||||
std::allocator_traits<OpAllocator>::deallocate(opAlloc, static_cast<Op*>(p), 1);
|
||||
});
|
||||
}
|
||||
|
||||
/// Ctor with handler #2
|
||||
template<typename Handler>
|
||||
AsioAsyncOp(Executor&& ex1, Handler&& handler)
|
||||
: AsioAsyncOp(std::forward<Executor>(ex1), Allocator(), std::forward<Handler>(handler))
|
||||
{}
|
||||
|
||||
/// Ctor with handler #3
|
||||
template<typename Handler>
|
||||
explicit AsioAsyncOp(Handler&& handler)
|
||||
: AsioAsyncOp(asio::system_executor(), std::forward<Handler>(handler))
|
||||
{}
|
||||
|
||||
auto IsCompleted() -> bool { return (fImpl == nullptr) || fImpl->IsCompleted(); }
|
||||
|
||||
auto Complete(std::error_code ec, SignatureArgTypes&&... args) -> void
|
||||
{
|
||||
if(IsCompleted()) {
|
||||
throw RuntimeError("Async operation already completed");
|
||||
}
|
||||
|
||||
fImpl->Complete(ec, std::forward<SignatureArgTypes>(args)...);
|
||||
fImpl.reset(nullptr);
|
||||
}
|
||||
|
||||
auto Complete(SignatureArgTypes&&... args) -> void
|
||||
{
|
||||
Complete(std::error_code(), std::forward<SignatureArgTypes>(args)...);
|
||||
}
|
||||
|
||||
auto Cancel(SignatureArgTypes&&... args) -> void
|
||||
{
|
||||
Complete(std::make_error_code(std::errc::operation_canceled),
|
||||
std::forward<SignatureArgTypes>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace sdk */
|
||||
} /* namespace mq */
|
||||
} /* namespace fair */
|
||||
|
||||
#endif /* FAIR_MQ_SDK_ASIOASYNCOP_H */
|
||||
|
76
fairmq/sdk/AsioBase.h
Normal file
76
fairmq/sdk/AsioBase.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_SDK_ASIOBASE_H
|
||||
#define FAIR_MQ_SDK_ASIOBASE_H
|
||||
|
||||
#include <asio/executor.hpp>
|
||||
#include <fairmq/sdk/Traits.h>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace fair {
|
||||
namespace mq {
|
||||
namespace sdk {
|
||||
|
||||
using DefaultExecutor = asio::executor;
|
||||
using DefaultAllocator = std::allocator<int>;
|
||||
|
||||
/**
|
||||
* @class AsioBase AsioBase.h <fairmq/sdk/AsioBase.h>
|
||||
* @tparam Executor Associated I/O executor
|
||||
* @tparam Allocator Associated default allocator
|
||||
* @brief Base for creating Asio-enabled I/O objects
|
||||
*
|
||||
* @par Thread Safety
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe.
|
||||
*/
|
||||
template<typename Executor, typename Allocator>
|
||||
class AsioBase
|
||||
{
|
||||
public:
|
||||
/// Member type of associated I/O executor
|
||||
using ExecutorType = Executor;
|
||||
/// Get associated I/O executor
|
||||
auto GetExecutor() const noexcept -> ExecutorType { return fExecutor; }
|
||||
|
||||
/// Member type of associated default allocator
|
||||
using AllocatorType = Allocator;
|
||||
/// Get associated default allocator
|
||||
auto GetAllocator() const noexcept -> AllocatorType { return fAllocator; }
|
||||
|
||||
/// NO default ctor
|
||||
AsioBase() = delete;
|
||||
|
||||
/// Construct with associated I/O executor
|
||||
explicit AsioBase(Executor ex, Allocator alloc)
|
||||
: fExecutor(std::move(ex))
|
||||
, fAllocator(std::move(alloc))
|
||||
{}
|
||||
|
||||
/// NOT copyable
|
||||
AsioBase(const AsioBase&) = delete;
|
||||
AsioBase& operator=(const AsioBase&) = delete;
|
||||
|
||||
/// movable
|
||||
AsioBase(AsioBase&&) noexcept = default;
|
||||
AsioBase& operator=(AsioBase&&) noexcept = default;
|
||||
|
||||
~AsioBase() = default;
|
||||
|
||||
private:
|
||||
ExecutorType fExecutor;
|
||||
AllocatorType fAllocator;
|
||||
};
|
||||
|
||||
} /* namespace sdk */
|
||||
} /* namespace mq */
|
||||
} /* namespace fair */
|
||||
|
||||
#endif /* FAIR_MQ_SDK_ASIOBASE_H */
|
37
fairmq/sdk/Exceptions.h
Normal file
37
fairmq/sdk/Exceptions.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_SDK_EXCEPTIONS_H
|
||||
#define FAIR_MQ_SDK_EXCEPTIONS_H
|
||||
|
||||
#include <fairmq/Tools.h>
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
|
||||
namespace fair {
|
||||
namespace mq {
|
||||
namespace sdk {
|
||||
|
||||
struct RuntimeError : ::std::runtime_error
|
||||
{
|
||||
template<typename... T>
|
||||
explicit RuntimeError(T&&... t)
|
||||
: ::std::runtime_error::runtime_error(tools::ToString(std::forward<T>(t)...))
|
||||
{}
|
||||
};
|
||||
|
||||
struct MixedStateError : RuntimeError
|
||||
{
|
||||
using RuntimeError::RuntimeError;
|
||||
};
|
||||
|
||||
} /* namespace sdk */
|
||||
} /* namespace mq */
|
||||
} /* namespace fair */
|
||||
|
||||
#endif /* FAIR_MQ_SDK_EXCEPTIONS_H */
|
50
fairmq/sdk/Traits.h
Normal file
50
fairmq/sdk/Traits.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_SDK_TRAITS_H
|
||||
#define FAIR_MQ_SDK_TRAITS_H
|
||||
|
||||
#include <asio/associated_allocator.hpp>
|
||||
#include <asio/associated_executor.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
/// Specialize to match our coding conventions
|
||||
template<typename T, typename Executor>
|
||||
struct associated_executor_impl<T,
|
||||
Executor,
|
||||
std::enable_if_t<is_executor<typename T::ExecutorType>::value>>
|
||||
{
|
||||
using type = typename T::ExecutorType;
|
||||
|
||||
static auto get(const T& obj, const Executor& /*ex = Executor()*/) noexcept -> type
|
||||
{
|
||||
return obj.GetExecutor();
|
||||
}
|
||||
};
|
||||
|
||||
/// Specialize to match our coding conventions
|
||||
template<typename T, typename Allocator>
|
||||
struct associated_allocator_impl<T,
|
||||
Allocator,
|
||||
std::enable_if_t<T::AllocatorType>>
|
||||
{
|
||||
using type = typename T::AllocatorType;
|
||||
|
||||
static auto get(const T& obj, const Allocator& /*alloc = Allocator()*/) noexcept -> type
|
||||
{
|
||||
return obj.GetAllocator();
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace asio */
|
||||
|
||||
#endif /* FAIR_MQ_SDK_TRAITS_H */
|
|
@ -287,6 +287,7 @@ if(BUILD_SDK)
|
|||
add_testsuite(SDK
|
||||
SOURCES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/runner.cxx
|
||||
sdk/_async_op.cxx
|
||||
sdk/_dds.cxx
|
||||
sdk/_topology.cxx
|
||||
sdk/Fixtures.h
|
||||
|
|
|
@ -10,12 +10,13 @@
|
|||
#define FAIR_MQ_TEST_FIXTURES
|
||||
|
||||
#include "TestEnvironment.h"
|
||||
#include <fairmq/SDK.h>
|
||||
#include <fairmq/Tools.h>
|
||||
|
||||
#include <asio/io_context.hpp>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <fairlogger/Logger.h>
|
||||
#include <fairmq/SDK.h>
|
||||
#include <fairmq/Tools.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <thread>
|
||||
|
||||
|
@ -76,6 +77,19 @@ struct TopologyFixture : ::testing::Test
|
|||
sdk::DDSEnvironment mDDSEnv;
|
||||
sdk::DDSSession mDDSSession;
|
||||
sdk::DDSTopology mDDSTopo;
|
||||
asio::io_context mIoContext;
|
||||
};
|
||||
|
||||
struct AsyncOpFixture : ::testing::Test
|
||||
{
|
||||
auto SetUp() -> void override {
|
||||
}
|
||||
|
||||
auto TearDown() -> void override {
|
||||
}
|
||||
|
||||
LoggerConfig mLoggerConfig;
|
||||
asio::io_context mIoContext;
|
||||
};
|
||||
|
||||
} /* namespace test */
|
||||
|
|
118
test/sdk/_async_op.cxx
Normal file
118
test/sdk/_async_op.cxx
Normal file
|
@ -0,0 +1,118 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include "Fixtures.h"
|
||||
|
||||
#include <fairmq/sdk/AsioBase.h>
|
||||
#include <fairmq/sdk/AsioAsyncOp.h>
|
||||
#include <asio/steady_timer.hpp>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
namespace {
|
||||
|
||||
using AsyncOp = fair::mq::test::AsyncOpFixture;
|
||||
|
||||
// template <typename Executor, typename Allocator>
|
||||
// class : public AsioBase<Executor, Allocator>
|
||||
|
||||
TEST_F(AsyncOp, DefaultConstruction)
|
||||
{
|
||||
using namespace fair::mq::sdk;
|
||||
|
||||
AsioAsyncOp<DefaultExecutor, DefaultAllocator, void(std::error_code, int)> op;
|
||||
EXPECT_TRUE(op.IsCompleted());
|
||||
}
|
||||
|
||||
TEST_F(AsyncOp, ConstructionWithHandler)
|
||||
{
|
||||
using namespace fair::mq::sdk;
|
||||
|
||||
AsioAsyncOp<DefaultExecutor, DefaultAllocator, void(std::error_code, int)> op(
|
||||
[](std::error_code, int) {});
|
||||
EXPECT_FALSE(op.IsCompleted());
|
||||
}
|
||||
|
||||
TEST_F(AsyncOp, Complete)
|
||||
{
|
||||
using namespace fair::mq::sdk;
|
||||
|
||||
AsioAsyncOp<DefaultExecutor, DefaultAllocator, void(std::error_code, int)> op(
|
||||
[](std::error_code ec, int v) {
|
||||
EXPECT_FALSE(ec); // success
|
||||
EXPECT_EQ(v, 42);
|
||||
});
|
||||
|
||||
EXPECT_FALSE(op.IsCompleted());
|
||||
op.Complete(42);
|
||||
EXPECT_TRUE(op.IsCompleted());
|
||||
|
||||
EXPECT_THROW(op.Complete(6), RuntimeError); // No double completion!
|
||||
}
|
||||
|
||||
TEST_F(AsyncOp, Cancel)
|
||||
{
|
||||
using namespace fair::mq::sdk;
|
||||
|
||||
AsioAsyncOp<DefaultExecutor, DefaultAllocator, void(std::error_code)> op(
|
||||
[](std::error_code ec) {
|
||||
EXPECT_TRUE(ec); // error
|
||||
EXPECT_EQ(ec, std::make_error_code(std::errc::operation_canceled));
|
||||
});
|
||||
|
||||
op.Cancel();
|
||||
}
|
||||
|
||||
TEST_F(AsyncOp, Timeout)
|
||||
{
|
||||
using namespace fair::mq::sdk;
|
||||
|
||||
asio::steady_timer timer(mIoContext.get_executor(), std::chrono::milliseconds(50));
|
||||
AsioAsyncOp<DefaultExecutor, DefaultAllocator, void(std::error_code)> op(
|
||||
mIoContext.get_executor(),
|
||||
[&timer](std::error_code ec) {
|
||||
timer.cancel();
|
||||
std::cout << "Completion with: " << ec.message() << std::endl;
|
||||
EXPECT_TRUE(ec); // error
|
||||
EXPECT_EQ(ec, std::make_error_code(std::errc::operation_canceled));
|
||||
});
|
||||
timer.async_wait([&op](asio::error_code ec) {
|
||||
std::cout << "Timer event" << std::endl;
|
||||
if (ec != asio::error::operation_aborted) {
|
||||
op.Cancel();
|
||||
}
|
||||
});
|
||||
|
||||
mIoContext.run();
|
||||
EXPECT_THROW(op.Complete(), RuntimeError);
|
||||
}
|
||||
|
||||
TEST_F(AsyncOp, Timeout2)
|
||||
{
|
||||
using namespace fair::mq::sdk;
|
||||
|
||||
asio::steady_timer timer(mIoContext.get_executor(), std::chrono::milliseconds(50));
|
||||
AsioAsyncOp<DefaultExecutor, DefaultAllocator, void(std::error_code)> op(
|
||||
mIoContext.get_executor(),
|
||||
[&timer](std::error_code ec) {
|
||||
timer.cancel();
|
||||
std::cout << "Completion with: " << ec.message() << std::endl;
|
||||
EXPECT_FALSE(ec); // success
|
||||
});
|
||||
op.Complete(); // Complete before timer
|
||||
timer.async_wait([&op](asio::error_code ec) {
|
||||
std::cout << "Timer event" << std::endl;
|
||||
if (ec != asio::error::operation_aborted) {
|
||||
op.Cancel();
|
||||
}
|
||||
});
|
||||
|
||||
mIoContext.run();
|
||||
EXPECT_THROW(op.Complete(), RuntimeError);
|
||||
}
|
||||
} // namespace
|
Loading…
Reference in New Issue
Block a user