2
0
mirror of https://github.com/boostorg/asio.git synced 2026-02-25 02:32:07 +00:00
Files
asio/test/experimental/promise.cpp
klemens-morgenstern 03ba758437 Added experimental::promise.
The experimental::promise type allows eager execution and
synchronisation of async operations.

    auto promise = async_read(
        stream, asio::buffer(my_buffer),
        asio::experimental::use_promise);

    ... do other stuff while the read is going on ...

    promise.async_wait( // completion the operation
        [](error_code ec, std::size_t bytes_read)
        {
          ...
        });

Promises can be safely disregarded if the result is no longer required.

Different operations can be combined to either wait for all to complete
or for one to complete (and cancel the rest). For example, to wait for
one to complete:

    auto timeout_promise =
      timer.async_wait(
        asio::experimental::use_promise);

    auto read_promise = async_read(
        stream, asio::buffer(my_buffer),
        asio::experimental::use_promise);

    auto promise =
      asio::experimental::promise<>::race(
        timeout_promise, read_promise);

    promise.async_wait(
        [](std::variant<error_code, std::tuple<error_code, std::size_t>> v)
        {
          if (v.index() == 0) {} //timed out
          else if (v.index() == 1) // completed in time
        });

or to wait for all to complete:

    auto write_promise = async_write(
        stream, asio::buffer(my_write_buffer),
        asio::experimental::use_promise);

    auto read_promise = async_read(
        stream, asio::buffer(my_buffer),
        asio::experimental::use_promise);

    auto promise =
      asio::experimental::promise<>::all(
        write_promise, read_promise);

    promise.async_wait(
        [](std::tuple<error_code, std::size_t> write_result,
          std::tuple<error_code, std::size_t> read_result)
        {
        });
2021-07-01 15:40:28 +10:00

323 lines
7.7 KiB
C++

