2
0
mirror of https://github.com/boostorg/fiber.git synced 2026-02-15 00:52:34 +00:00
Files
fiber/doc/html/fiber/callbacks.html
2015-09-05 13:49:29 +02:00

580 lines
57 KiB
HTML

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Integrating Fibers with Asynchronous Callbacks</title>
<link rel="stylesheet" href="../../../../../doc/src/boostbook.css" type="text/css">
<meta name="generator" content="DocBook XSL Stylesheets V1.75.2">
<link rel="home" href="../index.html" title="Chapter&#160;1.&#160;Fiber">
<link rel="up" href="../index.html" title="Chapter&#160;1.&#160;Fiber">
<link rel="prev" href="fls.html" title="Fiber local storage">
<link rel="next" href="nonblocking.html" title="Integrating Fibers with Nonblocking I/O">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<table cellpadding="2" width="100%"><tr>
<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../boost.png"></td>
<td align="center"><a href="../../../../../index.html">Home</a></td>
<td align="center"><a href="../../../../../libs/libraries.htm">Libraries</a></td>
<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
<td align="center"><a href="../../../../../more/index.htm">More</a></td>
</tr></table>
<hr>
<div class="spirit-nav">
<a accesskey="p" href="fls.html"><img src="../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../index.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="nonblocking.html"><img src="../../../../../doc/src/images/next.png" alt="Next"></a>
</div>
<div class="section">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="fiber.callbacks"></a><a name="callbacks"></a><a class="link" href="callbacks.html" title="Integrating Fibers with Asynchronous Callbacks">Integrating Fibers
with Asynchronous Callbacks</a>
</h2></div></div></div>
<h4>
<a name="fiber.callbacks.h0"></a>
<span><a name="fiber.callbacks.overview"></a></span><a class="link" href="callbacks.html#fiber.callbacks.overview">Overview</a>
</h4>
<p>
One of the primary benefits of <span class="bold"><strong>Boost.Fiber</strong></span>
is the ability to use asynchronous operations for efficiency, while at the
same time structuring the calling code <span class="emphasis"><em>as if</em></span> the operations
were synchronous. Asynchronous operations provide completion notification in
a variety of ways, but most involve a callback function of some kind. This
section discusses tactics for interfacing <span class="bold"><strong>Boost.Fiber</strong></span>
with an arbitrary async operation.
</p>
<p>
For purposes of illustration, consider the following hypothetical API:
</p>
<p>
</p>
<pre class="programlisting"><span class="keyword">class</span> <span class="identifier">AsyncAPI</span> <span class="special">{</span>
<span class="keyword">public</span><span class="special">:</span>
<span class="comment">// constructor acquires some resource that can be read and written</span>
<span class="identifier">AsyncAPI</span><span class="special">();</span>
<span class="comment">// callbacks accept an int error code; 0 == success</span>
<span class="keyword">typedef</span> <span class="keyword">int</span> <span class="identifier">errorcode</span><span class="special">;</span>
<span class="comment">// write callback only needs to indicate success or failure</span>
<span class="keyword">void</span> <span class="identifier">init_write</span><span class="special">(</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">data</span><span class="special">,</span>
<span class="identifier">std</span><span class="special">::</span><span class="identifier">function</span><span class="special">&lt;</span> <span class="keyword">void</span><span class="special">(</span> <span class="identifier">errorcode</span><span class="special">)</span> <span class="special">&gt;</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">callback</span><span class="special">);</span>
<span class="comment">// read callback needs to accept both errorcode and data</span>
<span class="keyword">void</span> <span class="identifier">init_read</span><span class="special">(</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">function</span><span class="special">&lt;</span> <span class="keyword">void</span><span class="special">(</span> <span class="identifier">errorcode</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="keyword">const</span><span class="special">&amp;)</span> <span class="special">&gt;</span> <span class="keyword">const</span><span class="special">&amp;);</span>
<span class="comment">// ... other operations ...</span>
<span class="special">};</span>
</pre>
<p>
</p>
<p>
The significant points about each of <code class="computeroutput"><span class="identifier">init_write</span><span class="special">()</span></code> and <code class="computeroutput"><span class="identifier">init_read</span><span class="special">()</span></code> are:
</p>
<div class="itemizedlist"><ul class="itemizedlist" type="disc">
<li class="listitem">
The <code class="computeroutput"><span class="identifier">AsyncAPI</span></code> method only
initiates the operation. It returns immediately, while the requested operation
is still pending.
</li>
<li class="listitem">
The method accepts a callback. When the operation completes, the callback
is called with relevant parameters (error code, data if applicable).
</li>
</ul></div>
<p>
We would like to wrap these asynchronous methods in functions that appear synchronous
by blocking the calling fiber until the operation completes. This lets us use
the wrapper function's return value to deliver relevant data.
</p>
<div class="tip"><table border="0" summary="Tip">
<tr>
<td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="../../../../../doc/src/images/tip.png"></td>
<th align="left">Tip</th>
</tr>
<tr><td align="left" valign="top"><p>
<a class="link" href="synchronization/futures/promise.html#class_promise"> <code class="computeroutput">promise&lt;&gt;</code></a> and <a class="link" href="synchronization/futures/future.html#class_future"> <code class="computeroutput">future&lt;&gt;</code></a> are your friends
here.
</p></td></tr>
</table></div>
<h4>
<a name="fiber.callbacks.h1"></a>
<span><a name="fiber.callbacks.return_errorcode"></a></span><a class="link" href="callbacks.html#fiber.callbacks.return_errorcode">Return
Errorcode</a>
</h4>
<p>
The <code class="computeroutput"><span class="identifier">AsyncAPI</span><span class="special">::</span><span class="identifier">init_write</span><span class="special">()</span></code>
callback passes only an <code class="computeroutput"><span class="identifier">errorcode</span></code>.
If we simply want the blocking wrapper to return that <code class="computeroutput"><span class="identifier">errorcode</span></code>,
this is an extremely straightforward use of <a class="link" href="synchronization/futures/promise.html#class_promise"> <code class="computeroutput">promise&lt;&gt;</code></a> and
<a class="link" href="synchronization/futures/future.html#class_future"> <code class="computeroutput">future&lt;&gt;</code></a>:
</p>
<p>
</p>
<pre class="programlisting"><span class="identifier">AsyncAPI</span><span class="special">::</span><span class="identifier">errorcode</span> <span class="identifier">write_ec</span><span class="special">(</span> <span class="identifier">AsyncAPI</span> <span class="special">&amp;</span> <span class="identifier">api</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">data</span><span class="special">)</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">promise</span><span class="special">&lt;</span> <span class="identifier">AsyncAPI</span><span class="special">::</span><span class="identifier">errorcode</span> <span class="special">&gt;</span> <span class="identifier">promise</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">future</span><span class="special">&lt;</span> <span class="identifier">AsyncAPI</span><span class="special">::</span><span class="identifier">errorcode</span> <span class="special">&gt;</span> <span class="identifier">future</span><span class="special">(</span> <span class="identifier">promise</span><span class="special">.</span><span class="identifier">get_future</span><span class="special">()</span> <span class="special">);</span>
<span class="comment">// We can confidently bind a reference to local variable 'promise' into</span>
<span class="comment">// the lambda callback because we know for a fact we're going to suspend</span>
<span class="comment">// (preserving the lifespan of both 'promise' and 'future') until the</span>
<span class="comment">// callback has fired.</span>
<span class="identifier">api</span><span class="special">.</span><span class="identifier">init_write</span><span class="special">(</span> <span class="identifier">data</span><span class="special">,</span>
<span class="special">[&amp;</span><span class="identifier">promise</span><span class="special">](</span> <span class="identifier">AsyncAPI</span><span class="special">::</span><span class="identifier">errorcode</span> <span class="identifier">ec</span><span class="special">){</span>
<span class="identifier">promise</span><span class="special">.</span><span class="identifier">set_value</span><span class="special">(</span> <span class="identifier">ec</span><span class="special">);</span>
<span class="special">});</span>
<span class="keyword">return</span> <span class="identifier">future</span><span class="special">.</span><span class="identifier">get</span><span class="special">();</span>
<span class="special">}</span>
</pre>
<p>
</p>
<p>
All we have to do is:
</p>
<div class="orderedlist"><ol class="orderedlist" type="1">
<li class="listitem">
Instantiate a <code class="computeroutput"><span class="identifier">promise</span><span class="special">&lt;&gt;</span></code>
of correct type.
</li>
<li class="listitem">
Obtain its <code class="computeroutput"><span class="identifier">future</span><span class="special">&lt;&gt;</span></code>.
</li>
<li class="listitem">
Arrange for the callback to call <a class="link" href="synchronization/futures/promise.html#promise_set_value"> <code class="computeroutput">promise::set_value()</code></a>.
</li>
<li class="listitem">
Block on <a class="link" href="synchronization/futures/future.html#future_get"> <code class="computeroutput">future::get()</code></a>.
</li>
</ol></div>
<div class="note"><table border="0" summary="Note">
<tr>
<td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="../../../../../doc/src/images/note.png"></td>
<th align="left">Note</th>
</tr>
<tr><td align="left" valign="top"><p>
This tactic for resuming a pending fiber works even if the callback is called
on a different thread than the one on which the initiating fiber is running.
In fact, <a href="../../../examples/adapt_callbacks.cpp" target="_top">the example program's</a>
dummy <code class="computeroutput"><span class="identifier">AsyncAPI</span></code> implementation
illustrates that: it simulates async I/O by launching a new thread that sleeps
briefly and then calls the relevant callback.
</p></td></tr>
</table></div>
<h4>
<a name="fiber.callbacks.h2"></a>
<span><a name="fiber.callbacks.success_or_exception"></a></span><a class="link" href="callbacks.html#fiber.callbacks.success_or_exception">Success
or Exception</a>
</h4>
<p>
A wrapper more aligned with modern C++ practice would use an exception, rather
than an <code class="computeroutput"><span class="identifier">errorcode</span></code>, to communicate
failure to its caller. This is straightforward to code in terms of <code class="computeroutput"><span class="identifier">write_ec</span><span class="special">()</span></code>:
</p>
<p>
</p>
<pre class="programlisting"><span class="keyword">void</span> <span class="identifier">write</span><span class="special">(</span> <span class="identifier">AsyncAPI</span> <span class="special">&amp;</span> <span class="identifier">api</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">data</span><span class="special">)</span> <span class="special">{</span>
<span class="identifier">AsyncAPI</span><span class="special">::</span><span class="identifier">errorcode</span> <span class="identifier">ec</span> <span class="special">=</span> <span class="identifier">write_ec</span><span class="special">(</span> <span class="identifier">api</span><span class="special">,</span> <span class="identifier">data</span><span class="special">);</span>
<span class="keyword">if</span> <span class="special">(</span> <span class="identifier">ec</span><span class="special">)</span> <span class="special">{</span>
<span class="keyword">throw</span> <span class="identifier">make_exception</span><span class="special">(</span><span class="string">"write"</span><span class="special">,</span> <span class="identifier">ec</span><span class="special">);</span>
<span class="special">}</span>
<span class="special">}</span>
</pre>
<p>
</p>
<p>
The point is that since each fiber has its own stack, you need not repeat messy
boilerplate: normal encapsulation works.
</p>
<h4>
<a name="fiber.callbacks.h3"></a>
<span><a name="fiber.callbacks.return_errorcode_or_data"></a></span><a class="link" href="callbacks.html#fiber.callbacks.return_errorcode_or_data">Return
Errorcode or Data</a>
</h4>
<p>
Things get a bit more interesting when the async operation's callback passes
multiple data items of interest. One approach would be to use <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">pair</span><span class="special">&lt;&gt;</span></code> to capture both:
</p>
<p>
</p>
<pre class="programlisting"><span class="identifier">std</span><span class="special">::</span><span class="identifier">pair</span><span class="special">&lt;</span> <span class="identifier">AsyncAPI</span><span class="special">::</span><span class="identifier">errorcode</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="special">&gt;</span> <span class="identifier">read_ec</span><span class="special">(</span> <span class="identifier">AsyncAPI</span> <span class="special">&amp;</span> <span class="identifier">api</span><span class="special">)</span> <span class="special">{</span>
<span class="keyword">typedef</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">pair</span><span class="special">&lt;</span> <span class="identifier">AsyncAPI</span><span class="special">::</span><span class="identifier">errorcode</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="special">&gt;</span> <span class="identifier">result_pair</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">promise</span><span class="special">&lt;</span> <span class="identifier">result_pair</span> <span class="special">&gt;</span> <span class="identifier">promise</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">future</span><span class="special">&lt;</span> <span class="identifier">result_pair</span> <span class="special">&gt;</span> <span class="identifier">future</span><span class="special">(</span> <span class="identifier">promise</span><span class="special">.</span><span class="identifier">get_future</span><span class="special">()</span> <span class="special">);</span>
<span class="comment">// We promise that both 'promise' and 'future' will survive until our</span>
<span class="comment">// lambda has been called.</span>
<span class="identifier">api</span><span class="special">.</span><span class="identifier">init_read</span><span class="special">(</span> <span class="special">[&amp;</span><span class="identifier">promise</span><span class="special">](</span> <span class="identifier">AsyncAPI</span><span class="special">::</span><span class="identifier">errorcode</span> <span class="identifier">ec</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">data</span><span class="special">){</span>
<span class="identifier">promise</span><span class="special">.</span><span class="identifier">set_value</span><span class="special">(</span> <span class="identifier">result_pair</span><span class="special">(</span> <span class="identifier">ec</span><span class="special">,</span> <span class="identifier">data</span><span class="special">)</span> <span class="special">);</span>
<span class="special">});</span>
<span class="keyword">return</span> <span class="identifier">future</span><span class="special">.</span><span class="identifier">get</span><span class="special">();</span>
<span class="special">}</span>
</pre>
<p>
</p>
<p>
Once you bundle the interesting data in <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">pair</span><span class="special">&lt;&gt;</span></code>,
the code is effectively identical to <code class="computeroutput"><span class="identifier">write_ec</span><span class="special">()</span></code>. You can call it like this:
</p>
<p>
</p>
<pre class="programlisting"><span class="identifier">std</span><span class="special">::</span><span class="identifier">tie</span><span class="special">(</span> <span class="identifier">ec</span><span class="special">,</span> <span class="identifier">data</span><span class="special">)</span> <span class="special">=</span> <span class="identifier">read_ec</span><span class="special">(</span> <span class="identifier">api</span><span class="special">);</span>
</pre>
<p>
</p>
<a name="Data_or_Exception"></a><h4>
<a name="fiber.callbacks.h4"></a>
<span><a name="fiber.callbacks.data_or_exception"></a></span><a class="link" href="callbacks.html#fiber.callbacks.data_or_exception">Data
or Exception</a>
</h4>
<p>
But a more natural API for a function that obtains data is to return only the
data on success, throwing an exception on error.
</p>
<p>
As with <code class="computeroutput"><span class="identifier">write</span><span class="special">()</span></code>
above, it's certainly possible to code a <code class="computeroutput"><span class="identifier">read</span><span class="special">()</span></code> wrapper in terms of <code class="computeroutput"><span class="identifier">read_ec</span><span class="special">()</span></code>. But since a given application is unlikely
to need both, let's code <code class="computeroutput"><span class="identifier">read</span><span class="special">()</span></code> from scratch, leveraging <a class="link" href="synchronization/futures/promise.html#promise_set_exception"> <code class="computeroutput">promise::set_exception()</code></a>:
</p>
<p>
</p>
<pre class="programlisting"><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">read</span><span class="special">(</span> <span class="identifier">AsyncAPI</span> <span class="special">&amp;</span> <span class="identifier">api</span><span class="special">)</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">promise</span><span class="special">&lt;</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="special">&gt;</span> <span class="identifier">promise</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">future</span><span class="special">&lt;</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="special">&gt;</span> <span class="identifier">future</span><span class="special">(</span> <span class="identifier">promise</span><span class="special">.</span><span class="identifier">get_future</span><span class="special">()</span> <span class="special">);</span>
<span class="comment">// Both 'promise' and 'future' will survive until our lambda has been</span>
<span class="comment">// called.</span>
<span class="identifier">api</span><span class="special">.</span><span class="identifier">init_read</span><span class="special">(</span> <span class="special">[&amp;</span><span class="identifier">promise</span><span class="special">](</span> <span class="identifier">AsyncAPI</span><span class="special">::</span><span class="identifier">errorcode</span> <span class="identifier">ec</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">data</span><span class="special">){</span>
<span class="keyword">if</span> <span class="special">(</span> <span class="special">!</span> <span class="identifier">ec</span><span class="special">)</span> <span class="special">{</span>
<span class="identifier">promise</span><span class="special">.</span><span class="identifier">set_value</span><span class="special">(</span> <span class="identifier">data</span><span class="special">);</span>
<span class="special">}</span> <span class="keyword">else</span> <span class="special">{</span>
<span class="identifier">promise</span><span class="special">.</span><span class="identifier">set_exception</span><span class="special">(</span>
<span class="identifier">std</span><span class="special">::</span><span class="identifier">make_exception_ptr</span><span class="special">(</span>
<span class="identifier">make_exception</span><span class="special">(</span><span class="string">"read"</span><span class="special">,</span> <span class="identifier">ec</span><span class="special">)</span> <span class="special">)</span> <span class="special">);</span>
<span class="special">}</span>
<span class="special">});</span>
<span class="keyword">return</span> <span class="identifier">future</span><span class="special">.</span><span class="identifier">get</span><span class="special">();</span>
<span class="special">}</span>
</pre>
<p>
</p>
<p>
<a class="link" href="synchronization/futures/future.html#future_get"> <code class="computeroutput">future::get()</code></a> will do the right thing, either returning <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span></code>
or throwing an exception.
</p>
<h4>
<a name="fiber.callbacks.h5"></a>
<span><a name="fiber.callbacks.success_error_virtual_methods"></a></span><a class="link" href="callbacks.html#fiber.callbacks.success_error_virtual_methods">Success/Error
Virtual Methods</a>
</h4>
<p>
One classic approach to completion notification is to define an abstract base
class with <code class="computeroutput"><span class="identifier">success</span><span class="special">()</span></code>
and <code class="computeroutput"><span class="identifier">error</span><span class="special">()</span></code>
methods. Code wishing to perform async I/O must derive a subclass, override
each of these methods and pass the async operation a pointer to a subclass
instance. The abstract base class might look like this:
</p>
<p>
</p>
<pre class="programlisting"><span class="comment">// every async operation receives a subclass instance of this abstract base</span>
<span class="comment">// class through which to communicate its result</span>
<span class="keyword">struct</span> <span class="identifier">Response</span> <span class="special">{</span>
<span class="keyword">typedef</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">shared_ptr</span><span class="special">&lt;</span> <span class="identifier">Response</span> <span class="special">&gt;</span> <span class="identifier">ptr</span><span class="special">;</span>
<span class="comment">// called if the operation succeeds</span>
<span class="keyword">virtual</span> <span class="keyword">void</span> <span class="identifier">success</span><span class="special">(</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">data</span><span class="special">)</span> <span class="special">=</span> <span class="number">0</span><span class="special">;</span>
<span class="comment">// called if the operation fails</span>
<span class="keyword">virtual</span> <span class="keyword">void</span> <span class="identifier">error</span><span class="special">(</span> <span class="identifier">AsyncAPIBase</span><span class="special">::</span><span class="identifier">errorcode</span> <span class="identifier">ec</span><span class="special">)</span> <span class="special">=</span> <span class="number">0</span><span class="special">;</span>
<span class="special">};</span>
</pre>
<p>
</p>
<p>
Now the <code class="computeroutput"><span class="identifier">AsyncAPI</span></code> operation
might look more like this:
</p>
<p>
</p>
<pre class="programlisting"><span class="comment">// derive Response subclass, instantiate, pass Response::ptr</span>
<span class="keyword">void</span> <span class="identifier">init_read</span><span class="special">(</span> <span class="identifier">Response</span><span class="special">::</span><span class="identifier">ptr</span><span class="special">);</span>
</pre>
<p>
</p>
<p>
We can address this by writing a one-size-fits-all <code class="computeroutput"><span class="identifier">PromiseResponse</span></code>:
</p>
<p>
</p>
<pre class="programlisting"><span class="keyword">class</span> <span class="identifier">PromiseResponse</span><span class="special">:</span> <span class="keyword">public</span> <span class="identifier">Response</span> <span class="special">{</span>
<span class="keyword">public</span><span class="special">:</span>
<span class="comment">// called if the operation succeeds</span>
<span class="keyword">virtual</span> <span class="keyword">void</span> <span class="identifier">success</span><span class="special">(</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">data</span><span class="special">)</span> <span class="special">{</span>
<span class="identifier">promise_</span><span class="special">.</span><span class="identifier">set_value</span><span class="special">(</span> <span class="identifier">data</span><span class="special">);</span>
<span class="special">}</span>
<span class="comment">// called if the operation fails</span>
<span class="keyword">virtual</span> <span class="keyword">void</span> <span class="identifier">error</span><span class="special">(</span> <span class="identifier">AsyncAPIBase</span><span class="special">::</span><span class="identifier">errorcode</span> <span class="identifier">ec</span><span class="special">)</span> <span class="special">{</span>
<span class="identifier">promise_</span><span class="special">.</span><span class="identifier">set_exception</span><span class="special">(</span>
<span class="identifier">std</span><span class="special">::</span><span class="identifier">make_exception_ptr</span><span class="special">(</span>
<span class="identifier">make_exception</span><span class="special">(</span><span class="string">"read"</span><span class="special">,</span> <span class="identifier">ec</span><span class="special">)</span> <span class="special">)</span> <span class="special">);</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">future</span><span class="special">&lt;</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="special">&gt;</span> <span class="identifier">get_future</span><span class="special">()</span> <span class="special">{</span>
<span class="keyword">return</span> <span class="identifier">promise_</span><span class="special">.</span><span class="identifier">get_future</span><span class="special">();</span>
<span class="special">}</span>
<span class="keyword">private</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">promise</span><span class="special">&lt;</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="special">&gt;</span> <span class="identifier">promise_</span><span class="special">;</span>
<span class="special">};</span>
</pre>
<p>
</p>
<p>
Now we can simply obtain the <code class="computeroutput"><span class="identifier">future</span><span class="special">&lt;&gt;</span></code> from that <code class="computeroutput"><span class="identifier">PromiseResponse</span></code>
and wait on its <code class="computeroutput"><span class="identifier">get</span><span class="special">()</span></code>:
</p>
<p>
</p>
<pre class="programlisting"><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="identifier">read</span><span class="special">(</span> <span class="identifier">AsyncAPI</span> <span class="special">&amp;</span> <span class="identifier">api</span><span class="special">)</span> <span class="special">{</span>
<span class="comment">// Because init_read() requires a shared_ptr, we must allocate our</span>
<span class="comment">// ResponsePromise on the heap, even though we know its lifespan.</span>
<span class="keyword">auto</span> <span class="identifier">promisep</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">PromiseResponse</span> <span class="special">&gt;()</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">future</span><span class="special">&lt;</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span> <span class="special">&gt;</span> <span class="identifier">future</span><span class="special">(</span> <span class="identifier">promisep</span><span class="special">-&gt;</span><span class="identifier">get_future</span><span class="special">()</span> <span class="special">);</span>
<span class="comment">// Both 'promisep' and 'future' will survive until our lambda has been</span>
<span class="comment">// called.</span>
<span class="identifier">api</span><span class="special">.</span><span class="identifier">init_read</span><span class="special">(</span> <span class="identifier">promisep</span><span class="special">);</span>
<span class="keyword">return</span> <span class="identifier">future</span><span class="special">.</span><span class="identifier">get</span><span class="special">();</span>
<span class="special">}</span>
</pre>
<p>
</p>
<p>
The source code above is found in <a href="../../../examples/adapt_callbacks.cpp" target="_top">adapt_callbacks.cpp</a>
and <a href="../../../examples/adapt_method_calls.cpp" target="_top">adapt_method_calls.cpp</a>.
</p>
<a name="callbacks_asio"></a><h4>
<a name="fiber.callbacks.h6"></a>
<span><a name="fiber.callbacks.then_there_s__ulink_url__http___www_boost_org_doc_libs_release_libs_asio_index_html__boost_asio__ulink_"></a></span><a class="link" href="callbacks.html#fiber.callbacks.then_there_s__ulink_url__http___www_boost_org_doc_libs_release_libs_asio_index_html__boost_asio__ulink_">Then
There's <a href="http://www.boost.org/doc/libs/release/libs/asio/index.html" target="_top">Boost.Asio</a></a>
</h4>
<p>
Since the simplest form of Boost.Asio asynchronous operation completion token
is a callback function, we could apply the same tactics for Asio as for our
hypothetical <code class="computeroutput"><span class="identifier">AsyncAPI</span></code> asynchronous
operations.
</p>
<p>
Fortunately we need not. Boost.Asio incorporates a mechanism by which the caller
can customize the notification behavior of every async operation. Therefore
we can construct a <span class="emphasis"><em>completion token</em></span> which, when passed
to a <a href="http://www.boost.org/doc/libs/release/libs/asio/index.html" target="_top">Boost.Asio</a>
async operation, requests blocking for the calling fiber. The underlying implementation
uses the same mechanism as described above.
</p>
<p>
<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>
is such a completion token. <code class="computeroutput"><span class="identifier">yield</span></code>
is an instance of <code class="computeroutput"><span class="identifier">yield_t</span></code>:
</p>
<p>
</p>
<pre class="programlisting"><span class="comment">/// A special value, similar to std::nothrow.</span>
<span class="identifier">BOOST_CONSTEXPR_OR_CONST</span> <span class="identifier">yield_t</span><span class="special">&lt;&gt;</span> <span class="identifier">yield</span><span class="special">;</span>
</pre>
<p>
</p>
<p>
which is a <code class="computeroutput"><span class="identifier">promise_completion_token</span></code>:
</p>
<p>
</p>
<pre class="programlisting"><span class="keyword">template</span><span class="special">&lt;</span> <span class="keyword">typename</span> <span class="identifier">Allocator</span> <span class="special">=</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">allocator</span><span class="special">&lt;</span> <span class="keyword">void</span> <span class="special">&gt;</span> <span class="special">&gt;</span>
<span class="keyword">class</span> <span class="identifier">yield_t</span> <span class="special">:</span> <span class="keyword">public</span> <span class="identifier">promise_completion_token</span><span class="special">&lt;</span> <span class="identifier">Allocator</span> <span class="special">&gt;</span> <span class="special">{</span>
<span class="keyword">public</span><span class="special">:</span>
<span class="comment">/// Construct with default-constructed allocator.</span>
<span class="identifier">BOOST_CONSTEXPR</span> <span class="identifier">yield_t</span><span class="special">()</span> <span class="special">{</span>
<span class="special">}</span>
<span class="comment">// ... ways to use an alternate allocator or bind an error_code ...</span>
<span class="special">};</span>
</pre>
<p>
</p>
<p>
<code class="computeroutput"><span class="identifier">promise_completion_token</span></code> is
common to both <code class="computeroutput"><span class="identifier">yield</span></code> and <code class="computeroutput"><span class="identifier">use_future</span></code>. (The interested reader is encouraged
to learn more about <code class="computeroutput"><span class="identifier">use_future</span></code>
in <a href="../../../examples/asio/use_future.hpp" target="_top">example source code</a>.)
</p>
<p>
<code class="computeroutput"><span class="identifier">promise_completion_token</span></code> is
in fact only a placeholder, a way to trigger Boost.Asio customization. It can
bind a custom allocator or <a href="http://www.boost.org/doc/libs/release/libs/system/doc/reference.html#Class-error_code" target="_top"><code class="computeroutput"><span class="identifier">boost</span><span class="special">::</span><span class="identifier">system</span><span class="special">::</span><span class="identifier">error_code</span></code></a>
for use by the actual handler.
</p>
<p>
</p>
<pre class="programlisting"><span class="keyword">template</span><span class="special">&lt;</span> <span class="keyword">typename</span> <span class="identifier">Allocator</span> <span class="special">&gt;</span>
<span class="keyword">class</span> <span class="identifier">promise_completion_token</span> <span class="special">{</span>
<span class="keyword">public</span><span class="special">:</span>
<span class="keyword">typedef</span> <span class="identifier">Allocator</span> <span class="identifier">allocator_type</span><span class="special">;</span>
<span class="comment">/// Construct using default-constructed allocator.</span>
<span class="identifier">BOOST_CONSTEXPR</span> <span class="identifier">promise_completion_token</span><span class="special">()</span> <span class="special">:</span>
<span class="identifier">ec_</span><span class="special">(</span> <span class="keyword">nullptr</span><span class="special">)</span> <span class="special">{</span>
<span class="special">}</span>
<span class="comment">/// Construct using specified allocator.</span>
<span class="keyword">explicit</span> <span class="identifier">promise_completion_token</span><span class="special">(</span> <span class="identifier">Allocator</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">allocator</span><span class="special">)</span> <span class="special">:</span>
<span class="identifier">ec_</span><span class="special">(</span> <span class="keyword">nullptr</span><span class="special">),</span>
<span class="identifier">allocator_</span><span class="special">(</span> <span class="identifier">allocator</span><span class="special">)</span> <span class="special">{</span>
<span class="special">}</span>
<span class="comment">/// Obtain allocator.</span>
<span class="identifier">allocator_type</span> <span class="identifier">get_allocator</span><span class="special">()</span> <span class="keyword">const</span> <span class="special">{</span>
<span class="keyword">return</span> <span class="identifier">allocator_</span><span class="special">;</span>
<span class="special">}</span>
<span class="comment">//private:</span>
<span class="comment">// used by some subclasses to bind an error_code to suppress exceptions</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="special">*</span> <span class="identifier">ec_</span><span class="special">;</span>
<span class="keyword">private</span><span class="special">:</span>
<span class="identifier">Allocator</span> <span class="identifier">allocator_</span><span class="special">;</span>
<span class="special">};</span>
</pre>
<p>
</p>
<p>
Asio customization is engaged by specializing <a href="http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/handler_type.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">handler_type</span><span class="special">&lt;&gt;</span></code></a> for <code class="computeroutput"><span class="identifier">yield_t</span></code>:
</p>
<p>
</p>
<pre class="programlisting"><span class="keyword">template</span><span class="special">&lt;</span> <span class="keyword">typename</span> <span class="identifier">Allocator</span><span class="special">,</span> <span class="keyword">typename</span> <span class="identifier">ReturnType</span><span class="special">,</span> <span class="keyword">typename</span> <span class="identifier">Arg2</span> <span class="special">&gt;</span>
<span class="keyword">struct</span> <span class="identifier">handler_type</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">yield_t</span><span class="special">&lt;</span> <span class="identifier">Allocator</span> <span class="special">&gt;,</span>
<span class="identifier">ReturnType</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="special">,</span> <span class="identifier">Arg2</span><span class="special">)</span> <span class="special">&gt;</span> <span class="special">{</span>
<span class="keyword">typedef</span> <span class="identifier">fibers</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">detail</span><span class="special">::</span><span class="identifier">yield_handler</span><span class="special">&lt;</span> <span class="identifier">Arg2</span> <span class="special">&gt;</span> <span class="identifier">type</span><span class="special">;</span>
<span class="special">};</span>
</pre>
<p>
</p>
<p>
(There are actually four different specializations in <a href="../../../examples/asio/detail/yield.hpp" target="_top">detail/yield.hpp</a>,
one for each of the four Asio async callback signatures we expect to have to
support.)
</p>
<p>
The above directs Asio to use <code class="computeroutput"><span class="identifier">yield_handler</span></code>
as the actual handler for an async operation to which <code class="computeroutput"><span class="identifier">yield</span></code>
is passed.
</p>
<p>
<code class="computeroutput"><span class="identifier">yield_handler</span></code> is simply an
alias for <code class="computeroutput"><span class="identifier">promise_handler</span></code>,
because <code class="computeroutput"><span class="identifier">promise_handler</span></code> is
shared with the <code class="computeroutput"><span class="identifier">use_future</span></code>
machinery:
</p>
<p>
</p>
<pre class="programlisting"><span class="keyword">template</span><span class="special">&lt;</span> <span class="keyword">typename</span> <span class="identifier">T</span> <span class="special">&gt;</span>
<span class="keyword">using</span> <span class="identifier">yield_handler</span> <span class="special">=</span> <span class="identifier">promise_handler</span><span class="special">&lt;</span> <span class="identifier">T</span> <span class="special">&gt;;</span>
</pre>
<p>
</p>
<p>
<code class="computeroutput"><span class="identifier">promise_handler</span></code> isa <code class="computeroutput"><span class="identifier">promise_handler_base</span></code>:
</p>
<p>
</p>
<pre class="programlisting"><span class="keyword">template</span><span class="special">&lt;</span> <span class="keyword">typename</span> <span class="identifier">T</span> <span class="special">&gt;</span>
<span class="keyword">class</span> <span class="identifier">promise_handler_base</span> <span class="special">{</span>
<span class="keyword">public</span><span class="special">:</span>
<span class="keyword">typedef</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">fibers</span><span class="special">::</span><span class="identifier">promise</span><span class="special">&lt;</span> <span class="identifier">T</span> <span class="special">&gt;</span> <span class="special">&gt;</span> <span class="identifier">promise_ptr</span><span class="special">;</span>
<span class="comment">// Construct from any promise_completion_token subclass special value.</span>
<span class="keyword">template</span><span class="special">&lt;</span> <span class="keyword">typename</span> <span class="identifier">Allocator</span> <span class="special">&gt;</span>
<span class="identifier">promise_handler_base</span><span class="special">(</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">promise_completion_token</span><span class="special">&lt;</span> <span class="identifier">Allocator</span> <span class="special">&gt;</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">pct</span><span class="special">)</span> <span class="special">:</span>
<span class="identifier">promise_</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">fibers</span><span class="special">::</span><span class="identifier">promise</span><span class="special">&lt;</span> <span class="identifier">T</span> <span class="special">&gt;</span> <span class="special">&gt;(</span>
<span class="identifier">std</span><span class="special">::</span><span class="identifier">allocator_arg</span><span class="special">,</span> <span class="identifier">pct</span><span class="special">.</span><span class="identifier">get_allocator</span><span class="special">()</span> <span class="special">)</span> <span class="special">)</span>
<span class="special">{}</span>
<span class="keyword">bool</span> <span class="identifier">should_set_value</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> <span class="identifier">ec</span><span class="special">)</span> <span class="special">{</span>
<span class="keyword">if</span> <span class="special">(</span> <span class="special">!</span> <span class="identifier">ec</span><span class="special">)</span> <span class="special">{</span>
<span class="comment">// whew, success</span>
<span class="keyword">return</span> <span class="keyword">true</span><span class="special">;</span>
<span class="special">}</span>
<span class="comment">// no bound error_code: cause promise_ to throw an exception</span>
<span class="identifier">promise_</span><span class="special">-&gt;</span><span class="identifier">set_exception</span><span class="special">(</span>
<span class="identifier">std</span><span class="special">::</span><span class="identifier">make_exception_ptr</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">system_error</span><span class="special">(</span> <span class="identifier">ec</span><span class="special">)</span> <span class="special">)</span> <span class="special">);</span>
<span class="comment">// caller should NOT call set_value()</span>
<span class="keyword">return</span> <span class="keyword">false</span><span class="special">;</span>
<span class="special">}</span>
<span class="identifier">promise_ptr</span> <span class="identifier">get_promise</span><span class="special">()</span> <span class="keyword">const</span> <span class="special">{</span>
<span class="keyword">return</span> <span class="identifier">promise_</span><span class="special">;</span>
<span class="special">}</span>
<span class="keyword">private</span><span class="special">:</span>
<span class="identifier">promise_ptr</span> <span class="identifier">promise_</span><span class="special">;</span>
<span class="special">};</span>
</pre>
<p>
</p>
<p>
As promised, <code class="computeroutput"><span class="identifier">promise_handler_base</span></code>
binds a <a class="link" href="synchronization/futures/promise.html#class_promise"> <code class="computeroutput">promise&lt;&gt;</code></a> of appropriate type. (We store a <code class="computeroutput"><span class="identifier">shared_ptr</span><span class="special">&lt;</span>
<span class="identifier">promise</span><span class="special">&lt;</span>
<span class="identifier">T</span> <span class="special">&gt;</span>
<span class="special">&gt;</span></code> because the <code class="computeroutput"><span class="identifier">promise_handler</span></code>
instance is copied on its way into underlying Asio machinery.)
</p>
<p>
Asio, having consulted the <code class="computeroutput"><span class="identifier">handler_type</span><span class="special">&lt;&gt;</span></code> traits specialization, instantiates
a <code class="computeroutput"><span class="identifier">yield_handler</span></code> (aka <code class="computeroutput"><span class="identifier">promise_handler</span></code>) as the async operation's
callback:
</p>
<p>
</p>
<pre class="programlisting"><span class="keyword">template</span><span class="special">&lt;</span> <span class="keyword">typename</span> <span class="identifier">T</span> <span class="special">&gt;</span>
<span class="keyword">class</span> <span class="identifier">promise_handler</span> <span class="special">:</span> <span class="keyword">public</span> <span class="identifier">promise_handler_base</span><span class="special">&lt;</span> <span class="identifier">T</span> <span class="special">&gt;</span> <span class="special">{</span>
<span class="keyword">private</span><span class="special">:</span>
<span class="keyword">public</span><span class="special">:</span>
<span class="comment">// Construct from any promise_completion_token subclass special value.</span>
<span class="keyword">template</span><span class="special">&lt;</span> <span class="keyword">typename</span> <span class="identifier">Allocator</span> <span class="special">&gt;</span>
<span class="identifier">promise_handler</span><span class="special">(</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">fibers</span><span class="special">::</span><span class="identifier">asio</span><span class="special">::</span><span class="identifier">promise_completion_token</span><span class="special">&lt;</span> <span class="identifier">Allocator</span> <span class="special">&gt;</span> <span class="keyword">const</span><span class="special">&amp;</span> <span class="identifier">pct</span><span class="special">)</span> <span class="special">:</span>
<span class="identifier">promise_handler_base</span><span class="special">&lt;</span> <span class="identifier">T</span> <span class="special">&gt;(</span> <span class="identifier">pct</span><span class="special">)</span> <span class="special">{</span>
<span class="special">}</span>
<span class="keyword">void</span> <span class="keyword">operator</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> <span class="identifier">ec</span><span class="special">,</span> <span class="identifier">T</span> <span class="identifier">t</span><span class="special">)</span> <span class="special">{</span>
<span class="keyword">if</span> <span class="special">(</span> <span class="identifier">should_set_value</span><span class="special">(</span> <span class="identifier">ec</span><span class="special">)</span> <span class="special">)</span> <span class="special">{</span>
<span class="identifier">get_promise</span><span class="special">()-&gt;</span><span class="identifier">set_value</span><span class="special">(</span> <span class="identifier">t</span><span class="special">);</span>
<span class="special">}</span>
<span class="special">}</span>
<span class="special">};</span>
</pre>
<p>
</p>
<p>
Like the lambda callback in our <a class="link" href="callbacks.html#Data_or_Exception"><code class="computeroutput"><span class="identifier">read</span><span class="special">(</span><span class="identifier">AsyncAPI</span><span class="special">&amp;)</span></code></a> presented earlier, <code class="computeroutput"><span class="identifier">promise_handler</span><span class="special">::</span><span class="keyword">operator</span><span class="special">()()</span></code>
either calls <a class="link" href="synchronization/futures/promise.html#promise_set_value"> <code class="computeroutput">promise::set_value()</code></a> or <a class="link" href="synchronization/futures/promise.html#promise_set_exception"> <code class="computeroutput">promise::set_exception()</code></a> (via
<code class="computeroutput"><span class="identifier">promise_handler_base</span><span class="special">::</span><span class="identifier">should_set_value</span><span class="special">()</span></code>).
</p>
<p>
The source code above is found in <a href="../../../examples/asio/yield.hpp" target="_top">yield.hpp</a>,
<a href="../../../examples/asio/promise_completion_token.hpp" target="_top">promise_completion_token.hpp</a>,
<a href="../../../examples/asio/detail/yield.hpp" target="_top">detail/yield.hpp</a>
and <a href="../../../examples/asio/detail/promise_handler.hpp" target="_top">detail/promise_handler.hpp</a>.
</p>
</div>
<table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr>
<td align="left"></td>
<td align="right"><div class="copyright-footer">Copyright &#169; 2013 Oliver Kowalke<p>
Distributed under the Boost Software License, Version 1.0. (See accompanying
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>)
</p>
</div></td>
</tr></table>
<hr>
<div class="spirit-nav">
<a accesskey="p" href="fls.html"><img src="../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../index.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="nonblocking.html"><img src="../../../../../doc/src/images/next.png" alt="Next"></a>
</div>
</body>
</html>