1<html> 2<head> 3<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 4<title>Deeper Dive into Boost.Asio</title> 5<link rel="stylesheet" href="../../../../../../doc/src/boostbook.css" type="text/css"> 6<meta name="generator" content="DocBook XSL Stylesheets V1.79.1"> 7<link rel="home" href="../../index.html" title="Chapter 1. Fiber"> 8<link rel="up" href="../integration.html" title="Sharing a Thread with Another Main Loop"> 9<link rel="prev" href="embedded_main_loop.html" title="Embedded Main Loop"> 10<link rel="next" href="../speculation.html" title="Specualtive execution"> 11</head> 12<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"> 13<table cellpadding="2" width="100%"><tr> 14<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../../boost.png"></td> 15<td align="center"><a href="../../../../../../index.html">Home</a></td> 16<td align="center"><a href="../../../../../../libs/libraries.htm">Libraries</a></td> 17<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td> 18<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td> 19<td align="center"><a href="../../../../../../more/index.htm">More</a></td> 20</tr></table> 21<hr> 22<div class="spirit-nav"> 23<a accesskey="p" href="embedded_main_loop.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../integration.html"><img src="../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="../speculation.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a> 24</div> 25<div class="section"> 26<div class="titlepage"><div><div><h3 class="title"> 27<a name="fiber.integration.deeper_dive_into___boost_asio__"></a><a class="link" href="deeper_dive_into___boost_asio__.html" title="Deeper Dive into Boost.Asio">Deeper 28 Dive into Boost.Asio</a> 29</h3></div></div></div> 30<p> 31 By now the alert reader is thinking: but surely, with Asio in particular, 32 we ought to be able to do much better than periodic polling pings! 33 </p> 34<p> 35 This turns out to be surprisingly tricky. We present a possible approach 36 in <a href="../../../../examples/asio/round_robin.hpp" target="_top"><code class="computeroutput"><span class="identifier">examples</span><span class="special">/</span><span class="identifier">asio</span><span class="special">/</span><span class="identifier">round_robin</span><span class="special">.</span><span class="identifier">hpp</span></code></a>. 37 </p> 38<p> 39 One consequence of using <a href="http://www.boost.org/doc/libs/release/libs/asio/index.html" target="_top">Boost.Asio</a> 40 is that you must always let Asio suspend the running thread. Since Asio is 41 aware of pending I/O requests, it can arrange to suspend the thread in such 42 a way that the OS will wake it on I/O completion. No one else has sufficient 43 knowledge. 44 </p> 45<p> 46 So the fiber scheduler must depend on Asio for suspension and resumption. 47 It requires Asio handler calls to wake it. 48 </p> 49<p> 50 One dismaying implication is that we cannot support multiple threads calling 51 <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/run.html" target="_top"><code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run</span><span class="special">()</span></code></a> 52 on the same <code class="computeroutput"><span class="identifier">io_service</span></code> instance. 53 The reason is that Asio provides no way to constrain a particular handler 54 to be called only on a specified thread. A fiber scheduler instance is locked 55 to a particular thread: that instance cannot manage any other thread’s fibers. 56 Yet if we allow multiple threads to call <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run</span><span class="special">()</span></code> 57 on the same <code class="computeroutput"><span class="identifier">io_service</span></code> instance, 58 a fiber scheduler which needs to sleep can have no guarantee that it will 59 reawaken in a timely manner. It can set an Asio timer, as described above 60 — but that timer’s handler may well execute on a different thread! 61 </p> 62<p> 63 Another implication is that since an Asio-aware fiber scheduler (not to mention 64 <a class="link" href="../callbacks/then_there_s____boost_asio__.html#callbacks_asio"><code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">yield</span></code></a>) 65 depends on handler calls from the <code class="computeroutput"><span class="identifier">io_service</span></code>, 66 it is the application’s responsibility to ensure that <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/stop.html" target="_top"><code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">stop</span><span class="special">()</span></code></a> 67 is not called until every fiber has terminated. 68 </p> 69<p> 70 It is easier to reason about the behavior of the presented <code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code> scheduler if we require that 71 after initial setup, the thread’s main fiber is the fiber that calls <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run</span><span class="special">()</span></code>, 72 so let’s impose that requirement. 73 </p> 74<p> 75 Naturally, the first thing we must do on each thread using a custom fiber 76 scheduler is call <a class="link" href="../fiber_mgmt/fiber.html#use_scheduling_algorithm"><code class="computeroutput">use_scheduling_algorithm()</code></a>. However, 77 since <code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code> requires an <code class="computeroutput"><span class="identifier">io_service</span></code> 78 instance, we must first declare that. 79 </p> 80<p> 81</p> 82<pre class="programlisting"><span class="identifier">std</span><span class="special">::</span><span class="identifier">shared_ptr</span><span class="special"><</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_context</span> <span class="special">></span> <span class="identifier">io_ctx</span> <span class="special">=</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">make_shared</span><span class="special"><</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_context</span> <span class="special">>();</span> 83<span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">use_scheduling_algorithm</span><span class="special"><</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span> <span class="special">>(</span> <span class="identifier">io_ctx</span><span class="special">);</span> 84</pre> 85<p> 86 </p> 87<p> 88 <code class="computeroutput"><span class="identifier">use_scheduling_algorithm</span><span class="special">()</span></code> instantiates <code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code>, 89 which naturally calls its constructor: 90 </p> 91<p> 92</p> 93<pre class="programlisting"><span class="identifier">round_robin</span><span class="special">(</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">shared_ptr</span><span class="special"><</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_context</span> <span class="special">></span> <span class="keyword">const</span><span class="special">&</span> <span class="identifier">io_ctx_</span><span class="special">)</span> <span class="special">:</span> 94 <span class="identifier">io_ctx_</span><span class="special">(</span> <span class="identifier">io_ctx</span><span class="special">),</span> 95 <span class="identifier">suspend_timer_</span><span class="special">(</span> <span class="special">*</span> <span class="identifier">io_ctx_</span><span class="special">)</span> <span class="special">{</span> 96 <span class="comment">// We use add_service() very deliberately. This will throw</span> 97 <span class="comment">// service_already_exists if you pass the same io_context instance to</span> 98 <span class="comment">// more than one round_robin instance.</span> 99 <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">add_service</span><span class="special">(</span> <span class="special">*</span> <span class="identifier">io_ctx_</span><span class="special">,</span> <span class="keyword">new</span> <span class="identifier">service</span><span class="special">(</span> <span class="special">*</span> <span class="identifier">io_ctx_</span><span class="special">)</span> <span class="special">);</span> 100 <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">post</span><span class="special">(</span> <span class="special">*</span> <span class="identifier">io_ctx_</span><span class="special">,</span> <span class="special">[</span><span class="keyword">this</span><span class="special">]()</span> <span class="keyword">mutable</span> <span class="special">{</span> 101</pre> 102<p> 103 </p> 104<p> 105 <code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code> binds the passed <code class="computeroutput"><span class="identifier">io_service</span></code> pointer and initializes a <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/steady_timer.html" target="_top"><code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">steady_timer</span></code></a>: 106 </p> 107<p> 108</p> 109<pre class="programlisting"><span class="identifier">std</span><span class="special">::</span><span class="identifier">shared_ptr</span><span class="special"><</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_context</span> <span class="special">></span> <span class="identifier">io_ctx_</span><span class="special">;</span> 110<span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">steady_timer</span> <span class="identifier">suspend_timer_</span><span class="special">;</span> 111</pre> 112<p> 113 </p> 114<p> 115 Then it calls <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/add_service.html" target="_top"><code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">add_service</span><span class="special">()</span></code></a> 116 with a nested <code class="computeroutput"><span class="identifier">service</span></code> struct: 117 </p> 118<p> 119</p> 120<pre class="programlisting"><span class="keyword">struct</span> <span class="identifier">service</span> <span class="special">:</span> <span class="keyword">public</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_context</span><span class="special">::</span><span class="identifier">service</span> <span class="special">{</span> 121 <span class="keyword">static</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_context</span><span class="special">::</span><span class="identifier">id</span> <span class="identifier">id</span><span class="special">;</span> 122 123 <span class="identifier">std</span><span class="special">::</span><span class="identifier">unique_ptr</span><span class="special"><</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_context</span><span class="special">::</span><span class="identifier">work</span> <span class="special">></span> <span class="identifier">work_</span><span class="special">;</span> 124 125 <span class="identifier">service</span><span class="special">(</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_context</span> <span class="special">&</span> <span class="identifier">io_ctx</span><span class="special">)</span> <span class="special">:</span> 126 <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_context</span><span class="special">::</span><span class="identifier">service</span><span class="special">(</span> <span class="identifier">io_ctx</span><span class="special">),</span> 127 <span class="identifier">work_</span><span class="special">{</span> <span class="keyword">new</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_context</span><span class="special">::</span><span class="identifier">work</span><span class="special">(</span> <span class="identifier">io_ctx</span><span class="special">)</span> <span class="special">}</span> <span class="special">{</span> 128 <span class="special">}</span> 129 130 <span class="keyword">virtual</span> <span class="special">~</span><span class="identifier">service</span><span class="special">()</span> <span class="special">{}</span> 131 132 <span class="identifier">service</span><span class="special">(</span> <span class="identifier">service</span> <span class="keyword">const</span><span class="special">&)</span> <span class="special">=</span> <span class="keyword">delete</span><span class="special">;</span> 133 <span class="identifier">service</span> <span class="special">&</span> <span class="keyword">operator</span><span class="special">=(</span> <span class="identifier">service</span> <span class="keyword">const</span><span class="special">&)</span> <span class="special">=</span> <span class="keyword">delete</span><span class="special">;</span> 134 135 <span class="keyword">void</span> <span class="identifier">shutdown_service</span><span class="special">()</span> <span class="identifier">override</span> <span class="identifier">final</span> <span class="special">{</span> 136 <span class="identifier">work_</span><span class="special">.</span><span class="identifier">reset</span><span class="special">();</span> 137 <span class="special">}</span> 138<span class="special">};</span> 139</pre> 140<p> 141 </p> 142<p> 143 ... [asio_rr_service_bottom] 144 </p> 145<p> 146 The <code class="computeroutput"><span class="identifier">service</span></code> struct has a 147 couple of roles. 148 </p> 149<p> 150 Its foremost role is to manage a <code class="literal">std::unique_ptr<<a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service__work.html" target="_top"><code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">work</span></code></a>></code>. We want the 151 <code class="computeroutput"><span class="identifier">io_service</span></code> instance to continue 152 its main loop even when there is no pending Asio I/O. 153 </p> 154<p> 155 But when <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service__service/shutdown_service.html" target="_top"><code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">service</span><span class="special">::</span><span class="identifier">shutdown_service</span><span class="special">()</span></code></a> 156 is called, we discard the <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">work</span></code> 157 instance so the <code class="computeroutput"><span class="identifier">io_service</span></code> 158 can shut down properly. 159 </p> 160<p> 161 Its other purpose is to <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/post.html" target="_top"><code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code></a> 162 a lambda (not yet shown). Let’s walk further through the example program before 163 coming back to explain that lambda. 164 </p> 165<p> 166 The <code class="computeroutput"><span class="identifier">service</span></code> constructor returns 167 to <code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code>’s constructor, which returns 168 to <code class="computeroutput"><span class="identifier">use_scheduling_algorithm</span><span class="special">()</span></code>, which returns to the application code. 169 </p> 170<p> 171 Once it has called <code class="computeroutput"><span class="identifier">use_scheduling_algorithm</span><span class="special">()</span></code>, the application may now launch some number 172 of fibers: 173 </p> 174<p> 175</p> 176<pre class="programlisting"><span class="comment">// server</span> 177<span class="identifier">tcp</span><span class="special">::</span><span class="identifier">acceptor</span> <span class="identifier">a</span><span class="special">(</span> <span class="special">*</span> <span class="identifier">io_ctx</span><span class="special">,</span> <span class="identifier">tcp</span><span class="special">::</span><span class="identifier">endpoint</span><span class="special">(</span> <span class="identifier">tcp</span><span class="special">::</span><span class="identifier">v4</span><span class="special">(),</span> <span class="number">9999</span><span class="special">)</span> <span class="special">);</span> 178<span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">fiber</span><span class="special">(</span> <span class="identifier">server</span><span class="special">,</span> <span class="identifier">io_ctx</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span> <span class="identifier">a</span><span class="special">)</span> <span class="special">).</span><span class="identifier">detach</span><span class="special">();</span> 179<span class="comment">// client</span> 180<span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="identifier">iterations</span> <span class="special">=</span> <span class="number">2</span><span class="special">;</span> 181<span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="identifier">clients</span> <span class="special">=</span> <span class="number">3</span><span class="special">;</span> 182<span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">barrier</span> <span class="identifier">b</span><span class="special">(</span> <span class="identifier">clients</span><span class="special">);</span> 183<span class="keyword">for</span> <span class="special">(</span> <span class="keyword">unsigned</span> <span class="identifier">i</span> <span class="special">=</span> <span class="number">0</span><span class="special">;</span> <span class="identifier">i</span> <span class="special"><</span> <span class="identifier">clients</span><span class="special">;</span> <span class="special">++</span><span class="identifier">i</span><span class="special">)</span> <span class="special">{</span> 184 <span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">fiber</span><span class="special">(</span> 185 <span class="identifier">client</span><span class="special">,</span> <span class="identifier">io_ctx</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span> <span class="identifier">a</span><span class="special">),</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span> <span class="identifier">b</span><span class="special">),</span> <span class="identifier">iterations</span><span class="special">).</span><span class="identifier">detach</span><span class="special">();</span> 186<span class="special">}</span> 187</pre> 188<p> 189 </p> 190<p> 191 Since we don’t specify a <a class="link" href="../fiber_mgmt.html#class_launch"><code class="computeroutput">launch</code></a>, these fibers are ready to run, 192 but have not yet been entered. 193 </p> 194<p> 195 Having set everything up, the application calls <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/run.html" target="_top"><code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run</span><span class="special">()</span></code></a>: 196 </p> 197<p> 198</p> 199<pre class="programlisting"><span class="identifier">io_ctx</span><span class="special">-></span><span class="identifier">run</span><span class="special">();</span> 200</pre> 201<p> 202 </p> 203<p> 204 Now what? 205 </p> 206<p> 207 Because this <code class="computeroutput"><span class="identifier">io_service</span></code> instance 208 owns an <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">work</span></code> instance, <code class="computeroutput"><span class="identifier">run</span><span class="special">()</span></code> does not immediately return. But — none of 209 the fibers that will perform actual work has even been entered yet! 210 </p> 211<p> 212 Without that initial <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code> call in <code class="computeroutput"><span class="identifier">service</span></code>’s 213 constructor, <span class="emphasis"><em>nothing</em></span> would happen. The application would 214 hang right here. 215 </p> 216<p> 217 So, what should the <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code> handler execute? Simply <a class="link" href="../fiber_mgmt/this_fiber.html#this_fiber_yield"><code class="computeroutput">this_fiber::yield()</code></a>? 218 </p> 219<p> 220 That would be a promising start. But we have no guarantee that any of the 221 other fibers will initiate any Asio operations to keep the ball rolling. 222 For all we know, every other fiber could reach a similar <code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">this_fiber</span><span class="special">::</span><span class="identifier">yield</span><span class="special">()</span></code> call first. Control would return to the 223 <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code> 224 handler, which would return to Asio, and... the application would hang. 225 </p> 226<p> 227 The <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code> 228 handler could <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code> 229 itself again. But as discussed in <a class="link" href="embedded_main_loop.html#embedded_main_loop">the 230 previous section</a>, once there are actual I/O operations in flight — once 231 we reach a state in which no fiber is ready — 232that would cause the thread to 233 spin. 234 </p> 235<p> 236 We could, of course, set an Asio timer — again as <a class="link" href="embedded_main_loop.html#embedded_main_loop">previously 237 discussed</a>. But in this <span class="quote">“<span class="quote">deeper dive,</span>”</span> we’re trying to 238 do a little better. 239 </p> 240<p> 241 The key to doing better is that since we’re in a fiber, we can run an actual 242 loop — not just a chain of callbacks. We can wait for <span class="quote">“<span class="quote">something to happen</span>”</span> 243 by calling <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/run_one.html" target="_top"><code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run_one</span><span class="special">()</span></code></a> 244 — or we can execute already-queued Asio handlers by calling <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/poll.html" target="_top"><code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">poll</span><span class="special">()</span></code></a>. 245 </p> 246<p> 247 Here’s the body of the lambda passed to the <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code> call. 248 </p> 249<p> 250</p> 251<pre class="programlisting"> <span class="keyword">while</span> <span class="special">(</span> <span class="special">!</span> <span class="identifier">io_ctx_</span><span class="special">-></span><span class="identifier">stopped</span><span class="special">()</span> <span class="special">)</span> <span class="special">{</span> 252 <span class="keyword">if</span> <span class="special">(</span> <span class="identifier">has_ready_fibers</span><span class="special">()</span> <span class="special">)</span> <span class="special">{</span> 253 <span class="comment">// run all pending handlers in round_robin</span> 254 <span class="keyword">while</span> <span class="special">(</span> <span class="identifier">io_ctx_</span><span class="special">-></span><span class="identifier">poll</span><span class="special">()</span> <span class="special">);</span> 255 <span class="comment">// block this fiber till all pending (ready) fibers are processed</span> 256 <span class="comment">// == round_robin::suspend_until() has been called</span> 257 <span class="identifier">std</span><span class="special">::</span><span class="identifier">unique_lock</span><span class="special"><</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">mutex</span> <span class="special">></span> <span class="identifier">lk</span><span class="special">(</span> <span class="identifier">mtx_</span><span class="special">);</span> 258 <span class="identifier">cnd_</span><span class="special">.</span><span class="identifier">wait</span><span class="special">(</span> <span class="identifier">lk</span><span class="special">);</span> 259 <span class="special">}</span> <span class="keyword">else</span> <span class="special">{</span> 260 <span class="comment">// run one handler inside io_context</span> 261 <span class="comment">// if no handler available, block this thread</span> 262 <span class="keyword">if</span> <span class="special">(</span> <span class="special">!</span> <span class="identifier">io_ctx_</span><span class="special">-></span><span class="identifier">run_one</span><span class="special">()</span> <span class="special">)</span> <span class="special">{</span> 263 <span class="keyword">break</span><span class="special">;</span> 264 <span class="special">}</span> 265 <span class="special">}</span> 266<span class="special">}</span> 267</pre> 268<p> 269 </p> 270<p> 271 We want this loop to exit once the <code class="computeroutput"><span class="identifier">io_service</span></code> 272 instance has been <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_service/stopped.html" target="_top"><code class="computeroutput"><span class="identifier">stopped</span><span class="special">()</span></code></a>. 273 </p> 274<p> 275 As long as there are ready fibers, we interleave running ready Asio handlers 276 with running ready fibers. 277 </p> 278<p> 279 If there are no ready fibers, we wait by calling <code class="computeroutput"><span class="identifier">run_one</span><span class="special">()</span></code>. Once any Asio handler has been called 280 — no matter which — <code class="computeroutput"><span class="identifier">run_one</span><span class="special">()</span></code> 281 returns. That handler may have transitioned some fiber to ready state, so 282 we loop back to check again. 283 </p> 284<p> 285 (We won’t describe <code class="computeroutput"><span class="identifier">awakened</span><span class="special">()</span></code>, <code class="computeroutput"><span class="identifier">pick_next</span><span class="special">()</span></code> or <code class="computeroutput"><span class="identifier">has_ready_fibers</span><span class="special">()</span></code>, as these are just like <a class="link" href="../scheduling.html#round_robin_awakened"><code class="computeroutput">round_robin::awakened()</code></a>, 286 <a class="link" href="../scheduling.html#round_robin_pick_next"><code class="computeroutput">round_robin::pick_next()</code></a> and <a class="link" href="../scheduling.html#round_robin_has_ready_fibers"><code class="computeroutput">round_robin::has_ready_fibers()</code></a>.) 287 </p> 288<p> 289 That leaves <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code> and <code class="computeroutput"><span class="identifier">notify</span><span class="special">()</span></code>. 290 </p> 291<p> 292 Doubtless you have been asking yourself: why are we calling <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run_one</span><span class="special">()</span></code> 293 in the lambda loop? Why not call it in <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code>, whose very API was designed for just such 294 a purpose? 295 </p> 296<p> 297 Under normal circumstances, when the fiber manager finds no ready fibers, 298 it calls <a class="link" href="../scheduling.html#algorithm_suspend_until"><code class="computeroutput">algorithm::suspend_until()</code></a>. Why test <code class="computeroutput"><span class="identifier">has_ready_fibers</span><span class="special">()</span></code> 299 in the lambda loop? Why not leverage the normal mechanism? 300 </p> 301<p> 302 The answer is: it matters who’s asking. 303 </p> 304<p> 305 Consider the lambda loop shown above. The only <span class="bold"><strong>Boost.Fiber</strong></span> 306 APIs it engages are <code class="computeroutput"><span class="identifier">has_ready_fibers</span><span class="special">()</span></code> and <a class="link" href="../fiber_mgmt/this_fiber.html#this_fiber_yield"><code class="computeroutput">this_fiber::yield()</code></a>. 307 <code class="computeroutput"><span class="identifier">yield</span><span class="special">()</span></code> 308 does not <span class="emphasis"><em>block</em></span> the calling fiber: the calling fiber 309 does not become unready. It is immediately passed back to <a class="link" href="../scheduling.html#algorithm_awakened"><code class="computeroutput">algorithm::awakened()</code></a>, 310 to be resumed in its turn when all other ready fibers have had a chance to 311 run. In other words: during a <code class="computeroutput"><span class="identifier">yield</span><span class="special">()</span></code> call, <span class="emphasis"><em>there is always at least 312 one ready fiber.</em></span> 313 </p> 314<p> 315 As long as this lambda loop is still running, the fiber manager does not 316 call <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code> 317 because it always has a fiber ready to run. 318 </p> 319<p> 320 However, the lambda loop <span class="emphasis"><em>itself</em></span> can detect the case 321 when no <span class="emphasis"><em>other</em></span> fibers are ready to run: the running fiber 322 is not <span class="emphasis"><em>ready</em></span> but <span class="emphasis"><em>running.</em></span> 323 </p> 324<p> 325 That said, <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code> and <code class="computeroutput"><span class="identifier">notify</span><span class="special">()</span></code> are in fact called during orderly shutdown 326 processing, so let’s try a plausible implementation. 327 </p> 328<p> 329</p> 330<pre class="programlisting"><span class="keyword">void</span> <span class="identifier">suspend_until</span><span class="special">(</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">chrono</span><span class="special">::</span><span class="identifier">steady_clock</span><span class="special">::</span><span class="identifier">time_point</span> <span class="keyword">const</span><span class="special">&</span> <span class="identifier">abs_time</span><span class="special">)</span> <span class="keyword">noexcept</span> <span class="special">{</span> 331 <span class="comment">// Set a timer so at least one handler will eventually fire, causing</span> 332 <span class="comment">// run_one() to eventually return.</span> 333 <span class="keyword">if</span> <span class="special">(</span> <span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">chrono</span><span class="special">::</span><span class="identifier">steady_clock</span><span class="special">::</span><span class="identifier">time_point</span><span class="special">::</span><span class="identifier">max</span><span class="special">)()</span> <span class="special">!=</span> <span class="identifier">abs_time</span><span class="special">)</span> <span class="special">{</span> 334 <span class="comment">// Each expires_at(time_point) call cancels any previous pending</span> 335 <span class="comment">// call. We could inadvertently spin like this:</span> 336 <span class="comment">// dispatcher calls suspend_until() with earliest wake time</span> 337 <span class="comment">// suspend_until() sets suspend_timer_</span> 338 <span class="comment">// lambda loop calls run_one()</span> 339 <span class="comment">// some other asio handler runs before timer expires</span> 340 <span class="comment">// run_one() returns to lambda loop</span> 341 <span class="comment">// lambda loop yields to dispatcher</span> 342 <span class="comment">// dispatcher finds no ready fibers</span> 343 <span class="comment">// dispatcher calls suspend_until() with SAME wake time</span> 344 <span class="comment">// suspend_until() sets suspend_timer_ to same time, canceling</span> 345 <span class="comment">// previous async_wait()</span> 346 <span class="comment">// lambda loop calls run_one()</span> 347 <span class="comment">// asio calls suspend_timer_ handler with operation_aborted</span> 348 <span class="comment">// run_one() returns to lambda loop... etc. etc.</span> 349 <span class="comment">// So only actually set the timer when we're passed a DIFFERENT</span> 350 <span class="comment">// abs_time value.</span> 351 <span class="identifier">suspend_timer_</span><span class="special">.</span><span class="identifier">expires_at</span><span class="special">(</span> <span class="identifier">abs_time</span><span class="special">);</span> 352 <span class="identifier">suspend_timer_</span><span class="special">.</span><span class="identifier">async_wait</span><span class="special">([](</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">system</span><span class="special">::</span><span class="identifier">error_code</span> <span class="keyword">const</span><span class="special">&){</span> 353 <span class="identifier">this_fiber</span><span class="special">::</span><span class="identifier">yield</span><span class="special">();</span> 354 <span class="special">});</span> 355 <span class="special">}</span> 356 <span class="identifier">cnd_</span><span class="special">.</span><span class="identifier">notify_one</span><span class="special">();</span> 357<span class="special">}</span> 358</pre> 359<p> 360 </p> 361<p> 362 As you might expect, <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code> sets an <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/steady_timer.html" target="_top"><code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">steady_timer</span></code></a> to <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/basic_waitable_timer/expires_at.html" target="_top"><code class="computeroutput"><span class="identifier">expires_at</span><span class="special">()</span></code></a> 363 the passed <a href="http://en.cppreference.com/w/cpp/chrono/steady_clock" target="_top"><code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">chrono</span><span class="special">::</span><span class="identifier">steady_clock</span><span class="special">::</span><span class="identifier">time_point</span></code></a>. 364 Usually. 365 </p> 366<p> 367 As indicated in comments, we avoid setting <code class="computeroutput"><span class="identifier">suspend_timer_</span></code> 368 multiple times to the <span class="emphasis"><em>same</em></span> <code class="computeroutput"><span class="identifier">time_point</span></code> 369 value since every <code class="computeroutput"><span class="identifier">expires_at</span><span class="special">()</span></code> call cancels any previous <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/basic_waitable_timer/async_wait.html" target="_top"><code class="computeroutput"><span class="identifier">async_wait</span><span class="special">()</span></code></a> 370 call. There is a chance that we could spin. Reaching <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code> means the fiber manager intends to yield 371 the processor to Asio. Cancelling the previous <code class="computeroutput"><span class="identifier">async_wait</span><span class="special">()</span></code> call would fire its handler, causing <code class="computeroutput"><span class="identifier">run_one</span><span class="special">()</span></code> 372 to return, potentially causing the fiber manager to call <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code> again with the same <code class="computeroutput"><span class="identifier">time_point</span></code> 373 value... 374 </p> 375<p> 376 Given that we suspend the thread by calling <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">run_one</span><span class="special">()</span></code>, what’s important is that our <code class="computeroutput"><span class="identifier">async_wait</span><span class="special">()</span></code> 377 call will cause a handler to run, which will cause <code class="computeroutput"><span class="identifier">run_one</span><span class="special">()</span></code> to return. It’s not so important specifically 378 what that handler does. 379 </p> 380<p> 381</p> 382<pre class="programlisting"><span class="keyword">void</span> <span class="identifier">notify</span><span class="special">()</span> <span class="keyword">noexcept</span> <span class="special">{</span> 383 <span class="comment">// Something has happened that should wake one or more fibers BEFORE</span> 384 <span class="comment">// suspend_timer_ expires. Reset the timer to cause it to fire</span> 385 <span class="comment">// immediately, causing the run_one() call to return. In theory we</span> 386 <span class="comment">// could use cancel() because we don't care whether suspend_timer_'s</span> 387 <span class="comment">// handler is called with operation_aborted or success. However --</span> 388 <span class="comment">// cancel() doesn't change the expiration time, and we use</span> 389 <span class="comment">// suspend_timer_'s expiration time to decide whether it's already</span> 390 <span class="comment">// set. If suspend_until() set some specific wake time, then notify()</span> 391 <span class="comment">// canceled it, then suspend_until() was called again with the same</span> 392 <span class="comment">// wake time, it would match suspend_timer_'s expiration time and we'd</span> 393 <span class="comment">// refrain from setting the timer. So instead of simply calling</span> 394 <span class="comment">// cancel(), reset the timer, which cancels the pending sleep AND sets</span> 395 <span class="comment">// a new expiration time. This will cause us to spin the loop twice --</span> 396 <span class="comment">// once for the operation_aborted handler, once for timer expiration</span> 397 <span class="comment">// -- but that shouldn't be a big problem.</span> 398 <span class="identifier">suspend_timer_</span><span class="special">.</span><span class="identifier">async_wait</span><span class="special">([](</span><span class="identifier">boost</span><span class="special">::</span><span class="identifier">system</span><span class="special">::</span><span class="identifier">error_code</span> <span class="keyword">const</span><span class="special">&){</span> 399 <span class="identifier">this_fiber</span><span class="special">::</span><span class="identifier">yield</span><span class="special">();</span> 400 <span class="special">});</span> 401 <span class="identifier">suspend_timer_</span><span class="special">.</span><span class="identifier">expires_at</span><span class="special">(</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">chrono</span><span class="special">::</span><span class="identifier">steady_clock</span><span class="special">::</span><span class="identifier">now</span><span class="special">()</span> <span class="special">);</span> 402<span class="special">}</span> 403</pre> 404<p> 405 </p> 406<p> 407 Since an <code class="computeroutput"><span class="identifier">expires_at</span><span class="special">()</span></code> 408 call cancels any previous <code class="computeroutput"><span class="identifier">async_wait</span><span class="special">()</span></code> call, we can make <code class="computeroutput"><span class="identifier">notify</span><span class="special">()</span></code> simply call <code class="computeroutput"><span class="identifier">steady_timer</span><span class="special">::</span><span class="identifier">expires_at</span><span class="special">()</span></code>. That should cause the <code class="computeroutput"><span class="identifier">io_service</span></code> 409 to call the <code class="computeroutput"><span class="identifier">async_wait</span><span class="special">()</span></code> 410 handler with <code class="computeroutput"><span class="identifier">operation_aborted</span></code>. 411 </p> 412<p> 413 The comments in <code class="computeroutput"><span class="identifier">notify</span><span class="special">()</span></code> 414 explain why we call <code class="computeroutput"><span class="identifier">expires_at</span><span class="special">()</span></code> rather than <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/basic_waitable_timer/cancel.html" target="_top"><code class="computeroutput"><span class="identifier">cancel</span><span class="special">()</span></code></a>. 415 </p> 416<p> 417 This <code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code> implementation is used in 418 <a href="../../../../examples/asio/autoecho.cpp" target="_top"><code class="computeroutput"><span class="identifier">examples</span><span class="special">/</span><span class="identifier">asio</span><span class="special">/</span><span class="identifier">autoecho</span><span class="special">.</span><span class="identifier">cpp</span></code></a>. 419 </p> 420<p> 421 It seems possible that you could put together a more elegant Fiber / Asio 422 integration. But as noted at the outset: it’s tricky. 423 </p> 424</div> 425<table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr> 426<td align="left"></td> 427<td align="right"><div class="copyright-footer">Copyright © 2013 Oliver Kowalke<p> 428 Distributed under the Boost Software License, Version 1.0. (See accompanying 429 file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>) 430 </p> 431</div></td> 432</tr></table> 433<hr> 434<div class="spirit-nav"> 435<a accesskey="p" href="embedded_main_loop.html"><img src="../../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../integration.html"><img src="../../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="../speculation.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a> 436</div> 437</body> 438</html> 439