{"id":15915,"date":"2017-05-19T10:39:08","date_gmt":"2017-05-19T17:39:08","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vcblog\/?p=15915"},"modified":"2023-05-08T14:36:56","modified_gmt":"2023-05-08T14:36:56","slug":"using-c-coroutines-with-boost-c-libraries","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/using-c-coroutines-with-boost-c-libraries\/","title":{"rendered":"Using C++ Coroutines with Boost C++ Libraries"},"content":{"rendered":"<p><strong><em>This article was written by Gor Nishanov.<\/em><\/strong><\/p>\n<p>Last month, Jim Springfield wrote a great <a href=\"https:\/\/blogs.msdn.microsoft.com\/vcblog\/2017\/02\/02\/using-ibuv-with-c-resumable-functions\/\">article on using C++ Coroutines with Libuv<\/a>\u00a0(a multi-platform C library for asynchronous I\/O). This month we will look at how to use coroutines with components of Boost C++ libraries, namely boost::future and boost::asio.<\/p>\n<h2>Getting Boost<\/h2>\n<p>If you already have <a href=\"http:\/\/www.boost.org\/\">boost<\/a> installed, skip this step. Otherwise, I recommend using <a href=\"https:\/\/github.com\/Microsoft\/vcpkg\">vcpkg<\/a> to quickly get boost installed on your machine. Follow the <a href=\"https:\/\/github.com\/Microsoft\/vcpkg\/blob\/master\/README.md\">instructions<\/a> to get vcpkg and then enter the following line to install 32bit and 64bit versions of boost:<\/p>\n<pre style=\"padding-left: 30px;\">.\\vcpkg install boost boost:x64-windows<\/pre>\n<p>To make sure everything got installed correctly, open and create a C++ Win32 Console Application:<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\n#define BOOST_THREAD_PROVIDES_FUTURE\r\n#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION \/\/ Enables future::then\r\n#include &lt;boost\/thread.hpp&gt;\r\n#include &lt;boost\/asio.hpp&gt;\r\n#include &lt;cstdio&gt;\r\n\r\nusing namespace boost;\r\nusing namespace boost::asio;\r\n\r\nint main() {\r\nio_service io;\r\npromise&lt;int&gt; p;\r\nauto f = p.get_future();\r\n\r\nio.post([&amp;] { p.set_value(42); });\r\nio.run();\r\n\r\nprintf(\"%d\\n\", f.get());\r\n}<\/code><\/pre>\n<p>When you run it, it should print 42.<\/p>\n<h2>Boost::Future: Coroutine Part<\/h2>\n<p>When a compiler encounters <strong>co_await<\/strong>, <strong>co_yield<\/strong> or <strong>co_return<\/strong> in a function, it treats the function as a coroutine. By itself C++ does not define the semantics of the coroutine, a user or a library writer needs to provide a specialization of the <strong>std::experimental::coroutine_traits<\/strong> template that tells the compiler what to do. (Compiler instantiates coroutine_traits by passing the types of the return value and types of all of the parameters passed to a function).<\/p>\n<p>We would like to be able to author coroutines that return a boost::future. To do that, we are going to specialize coroutine_traits as follows:<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\ntemplate &lt;typename... Args&gt;\r\nstruct std::experimental::coroutine_traits&lt;boost::future&lt;void&gt;, Args...&gt; {\r\nstruct promise_type {\r\nboost::promise&lt;void&gt; p;\r\nauto get_return_object() { return p.get_future(); }\r\nstd::experimental::suspend_never initial_suspend() { return {}; }\r\nstd::experimental::suspend_never final_suspend() { return {}; }\r\nvoid set_exception(std::exception_ptr e) { p.set_exception(std::move(e)); }\r\nvoid return_void() { p.set_value(); }\r\n};\r\n};<\/code>\r\n<\/pre>\n<p>When a coroutine gets suspended, it needs to return a future that will be satisfied when the coroutine runs to completion or completes with an exception.<\/p>\n<p>The member function <code>promise_type::get_return_object<\/code> defines how to obtain a future that will be connected to a particular instance of a coroutine.\u00a0The member function <code>promise_type::set_exception<\/code> defines what happens if an unhandled exception happens in a coroutine. In our case, we would like to store that exception into the promise connected to the future we returned from a coroutine.<\/p>\n<p>The member function <code>promise_type::return_void<\/code> defines what happens when execution reaches <strong>co_return<\/strong> statement or control flows runs to the end of the coroutine.<\/p>\n<p>Member functions <code>initial_suspend<\/code> and <code>final_suspend<\/code>, as we defined them, tell the compiler that we would like to start executing the coroutine immediately after it is called and to destroy the coroutine as soon as it runs to completion.<\/p>\n<p>To handle non-void futures, define specialization for boost::future for arbitrary types:<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\ntemplate &lt;typename R, typename... Args&gt;\r\nstruct std::experimental::coroutine_traits&lt;boost::future&lt;R&gt;, Args...&gt; {\r\nstruct promise_type {\r\nboost::promise&lt;R&gt; p;\r\nauto get_return_object() { return p.get_future(); }\r\nstd::experimental::suspend_never initial_suspend() { return {}; }\r\nstd::experimental::suspend_never final_suspend() { return {}; }\r\nvoid set_exception(std::exception_ptr e) { p.set_exception(std::move(e)); }\r\ntemplate &lt;typename U&gt; void return_value(U &amp;&amp;u) {\r\np.set_value(std::forward&lt;U&gt;(u));\r\n}\r\n};\r\n};\r\n<\/code><\/pre>\n<p>Note that in this case we defined <code>return_value<\/code>, as opposed to <code>return_void<\/code> as it was in the previous example. This tells the compiler that we expect that a coroutine needs to eventually return some non-void value (via a <code>co_return<\/code> statement) and that value will be propagated to the future associated with this coroutine. (There is a lot of common code between these two specializations; it can be factored out if desired).<\/p>\n<p>Now, we are ready to test it out. Add an \u201c\/await\u201d command line option to enable coroutine support in the compiler (since coroutines are not yet part of the C++ standard, an explicit opt-in is required to turn them on).<\/p>\n<p>Also, add an include for the coroutine support header that defines primary template for <code>std::experimental::coroutine_traits<\/code> that we want to specialize:<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\n#include &lt;experimental\/coroutine&gt;\r\n\r\n\/\/\u2026 includes and specializations of coroutine_traits \u2026\r\n\r\nboost::future&lt;void&gt; f() {\r\nputs(\"Hi!\");\r\nco_return;\r\n}\r\n\r\nboost::future&lt;int&gt; g() {\r\nco_return 42;\r\n}\r\n\r\nint main() {\r\nf().get();\r\nprintf(\"%d\\n\", g().get());\r\n};\r\n<\/code><\/pre>\n<p>When it runs, it should print: \u201cHi!\u201d and 42.<\/p>\n<h2>Boost::Future: Await Part<\/h2>\n<p>The next step is to explain to the compiler what to do if you are trying to \u2018await\u2019 on the boost::future.<\/p>\n<p>Given an expression to be awaited upon, the compiler needs to know three things:<\/p>\n<ol>\n<li>Is it ready?<\/li>\n<li>If it is ready, how to get the result.<\/li>\n<li>If it is not ready, how to subscribe to get notified when it becomes ready.<\/li>\n<\/ol>\n<p>To get answers to those questions, the compiler looks for three member functions: <code>await_ready()<\/code> that should return \u2018true\u2019 or \u2018false\u2019, <code>await_resume()<\/code> that compiler will call when the expression is ready to get the result (the result of the call to <code>await_resume()<\/code> becomes the result of the entire await expression), and, finally, await_suspend() that compiler will call to subscribe to get notified when the result is ready and will pass a coroutine handle that can be used to resume or destroy the coroutine.<\/p>\n<p>In case of the boost::future, it has facilities to give the answers, but, it does not have the required member functions as described in the previous paragraph. To deal with that, we can define an <code>operator co_await<\/code> that can translate what boost::future has into what the compiler wants.<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\ntemplate &lt;typename R&gt; auto operator co_await(boost::future&lt;R&gt; &amp;&amp;f) {\r\nstruct Awaiter {\r\nboost::future&lt;R&gt; &amp;&amp;input;\r\nboost::future&lt;R&gt; output;\r\nbool await_ready() { return false; }\r\nauto await_resume() { return output.get(); }\r\nvoid await_suspend(std::experimental::coroutine_handle&lt;&gt; coro) {\r\ninput.then([this, coro](auto result_future) {\r\nthis-&gt;output = std::move(result_future);\r\ncoro.resume();\r\n});\r\n}\r\n};\r\nreturn Awaiter{static_cast&lt;boost::future&lt;R&gt;&amp;&amp;&gt;(f)};\r\n}\r\n<\/code><\/pre>\n<p>Note that in the adapter above, we always return <code>false<\/code> from <code>await_ready()<\/code>, even when it *is* ready, forcing the compiler always to call await_suspend to subscribe to get a continuation via future::then. Another approach is to write await_ready as follows:<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\nbool await_ready() {\r\nif (input.is_ready()) {\r\noutput = std::move(input);\r\nreturn true;\r\n}\r\nreturn false;\r\n}\r\n<\/code><\/pre>\n<p>In this case, if the future is ready, the coroutine bypasses suspension via <code>await_suspend<\/code> and immediately proceeds to getting the result via <code>await_resume<\/code>.<\/p>\n<p>Depending on the application, one approach may be more beneficial than the other. For example, if you are writing a client application, naturally your application will run a little bit faster if during those times when the future is already ready, you don\u2019t have to go through suspension followed by subsequent resuming of a coroutine by the boost::future. In server applications, with your server handling hundreds of simultaneous requests, always going via .then could be beneficial as it may produce more predictable response times if continuations are always scheduled in the fair manner. It is easy to imagine a streak where a particular coroutine is always lucky and has its futures completed by the time it asks whether they are ready. Such a coroutine will hog the thread and might starve other clients.<\/p>\n<p>Pick any approach you like and try our our brand new operator co_await:<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\n\/\/\u2026 includes, specializations of coroutine_traits, operator co_await.\r\n\r\nboost::future&lt;int&gt; g() {\r\nco_return 42;\r\n}\r\n\r\nboost::future&lt;void&gt; f() {\r\nprintf(\"%d\\n\", co_await g());\r\n}\r\n\r\nint main() {\r\nf().get();\r\n};\r\n<\/code><\/pre>\n<p>As usual, when you run this fragment, it will print 42. Note, that we no longer need a <code>co_return<\/code> in function <code>f<\/code>. The compiler knows it is a coroutine due the presence of an await expression.<\/p>\n<h2>Boost::asio<\/h2>\n<p>With the adapters that we have developed so far, you now are free to use coroutines that returnboost::future and to deal with any APIs and libraries that return boost::futures. But what if you have some library that does not return boost::future and uses callbacks as a continuation mechanism?<\/p>\n<p>As the model, we will use the async_wait member function of the boost::asio::system_timer. Without coroutines, you might use system_timer as follows:<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\n#include &lt;boost\/asio\/system_timer.hpp&gt;\r\n#include &lt;boost\/thread.hpp&gt;\r\n\r\nusing namespace boost::asio;\r\nusing namespace std::chrono;\r\n\r\nint main() {\r\nio_service io;\r\nsystem_timer timer(io);\r\n\r\ntimer.expires_from_now(100ms);\r\ntimer.async_wait([](boost::system::error_code ec) {\r\nif (ec) printf(\"timer failed: %d\\n\", ec.value());\r\nelse puts(\"tick\");\r\n});\r\n\r\nputs(\"waiting for a tick\");\r\nio.run();\r\n};\r\n<\/code><\/pre>\n<p>When you run this program, it will print \u201cwaiting for a tick\u201d, followed by a \u201ctick\u201d 100ms later.\nLet\u2019s create a wrapper around timer\u2019s async_await to make it usable with coroutines. We would like to be able to use this construct:<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\nco_await async_await(timer, 100ms);<\/code><\/pre>\n<p>to suspend its execution for the required duration using the specified timer. The overall structure will look similar to how we defined operator co_await for boost::future. We need to return from async_wait an object that can tell the compiler when to suspend, when to wake up and what is the result of the operation.<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\ntemplate &lt;typename R, typename P&gt;\r\nauto async_await(boost::asio::system_timer &amp;t, std::chrono::duration&lt;R, P&gt; d) {\r\nstruct Awaiter { &lt;stuff&gt; };\r\nreturn Awaiter{ t, d };\r\n}\r\n<\/code><\/pre>\n<p>Note that we pass parameters t and d when constructing Awaiter. We will need to store them in the awaiter so that we can get access to them in the await_ready and await_suspend member functions.<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\nboost::asio::system_timer &amp;t;\r\nstd::chrono::duration&lt;R, P&gt; d;\r\n<\/code><\/pre>\n<p>Also, you probably noticed in the system_timer example that a completion callback for async_wait has a parameter that receives an error code that indicates whether the wait completed successfully or with an error (timer was cancelled, for example). We would need to add a member variable to the awaiter to store the error code until it is consumed by <code>await_resume<\/code>.<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\nboost::system::error_code ec;\r\n<\/code><\/pre>\n<p>Member function await_ready will tells us whether we need to suspend at all. If we implement it as follows, we will tell the compiler not to suspend a coroutine if the wait duration is zero.<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\nbool await_ready() { return d.count() == 0; }\r\n<\/code><\/pre>\n<p>In await_suspend, we will call timer.async_await to subscribe a continuation. When boost::asio will call us back we will remember the error code and resume the coroutine.<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\nvoid await_suspend(std::experimental::coroutine_handle&lt;&gt; coro) {\r\nt.expires_from_now(d);\r\nt.async_wait([this, coro](auto ec) {\r\nthis-&gt;ec = ec;\r\ncoro.resume();\r\n});\r\n}\r\n<\/code><\/pre>\n<p>Finally, when a coroutine is resumed we will check the error code and propagate it as an exception if the wait is not successful.<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\nvoid await_resume() {\r\nif (ec)\r\nthrow boost::system::system_error(ec);\r\n}\r\n<\/code><\/pre>\n<p>And for your convenience, the entire adapter in one piece:<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\ntemplate &lt;typename R, typename P&gt;\r\nauto async_await(boost::asio::system_timer &amp;t, std::chrono::duration&lt;R, P&gt; d) {\r\nstruct Awaiter {\r\nboost::asio::system_timer &amp;t;\r\nstd::chrono::duration&lt;R, P&gt; d;\r\nboost::system::error_code ec;\r\n\r\nbool await_ready() { return d.count() == 0; }\r\nvoid await_resume() {\r\nif (ec)\r\nthrow boost::system::system_error(ec);\r\n}\r\nvoid await_suspend(std::experimental::coroutine_handle&lt;&gt; coro) {\r\nt.expires_from_now(d);\r\nt.async_wait([this, coro](auto ec) {\r\nthis-&gt;ec = ec;\r\ncoro.resume();\r\n});\r\n}\r\n};\r\nreturn Awaiter{ t, d };\r\n}\r\n<\/code><\/pre>\n<p>And a small example using it:<\/p>\n<pre lang=\"cpp\"><code lang=\"cpp\">\r\n\/\/\u2026 includes, specializations of coroutine_traits, etc.\r\n\r\nusing namespace boost::asio;\r\nusing namespace std::chrono;\r\n\r\nboost::future&lt;void&gt; sleepy(io_service &amp;io) {\r\nsystem_timer timer(io);\r\nco_await async_await(timer, 100ms);\r\nputs(\"tick1\");\r\nco_await async_await(timer, 100ms);\r\nputs(\"tick2\");\r\nco_await async_await(timer, 100ms);\r\nputs(\"tick3\");\r\n}\r\n\r\nint main() {\r\nio_service io;\r\nsleepy(io);\r\nio.run();\r\n};\r\n<\/code><\/pre>\n<p>When you run it, it should print tick1, tick2 and tick3 100 milliseconds apart.<\/p>\n<h2>Conclusion<\/h2>\n<p>We took a quick tour on how to develop adapters that enable the use of coroutines with existing C++ libraries. Please try it out, and experiment with adding more adapters. Also tune in for the upcoming blog post on how to use CompletionToken traits of boost::asio to create coroutine adapters without having to write them by hand.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This article was written by Gor Nishanov. Last month, Jim Springfield wrote a great article on using C++ Coroutines with Libuv\u00a0(a multi-platform C library for asynchronous I\/O). This month we will look at how to use coroutines with components of Boost C++ libraries, namely boost::future and boost::asio. Getting Boost If you already have boost installed, [&hellip;]<\/p>\n","protected":false},"author":264,"featured_media":35994,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-15915","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus"],"acf":[],"blog_post_summary":"<p>This article was written by Gor Nishanov. Last month, Jim Springfield wrote a great article on using C++ Coroutines with Libuv\u00a0(a multi-platform C library for asynchronous I\/O). This month we will look at how to use coroutines with components of Boost C++ libraries, namely boost::future and boost::asio. Getting Boost If you already have boost installed, [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/15915","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/264"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=15915"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/15915\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/35994"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=15915"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=15915"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=15915"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}