]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/fiber/doc/html/fiber/integration/deeper_dive_into___boost_asio__.html
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / fiber / doc / html / fiber / integration / deeper_dive_into___boost_asio__.html
CommitLineData
7c673cae
FG
1<html>
2<head>
3<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
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.75.2">
7<link rel="home" href="../../index.html" title="Chapter&#160;1.&#160;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="../performance.html" title="Performance">
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="../performance.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 <a href="http://www.boost.org/doc/libs/release/libs/asio/index.html" target="_top">Boost.Asio</a></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&#8217;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 &#8212; but that timer&#8217;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&#8217;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&#8217;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&#8217;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">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span> <span class="identifier">io_svc</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">&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_svc</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">boost</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">io_service</span> <span class="special">&amp;</span> <span class="identifier">io_svc</span><span class="special">)</span> <span class="special">:</span>
94 <span class="identifier">io_svc_</span><span class="special">(</span> <span class="identifier">io_svc</span><span class="special">),</span>
95 <span class="identifier">suspend_timer_</span><span class="special">(</span> <span class="identifier">io_svc_</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_service 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="identifier">io_svc_</span><span class="special">,</span> <span class="keyword">new</span> <span class="identifier">service</span><span class="special">(</span> <span class="identifier">io_svc_</span><span class="special">));</span>
100<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> reference and initializes a
106 <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>:
107 </p>
108<p>
109</p>
110<pre class="programlisting"><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">&amp;</span> <span class="identifier">io_svc_</span><span class="special">;</span>
111<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>
112</pre>
113<p>
114 </p>
115<p>
116 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>
117 with a nested <code class="computeroutput"><span class="identifier">service</span></code> struct:
118 </p>
119<p>
120</p>
121<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_service</span><span class="special">::</span><span class="identifier">service</span> <span class="special">{</span>
122 <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_service</span><span class="special">::</span><span class="identifier">id</span> <span class="identifier">id</span><span class="special">;</span>
123
124 <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_service</span><span class="special">::</span><span class="identifier">work</span> <span class="special">&gt;</span> <span class="identifier">work_</span><span class="special">;</span>
125
126 <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_service</span> <span class="special">&amp;</span> <span class="identifier">io_svc</span><span class="special">)</span> <span class="special">:</span>
127 <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">io_svc</span><span class="special">),</span>
128 <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_service</span><span class="special">::</span><span class="identifier">work</span><span class="special">(</span> <span class="identifier">io_svc</span><span class="special">)</span> <span class="special">}</span> <span class="special">{</span>
129 <span class="identifier">io_svc</span><span class="special">.</span><span class="identifier">post</span><span class="special">([&amp;</span><span class="identifier">io_svc</span><span class="special">](){</span>
130</pre>
131<p>
132 </p>
133<p>
134 ...
135 </p>
136<p>
137</p>
138<pre class="programlisting"> <span class="special">});</span>
139 <span class="special">}</span>
140
141 <span class="keyword">virtual</span> <span class="special">~</span><span class="identifier">service</span><span class="special">()</span> <span class="special">{}</span>
142
143 <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>
144 <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>
145
146 <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>
147 <span class="identifier">work_</span><span class="special">.</span><span class="identifier">reset</span><span class="special">();</span>
148 <span class="special">}</span>
149<span class="special">};</span>
150</pre>
151<p>
152 </p>
153<p>
154 The <code class="computeroutput"><span class="identifier">service</span></code> struct has a
155 couple of roles.
156 </p>
157<p>
158 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
159 <code class="computeroutput"><span class="identifier">io_service</span></code> instance to continue
160 its main loop even when there is no pending Asio I/O.
161 </p>
162<p>
163 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>
164 is called, we discard the <code class="computeroutput"><span class="identifier">io_service</span><span class="special">::</span><span class="identifier">work</span></code>
165 instance so the <code class="computeroutput"><span class="identifier">io_service</span></code>
166 can shut down properly.
167 </p>
168<p>
169 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>
170 a lambda (not yet shown). Let&#8217;s walk further through the example program before
171 coming back to explain that lambda.
172 </p>
173<p>
174 The <code class="computeroutput"><span class="identifier">service</span></code> constructor returns
175 to <code class="computeroutput"><span class="identifier">asio</span><span class="special">::</span><span class="identifier">round_robin</span></code>&#8217;s constructor, which returns
176 to <code class="computeroutput"><span class="identifier">use_scheduling_algorithm</span><span class="special">()</span></code>, which returns to the application code.
177 </p>
178<p>
179 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
180 of fibers:
181 </p>
182<p>
183</p>
184<pre class="programlisting"><span class="comment">// server</span>
185<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="identifier">io_svc</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>
186<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">std</span><span class="special">::</span><span class="identifier">ref</span><span class="special">(</span> <span class="identifier">io_svc</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>
187<span class="comment">// client</span>
188<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>
189<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>
190<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>
191<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>
192 <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>
193 <span class="identifier">client</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">io_svc</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>
194<span class="special">}</span>
195</pre>
196<p>
197 </p>
198<p>
199 Since we don&#8217;t specify a <a class="link" href="../fiber_mgmt.html#class_launch"><code class="computeroutput">launch</code></a>, these fibers are ready to run,
200 but have not yet been entered.
201 </p>
202<p>
203 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>:
204 </p>
205<p>
206</p>
207<pre class="programlisting"><span class="identifier">io_svc</span><span class="special">.</span><span class="identifier">run</span><span class="special">();</span>
208</pre>
209<p>
210 </p>
211<p>
212 Now what?
213 </p>
214<p>
215 Because this <code class="computeroutput"><span class="identifier">io_service</span></code> instance
216 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 &#8212; none of
217 the fibers that will perform actual work has even been entered yet!
218 </p>
219<p>
220 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>&#8217;s
221 constructor, <span class="emphasis"><em>nothing</em></span> would happen. The application would
222 hang right here.
223 </p>
224<p>
225 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>?
226 </p>
227<p>
228 That would be a promising start. But we have no guarantee that any of the
229 other fibers will initiate any Asio operations to keep the ball rolling.
230 For all we know, every other fiber could reach a similar <code class="computeroutput"><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
231 <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code>
232 handler, which would return to Asio, and... the application would hang.
233 </p>
234<p>
235 The <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code>
236 handler could <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code>
237 itself again. But as discussed in <a class="link" href="embedded_main_loop.html#embedded_main_loop">the
238 previous section</a>, once there are actual I/O operations in flight &#8212; once
239 we reach a state in which no fiber is ready &#8212;
240that would cause the thread to
241 spin.
242 </p>
243<p>
244 We could, of course, set an Asio timer &#8212; again as <a class="link" href="embedded_main_loop.html#embedded_main_loop">previously
245 discussed</a>. But in this <span class="quote">&#8220;<span class="quote">deeper dive,</span>&#8221;</span> we&#8217;re trying to
246 do a little better.
247 </p>
248<p>
249 The key to doing better is that since we&#8217;re in a fiber, we can run an actual
250 loop &#8212; not just a chain of callbacks. We can wait for <span class="quote">&#8220;<span class="quote">something to happen</span>&#8221;</span>
251 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>
252 &#8212; 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>.
253 </p>
254<p>
255 Here&#8217;s the body of the lambda passed to the <code class="computeroutput"><span class="identifier">post</span><span class="special">()</span></code> call.
256 </p>
257<p>
258</p>
259<pre class="programlisting"><span class="keyword">while</span> <span class="special">(</span> <span class="special">!</span> <span class="identifier">io_svc</span><span class="special">.</span><span class="identifier">stopped</span><span class="special">()</span> <span class="special">)</span> <span class="special">{</span>
260 <span class="keyword">if</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">has_ready_fibers</span><span class="special">()</span> <span class="special">)</span> <span class="special">{</span>
261 <span class="comment">// run all pending handlers in round_robin</span>
262 <span class="keyword">while</span> <span class="special">(</span> <span class="identifier">io_svc</span><span class="special">.</span><span class="identifier">poll</span><span class="special">()</span> <span class="special">);</span>
263 <span class="comment">// run pending (ready) fibers</span>
264 <span class="identifier">this_fiber</span><span class="special">::</span><span class="identifier">yield</span><span class="special">();</span>
265 <span class="special">}</span> <span class="keyword">else</span> <span class="special">{</span>
266 <span class="comment">// run one handler inside io_service</span>
267 <span class="comment">// if no handler available, block this thread</span>
268 <span class="keyword">if</span> <span class="special">(</span> <span class="special">!</span> <span class="identifier">io_svc</span><span class="special">.</span><span class="identifier">run_one</span><span class="special">()</span> <span class="special">)</span> <span class="special">{</span>
269 <span class="keyword">break</span><span class="special">;</span>
270 <span class="special">}</span>
271 <span class="special">}</span>
272<span class="special">}</span>
273</pre>
274<p>
275 </p>
276<p>
277 We want this loop to exit once the <code class="computeroutput"><span class="identifier">io_service</span></code>
278 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>.
279 </p>
280<p>
281 As long as there are ready fibers, we interleave running ready Asio handlers
282 with running ready fibers.
283 </p>
284<p>
285 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
286 &#8212; no matter which &#8212; <code class="computeroutput"><span class="identifier">run_one</span><span class="special">()</span></code>
287 returns. That handler may have transitioned some fiber to ready state, so
288 we loop back to check again.
289 </p>
290<p>
291 (We won&#8217;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>,
292 <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>.)
293 </p>
294<p>
295 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>.
296 </p>
297<p>
298 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>
299 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
300 a purpose?
301 </p>
302<p>
303 Under normal circumstances, when the fiber manager finds no ready fibers,
304 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>
305 in the lambda loop? Why not leverage the normal mechanism?
306 </p>
307<p>
308 The answer is: it matters who&#8217;s asking.
309 </p>
310<p>
311 Consider the lambda loop shown above. The only <span class="bold"><strong>Boost.Fiber</strong></span>
312 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>.
313 <code class="computeroutput"><span class="identifier">yield</span><span class="special">()</span></code>
314 does not <span class="emphasis"><em>block</em></span> the calling fiber: the calling fiber
315 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>,
316 to be resumed in its turn when all other ready fibers have had a chance to
317 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
318 one ready fiber.</em></span>
319 </p>
320<p>
321 As long as this lambda loop is still running, the fiber manager does not
322 call <code class="computeroutput"><span class="identifier">suspend_until</span><span class="special">()</span></code>
323 because it always has a fiber ready to run.
324 </p>
325<p>
326 However, the lambda loop <span class="emphasis"><em>itself</em></span> can detect the case
327 when no <span class="emphasis"><em>other</em></span> fibers are ready to run: the running fiber
328 is not <span class="emphasis"><em>ready</em></span> but <span class="emphasis"><em>running.</em></span>
329 </p>
330<p>
331 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
332 processing, so let&#8217;s try a plausible implementation.
333 </p>
334<p>
335</p>
336<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>
337 <span class="comment">// Set a timer so at least one handler will eventually fire, causing</span>
338 <span class="comment">// run_one() to eventually return. Set a timer even if abs_time ==</span>
339 <span class="comment">// time_point::max() so the timer can be canceled by our notify()</span>
340 <span class="comment">// method -- which calls the handler.</span>
341 <span class="keyword">if</span> <span class="special">(</span> <span class="identifier">suspend_timer_</span><span class="special">.</span><span class="identifier">expires_at</span><span class="special">()</span> <span class="special">!=</span> <span class="identifier">abs_time</span><span class="special">)</span> <span class="special">{</span>
342 <span class="comment">// Each expires_at(time_point) call cancels any previous pending</span>
343 <span class="comment">// call. We could inadvertently spin like this:</span>
344 <span class="comment">// dispatcher calls suspend_until() with earliest wake time</span>
345 <span class="comment">// suspend_until() sets suspend_timer_</span>
346 <span class="comment">// lambda loop calls run_one()</span>
347 <span class="comment">// some other asio handler runs before timer expires</span>
348 <span class="comment">// run_one() returns to lambda loop</span>
349 <span class="comment">// lambda loop yields to dispatcher</span>
350 <span class="comment">// dispatcher finds no ready fibers</span>
351 <span class="comment">// dispatcher calls suspend_until() with SAME wake time</span>
352 <span class="comment">// suspend_until() sets suspend_timer_ to same time, canceling</span>
353 <span class="comment">// previous async_wait()</span>
354 <span class="comment">// lambda loop calls run_one()</span>
355 <span class="comment">// asio calls suspend_timer_ handler with operation_aborted</span>
356 <span class="comment">// run_one() returns to lambda loop... etc. etc.</span>
357 <span class="comment">// So only actually set the timer when we're passed a DIFFERENT</span>
358 <span class="comment">// abs_time value.</span>
359 <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>
360 <span class="comment">// It really doesn't matter what the suspend_timer_ handler does,</span>
361 <span class="comment">// or even whether it's called because the timer ran out or was</span>
362 <span class="comment">// canceled. The whole point is to cause the run_one() call to</span>
363 <span class="comment">// return. So just pass a no-op lambda with proper signature.</span>
364 <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>
365 <span class="special">}</span>
366<span class="special">}</span>
367</pre>
368<p>
369 </p>
370<p>
371 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>
372 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>.
373 Usually.
374 </p>
375<p>
376 As indicated in comments, we avoid setting <code class="computeroutput"><span class="identifier">suspend_timer_</span></code>
377 multiple times to the <span class="emphasis"><em>same</em></span> <code class="computeroutput"><span class="identifier">time_point</span></code>
378 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>
379 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
380 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>
381 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>
382 value...
383 </p>
384<p>
385 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&#8217;s important is that our <code class="computeroutput"><span class="identifier">async_wait</span><span class="special">()</span></code>
386 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&#8217;s not so important specifically
387 what that handler does.
388 </p>
389<p>
390</p>
391<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>
392 <span class="comment">// Something has happened that should wake one or more fibers BEFORE</span>
393 <span class="comment">// suspend_timer_ expires. Reset the timer to cause it to fire</span>
394 <span class="comment">// immediately, causing the run_one() call to return. In theory we</span>
395 <span class="comment">// could use cancel() because we don't care whether suspend_timer_'s</span>
396 <span class="comment">// handler is called with operation_aborted or success. However --</span>
397 <span class="comment">// cancel() doesn't change the expiration time, and we use</span>
398 <span class="comment">// suspend_timer_'s expiration time to decide whether it's already</span>
399 <span class="comment">// set. If suspend_until() set some specific wake time, then notify()</span>
400 <span class="comment">// canceled it, then suspend_until() was called again with the same</span>
401 <span class="comment">// wake time, it would match suspend_timer_'s expiration time and we'd</span>
402 <span class="comment">// refrain from setting the timer. So instead of simply calling</span>
403 <span class="comment">// cancel(), reset the timer, which cancels the pending sleep AND sets</span>
404 <span class="comment">// a new expiration time. This will cause us to spin the loop twice --</span>
405 <span class="comment">// once for the operation_aborted handler, once for timer expiration</span>
406 <span class="comment">// -- but that shouldn't be a big problem.</span>
407 <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>
408<span class="special">}</span>
409</pre>
410<p>
411 </p>
412<p>
413 Since an <code class="computeroutput"><span class="identifier">expires_at</span><span class="special">()</span></code>
414 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>
415 to call the <code class="computeroutput"><span class="identifier">async_wait</span><span class="special">()</span></code>
416 handler with <code class="computeroutput"><span class="identifier">operation_aborted</span></code>.
417 </p>
418<p>
419 The comments in <code class="computeroutput"><span class="identifier">notify</span><span class="special">()</span></code>
420 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>.
421 </p>
422<p>
423 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
424 <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>.
425 </p>
426<p>
427 It seems possible that you could put together a more elegant Fiber / Asio
428 integration. But as noted at the outset: it&#8217;s tricky.
429 </p>
430</div>
431<table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr>
432<td align="left"></td>
433<td align="right"><div class="copyright-footer">Copyright &#169; 2013 Oliver Kowalke<p>
434 Distributed under the Boost Software License, Version 1.0. (See accompanying
435 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>)
436 </p>
437</div></td>
438</tr></table>
439<hr>
440<div class="spirit-nav">
441<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="../performance.html"><img src="../../../../../../doc/src/images/next.png" alt="Next"></a>
442</div>
443</body>
444</html>