In modern C++ development, coroutines have brought revolutionary changes to asynchronous programming. However, when using boost::asio or standalone asio, we often encounter scenarios where we need to convert traditional std::future<T> to asio::awaitable<T>. This article will detail an efficient, thread-safe conversion method.
Problem Background
When using asio coroutines, we often encounter scenarios like:
- Need to call third-party libraries that return std::future (such as database drivers)
- Want to use co_await in coroutines to handle these asynchronous operations
- Don’t want to block IO threads, maintaining high performance
Traditional solutions might use timer polling or directly call future.get() in IO threads, but these methods are either inefficient or block IO threads.
Core Solution
Our solution is based on asio::async_initiate, which provides perfect integration with the asio coroutine system while avoiding the problem of blocking IO threads.
Core Implementation
Usage Examples
Basic Usage
Exception Handling
Technical Points Analysis
1. Advantages of Using async_initiate
- Perfect Integration: Seamlessly integrates with asio coroutine system
- Type Safety: Compile-time type checking
- Performance Optimization: Avoids timer polling overhead
2. Thread Pool Design
- Dedicated to handling blocking operations
- Avoids blocking IO threads
- Thread count can be adjusted as needed
3. Exception Handling Strategy
Using std::tuple<std::optional<T>, std::exception_ptr> to handle two scenarios:
- Normal result: {std::optional<T>, nullptr}
- Exception case: {std::nullopt, std::exception_ptr}
This design correctly handles edge cases, such as when the return type itself is std::exception_ptr.
4. Executor Context Preservation
Ensures that the final handler call occurs in the correct executor context, maintaining asio’s executor semantics.
Performance Considerations
- Avoid IO Thread Blocking: All blocking operations are executed in independent thread pools
- Move Semantics: Extensive use of std::move to avoid unnecessary copying
- Zero-Copy Design: Results are passed directly between threads without additional copying
Practical Application Scenarios
This conversion method is particularly suitable for the following scenarios:
- Database Operations: Converting database driver async interfaces to coroutine-friendly forms
- File I/O: Handling potentially blocking file operations
- Third-Party Library Integration: Integrating with libraries that return std::future
- CPU-Intensive Tasks: Converting CPU-intensive tasks to awaitable forms
Summary
By using asio::async_initiate and thread pools, we’ve implemented an efficient, thread-safe solution for converting std::future to asio::awaitable. This approach not only avoids blocking IO threads but also provides perfect exception handling mechanisms, making it one of the best practices for modern C++ asynchronous programming.
This design pattern can be easily extended to other asynchronous scenarios, laying a solid foundation for building high-performance coroutine applications.
.png)

