• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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">&lt;</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">&gt;</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">&lt;</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">&gt;();</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">&lt;</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">&gt;(</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">&lt;</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">&gt;</span> <span class="keyword">const</span><span class="special">&amp;</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">&lt;</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">&gt;</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">&lt;</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">&gt;</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">&amp;</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">&amp;)</span> <span class="special">=</span> <span class="keyword">delete</span><span class="special">;</span>
133    <span class="identifier">service</span> <span class="special">&amp;</span> <span class="keyword">operator</span><span class="special">=(</span> <span class="identifier">service</span> <span class="keyword">const</span><span class="special">&amp;)</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&lt;<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>&gt;</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">&lt;</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">-&gt;</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">-&gt;</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">-&gt;</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">&lt;</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">&gt;</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">-&gt;</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">&amp;</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">&amp;){</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">&amp;){</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