by Tyler Calabrese
April - 4 - 2024
Presentation created from Markdown using marp
Preface: A little bit about Boost
Welcome to Boost.org! Boost provides free peer-reviewed portable C++ source libraries. (boost)
The Boost community emerged around 1998, when the first version of the standard was released. It has grown continuously since then and now plays a big role in the standardization of C++. (wikipedia)
Many modern C++ features that developers now take for granted, such as smart pointers, tuples, and even std::thread, began as Boost library features. (wikipedia)
As a full-time C++ developer, here are, in my professional opinion, the best ways to write an async program in C++:
As a full-time C++ developer, here are, in my professional opinion, the best ways to write an async program in C++:
As a full-time C++ developer, here are, in my professional opinion, the best ways to write an async program in C++:
Ok, then, why:
Notice what these all have in common:
Whether writing to a file or communicating over a network, these are all I/O operations.
Hence, boost async io. asio!
This I/O execution context represents your program’s link to the operating system’s I/O services. (boost)
Example: Post a single operation to an I/O context
int main()
{
boost::asio::io_context ctx;
boost::asio::post(ctx, [](){ std::cerr << "hi!\n"; });
ctx.run();
}
the I/O execution context dequeues the result, translates it into an
error_code
, and then passes it to your completion handler. (boost)
use_future
and use_awaitable
.Example: Repeat Timer
class RepeatTimer
{
public:
explicit RepeatTimer(boost::asio::io_context& ctx, std::chrono::milliseconds every, std::function<void()> task)
: m_timer(boost::asio::make_strand(ctx), every), m_every(every), m_task(task)
{
m_timer.async_wait([this](boost::system::error_code ec){ doRunOne(ec); });
}
~RepeatTimer() = default; // destroying the timer cancels it, too
void cancel() { m_timer.cancel(); }
private:
void doRunOne(boost::system::error_code ec);
boost::asio::steady_timer m_timer;
std::chrono::milliseconds m_every;
std::function<void()> m_task;
};
void RepeatTimer::doRunOne(boost::system::error_code ec)
{
// we canceled or destroyed the timer
if (ec == boost::asio::error::operation_aborted)
return;
// there was a problem
else if (ec)
throw std::runtime_error{"Repeat timer got error code " + std::to_string(ec.value())};
// normal operation. Run our task and repeat
m_timer.expires_after(m_every);
m_task();
m_timer.async_wait([this](boost::system::error_code ec){ doRunOne(ec); });
}
Example: Repeat Timer
int main()
{
int counter = 0;
boost::asio::io_context ctx;
RepeatTimer t{ctx, std::chrono::seconds{1}, [&](){
std::cerr << counter++ << std::endl;
}};
ctx.run_for(std::chrono::seconds{4});
}
Running an I/O Context from Multiple Threads
int main()
{
constexpr int c_num_threads = 3;
boost::asio::io_context ctx;
for (int i = 0; i < c_num_threads; i++)
{
boost::asio::post(ctx, [](){
std::this_thread::sleep_for(std::chrono::seconds{1});
std::cerr << "hi!\n";
});
}
boost::asio::thread_pool th{c_num_threads};
for (int i = 0; i < c_num_threads; i++)
boost::asio::post(th, [&ctx](){ ctx.run(); });
th.join();
return EXIT_SUCCESS;
}
Exercise: Let’s asio-ify this code together
Exercise: Let’s asio-ify this code together