//
// promise.cpp
// ~~~~~~~~~~~
//
// Copyright (c) 2021 Klemens D. Morgenstern
// (klemens dot morgenstern at gmx dot net)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Disable autolinking for unit tests.
#if !defined(BOOST_ALL_NO_LIB)
#define BOOST_ALL_NO_LIB 1
#endif // !defined(BOOST_ALL_NO_LIB)
// Test that header file is self-contained.
#include <boost/asio/experimental/promise.hpp>
#include <boost/asio/steady_timer.hpp>
#include "../unit_test.hpp"
namespace promise {
void promise_tester()
{
using namespace boost::asio;
using boost::system::error_code;
using namespace std::chrono;
io_context ctx;
steady_timer timer1{ctx}, timer2{ctx};
timer1.expires_after(milliseconds(100));
timer2.expires_after(milliseconds(50));
auto p = timer1.async_wait(experimental::use_promise);
steady_clock::time_point completed_when;
error_code ec;
bool called = false;
p.async_wait(
[&](auto ec_)
{
ec = ec_;
called = true;
completed_when = steady_clock::now();
});
steady_clock::time_point timer2_done;
timer2.async_wait([&](auto) {
timer2_done = steady_clock::now();;
p.cancel();
});
ctx.run();
BOOST_ASIO_CHECK(timer2_done + milliseconds(1) > completed_when);
BOOST_ASIO_CHECK(called);
BOOST_ASIO_CHECK(ec == error::operation_aborted);
}
void promise_race_tester()
{
using namespace boost::asio;
using boost::system::error_code;
using namespace std::chrono;
io_context ctx;
steady_timer timer1{ctx}, timer2{ctx};
timer1.expires_after(milliseconds(100));
timer2.expires_after(milliseconds(50));
const auto started_when = steady_clock::now();
experimental::promise<void(std::variant<error_code, error_code>)> p =
experimental::promise<>::race(
timer1.async_wait(experimental::use_promise),
timer2.async_wait(experimental::use_promise));
auto called = false;
error_code ec;
steady_clock::time_point completed_when;
p.async_wait(
[&](auto v)
{
BOOST_ASIO_CHECK(v.index() == 1);
ec = get<1>(v);
called = true;
completed_when = steady_clock::now();
});
ctx.run();
BOOST_ASIO_CHECK(started_when + milliseconds(50) <= completed_when);
BOOST_ASIO_CHECK(started_when + milliseconds(55) > completed_when);
BOOST_ASIO_CHECK(called);
BOOST_ASIO_CHECK(!ec);
}
void promise_all_tester()
{
using namespace boost::asio;
using boost::system::error_code;
using namespace std::chrono;
io_context ctx;
steady_timer timer1{ctx},
timer2{ctx};
timer1.expires_after(milliseconds(100));
timer2.expires_after(milliseconds(50));
const auto started_when = steady_clock::now();
experimental::promise<void(error_code, error_code)> p =
experimental::promise<>::all(
timer1.async_wait(experimental::use_promise),
timer2.async_wait(experimental::use_promise));
bool called = false;
steady_clock::time_point completed_when;
p.async_wait(
[&](auto ec1, auto ec2)
{
BOOST_ASIO_CHECK(!ec1);
BOOST_ASIO_CHECK(!ec2);
called = true;
completed_when = steady_clock::now();
});
ctx.run();
BOOST_ASIO_CHECK(started_when + milliseconds(100) <= completed_when);
BOOST_ASIO_CHECK(started_when + milliseconds(105) > completed_when);
BOOST_ASIO_CHECK(called);
}
void promise_race_ranged_tester()
{
using namespace boost::asio;
using boost::system::error_code;
using namespace std::chrono;
io_context ctx;
steady_timer timer1{ctx}, timer2{ctx};
timer1.expires_after(milliseconds(100));
timer2.expires_after(milliseconds(50));
const auto started_when = steady_clock::now();
// promise<
// std::variant<
// tuple<error_code, std::size_t>,
// tuple<error_code, std::size_t>>>
experimental::promise<void(std::size_t, error_code)> p =
experimental::promise<>::race(
std::array{
timer1.async_wait(experimental::use_promise),
timer2.async_wait(experimental::use_promise)
});
auto called = false;
auto completed_when = steady_clock::time_point();
p.async_wait([&](auto idx, auto ec )
{
BOOST_ASIO_CHECK(idx == 1);
called = true;
completed_when = steady_clock::now();
BOOST_ASIO_CHECK(!ec);
});
std::array<experimental::promise<void()>, 0u> arr;
experimental::promise<>::race(
ctx.get_executor(), std::move(arr)
).async_wait(
[](std::size_t idx) {BOOST_ASIO_CHECK(idx == std::size_t(-1));}
);
ctx.run();
BOOST_ASIO_CHECK(started_when + milliseconds(50) <= completed_when);
BOOST_ASIO_CHECK(started_when + milliseconds(55) > completed_when);
BOOST_ASIO_CHECK(called);
std::exception_ptr ex;
try
{
experimental::promise<>::race(std::move(arr));
}
catch (...)
{
ex = std::current_exception();
}
BOOST_ASIO_CHECK(ex);
}
void promise_all_ranged_tester()
{
using namespace boost::asio;
using boost::system::error_code;
using namespace std::chrono;
io_context ctx;
steady_timer timer1{ctx}, timer2{ctx};
timer1.expires_after(milliseconds(100));
timer2.expires_after(milliseconds(50));
const auto started_when = steady_clock::now();
// promise<
// std::variant<
// tuple<error_code, std::size_t>,
// tuple<error_code, std::size_t>>>
experimental::promise<void(std::vector<error_code>)> p =
experimental::promise<>::all(
std::array{
timer1.async_wait(experimental::use_promise),
timer2.async_wait(experimental::use_promise)
});
auto called = false;
auto completed_when = steady_clock::time_point();
p.async_wait(
[&](auto v){
BOOST_ASIO_CHECK(v.size() == 2u);
completed_when = steady_clock::now();
BOOST_ASIO_CHECK(!v[0]);
BOOST_ASIO_CHECK(!v[1]);
called = true;
});
std::array<experimental::promise<void()>, 0u> arr;
experimental::promise<>::all(
ctx.get_executor(), std::move(arr)
).async_wait(
[](auto v) {BOOST_ASIO_CHECK(v.size() == 0);}
);
ctx.run();
BOOST_ASIO_CHECK(started_when + milliseconds(100) <= completed_when);
BOOST_ASIO_CHECK(started_when + milliseconds(105) > completed_when);
BOOST_ASIO_CHECK(called == true);
std::exception_ptr ex;
try
{
experimental::promise<>::all(std::move(arr));
}
catch (...)
{
ex = std::current_exception();
}
BOOST_ASIO_CHECK(ex);
}
void promise_cancel_tester()
{
using namespace boost::asio;
using boost::system::error_code;
using namespace std::chrono;
io_context ctx;
steady_timer timer1{ctx}, timer2{ctx};
timer1.expires_after(milliseconds(100));
timer2.expires_after(milliseconds(50));
// promise<
// std::variant<
// tuple<error_code, std::size_t>,
// tuple<error_code, std::size_t>>>
experimental::promise<void(error_code, error_code)> p =
experimental::promise<>::all(
timer1.async_wait(experimental::use_promise),
timer2.async_wait(experimental::use_promise));
bool called = false;
p.async_wait(
[&](auto ec1, auto ec2)
{
called = true;
BOOST_ASIO_CHECK(ec1 == error::operation_aborted);
BOOST_ASIO_CHECK(ec2 == error::operation_aborted);
});
post(ctx, [&]{p.cancel();});
ctx.run();
BOOST_ASIO_CHECK(called);
}
} // namespace promise
BOOST_ASIO_TEST_SUITE
(
"promise",
BOOST_ASIO_TEST_CASE(promise::promise_tester)
BOOST_ASIO_TEST_CASE(promise::promise_race_tester)
BOOST_ASIO_TEST_CASE(promise::promise_all_tester)
BOOST_ASIO_TEST_CASE(promise::promise_race_ranged_tester)
BOOST_ASIO_TEST_CASE(promise::promise_all_ranged_tester)
BOOST_ASIO_TEST_CASE(promise::promise_cancel_tester)
)