mirror of
https://github.com/boostorg/context.git
synced 2026-01-19 04:02:17 +00:00
Revert "support for spawning fiber managed by boost.asio"
This reverts commit 520128fab2.
revert because of dependency cycle
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
|
||||
[section:acknowledgements Acknowledgments]
|
||||
|
||||
I'd like to thank Adreas Fett, Artyom Beilis, Casey Bodley, Daniel Larimer, David Deakins,
|
||||
I'd like to thank Adreas Fett, Artyom Beilis, Daniel Larimer, David Deakins,
|
||||
Evgeny Shapovalov, Fernando Pelliccioni, Giovanni Piero Deretta, Gordon
|
||||
Woodhull, Helge Bahmann, Holger Grund, Jeffrey Lee Hellrung (Jr.), Keith
|
||||
Jeffery, Martin Husemann, Phil Endecott, Robert Stewart, Sergey Cheban, Steven
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
]
|
||||
|
||||
|
||||
[def __boost_asio__ [*Boost.Asio]]
|
||||
[def __boost_build__ [*Boost.Build]]
|
||||
[def __boost_context__ [*Boost.Context]]
|
||||
|
||||
@@ -138,7 +137,6 @@
|
||||
[def __resume_with__ ['continuation::resume_with()]]
|
||||
[def __segmented__ [link segmented ['segmented_stack]]]
|
||||
[def __segmented_stack__ ['segmented_stack]]
|
||||
[def __spawn__ [link spawn ['spawn()]]]
|
||||
[def __stack_context__ ['stack_context]]
|
||||
[def __winfib__ ['WinFiber]]
|
||||
|
||||
@@ -167,7 +165,6 @@
|
||||
[include callcc.qbk]
|
||||
[include stack.qbk]
|
||||
[include preallocated.qbk]
|
||||
[include spawn.qbk]
|
||||
[include performance.qbk]
|
||||
[include architectures.qbk]
|
||||
[include rationale.qbk]
|
||||
|
||||
217
doc/context.xml
217
doc/context.xml
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN" "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
|
||||
<library id="context" name="Context" dirname="context" last-revision="$Date: 2021/09/19 06:17:32 $"
|
||||
<library id="context" name="Context" dirname="context" last-revision="$Date: 2019/10/02 06:15:27 $"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<libraryinfo>
|
||||
<authorgroup>
|
||||
@@ -319,7 +319,8 @@
|
||||
</para>
|
||||
<important>
|
||||
<para>
|
||||
Do not jump from inside a catch block.
|
||||
Do not jump from inside a catch block and then re-throw the exception in
|
||||
another fiber.
|
||||
</para>
|
||||
</important>
|
||||
<anchor id="ff_ontop"/>
|
||||
@@ -331,7 +332,7 @@
|
||||
Sometimes it is useful to execute a new function on top of a resumed fiber.
|
||||
For this purpose <emphasis>continuation::resume_with()</emphasis> has to be
|
||||
used. The function passed as argument must accept a rvalue reference to <link
|
||||
linkend="ff"><emphasis>fiber</emphasis></link> and return <link linkend="ff"><emphasis>fiber</emphasis></link>.
|
||||
linkend="ff"><emphasis>fiber</emphasis></link> and return <code><phrase role="keyword">void</phrase></code>.
|
||||
</para>
|
||||
<programlisting><phrase role="keyword">namespace</phrase> <phrase role="identifier">ctx</phrase><phrase role="special">=</phrase><phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">context</phrase><phrase role="special">;</phrase>
|
||||
<phrase role="keyword">int</phrase> <phrase role="identifier">data</phrase><phrase role="special">=</phrase><phrase role="number">0</phrase><phrase role="special">;</phrase>
|
||||
@@ -351,7 +352,7 @@
|
||||
<phrase role="identifier">f1</phrase><phrase role="special">=</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">move</phrase><phrase role="special">(</phrase><phrase role="identifier">f1</phrase><phrase role="special">).</phrase><phrase role="identifier">resume</phrase><phrase role="special">();</phrase>
|
||||
<phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">cout</phrase> <phrase role="special"><<</phrase> <phrase role="string">"f1: returned second time: "</phrase> <phrase role="special"><<</phrase> <phrase role="identifier">data</phrase> <phrase role="special"><<</phrase> <phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">endl</phrase><phrase role="special">;</phrase>
|
||||
<phrase role="identifier">data</phrase><phrase role="special">+=</phrase><phrase role="number">1</phrase><phrase role="special">;</phrase>
|
||||
<phrase role="identifier">f1</phrase><phrase role="special">=</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">move</phrase><phrase role="special">(</phrase><phrase role="identifier">f1</phrase><phrase role="special">).</phrase><phrase role="identifier">resume_with</phrase><phrase role="special">([&</phrase><phrase role="identifier">data</phrase><phrase role="special">](</phrase><phrase role="identifier">ctx</phrase><phrase role="special">::</phrase><phrase role="identifier">fiber</phrase><phrase role="special">&&</phrase> <phrase role="identifier">f2</phrase><phrase role="special">){</phrase>
|
||||
<phrase role="identifier">f1</phrase><phrase role="special">=</phrase><phrase role="identifier">f1</phrase><phrase role="special">.</phrase><phrase role="identifier">resume_with</phrase><phrase role="special">([&</phrase><phrase role="identifier">data</phrase><phrase role="special">](</phrase><phrase role="identifier">ctx</phrase><phrase role="special">::</phrase><phrase role="identifier">fiber</phrase><phrase role="special">&&</phrase> <phrase role="identifier">f2</phrase><phrase role="special">){</phrase>
|
||||
<phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">cout</phrase> <phrase role="special"><<</phrase> <phrase role="string">"f2: entered: "</phrase> <phrase role="special"><<</phrase> <phrase role="identifier">data</phrase> <phrase role="special"><<</phrase> <phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">endl</phrase><phrase role="special">;</phrase>
|
||||
<phrase role="identifier">data</phrase><phrase role="special">=-</phrase><phrase role="number">1</phrase><phrase role="special">;</phrase>
|
||||
<phrase role="keyword">return</phrase> <phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">move</phrase><phrase role="special">(</phrase><phrase role="identifier">f2</phrase><phrase role="special">);</phrase>
|
||||
@@ -583,7 +584,7 @@
|
||||
<phrase role="identifier">source</phrase><phrase role="special">=</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">move</phrase><phrase role="special">(</phrase><phrase role="identifier">source</phrase><phrase role="special">).</phrase><phrase role="identifier">resume</phrase><phrase role="special">();</phrase>
|
||||
<phrase role="keyword">while</phrase><phrase role="special">(!</phrase><phrase role="identifier">done</phrase><phrase role="special">){</phrase>
|
||||
<phrase role="identifier">printf</phrase><phrase role="special">(</phrase><phrase role="string">"Parsed: %c\n"</phrase><phrase role="special">,</phrase><phrase role="identifier">c</phrase><phrase role="special">);</phrase>
|
||||
<phrase role="identifier">source</phrase><phrase role="special">=</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">move</phrase><phrase role="special">(</phrase><phrase role="identifier">source</phrase><phrase role="special">).</phrase><phrase role="identifier">resume</phrase><phrase role="special">();</phrase>
|
||||
<phrase role="identifier">source</phrase><phrase role="special">=</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">Move</phrase><phrase role="special">(</phrase><phrase role="identifier">source</phrase><phrase role="special">).</phrase><phrase role="identifier">resume</phrase><phrase role="special">();</phrase>
|
||||
<phrase role="special">}</phrase>
|
||||
|
||||
<phrase role="identifier">output</phrase><phrase role="special">:</phrase>
|
||||
@@ -1401,7 +1402,8 @@
|
||||
</para>
|
||||
<important>
|
||||
<para>
|
||||
Do not jump from inside a catch block.
|
||||
Do not jump from inside a catch block and then re-throw the exception in
|
||||
another continuation.
|
||||
</para>
|
||||
</important>
|
||||
<anchor id="cc_ontop"/>
|
||||
@@ -1414,7 +1416,8 @@
|
||||
Sometimes it is useful to execute a new function on top of a resumed continuation.
|
||||
For this purpose <emphasis>continuation::resume_with()</emphasis> has to be
|
||||
used. The function passed as argument must accept a rvalue reference to <link
|
||||
linkend="cc"><emphasis>continuation</emphasis></link> and return <link linkend="cc"><emphasis>continuation</emphasis></link>.
|
||||
linkend="cc"><emphasis>continuation</emphasis></link> and return <code><phrase
|
||||
role="keyword">void</phrase></code>.
|
||||
</para>
|
||||
<programlisting><phrase role="keyword">namespace</phrase> <phrase role="identifier">ctx</phrase><phrase role="special">=</phrase><phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">context</phrase><phrase role="special">;</phrase>
|
||||
<phrase role="keyword">int</phrase> <phrase role="identifier">data</phrase><phrase role="special">=</phrase><phrase role="number">0</phrase><phrase role="special">;</phrase>
|
||||
@@ -3202,188 +3205,6 @@
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</section>
|
||||
<section id="context.spawn">
|
||||
<title><anchor id="spawn"/><link linkend="context.spawn">Using fibers in Boost.Asio
|
||||
with spawn()</link></title>
|
||||
<para>
|
||||
<link linkend="spawn"><emphasis>spawn()</emphasis></link> creates a new <emphasis>fiber</emphasis>
|
||||
and starts new stackful thread of execution. The <link linkend="spawn"><emphasis>spawn()</emphasis></link>
|
||||
function is a high-level wrapper over the <emphasis role="bold">Boost.Context</emphasis>
|
||||
library. This function enables programs to implement asynchronous logic in
|
||||
a synchronous manner. Suspending/resuming of the spawned <emphasis>fiber</emphasis>
|
||||
is controlled by <emphasis role="bold">Boost.Asio</emphasis>.
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
In contrast to <emphasis role="bold">Boost.Asio</emphasis> this implementation
|
||||
of <link linkend="spawn"><emphasis>spawn()</emphasis></link> is based only
|
||||
on <link linkend="ff"><emphasis>fiber</emphasis></link>.
|
||||
</para>
|
||||
</note>
|
||||
<bridgehead renderas="sect3" id="context.spawn.h0">
|
||||
<phrase id="context.spawn.usage"/><link linkend="context.spawn.usage">Usage</link>
|
||||
</bridgehead>
|
||||
<programlisting><phrase role="keyword">void</phrase> <phrase role="identifier">do_echo</phrase><phrase role="special">(</phrase><phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">context</phrase><phrase role="special">::</phrase><phrase role="identifier">yield_context</phrase> <phrase role="identifier">yield</phrase><phrase role="special">)</phrase> <phrase role="special">{</phrase>
|
||||
<phrase role="keyword">try</phrase> <phrase role="special">{</phrase>
|
||||
<phrase role="keyword">char</phrase> <phrase role="identifier">data</phrase><phrase role="special">[</phrase><phrase role="number">128</phrase><phrase role="special">];</phrase>
|
||||
<phrase role="keyword">for</phrase> <phrase role="special">(;;)</phrase> <phrase role="special">{</phrase>
|
||||
<phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">size_t</phrase> <phrase role="identifier">length</phrase> <phrase role="special">=</phrase> <phrase role="identifier">my_socket</phrase><phrase role="special">.</phrase><phrase role="identifier">async_read_some</phrase><phrase role="special">(</phrase><phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">asio</phrase><phrase role="special">::</phrase><phrase role="identifier">buffer</phrase><phrase role="special">(</phrase><phrase role="identifier">data</phrase><phrase role="special">),</phrase> <phrase role="identifier">yield</phrase><phrase role="special">);</phrase>
|
||||
<phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">asio</phrase><phrase role="special">::</phrase><phrase role="identifier">async_write</phrase><phrase role="special">(</phrase><phrase role="identifier">my_socket</phrase><phrase role="special">,</phrase> <phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">asio</phrase><phrase role="special">::</phrase><phrase role="identifier">buffer</phrase><phrase role="special">(</phrase><phrase role="identifier">data</phrase><phrase role="special">,</phrase> <phrase role="identifier">length</phrase><phrase role="special">),</phrase> <phrase role="identifier">yield</phrase><phrase role="special">);</phrase>
|
||||
<phrase role="special">}</phrase>
|
||||
<phrase role="special">}</phrase> <phrase role="keyword">catch</phrase> <phrase role="special">(</phrase><phrase role="identifier">std</phrase><phrase role="special">::</phrase><phrase role="identifier">exception</phrase> <phrase role="keyword">const</phrase><phrase role="special">&</phrase> <phrase role="identifier">e</phrase><phrase role="special">)</phrase> <phrase role="special">{</phrase>
|
||||
<phrase role="comment">// ...</phrase>
|
||||
<phrase role="special">}</phrase>
|
||||
<phrase role="special">}</phrase>
|
||||
<phrase role="comment">// ...</phrase>
|
||||
<phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">context</phrase><phrase role="special">::</phrase><phrase role="identifier">spawn</phrase><phrase role="special">(</phrase><phrase role="identifier">my_strand</phrase><phrase role="special">,</phrase> <phrase role="identifier">do_echo</phrase><phrase role="special">);</phrase>
|
||||
</programlisting>
|
||||
<para>
|
||||
This simple example demonstrates the basic usage of <link linkend="spawn"><emphasis>spawn()</emphasis></link>.
|
||||
Function <code><phrase role="identifier">do_echo</phrase></code> is executed
|
||||
by a new <emphasis>fiber</emphasis> that has been created by <link linkend="spawn"><emphasis>spawn()</emphasis></link>.
|
||||
</para>
|
||||
<para>
|
||||
<code><phrase role="identifier">do_echo</phrase></code> gets suspended while
|
||||
asynchronous operations like <code><phrase role="identifier">async_read_some</phrase></code>
|
||||
and <code><phrase role="identifier">async_write</phrase></code> are started
|
||||
and resumed after the asynchronous operation completed. Therefore <code><phrase
|
||||
role="identifier">do_echo</phrase></code> does not require callbacks (the code
|
||||
looks like synchronous).
|
||||
</para>
|
||||
<bridgehead renderas="sect3" id="context.spawn.h1">
|
||||
<phrase id="context.spawn.call_with_current_continuation"/><link linkend="context.spawn.call_with_current_continuation">Call
|
||||
with current continuation</link>
|
||||
</bridgehead>
|
||||
<programlisting><phrase role="preprocessor">#include</phrase> <phrase role="special"><</phrase><phrase role="identifier">boost</phrase><phrase role="special">/</phrase><phrase role="identifier">context</phrase><phrase role="special">/</phrase><phrase role="identifier">spawn</phrase><phrase role="special">.</phrase><phrase role="identifier">hpp</phrase><phrase role="special">></phrase>
|
||||
|
||||
<phrase role="keyword">template</phrase><phrase role="special"><</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">Function</phrase><phrase role="special">,</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">=</phrase> <phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">context</phrase><phrase role="special">::</phrase><phrase role="identifier">default_stack</phrase> <phrase role="special">></phrase>
|
||||
<phrase role="keyword">auto</phrase> <phrase role="identifier">spawn</phrase><phrase role="special">(</phrase> <phrase role="identifier">Function</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">fn</phrase><phrase role="special">,</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">salloc</phrase> <phrase role="special">=</phrase> <phrase role="identifier">StackAllocator</phrase><phrase role="special">()</phrase> <phrase role="special">);</phrase>
|
||||
</programlisting>
|
||||
<variablelist>
|
||||
<title></title>
|
||||
<varlistentry>
|
||||
<term>Effects:</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This function is used to launch a new execution context on behalf of
|
||||
spawned context. Parameter <code><phrase role="identifier">fn</phrase></code>
|
||||
is the fiber function and must have signature <code><phrase role="keyword">void</phrase><phrase
|
||||
role="special">(</phrase><phrase role="identifier">basic_yield_context</phrase><phrase
|
||||
role="special"><</phrase><phrase role="identifier">Handler</phrase><phrase
|
||||
role="special">>)</phrase></code>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<programlisting><phrase role="keyword">template</phrase><phrase role="special"><</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">Handler</phrase><phrase role="special">,</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">Function</phrase><phrase role="special">,</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">=</phrase> <phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">context</phrase><phrase role="special">::</phrase><phrase role="identifier">default_stack</phrase> <phrase role="special">></phrase>
|
||||
<phrase role="keyword">auto</phrase> <phrase role="identifier">spawn</phrase><phrase role="special">(</phrase> <phrase role="identifier">Handler</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">hndlr</phrase><phrase role="special">,</phrase> <phrase role="identifier">Function</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">fn</phrase><phrase role="special">,</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">salloc</phrase> <phrase role="special">=</phrase> <phrase role="identifier">StackAllocator</phrase><phrase role="special">()</phrase> <phrase role="special">);</phrase>
|
||||
</programlisting>
|
||||
<variablelist>
|
||||
<title></title>
|
||||
<varlistentry>
|
||||
<term>Effects:</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This function is used to launch a new execution context on behalf of
|
||||
spawned context, calling the specified handler <code><phrase role="identifier">hndlr</phrase></code>
|
||||
when the fiber completes. <code><phrase role="identifier">hndlr</phrase></code>
|
||||
provides an execution context (via the the handler invocation hook) for
|
||||
the fiber. The handler must have the signature <code><phrase role="keyword">void</phrase><phrase
|
||||
role="special">()</phrase></code>. Parameter <code><phrase role="identifier">fn</phrase></code>
|
||||
is the fiber function and must have signature <code><phrase role="keyword">void</phrase><phrase
|
||||
role="special">(</phrase><phrase role="identifier">basic_yield_context</phrase><phrase
|
||||
role="special"><</phrase><phrase role="identifier">Handler</phrase><phrase
|
||||
role="special">>)</phrase></code>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<programlisting><phrase role="keyword">template</phrase><phrase role="special"><</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">Handler</phrase><phrase role="special">,</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">Function</phrase><phrase role="special">,</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">=</phrase> <phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">context</phrase><phrase role="special">::</phrase><phrase role="identifier">default_stack</phrase> <phrase role="special">></phrase>
|
||||
<phrase role="keyword">auto</phrase> <phrase role="identifier">spawn</phrase><phrase role="special">(</phrase> <phrase role="identifier">context</phrase><phrase role="special">::</phrase><phrase role="identifier">basic_yield_context</phrase><phrase role="special"><</phrase> <phrase role="identifier">Handler</phrase> <phrase role="special">></phrase> <phrase role="identifier">ctx</phrase><phrase role="special">,</phrase> <phrase role="identifier">Function</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">function</phrase><phrase role="special">,</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">salloc</phrase> <phrase role="special">=</phrase> <phrase role="identifier">StackAllocator</phrase><phrase role="special">()</phrase> <phrase role="special">)</phrase>
|
||||
</programlisting>
|
||||
<variablelist>
|
||||
<title></title>
|
||||
<varlistentry>
|
||||
<term>Effects:</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This function is used to launch a new execution context on behalf of
|
||||
spawned context. Parameter <code><phrase role="identifier">fn</phrase></code>
|
||||
is the fiber function and must have signature <code><phrase role="keyword">void</phrase><phrase
|
||||
role="special">(</phrase><phrase role="identifier">basic_yield_context</phrase><phrase
|
||||
role="special"><</phrase><phrase role="identifier">Handler</phrase><phrase
|
||||
role="special">>)</phrase></code>. <code><phrase role="identifier">ctx</phrase></code>
|
||||
identifies the current execution context as a parent of the new fiber.
|
||||
This specifies that the new fiber should inherit the execution context
|
||||
of the parent. For example, if the parent fiber is executing in a particular
|
||||
strand, then the new fiber will execute in the same strand.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<programlisting><phrase role="keyword">template</phrase><phrase role="special"><</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">Function</phrase><phrase role="special">,</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">Executor</phrase><phrase role="special">,</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">=</phrase> <phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">context</phrase><phrase role="special">::</phrase><phrase role="identifier">default_stack</phrase> <phrase role="special">></phrase>
|
||||
<phrase role="keyword">auto</phrase> <phrase role="identifier">spawn</phrase><phrase role="special">(</phrase> <phrase role="identifier">Executor</phrase> <phrase role="keyword">const</phrase><phrase role="special">&</phrase> <phrase role="identifier">ex</phrase><phrase role="special">,</phrase> <phrase role="identifier">Function</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">function</phrase><phrase role="special">,</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">salloc</phrase> <phrase role="special">=</phrase> <phrase role="identifier">StackAllocator</phrase><phrase role="special">()</phrase> <phrase role="special">)</phrase>
|
||||
</programlisting>
|
||||
<variablelist>
|
||||
<title></title>
|
||||
<varlistentry>
|
||||
<term>Effects:</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This function is used to launch a new execution context on behalf of
|
||||
spawned context. Parameter <code><phrase role="identifier">fn</phrase></code>
|
||||
is the fiber function and must have signature <code><phrase role="keyword">void</phrase><phrase
|
||||
role="special">(</phrase><phrase role="identifier">basic_yield_context</phrase><phrase
|
||||
role="special"><</phrase><phrase role="identifier">Handler</phrase><phrase
|
||||
role="special">>)</phrase></code>. <code><phrase role="identifier">ex</phrase></code>
|
||||
identifies the executor that will run the fiber. The new fiber is implicitly
|
||||
given its own strand within this executor.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<programlisting><phrase role="keyword">template</phrase><phrase role="special"><</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">Function</phrase><phrase role="special">,</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">Executor</phrase><phrase role="special">,</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">=</phrase> <phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">context</phrase><phrase role="special">::</phrase><phrase role="identifier">default_stack</phrase> <phrase role="special">></phrase>
|
||||
<phrase role="keyword">auto</phrase> <phrase role="identifier">spawn</phrase><phrase role="special">(</phrase> <phrase role="identifier">context</phrase><phrase role="special">::</phrase><phrase role="identifier">detail</phrase><phrase role="special">::</phrase><phrase role="identifier">net</phrase><phrase role="special">::</phrase><phrase role="identifier">strand</phrase><phrase role="special"><</phrase> <phrase role="identifier">Executor</phrase> <phrase role="special">></phrase> <phrase role="keyword">const</phrase><phrase role="special">&</phrase> <phrase role="identifier">ex</phrase><phrase role="special">,</phrase> <phrase role="identifier">Function</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">function</phrase><phrase role="special">,</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">salloc</phrase> <phrase role="special">=</phrase> <phrase role="identifier">StackAllocator</phrase><phrase role="special">()</phrase> <phrase role="special">)</phrase>
|
||||
</programlisting>
|
||||
<variablelist>
|
||||
<title></title>
|
||||
<varlistentry>
|
||||
<term>Effects:</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This function is used to launch a new execution context on behalf of
|
||||
spawned context. Parameter <code><phrase role="identifier">fn</phrase></code>
|
||||
is the fiber function and must have signature <code><phrase role="keyword">void</phrase><phrase
|
||||
role="special">(</phrase><phrase role="identifier">basic_yield_context</phrase><phrase
|
||||
role="special"><</phrase><phrase role="identifier">Handler</phrase><phrase
|
||||
role="special">>)</phrase></code>. <code><phrase role="identifier">ex</phrase></code>
|
||||
identifies the strand that will run the fiber.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
<programlisting><phrase role="keyword">template</phrase><phrase role="special"><</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">Function</phrase><phrase role="special">,</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">ExecutionContext</phrase><phrase role="special">,</phrase> <phrase role="keyword">typename</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">=</phrase> <phrase role="identifier">boost</phrase><phrase role="special">::</phrase><phrase role="identifier">context</phrase><phrase role="special">::</phrase><phrase role="identifier">default_stack</phrase> <phrase role="special">></phrase>
|
||||
<phrase role="keyword">auto</phrase> <phrase role="identifier">spawn</phrase><phrase role="special">(</phrase> <phrase role="identifier">ExecutionContext</phrase> <phrase role="special">&</phrase> <phrase role="identifier">ctx</phrase><phrase role="special">,</phrase> <phrase role="identifier">Function</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">function</phrase><phrase role="special">,</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">&&</phrase> <phrase role="identifier">salloc</phrase> <phrase role="special">=</phrase> <phrase role="identifier">StackAllocator</phrase><phrase role="special">()</phrase> <phrase role="special">)</phrase>
|
||||
</programlisting>
|
||||
<variablelist>
|
||||
<title></title>
|
||||
<varlistentry>
|
||||
<term>Effects:</term>
|
||||
<listitem>
|
||||
<para>
|
||||
This function is used to launch a new execution context on behalf of
|
||||
spawned context. Parameter <code><phrase role="identifier">fn</phrase></code>
|
||||
is the fiber function and must have signature <code><phrase role="keyword">void</phrase><phrase
|
||||
role="special">(</phrase><phrase role="identifier">basic_yield_context</phrase><phrase
|
||||
role="special"><</phrase><phrase role="identifier">Handler</phrase><phrase
|
||||
role="special">>)</phrase></code>. <code><phrase role="identifier">ctx</phrase></code>
|
||||
identifies the execution context that will run the fiber. The new fiber
|
||||
is implicitly given its own strand within this execution context.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</section>
|
||||
<section id="context.performance">
|
||||
<title><anchor id="performance"/><link linkend="context.performance">Performance</link></title>
|
||||
<para>
|
||||
@@ -3522,7 +3343,7 @@
|
||||
</entry>
|
||||
<entry>
|
||||
<para>
|
||||
AAPCS|MACH-O
|
||||
-
|
||||
</para>
|
||||
</entry>
|
||||
<entry>
|
||||
@@ -3566,7 +3387,7 @@
|
||||
</entry>
|
||||
<entry>
|
||||
<para>
|
||||
O32|N64|ELF
|
||||
O32,N64|ELF
|
||||
</para>
|
||||
</entry>
|
||||
<entry>
|
||||
@@ -3593,7 +3414,7 @@
|
||||
</entry>
|
||||
<entry>
|
||||
<para>
|
||||
SYSV|ELF|XCOFF
|
||||
SYSV|ELF,XCOFF
|
||||
</para>
|
||||
</entry>
|
||||
<entry>
|
||||
@@ -3620,7 +3441,7 @@
|
||||
</entry>
|
||||
<entry>
|
||||
<para>
|
||||
SYSV|ELF|XCOFF
|
||||
SYSV|ELF,XCOFF
|
||||
</para>
|
||||
</entry>
|
||||
<entry>
|
||||
@@ -4055,11 +3876,11 @@
|
||||
<section id="context.acknowledgements">
|
||||
<title><link linkend="context.acknowledgements">Acknowledgments</link></title>
|
||||
<para>
|
||||
I'd like to thank Adreas Fett, Artyom Beilis, Casey Bodley, Daniel Larimer,
|
||||
David Deakins, Evgeny Shapovalov, Fernando Pelliccioni, Giovanni Piero Deretta,
|
||||
Gordon Woodhull, Helge Bahmann, Holger Grund, Jeffrey Lee Hellrung (Jr.), Keith
|
||||
Jeffery, Martin Husemann, Phil Endecott, Robert Stewart, Sergey Cheban, Steven
|
||||
Watanabe, Vicente J. Botet Escriba, Wayne Piekarski.
|
||||
I'd like to thank Adreas Fett, Artyom Beilis, Daniel Larimer, David Deakins,
|
||||
Evgeny Shapovalov, Fernando Pelliccioni, Giovanni Piero Deretta, Gordon Woodhull,
|
||||
Helge Bahmann, Holger Grund, Jeffrey Lee Hellrung (Jr.), Keith Jeffery, Martin
|
||||
Husemann, Phil Endecott, Robert Stewart, Sergey Cheban, Steven Watanabe, Vicente
|
||||
J. Botet Escriba, Wayne Piekarski.
|
||||
</para>
|
||||
</section>
|
||||
</library>
|
||||
|
||||
106
doc/spawn.qbk
106
doc/spawn.qbk
@@ -1,106 +0,0 @@
|
||||
[/
|
||||
Copyright Oliver Kowalke 2021.
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt
|
||||
]
|
||||
|
||||
[#spawn]
|
||||
[section:spawn Using fibers in Boost.Asio with spawn()]
|
||||
|
||||
__spawn__ creates a new __fiber__ and starts new stackful thread of execution.
|
||||
The __spawn__ function is a high-level wrapper over the __boost_context__ library. This function
|
||||
enables programs to implement asynchronous logic in a synchronous manner.
|
||||
Suspending/resuming of the spawned __fiber__ is controlled by __boost_asio__.
|
||||
|
||||
[note In contrast to __boost_asio__ this implementation of __spawn__ is based only on __fib__.]
|
||||
|
||||
[heading Usage]
|
||||
|
||||
void do_echo(boost::context::yield_context yield) {
|
||||
try {
|
||||
char data[128];
|
||||
for (;;) {
|
||||
std::size_t length = my_socket.async_read_some(boost::asio::buffer(data), yield);
|
||||
boost::asio::async_write(my_socket, boost::asio::buffer(data, length), yield);
|
||||
}
|
||||
} catch (std::exception const& e) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
// ...
|
||||
boost::context::spawn(my_strand, do_echo);
|
||||
|
||||
This simple example demonstrates the basic usage of __spawn__.
|
||||
Function `do_echo` is executed by a new __fiber__ that has been created by __spawn__.
|
||||
|
||||
`do_echo` gets suspended while asynchronous operations like `async_read_some` and `async_write` are
|
||||
started and resumed after the asynchronous operation completed.
|
||||
Therefore `do_echo` does not require callbacks (the code looks like synchronous).
|
||||
|
||||
|
||||
[heading Call with current continuation]
|
||||
|
||||
#include <boost/context/spawn.hpp>
|
||||
|
||||
template< typename Function, typename StackAllocator = boost::context::default_stack >
|
||||
auto spawn( Function && fn, StackAllocator && salloc = StackAllocator() );
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [This function is used to launch a new execution context on behalf of spawned context.
|
||||
Parameter `fn` is the fiber function and must have signature `void(basic_yield_context<Handler>)`.]]
|
||||
]
|
||||
|
||||
template< typename Handler, typename Function, typename StackAllocator = boost::context::default_stack >
|
||||
auto spawn( Handler && hndlr, Function && fn, StackAllocator && salloc = StackAllocator() );
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [This function is used to launch a new execution context on behalf of spawned context,
|
||||
calling the specified handler `hndlr` when the fiber completes. `hndlr` provides an execution
|
||||
context (via the the handler invocation hook) for the fiber. The handler must have the signature
|
||||
`void()`.
|
||||
Parameter `fn` is the fiber function and must have signature `void(basic_yield_context<Handler>)`.]]
|
||||
]
|
||||
|
||||
template< typename Handler, typename Function, typename StackAllocator = boost::context::default_stack >
|
||||
auto spawn( context::basic_yield_context< Handler > ctx, Function && function, StackAllocator && salloc = StackAllocator() )
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [This function is used to launch a new execution context on behalf of spawned context.
|
||||
Parameter `fn` is the fiber function and must have signature `void(basic_yield_context<Handler>)`.
|
||||
`ctx` identifies the current execution context as a parent of the new fiber. This specifies that the
|
||||
new fiber should inherit the execution context of the parent. For example, if the parent fiber is
|
||||
executing in a particular strand, then the new fiber will execute in the same strand.]]
|
||||
]
|
||||
|
||||
template< typename Function, typename Executor, typename StackAllocator = boost::context::default_stack >
|
||||
auto spawn( Executor const& ex, Function && function, StackAllocator && salloc = StackAllocator() )
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [This function is used to launch a new execution context on behalf of spawned context.
|
||||
Parameter `fn` is the fiber function and must have signature `void(basic_yield_context<Handler>)`.
|
||||
`ex` identifies the executor that will run the fiber. The new fiber is implicitly given its own
|
||||
strand within this executor.]]
|
||||
]
|
||||
|
||||
template< typename Function, typename Executor, typename StackAllocator = boost::context::default_stack >
|
||||
auto spawn( context::detail::net::strand< Executor > const& ex, Function && function, StackAllocator && salloc = StackAllocator() )
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [This function is used to launch a new execution context on behalf of spawned context.
|
||||
Parameter `fn` is the fiber function and must have signature `void(basic_yield_context<Handler>)`.
|
||||
`ex` identifies the strand that will run the fiber.]]
|
||||
]
|
||||
|
||||
template< typename Function, typename ExecutionContext, typename StackAllocator = boost::context::default_stack >
|
||||
auto spawn( ExecutionContext & ctx, Function && function, StackAllocator && salloc = StackAllocator() )
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [This function is used to launch a new execution context on behalf of spawned context.
|
||||
Parameter `fn` is the fiber function and must have signature `void(basic_yield_context<Handler>)`.
|
||||
`ctx` identifies the execution context that will run the fiber. The new fiber is implicitly given
|
||||
its own strand within this execution context.]]
|
||||
]
|
||||
|
||||
|
||||
[endsect]
|
||||
@@ -81,37 +81,3 @@ exe circle
|
||||
#exe echosse
|
||||
# : echosse.cpp
|
||||
# ;
|
||||
|
||||
exe server
|
||||
: echo_server.cpp
|
||||
/boost/system//boost_system
|
||||
/boost/chrono//boost_chrono
|
||||
: <define>BOOST_ALL_NO_LIB=1
|
||||
<threading>multi
|
||||
<target-os>solaris:<library>socket
|
||||
<target-os>solaris:<library>nsl
|
||||
<target-os>windows:<define>_WIN32_WINNT=0x0501
|
||||
<target-os>windows,<toolset>gcc:<library>ws2_32
|
||||
<target-os>windows,<toolset>gcc:<library>mswsock
|
||||
<target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
|
||||
<target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
|
||||
<target-os>hpux:<library>ipv6
|
||||
<target-os>haiku:<library>network
|
||||
;
|
||||
|
||||
exe client
|
||||
: echo_client.cpp
|
||||
/boost/system//boost_system
|
||||
/boost/chrono//boost_chrono
|
||||
: <define>BOOST_ALL_NO_LIB=1
|
||||
<threading>multi
|
||||
<target-os>solaris:<library>socket
|
||||
<target-os>solaris:<library>nsl
|
||||
<target-os>windows:<define>_WIN32_WINNT=0x0501
|
||||
<target-os>windows,<toolset>gcc:<library>ws2_32
|
||||
<target-os>windows,<toolset>gcc:<library>mswsock
|
||||
<target-os>windows,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
|
||||
<target-os>hpux,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
|
||||
<target-os>hpux:<library>ipv6
|
||||
<target-os>haiku:<library>network
|
||||
;
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
//
|
||||
// blocking_tcp_echo_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
enum { max_length = 1024 };
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: blocking_tcp_echo_client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::io_context io_context;
|
||||
|
||||
tcp::resolver resolver(io_context);
|
||||
tcp::resolver::results_type endpoints =
|
||||
resolver.resolve(tcp::v4(), argv[1], argv[2]);
|
||||
|
||||
tcp::socket s(io_context);
|
||||
boost::asio::connect(s, endpoints);
|
||||
|
||||
using namespace std; // For strlen.
|
||||
std::cout << "Enter message: ";
|
||||
char request[max_length];
|
||||
std::cin.getline(request, max_length);
|
||||
size_t request_length = strlen(request);
|
||||
boost::asio::write(s, boost::asio::buffer(request, request_length));
|
||||
|
||||
char reply[max_length];
|
||||
size_t reply_length = boost::asio::read(s,
|
||||
boost::asio::buffer(reply, request_length));
|
||||
std::cout << "Reply is: ";
|
||||
std::cout.write(reply, reply_length);
|
||||
std::cout << "\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
//
|
||||
// echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
|
||||
#include <boost/context/spawn.hpp>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
class session : public boost::enable_shared_from_this<session>
|
||||
{
|
||||
public:
|
||||
explicit session(boost::asio::io_context& io_context)
|
||||
: strand_(boost::asio::make_strand(io_context)),
|
||||
socket_(io_context),
|
||||
timer_(io_context)
|
||||
{
|
||||
}
|
||||
|
||||
tcp::socket& socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void go()
|
||||
{
|
||||
boost::context::spawn(strand_,
|
||||
boost::bind(&session::echo,
|
||||
shared_from_this(), boost::placeholders::_1));
|
||||
boost::context::spawn(strand_,
|
||||
boost::bind(&session::timeout,
|
||||
shared_from_this(), boost::placeholders::_1));
|
||||
}
|
||||
|
||||
private:
|
||||
void echo(boost::context::yield_context yield)
|
||||
{
|
||||
try
|
||||
{
|
||||
char data[128];
|
||||
for (;;)
|
||||
{
|
||||
timer_.expires_after(boost::asio::chrono::seconds(10));
|
||||
std::size_t n = socket_.async_read_some(boost::asio::buffer(data), yield);
|
||||
boost::asio::async_write(socket_, boost::asio::buffer(data, n), yield);
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
socket_.close();
|
||||
timer_.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void timeout(boost::context::yield_context yield)
|
||||
{
|
||||
while (socket_.is_open())
|
||||
{
|
||||
boost::system::error_code ignored_ec;
|
||||
timer_.async_wait(yield[ignored_ec]);
|
||||
if (timer_.expiry() <= boost::asio::steady_timer::clock_type::now())
|
||||
socket_.close();
|
||||
}
|
||||
}
|
||||
|
||||
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
|
||||
tcp::socket socket_;
|
||||
boost::asio::steady_timer timer_;
|
||||
};
|
||||
|
||||
void do_accept(boost::asio::io_context& io_context,
|
||||
unsigned short port, boost::context::yield_context yield)
|
||||
{
|
||||
tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), port));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
boost::shared_ptr<session> new_session(new session(io_context));
|
||||
acceptor.async_accept(new_session->socket(), yield[ec]);
|
||||
if (!ec) new_session->go();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::io_context io_context;
|
||||
|
||||
boost::context::spawn(io_context,
|
||||
boost::bind(do_accept,
|
||||
boost::ref(io_context), atoi(argv[1]), boost::placeholders::_1));
|
||||
|
||||
io_context.run();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
// Copyright Casey Brodley 2019.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_CONTEXT_DETAIL_ALLOCATOR_H
|
||||
#define BOOST_CONTEXT_DETAIL_ALLOCATOR_H
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <boost/type_traits/make_void.hpp>
|
||||
|
||||
#include <boost/context/stack_context.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace context {
|
||||
namespace detail {
|
||||
|
||||
template< typename T, typename = void >
|
||||
struct is_stack_allocator : public std::false_type {
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
struct is_stack_allocator<
|
||||
T,
|
||||
boost::void_t<
|
||||
decltype(
|
||||
// boost::context::stack_context c = salloc.allocate();
|
||||
std::declval<boost::context::stack_context>() = std::declval<T&>().allocate(),
|
||||
// salloc.deallocate(c);
|
||||
std::declval<T&>().deallocate(std::declval<boost::context::stack_context&>())
|
||||
)
|
||||
>
|
||||
> : public std::true_type {
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif // BOOST_CONTEXT_DETAIL_ALLOCATOR_H
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
// Copyright Casey Brodley 2019.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_CONTEXT_DETAIL_NET_H
|
||||
#define BOOST_CONTEXT_DETAIL_NET_H
|
||||
|
||||
#include <boost/asio/associated_allocator.hpp>
|
||||
#include <boost/asio/associated_executor.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
#include <boost/asio/executor.hpp>
|
||||
#include <boost/asio/is_executor.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
|
||||
#define CONTEXT_NET_NAMESPACE boost::asio
|
||||
|
||||
namespace boost {
|
||||
namespace context {
|
||||
namespace detail {
|
||||
namespace net {
|
||||
|
||||
using boost::asio::associated_executor_t;
|
||||
using boost::asio::get_associated_executor;
|
||||
|
||||
using boost::asio::associated_allocator_t;
|
||||
using boost::asio::get_associated_allocator;
|
||||
|
||||
using boost::asio::execution_context;
|
||||
using boost::asio::executor;
|
||||
using boost::asio::executor_binder;
|
||||
using boost::asio::is_executor;
|
||||
|
||||
using boost::asio::strand;
|
||||
|
||||
}}}}
|
||||
|
||||
#endif // BOOST_CONTEXT_DETAIL_NET_H
|
||||
@@ -1,487 +0,0 @@
|
||||
|
||||
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
// Copyright (c) 2017,2021 Oliver Kowalke (oliver dot kowalke at gmail dot com)
|
||||
// Copyright (c) 2019 Casey Bodley (cbodley at redhat dot com)
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_CONTEXT_IMPL_SPAWN_H
|
||||
#define BOOST_CONTEXT_IMPL_SPAWN_H
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/system/system_error.hpp>
|
||||
|
||||
#include <boost/context/detail/net.hpp>
|
||||
#include <boost/context/detail/is_stack_allocator.hpp>
|
||||
#include <boost/context/fiber.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace context {
|
||||
namespace detail {
|
||||
|
||||
class spawn_context {
|
||||
public:
|
||||
fiber_context ctx_;
|
||||
std::exception_ptr eptr_{};
|
||||
|
||||
spawn_context() = default;
|
||||
|
||||
template< typename StackAlloc, typename Fn >
|
||||
spawn_context( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) :
|
||||
ctx_{
|
||||
std::allocator_arg,
|
||||
std::forward< StackAlloc >( salloc),
|
||||
std::forward< Fn >( fn) } {
|
||||
}
|
||||
|
||||
void resume() {
|
||||
ctx_ = std::move( ctx_).resume();
|
||||
if ( eptr_) {
|
||||
std::rethrow_exception( std::move( eptr_) );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template< typename Handler, typename ...Ts >
|
||||
class coro_handler {
|
||||
public:
|
||||
coro_handler( basic_yield_context< Handler > ctx) :
|
||||
callee_{ ctx.callee_.lock() },
|
||||
caller_{ ctx.caller_ },
|
||||
handler_{ ctx.handler_ },
|
||||
ready_{ 0 },
|
||||
ec_{ ctx.ec_ },
|
||||
value_{ 0 } {
|
||||
}
|
||||
|
||||
void operator()( Ts... values) {
|
||||
*ec_ = boost::system::error_code{};
|
||||
*value_ = std::forward_as_tuple( std::move( values) ...);
|
||||
if ( --*ready_ == 0) {
|
||||
callee_->resume();
|
||||
}
|
||||
}
|
||||
|
||||
void operator()( boost::system::error_code ec, Ts... values) {
|
||||
*ec_ = ec;
|
||||
*value_ = std::forward_as_tuple( std::move( values) ...);
|
||||
if ( --*ready_ == 0) {
|
||||
callee_->resume();
|
||||
}
|
||||
}
|
||||
|
||||
//private:
|
||||
std::shared_ptr< spawn_context > callee_;
|
||||
spawn_context & caller_;
|
||||
Handler handler_;
|
||||
std::atomic< long > * ready_;
|
||||
boost::system::error_code * ec_;
|
||||
boost::optional< std::tuple< Ts... > > * value_;
|
||||
};
|
||||
|
||||
template< typename Handler, typename T >
|
||||
class coro_handler< Handler, T > {
|
||||
public:
|
||||
coro_handler( basic_yield_context< Handler > ctx) :
|
||||
callee_{ ctx.callee_.lock() },
|
||||
caller_{ ctx.caller_ },
|
||||
handler_{ ctx.handler_ },
|
||||
ready_{ 0 },
|
||||
ec_{ ctx.ec_ },
|
||||
value_{ 0 } {
|
||||
}
|
||||
|
||||
void operator()( T value) {
|
||||
*ec_ = boost::system::error_code();
|
||||
*value_ = std::move( value);
|
||||
if ( --*ready_ == 0) {
|
||||
callee_->resume();
|
||||
}
|
||||
}
|
||||
|
||||
void operator()( boost::system::error_code ec, T value) {
|
||||
*ec_ = ec;
|
||||
*value_ = std::move( value);
|
||||
if ( --*ready_ == 0) {
|
||||
callee_->resume();
|
||||
}
|
||||
}
|
||||
|
||||
//private:
|
||||
std::shared_ptr< spawn_context > callee_;
|
||||
spawn_context & caller_;
|
||||
Handler handler_;
|
||||
std::atomic< long > * ready_;
|
||||
boost::system::error_code * ec_;
|
||||
boost::optional< T > * value_;
|
||||
};
|
||||
|
||||
template< typename Handler >
|
||||
class coro_handler< Handler, void > {
|
||||
public:
|
||||
coro_handler( basic_yield_context< Handler > ctx) :
|
||||
callee_{ ctx.callee_.lock() },
|
||||
caller_{ ctx.caller_ },
|
||||
handler_{ ctx.handler_ },
|
||||
ready_{ 0 },
|
||||
ec_{ ctx.ec_ } {
|
||||
}
|
||||
|
||||
void operator()() {
|
||||
*ec_ = boost::system::error_code();
|
||||
if ( --*ready_ == 0) {
|
||||
callee_->resume();
|
||||
}
|
||||
}
|
||||
|
||||
void operator()( boost::system::error_code ec) {
|
||||
*ec_ = ec;
|
||||
if ( --*ready_ == 0) {
|
||||
callee_->resume();
|
||||
}
|
||||
}
|
||||
|
||||
//private:
|
||||
std::shared_ptr< spawn_context > callee_;
|
||||
spawn_context & caller_;
|
||||
Handler handler_;
|
||||
std::atomic< long > * ready_;
|
||||
boost::system::error_code * ec_;
|
||||
};
|
||||
|
||||
template< typename Handler, typename ...Ts >
|
||||
class coro_async_result {
|
||||
public:
|
||||
using completion_handler_type = coro_handler< Handler, Ts... >;
|
||||
using return_type = std::tuple< Ts... >;
|
||||
|
||||
explicit coro_async_result( completion_handler_type & h) :
|
||||
handler_{ h },
|
||||
caller_{ h.caller_ },
|
||||
ready_{ 2 } {
|
||||
h.ready_ = & ready_;
|
||||
out_ec_ = h.ec_;
|
||||
if ( ! out_ec_) {
|
||||
h.ec_ = & ec_;
|
||||
}
|
||||
h.value_ = & value_;
|
||||
}
|
||||
|
||||
return_type get() {
|
||||
// Must not hold shared_ptr while suspended.
|
||||
handler_.callee_.reset();
|
||||
if ( --ready_ != 0) {
|
||||
caller_.resume(); // suspend caller
|
||||
}
|
||||
if ( ! out_ec_ && ec_) {
|
||||
throw boost::system::system_error( ec_);
|
||||
}
|
||||
return std::move( * value_);
|
||||
}
|
||||
|
||||
private:
|
||||
completion_handler_type & handler_;
|
||||
spawn_context & caller_;
|
||||
std::atomic< long > ready_;
|
||||
boost::system::error_code * out_ec_;
|
||||
boost::system::error_code ec_;
|
||||
boost::optional< return_type > value_;
|
||||
};
|
||||
|
||||
template< typename Handler, typename T >
|
||||
class coro_async_result< Handler, T > {
|
||||
public:
|
||||
using completion_handler_type = coro_handler< Handler, T >;
|
||||
using return_type = T;
|
||||
|
||||
explicit coro_async_result( completion_handler_type & h) :
|
||||
handler_{ h },
|
||||
caller_{ h.caller_ },
|
||||
ready_{ 2 } {
|
||||
h.ready_ = & ready_;
|
||||
out_ec_ = h.ec_;
|
||||
if ( ! out_ec_) {
|
||||
h.ec_ = & ec_;
|
||||
}
|
||||
h.value_ = & value_;
|
||||
}
|
||||
|
||||
return_type get() {
|
||||
// Must not hold shared_ptr while suspended.
|
||||
handler_.callee_.reset();
|
||||
if ( --ready_ != 0) {
|
||||
caller_.resume(); // suspend caller
|
||||
}
|
||||
if ( ! out_ec_ && ec_) {
|
||||
throw boost::system::system_error( ec_);
|
||||
}
|
||||
return std::move( * value_);
|
||||
}
|
||||
|
||||
private:
|
||||
completion_handler_type & handler_;
|
||||
spawn_context & caller_;
|
||||
std::atomic< long > ready_;
|
||||
boost::system::error_code * out_ec_;
|
||||
boost::system::error_code ec_;
|
||||
boost::optional< return_type > value_;
|
||||
};
|
||||
|
||||
template< typename Handler >
|
||||
class coro_async_result< Handler, void > {
|
||||
public:
|
||||
using completion_handler_type = coro_handler< Handler, void >;
|
||||
using return_type = void;
|
||||
|
||||
explicit coro_async_result( completion_handler_type & h) :
|
||||
handler_{ h },
|
||||
caller_{ h.caller_ },
|
||||
ready_{ 2 } {
|
||||
h.ready_ = & ready_;
|
||||
out_ec_ = h.ec_;
|
||||
if ( ! out_ec_) {
|
||||
h.ec_ = & ec_;
|
||||
}
|
||||
}
|
||||
|
||||
void get() {
|
||||
// Must not hold shared_ptr while suspended.
|
||||
handler_.callee_.reset();
|
||||
if ( --ready_ != 0) {
|
||||
caller_.resume(); // suspend caller
|
||||
}
|
||||
if ( ! out_ec_ && ec_) {
|
||||
throw boost::system::system_error( ec_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
completion_handler_type & handler_;
|
||||
spawn_context & caller_;
|
||||
std::atomic< long > ready_;
|
||||
boost::system::error_code * out_ec_;
|
||||
boost::system::error_code ec_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace context
|
||||
|
||||
#if !defined(GENERATING_DOCUMENTATION)
|
||||
|
||||
template< typename Handler, typename ReturnType >
|
||||
class CONTEXT_NET_NAMESPACE::async_result< context::basic_yield_context< Handler >, ReturnType() > :
|
||||
public context::detail::coro_async_result< Handler, void > {
|
||||
public:
|
||||
explicit async_result(
|
||||
typename context::detail::coro_async_result< Handler, void >::completion_handler_type & h) :
|
||||
context::detail::coro_async_result< Handler, void >{ h } {
|
||||
}
|
||||
};
|
||||
|
||||
template< typename Handler, typename ReturnType, typename ...Args >
|
||||
class CONTEXT_NET_NAMESPACE::async_result< context::basic_yield_context< Handler >, ReturnType( Args...) > :
|
||||
public context::detail::coro_async_result< Handler, typename std::decay< Args >::type... > {
|
||||
public:
|
||||
explicit async_result(
|
||||
typename context::detail::coro_async_result< Handler, typename std::decay< Args >::type... >::completion_handler_type & h) :
|
||||
context::detail::coro_async_result< Handler, typename std::decay< Args >::type... >{ h } {
|
||||
}
|
||||
};
|
||||
|
||||
template< typename Handler, typename ReturnType >
|
||||
class CONTEXT_NET_NAMESPACE::async_result< context::basic_yield_context< Handler >, ReturnType( boost::system::error_code) > :
|
||||
public context::detail::coro_async_result< Handler, void > {
|
||||
public:
|
||||
explicit async_result(
|
||||
typename context::detail::coro_async_result< Handler, void>::completion_handler_type & h) :
|
||||
context::detail::coro_async_result< Handler, void >{ h } {
|
||||
}
|
||||
};
|
||||
|
||||
template< typename Handler, typename ReturnType, typename ...Args >
|
||||
class CONTEXT_NET_NAMESPACE::async_result< context::basic_yield_context< Handler >, ReturnType( boost::system::error_code, Args...) > :
|
||||
public context::detail::coro_async_result< Handler, typename std::decay< Args >::type... > {
|
||||
public:
|
||||
explicit async_result(
|
||||
typename context::detail::coro_async_result< Handler, typename std::decay< Args >::type...>::completion_handler_type & h) :
|
||||
context::detail::coro_async_result< Handler, typename std::decay< Args >::type... >{ h } {
|
||||
}
|
||||
};
|
||||
|
||||
template< typename Handler, typename Allocator, typename ...Ts >
|
||||
struct CONTEXT_NET_NAMESPACE::associated_allocator< context::detail::coro_handler< Handler, Ts... >, Allocator > {
|
||||
using type = associated_allocator_t< Handler, Allocator >;
|
||||
|
||||
static type get( context::detail::coro_handler< Handler, Ts... > const& h, Allocator const& a = Allocator{} ) noexcept {
|
||||
return associated_allocator< Handler, Allocator >::get( h.handler_, a);
|
||||
}
|
||||
};
|
||||
|
||||
template< typename Handler, typename Executor, typename ...Ts >
|
||||
struct CONTEXT_NET_NAMESPACE::associated_executor< context::detail::coro_handler< Handler, Ts... >, Executor> {
|
||||
using type = associated_executor_t< Handler, Executor >;
|
||||
|
||||
static type get( context::detail::coro_handler< Handler, Ts... > const& h, Executor const& ex = Executor{} ) noexcept {
|
||||
return associated_executor< Handler, Executor >::get( h.handler_, ex);
|
||||
}
|
||||
};
|
||||
|
||||
namespace context {
|
||||
namespace detail {
|
||||
|
||||
template< typename Handler, typename Function, typename StackAllocator >
|
||||
struct spawn_data {
|
||||
template< typename Hand, typename Func, typename Stack >
|
||||
spawn_data( Hand && handler, bool call_handler, Func && function, Stack && salloc) :
|
||||
handler_{ std::forward< Hand >( handler) },
|
||||
call_handler_{ call_handler },
|
||||
function_{ std::forward< Func >( function) },
|
||||
salloc_{ std::forward< Stack >( salloc) } {
|
||||
}
|
||||
|
||||
spawn_data( spawn_data const&) = delete;
|
||||
spawn_data & operator=( spawn_data const&) = delete;
|
||||
|
||||
Handler handler_;
|
||||
bool call_handler_;
|
||||
Function function_;
|
||||
StackAllocator salloc_;
|
||||
spawn_context caller_;
|
||||
};
|
||||
|
||||
template< typename Handler, typename Function, typename StackAllocator >
|
||||
struct spawn_helper {
|
||||
void operator()() {
|
||||
callee_.reset(
|
||||
new spawn_context{
|
||||
std::allocator_arg,
|
||||
std::move( data_->salloc_),
|
||||
[this] (fiber_context && f) {
|
||||
std::shared_ptr< spawn_data< Handler, Function, StackAllocator > > data = data_;
|
||||
data->caller_.ctx_ = std::move( f);
|
||||
const basic_yield_context< Handler > yh{ callee_, data->caller_, data->handler_ };
|
||||
try {
|
||||
( data->function_)( yh);
|
||||
if ( data->call_handler_) {
|
||||
( data->handler_)();
|
||||
}
|
||||
} catch ( boost::context::detail::forced_unwind const& e) {
|
||||
throw; // must allow forced_unwind to propagate
|
||||
} catch (...) {
|
||||
auto callee = yh.callee_.lock();
|
||||
if ( callee) {
|
||||
callee->eptr_ = std::current_exception();
|
||||
}
|
||||
}
|
||||
fiber_context caller = std::move( data->caller_.ctx_);
|
||||
data.reset();
|
||||
return caller;
|
||||
} } );
|
||||
callee_->ctx_ = std::move( callee_->ctx_).resume();
|
||||
if ( callee_->eptr_) {
|
||||
std::rethrow_exception( std::move( callee_->eptr_) );
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr< spawn_context > callee_;
|
||||
std::shared_ptr< spawn_data< Handler, Function, StackAllocator > > data_;
|
||||
};
|
||||
|
||||
inline
|
||||
void default_spawn_handler() {
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template< typename Function, typename StackAllocator >
|
||||
auto spawn( Function && function, StackAllocator && salloc)
|
||||
-> typename std::enable_if<
|
||||
context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value
|
||||
>::type {
|
||||
auto ex = context::detail::net::get_associated_executor( function);
|
||||
spawn( ex, std::forward< Function >( function), std::forward< StackAllocator >( salloc) );
|
||||
}
|
||||
|
||||
template< typename Handler, typename Function, typename StackAllocator >
|
||||
auto spawn( Handler && handler, Function && function, StackAllocator && salloc)
|
||||
-> typename std::enable_if<
|
||||
! context::detail::net::is_executor< typename std::decay< Handler >::type >::value &&
|
||||
! std::is_convertible< Handler &, context::detail::net::execution_context & >::value &&
|
||||
! context::detail::is_stack_allocator< typename std::decay< Function >::type >::value &&
|
||||
context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value
|
||||
>::type {
|
||||
using handler_type = typename std::decay< Handler >::type;
|
||||
using function_type = typename std::decay< Function >::type;
|
||||
|
||||
auto ex = context::detail::net::get_associated_executor( handler);
|
||||
auto a = context::detail::net::get_associated_allocator( handler);
|
||||
context::detail::spawn_helper< handler_type, function_type, StackAllocator > helper;
|
||||
helper.data_ = std::make_shared<
|
||||
context::detail::spawn_data< handler_type, function_type, StackAllocator > >(
|
||||
std::forward< Handler >( handler), true,
|
||||
std::forward< Function >( function),
|
||||
std::forward< StackAllocator >( salloc) );
|
||||
ex.dispatch( helper, a);
|
||||
}
|
||||
|
||||
template< typename Handler, typename Function, typename StackAllocator >
|
||||
auto spawn( context::basic_yield_context< Handler > ctx, Function && function, StackAllocator && salloc)
|
||||
-> typename std::enable_if<
|
||||
context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value
|
||||
>::type {
|
||||
using function_type = typename std::decay< Function >::type;
|
||||
|
||||
Handler handler{ ctx.handler_ }; // Explicit copy that might be moved from.
|
||||
auto ex = context::detail::net::get_associated_executor( handler);
|
||||
auto a = context::detail::net::get_associated_allocator( handler);
|
||||
context::detail::spawn_helper< Handler, function_type, StackAllocator > helper;
|
||||
helper.data_ = std::make_shared<
|
||||
context::detail::spawn_data< Handler, function_type, StackAllocator > >(
|
||||
std::forward< Handler >( handler), false,
|
||||
std::forward< Function >( function),
|
||||
std::forward< StackAllocator >( salloc) );
|
||||
ex.dispatch( helper, a);
|
||||
}
|
||||
|
||||
template< typename Function, typename Executor, typename StackAllocator >
|
||||
auto spawn( Executor const& ex, Function && function, StackAllocator && salloc)
|
||||
-> typename std::enable_if<
|
||||
context::detail::net::is_executor< Executor >::value &&
|
||||
context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value
|
||||
>::type {
|
||||
spawn( context::detail::net::strand< Executor >{ ex },
|
||||
std::forward< Function >( function),
|
||||
std::forward< StackAllocator >( salloc) );
|
||||
}
|
||||
|
||||
template< typename Function, typename Executor, typename StackAllocator >
|
||||
auto spawn( context::detail::net::strand< Executor > const& ex, Function && function, StackAllocator && salloc)
|
||||
-> typename std::enable_if<
|
||||
context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value
|
||||
>::type {
|
||||
spawn( bind_executor( ex, & context::detail::default_spawn_handler),
|
||||
std::forward< Function >( function),
|
||||
std::forward< StackAllocator >( salloc) );
|
||||
}
|
||||
|
||||
template< typename Function, typename ExecutionContext, typename StackAllocator >
|
||||
auto spawn( ExecutionContext & ctx, Function && function, StackAllocator && salloc)
|
||||
-> typename std::enable_if<
|
||||
std::is_convertible< ExecutionContext &, context::detail::net::execution_context & >::value &&
|
||||
context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value
|
||||
>::type {
|
||||
spawn( ctx.get_executor(),
|
||||
std::forward< Function >( function),
|
||||
std::forward< StackAllocator >( salloc) );
|
||||
}
|
||||
|
||||
#endif // !defined(GENERATING_DOCUMENTATION)
|
||||
|
||||
}}
|
||||
|
||||
#endif // BOOST_CONTEXT_IMPL_SPAWN_H
|
||||
@@ -1,129 +0,0 @@
|
||||
|
||||
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
// Copyright (c) 2017,2021 Oliver Kowalke (oliver dot kowalke at gmail dot com)
|
||||
// Copyright (c) 2019 Casey Bodley (cbodley at redhat dot com)
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_CONTEXT_SPAWN_H
|
||||
#define BOOST_CONTEXT_SPAWN_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <boost/system/system_error.hpp>
|
||||
|
||||
#include <boost/context/detail/net.hpp>
|
||||
#include <boost/context/detail/is_stack_allocator.hpp>
|
||||
#include <boost/context/fixedsize_stack.hpp>
|
||||
#include <boost/context/segmented_stack.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace context {
|
||||
namespace detail {
|
||||
|
||||
class spawn_context;
|
||||
|
||||
}
|
||||
|
||||
// Context object represents the current execution context.
|
||||
// The basic_yield_context class is used to represent the current execution
|
||||
// context. A basic_yield_context may be passed as a handler to an
|
||||
// asynchronous operation. For example:
|
||||
template< typename Handler >
|
||||
class basic_yield_context {
|
||||
public:
|
||||
// Construct a yield context to represent the specified execution context.
|
||||
// Most applications do not need to use this constructor. Instead, the
|
||||
// spawn() function passes a yield context as an argument to the fiber
|
||||
// function.
|
||||
basic_yield_context(
|
||||
std::weak_ptr< detail::spawn_context > const& callee,
|
||||
detail::spawn_context & caller,
|
||||
Handler & handler) :
|
||||
callee_{ callee },
|
||||
caller_{ caller },
|
||||
handler_{ handler },
|
||||
ec_{ 0 } {
|
||||
}
|
||||
|
||||
// Construct a yield context from another yield context type.
|
||||
// Requires that OtherHandler be convertible to Handler.
|
||||
template< typename OtherHandler >
|
||||
basic_yield_context(
|
||||
basic_yield_context< OtherHandler > const& other) :
|
||||
callee_{ other.callee_ },
|
||||
caller_{ other.caller_ },
|
||||
handler_{ other.handler_ },
|
||||
ec_{ other.ec_ } {
|
||||
}
|
||||
|
||||
// Return a yield context that sets the specified error_code.
|
||||
// By default, when a yield context is used with an asynchronous operation, a
|
||||
// non-success error_code is converted to system_error and thrown. This
|
||||
// operator may be used to specify an error_code object that should instead be
|
||||
// set with the asynchronous operation's result. For example:
|
||||
basic_yield_context operator[]( boost::system::error_code & ec) const {
|
||||
basic_yield_context tmp{ * this };
|
||||
tmp.ec_ = & ec;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
//private:
|
||||
std::weak_ptr< detail::spawn_context > callee_;
|
||||
detail::spawn_context & caller_;
|
||||
Handler handler_;
|
||||
boost::system::error_code * ec_;
|
||||
};
|
||||
|
||||
using yield_context = basic_yield_context< detail::net::executor_binder< void(*)(), detail::net::executor > >;
|
||||
|
||||
// The spawn() function is a high-level wrapper over the Boost.Context
|
||||
// library (spawn_context). This function enables programs to
|
||||
// implement asynchronous logic in a synchronous manner.
|
||||
template< typename Function, typename StackAllocator = boost::context::default_stack >
|
||||
auto spawn( Function && fn, StackAllocator && salloc = StackAllocator() )
|
||||
-> typename std::enable_if<
|
||||
boost::context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value
|
||||
>::type;
|
||||
|
||||
template< typename Handler, typename Function, typename StackAllocator = boost::context::default_stack >
|
||||
auto spawn( Handler && hndlr, Function && fn, StackAllocator && salloc = StackAllocator() )
|
||||
-> typename std::enable_if<
|
||||
! context::detail::net::is_executor< typename std::decay< Handler >::type >::value &&
|
||||
! std::is_convertible< Handler &, context::detail::net::execution_context & >::value &&
|
||||
! context::detail::is_stack_allocator< typename std::decay< Function >::type >::value &&
|
||||
context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value
|
||||
>::type;
|
||||
|
||||
template< typename Handler, typename Function, typename StackAllocator = boost::context::default_stack >
|
||||
auto spawn( context::basic_yield_context< Handler > ctx, Function && fn, StackAllocator && salloc = StackAllocator() )
|
||||
-> typename std::enable_if<
|
||||
context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value
|
||||
>::type;
|
||||
|
||||
template< typename Function, typename Executor, typename StackAllocator = boost::context::default_stack >
|
||||
auto spawn( Executor const& ex, Function && function, StackAllocator && salloc = StackAllocator() )
|
||||
-> typename std::enable_if<
|
||||
context::detail::net::is_executor< Executor >::value &&
|
||||
context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value
|
||||
>::type;
|
||||
|
||||
template< typename Function, typename Executor, typename StackAllocator = boost::context::default_stack >
|
||||
auto spawn( context::detail::net::strand< Executor > const& ex, Function && function, StackAllocator && salloc = StackAllocator() )
|
||||
-> typename std::enable_if<
|
||||
context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value
|
||||
>::type;
|
||||
|
||||
template< typename Function, typename ExecutionContext, typename StackAllocator = boost::context::default_stack >
|
||||
auto spawn( ExecutionContext & ctx, Function && function, StackAllocator && salloc = StackAllocator() )
|
||||
-> typename std::enable_if<
|
||||
std::is_convertible< ExecutionContext &, context::detail::net::execution_context & >::value &&
|
||||
context::detail::is_stack_allocator< typename std::decay< StackAllocator >::type >::value
|
||||
>::type;
|
||||
|
||||
}}
|
||||
|
||||
#include <boost/context/impl/spawn.hpp>
|
||||
|
||||
#endif // BOOST_CONTEXT_SPAWN_H
|
||||
@@ -203,43 +203,7 @@ test-suite minimal :
|
||||
cxx11_template_aliases
|
||||
cxx11_thread_local
|
||||
cxx11_variadic_templates ]
|
||||
: test_callcc_segmented ]
|
||||
|
||||
[ run test_spawn.cpp :
|
||||
: :
|
||||
<context-impl>fcontext
|
||||
[ requires cxx11_auto_declarations
|
||||
cxx11_constexpr
|
||||
cxx11_defaulted_functions
|
||||
cxx11_final
|
||||
cxx11_hdr_thread
|
||||
cxx11_hdr_tuple
|
||||
cxx11_lambdas
|
||||
cxx11_noexcept
|
||||
cxx11_nullptr
|
||||
cxx11_rvalue_references
|
||||
cxx11_template_aliases
|
||||
cxx11_thread_local
|
||||
cxx11_variadic_templates ]
|
||||
: test_spawn_asm ]
|
||||
|
||||
[ run test_spawn.cpp :
|
||||
: :
|
||||
<conditional>@native-impl
|
||||
[ requires cxx11_auto_declarations
|
||||
cxx11_constexpr
|
||||
cxx11_defaulted_functions
|
||||
cxx11_final
|
||||
cxx11_hdr_thread
|
||||
cxx11_hdr_tuple
|
||||
cxx11_lambdas
|
||||
cxx11_noexcept
|
||||
cxx11_nullptr
|
||||
cxx11_rvalue_references
|
||||
cxx11_template_aliases
|
||||
cxx11_thread_local
|
||||
cxx11_variadic_templates ]
|
||||
: test_spawn_native ] ;
|
||||
: test_callcc_segmented ] ;
|
||||
|
||||
|
||||
test-suite full :
|
||||
|
||||
@@ -1,596 +0,0 @@
|
||||
|
||||
// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
// Copyright (c) 2019 Casey Bodley (cbodley at redhat dot com)
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/context/spawn.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/system_timer.hpp>
|
||||
#include <boost/context/protected_fixedsize_stack.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
// make assertions about async_result::return_type with different signatures
|
||||
// this is a compilation test only
|
||||
|
||||
template< typename Sig>
|
||||
struct yield_result : boost::asio::async_result<boost::context::yield_context, Sig> {};
|
||||
|
||||
template< typename T, typename Sig>
|
||||
struct yield_returns : std::is_same<T, typename yield_result<Sig>::return_type> {};
|
||||
|
||||
// no return value
|
||||
static_assert(yield_returns< void, void() >::value,
|
||||
"wrong return value for void()");
|
||||
static_assert(yield_returns< void, void(boost::system::error_code) >::value,
|
||||
"wrong return value for void(error_code)");
|
||||
// single-parameter return value
|
||||
static_assert(yield_returns<int, void(int) >::value,
|
||||
"wrong return value for void(int)");
|
||||
static_assert(yield_returns<int, void(boost::system::error_code, int) >::value,
|
||||
"wrong return value for void(error_code, int)");
|
||||
// multiple-parameter return value
|
||||
static_assert(yield_returns<std::tuple<int, std::string>,
|
||||
void(int, std::string) >::value,
|
||||
"wrong return value for void(int, string)");
|
||||
static_assert(yield_returns<std::tuple<int, std::string>,
|
||||
void(boost::system::error_code, int, std::string) >::value,
|
||||
"wrong return value for void(error_code, int, string)");
|
||||
// single-tuple-parameter return value
|
||||
static_assert(yield_returns<std::tuple<int, std::string>,
|
||||
void(std::tuple<int, std::string>) >::value,
|
||||
"wrong return value for void(std::tuple<int>)");
|
||||
static_assert(yield_returns<std::tuple<int, std::string>,
|
||||
void(boost::system::error_code, std::tuple<int, std::string>) >::value,
|
||||
"wrong return value for void(error_code, std::tuple<int>)");
|
||||
// single-pair-parameter return value
|
||||
static_assert(yield_returns<std::pair<int, std::string>,
|
||||
void(std::pair<int, std::string>) >::value,
|
||||
"wrong return value for void(std::tuple<int>)");
|
||||
static_assert(yield_returns<std::pair<int, std::string>,
|
||||
void(boost::system::error_code, std::pair<int, std::string>) >::value,
|
||||
"wrong return value for void(error_code, std::tuple<int>)");
|
||||
|
||||
boost::context::protected_fixedsize_stack with_stack_allocator() {
|
||||
return boost::context::protected_fixedsize_stack{ 65536 };
|
||||
}
|
||||
|
||||
struct counting_handler {
|
||||
int & count;
|
||||
|
||||
counting_handler( int & count) :
|
||||
count(count) {
|
||||
}
|
||||
|
||||
void operator()() {
|
||||
++count;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
void operator()( boost::context::basic_yield_context< T >) {
|
||||
++count;
|
||||
}
|
||||
};
|
||||
|
||||
void spawnFunction() {
|
||||
boost::asio::io_context ioc;
|
||||
int called = 0;
|
||||
boost::context::spawn( counting_handler{ called } );
|
||||
BOOST_CHECK_EQUAL(0, ioc.run()); // runs in system executor
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(1, called);
|
||||
}
|
||||
|
||||
void spawnBoundFunction() {
|
||||
boost::asio::io_context ioc;
|
||||
int called = 0;
|
||||
boost::context::spawn( bind_executor( ioc.get_executor(), counting_handler{ called } ) );
|
||||
BOOST_CHECK_EQUAL(1, ioc.run());
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(1, called);
|
||||
}
|
||||
|
||||
void spawnFunctionStackAllocator() {
|
||||
boost::asio::io_context ioc;
|
||||
int called = 0;
|
||||
boost::context::spawn(
|
||||
counting_handler{ called },
|
||||
with_stack_allocator() );
|
||||
BOOST_CHECK_EQUAL(0, ioc.run());
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(1, called);
|
||||
}
|
||||
|
||||
void spawnHandler() {
|
||||
boost::asio::io_context ioc;
|
||||
boost::asio::strand< boost::asio::io_context::executor_type > strand{ ioc.get_executor() };
|
||||
int called = 0;
|
||||
boost::context::spawn(
|
||||
bind_executor( strand, counting_handler{ called } ),
|
||||
counting_handler{ called } );
|
||||
BOOST_CHECK_EQUAL(1, ioc.run());
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(2, called);
|
||||
}
|
||||
|
||||
void spawnHandlerStackAllocator() {
|
||||
boost::asio::io_context ioc;
|
||||
typedef boost::asio::io_context::executor_type executor_type;
|
||||
boost::asio::strand< executor_type > strand{ ioc.get_executor() };
|
||||
int called = 0;
|
||||
boost::context::spawn(
|
||||
bind_executor( strand, counting_handler{ called } ),
|
||||
counting_handler{ called },
|
||||
with_stack_allocator() );
|
||||
BOOST_CHECK_EQUAL(1, ioc.run());
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(2, called);
|
||||
}
|
||||
|
||||
struct spawn_counting_handler {
|
||||
int & count;
|
||||
|
||||
spawn_counting_handler( int & count) :
|
||||
count{ count } {
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
void operator()( boost::context::basic_yield_context< T > y) {
|
||||
boost::context::spawn( y, counting_handler{ count } );
|
||||
++count;
|
||||
}
|
||||
};
|
||||
|
||||
void spawnYieldContext() {
|
||||
boost::asio::io_context ioc;
|
||||
int called = 0;
|
||||
boost::context::spawn(
|
||||
bind_executor( ioc.get_executor(), counting_handler{ called } ),
|
||||
spawn_counting_handler{ called } );
|
||||
BOOST_CHECK_EQUAL(1, ioc.run());
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(3, called);
|
||||
}
|
||||
|
||||
struct spawn_alloc_counting_handler {
|
||||
int & count;
|
||||
|
||||
spawn_alloc_counting_handler( int & count) :
|
||||
count{ count } {
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
void operator()( boost::context::basic_yield_context< T > y) {
|
||||
boost::context::spawn(
|
||||
y,
|
||||
counting_handler{ count },
|
||||
with_stack_allocator() );
|
||||
++count;
|
||||
}
|
||||
};
|
||||
|
||||
void spawnYieldContextStackAllocator() {
|
||||
boost::asio::io_context ioc;
|
||||
int called = 0;
|
||||
boost::context::spawn(
|
||||
bind_executor( ioc.get_executor(), counting_handler{ called } ),
|
||||
spawn_alloc_counting_handler{ called } );
|
||||
BOOST_CHECK_EQUAL(1, ioc.run());
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(3, called);
|
||||
}
|
||||
|
||||
void spawnExecutor() {
|
||||
boost::asio::io_context ioc;
|
||||
int called = 0;
|
||||
boost::context::spawn( ioc.get_executor(), counting_handler{ called } );
|
||||
BOOST_CHECK_EQUAL(1, ioc.run());
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(1, called);
|
||||
}
|
||||
|
||||
void spawnExecutorStackAllocator() {
|
||||
boost::asio::io_context ioc;
|
||||
int called = 0;
|
||||
boost::context::spawn(
|
||||
ioc.get_executor(),
|
||||
counting_handler{ called },
|
||||
with_stack_allocator() );
|
||||
BOOST_CHECK_EQUAL(1, ioc.run());
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(1, called);
|
||||
}
|
||||
|
||||
void spawnStrand() {
|
||||
boost::asio::io_context ioc;
|
||||
typedef boost::asio::io_context::executor_type executor_type;
|
||||
int called = 0;
|
||||
boost::context::spawn(
|
||||
boost::asio::strand< executor_type >{ ioc.get_executor() },
|
||||
counting_handler{ called } );
|
||||
BOOST_CHECK_EQUAL(1, ioc.run());
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(1, called);
|
||||
}
|
||||
|
||||
void spawnStrandStackAllocator() {
|
||||
boost::asio::io_context ioc;
|
||||
typedef boost::asio::io_context::executor_type executor_type;
|
||||
int called = 0;
|
||||
boost::context::spawn(
|
||||
boost::asio::strand< executor_type >{ ioc.get_executor() },
|
||||
counting_handler{ called },
|
||||
with_stack_allocator() );
|
||||
BOOST_CHECK_EQUAL(1, ioc.run());
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(1, called);
|
||||
}
|
||||
|
||||
void spawnExecutionContext() {
|
||||
boost::asio::io_context ioc;
|
||||
int called = 0;
|
||||
boost::context::spawn( ioc, counting_handler{ called } );
|
||||
BOOST_CHECK_EQUAL(1, ioc.run());
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(1, called);
|
||||
}
|
||||
|
||||
void spawnExecutionContextStackAllocator() {
|
||||
boost::asio::io_context ioc;
|
||||
int called = 0;
|
||||
boost::context::spawn(
|
||||
ioc,
|
||||
counting_handler{ called },
|
||||
with_stack_allocator() );
|
||||
BOOST_CHECK_EQUAL(1, ioc.run());
|
||||
BOOST_CHECK(ioc.stopped());
|
||||
BOOST_CHECK_EQUAL(1, called);
|
||||
}
|
||||
|
||||
typedef boost::asio::system_timer timer_type;
|
||||
|
||||
struct spawn_wait_handler {
|
||||
timer_type & timer;
|
||||
|
||||
spawn_wait_handler( timer_type & timer) :
|
||||
timer{ timer } {
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
void operator()( boost::context::basic_yield_context< T > yield) {
|
||||
timer.async_wait( yield);
|
||||
}
|
||||
};
|
||||
|
||||
void spawnTimer() {
|
||||
int called = 0;
|
||||
{
|
||||
boost::asio::io_context ioc;
|
||||
timer_type timer{ ioc, boost::asio::chrono::hours{ 0 } };
|
||||
boost::context::spawn(
|
||||
bind_executor( ioc.get_executor(), counting_handler{ called } ),
|
||||
spawn_wait_handler{ timer } );
|
||||
BOOST_CHECK_EQUAL(2, ioc.run() );
|
||||
BOOST_CHECK( ioc.stopped() );
|
||||
}
|
||||
BOOST_CHECK_EQUAL(1, called);
|
||||
}
|
||||
|
||||
void spawnTimerDestruct() {
|
||||
int called = 0;
|
||||
{
|
||||
boost::asio::io_context ioc;
|
||||
timer_type timer{ ioc, boost::asio::chrono::hours{ 65536 } };
|
||||
boost::context::spawn(
|
||||
bind_executor( ioc.get_executor(), counting_handler{ called } ),
|
||||
spawn_wait_handler{ timer } );
|
||||
BOOST_CHECK_EQUAL(1, ioc.run_one() );
|
||||
BOOST_CHECK(!ioc.stopped() );
|
||||
}
|
||||
BOOST_CHECK_EQUAL(0, called);
|
||||
}
|
||||
|
||||
using boost::system::error_code;
|
||||
|
||||
template< typename Handler, typename ...Args >
|
||||
void post( Handler & h, Args && ...args) {
|
||||
auto ex = boost::asio::get_associated_executor( h);
|
||||
//auto a = boost::asio::get_associated_allocator( h);
|
||||
auto b = std::bind( std::move( h), std::forward< Args >( args) ...);
|
||||
boost::asio::post( ex, std::move( b));
|
||||
//ex.post(std::move(b), a);
|
||||
}
|
||||
|
||||
struct single_tuple_handler {
|
||||
std::tuple< int, std::string > & result;
|
||||
|
||||
void operator()( boost::context::yield_context y) {
|
||||
using Signature = void(std::tuple<int, std::string>);
|
||||
boost::asio::async_completion< boost::context::yield_context, Signature > init{ y };
|
||||
post(
|
||||
init.completion_handler,
|
||||
std::make_tuple( 42, std::string{ "test" } ) );
|
||||
result = init.result.get();
|
||||
}
|
||||
};
|
||||
|
||||
void returnSingleTuple() {
|
||||
boost::asio::io_context ioc;
|
||||
std::tuple< int, std::string > result;
|
||||
boost::context::spawn( ioc, single_tuple_handler{ result } );
|
||||
BOOST_CHECK_EQUAL(2, ioc.poll() );
|
||||
BOOST_CHECK(std::make_tuple(42, std::string{"test"}) == result);
|
||||
}
|
||||
|
||||
struct multiple2_handler {
|
||||
std::tuple<int, std::string>& result;
|
||||
void operator()( boost::context::yield_context y) {
|
||||
using Signature = void(error_code, int, std::string);
|
||||
boost::asio::async_completion< boost::context::yield_context, Signature > init{ y };
|
||||
post(
|
||||
init.completion_handler,
|
||||
error_code{},
|
||||
42,
|
||||
std::string{"test"} );
|
||||
result = init.result.get();
|
||||
}
|
||||
};
|
||||
|
||||
void returnMultiple2() {
|
||||
boost::asio::io_context ioc;
|
||||
std::tuple< int, std::string > result;
|
||||
boost::context::spawn( ioc, multiple2_handler{result});
|
||||
BOOST_CHECK_EQUAL(2, ioc.poll() );
|
||||
BOOST_CHECK(std::make_tuple(42, std::string{"test"}) == result);
|
||||
}
|
||||
|
||||
struct multiple2_with_moveonly_handler {
|
||||
std::tuple< int, std::unique_ptr< int > > & result;
|
||||
|
||||
void operator()( boost::context::yield_context y) {
|
||||
using Signature = void(int, std::unique_ptr<int>);
|
||||
boost::asio::async_completion< boost::context::yield_context, Signature > init{ y };
|
||||
std::unique_ptr< int > ptr{ new int(42) };
|
||||
init.completion_handler( 42, std::move( ptr) );
|
||||
result = init.result.get();
|
||||
}
|
||||
};
|
||||
|
||||
void returnMultiple2MoveOnly() {
|
||||
boost::asio::io_context ioc;
|
||||
std::tuple< int, std::unique_ptr< int > > result;
|
||||
boost::context::spawn( ioc, multiple2_with_moveonly_handler{ result } );
|
||||
BOOST_CHECK_EQUAL(1, ioc.poll() );
|
||||
BOOST_CHECK_EQUAL(42, std::get<0>(result) );
|
||||
BOOST_CHECK(std::get<1>(result) );
|
||||
BOOST_CHECK_EQUAL(42, *std::get<1>(result) );
|
||||
}
|
||||
|
||||
struct multiple3_handler {
|
||||
std::tuple< int, std::string, double > & result;
|
||||
|
||||
void operator()( boost::context::yield_context y) {
|
||||
using Signature = void(error_code, int, std::string, double);
|
||||
boost::asio::async_completion< boost::context::yield_context, Signature > init{ y };
|
||||
post(
|
||||
init.completion_handler,
|
||||
error_code{},
|
||||
42,
|
||||
std::string{"test"},
|
||||
2.0);
|
||||
result = init.result.get();
|
||||
}
|
||||
};
|
||||
|
||||
void returnMultiple3() {
|
||||
boost::asio::io_context ioc;
|
||||
std::tuple< int, std::string, double > result;
|
||||
boost::context::spawn( ioc, multiple3_handler{result});
|
||||
BOOST_CHECK_EQUAL(2, ioc.poll() );
|
||||
BOOST_CHECK(std::make_tuple( 42, std::string{"test"}, 2.0) == result);
|
||||
}
|
||||
|
||||
struct non_default_constructible {
|
||||
non_default_constructible() = delete;
|
||||
non_default_constructible( std::nullptr_t) {
|
||||
}
|
||||
};
|
||||
|
||||
struct non_default_constructible_handler {
|
||||
boost::optional< non_default_constructible > & result;
|
||||
|
||||
void operator()( boost::context::yield_context y) {
|
||||
using Signature = void(non_default_constructible);
|
||||
boost::asio::async_completion< boost::context::yield_context, Signature > init{ y };
|
||||
post(
|
||||
init.completion_handler,
|
||||
non_default_constructible{ nullptr } );
|
||||
result = init.result.get();
|
||||
}
|
||||
};
|
||||
|
||||
void returnNonDefaultConstructible() {
|
||||
boost::asio::io_context ioc;
|
||||
boost::optional< non_default_constructible > result;
|
||||
boost::context::spawn( ioc, non_default_constructible_handler{ result } );
|
||||
BOOST_CHECK_EQUAL(2, ioc.poll() );
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
|
||||
struct multiple_non_default_constructible_handler {
|
||||
boost::optional< std::tuple< int, non_default_constructible > > & result;
|
||||
|
||||
void operator()( boost::context::yield_context y) {
|
||||
using Signature = void(error_code, int, non_default_constructible);
|
||||
boost::asio::async_completion< boost::context::yield_context, Signature > init{ y };
|
||||
post(
|
||||
init.completion_handler,
|
||||
error_code{},
|
||||
42,
|
||||
non_default_constructible{ nullptr } );
|
||||
result = init.result.get();
|
||||
}
|
||||
};
|
||||
|
||||
void returnMultipleNonDefaultConstructible() {
|
||||
boost::asio::io_context ioc;
|
||||
boost::optional< std::tuple< int, non_default_constructible > > result;
|
||||
boost::context::spawn( ioc, multiple_non_default_constructible_handler{ result } );
|
||||
BOOST_CHECK_EQUAL(2, ioc.poll() );
|
||||
BOOST_CHECK(result);
|
||||
}
|
||||
|
||||
struct throwing_handler {
|
||||
template< typename T >
|
||||
void operator()( boost::context::basic_yield_context< T >) {
|
||||
throw std::runtime_error{ "" };
|
||||
}
|
||||
};
|
||||
|
||||
void spawnThrowInHelper() {
|
||||
boost::asio::io_context ioc;
|
||||
boost::context::spawn( ioc, throwing_handler{} );
|
||||
BOOST_CHECK_THROW(ioc.run_one(), std::runtime_error); // spawn->throw
|
||||
}
|
||||
|
||||
struct noop_handler {
|
||||
template< typename T >
|
||||
void operator()( boost::context::basic_yield_context< T >) {
|
||||
}
|
||||
};
|
||||
|
||||
struct throwing_completion_handler {
|
||||
void operator()() {
|
||||
throw std::runtime_error{ "" };
|
||||
}
|
||||
};
|
||||
|
||||
void spawnHandlerThrowInHelper() {
|
||||
boost::asio::io_context ioc;
|
||||
boost::context::spawn(
|
||||
bind_executor( ioc.get_executor(), throwing_completion_handler{} ),
|
||||
noop_handler{} );
|
||||
BOOST_CHECK_THROW( ioc.run_one(), std::runtime_error); // spawn->throw
|
||||
}
|
||||
|
||||
template< typename CompletionToken >
|
||||
auto async_yield( CompletionToken && token) -> BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void() ) {
|
||||
boost::asio::async_completion< CompletionToken, void() > init{ token };
|
||||
boost::asio::post( std::move( init.completion_handler) );
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
struct yield_throwing_handler {
|
||||
template< typename T >
|
||||
void operator()( boost::context::basic_yield_context< T > y) {
|
||||
async_yield( y); // suspend and resume before throwing
|
||||
throw std::runtime_error{ "" };
|
||||
}
|
||||
};
|
||||
|
||||
void spawnThrowAfterYield() {
|
||||
boost::asio::io_context ioc;
|
||||
boost::context::spawn( ioc, yield_throwing_handler{} );
|
||||
BOOST_CHECK_NO_THROW(ioc.run_one() ); // yield_throwing_handler suspend
|
||||
BOOST_CHECK_THROW(ioc.run_one(), std::runtime_error); // resume + throw
|
||||
}
|
||||
|
||||
struct yield_handler {
|
||||
template< typename T >
|
||||
void operator()( boost::context::basic_yield_context< T > y) {
|
||||
async_yield( y);
|
||||
}
|
||||
};
|
||||
|
||||
void spawnHandlerThrowAfterYield() {
|
||||
boost::asio::io_context ioc;
|
||||
boost::context::spawn(
|
||||
bind_executor( ioc.get_executor(), throwing_completion_handler{} ),
|
||||
yield_handler{} );
|
||||
BOOST_CHECK_NO_THROW(ioc.run_one() ); // yield_handler suspend
|
||||
BOOST_CHECK_THROW(ioc.run_one(), std::runtime_error); // resume + throw
|
||||
}
|
||||
|
||||
struct nested_throwing_handler {
|
||||
template< typename T >
|
||||
void operator()( boost::context::basic_yield_context< T > y) {
|
||||
boost::context::spawn( y, throwing_handler{} );
|
||||
}
|
||||
};
|
||||
|
||||
void spawnThrowInNestedHelper() {
|
||||
boost::asio::io_context ioc;
|
||||
boost::context::spawn( ioc, nested_throwing_handler{} );
|
||||
BOOST_CHECK_THROW(ioc.run_one(), std::runtime_error); // spawn->spawn->throw
|
||||
}
|
||||
|
||||
struct yield_nested_throwing_handler {
|
||||
template< typename T >
|
||||
void operator()( boost::context::basic_yield_context< T > y) {
|
||||
async_yield( y); // suspend and resume before spawning
|
||||
boost::context::spawn( y, yield_throwing_handler{} );
|
||||
}
|
||||
};
|
||||
|
||||
void spawnThrowAfterNestedYield() {
|
||||
boost::asio::io_context ioc;
|
||||
boost::context::spawn( ioc, yield_nested_throwing_handler{} );
|
||||
BOOST_CHECK_NO_THROW(ioc.run_one() ); // yield_nested_throwing_handler suspend
|
||||
BOOST_CHECK_NO_THROW(ioc.run_one() ); // yield_throwing_handler suspend
|
||||
BOOST_CHECK_THROW(ioc.run_one(), std::runtime_error); // resume + throw
|
||||
}
|
||||
|
||||
struct yield_throw_after_nested_handler {
|
||||
template< typename T >
|
||||
void operator()( boost::context::basic_yield_context< T > y) {
|
||||
async_yield( y); // suspend and resume before spawning
|
||||
boost::context::spawn( y, yield_handler{} );
|
||||
throw std::runtime_error{ "" };
|
||||
}
|
||||
};
|
||||
|
||||
void spawnThrowAfterNestedSpawn() {
|
||||
boost::asio::io_context ioc;
|
||||
boost::context::spawn( ioc, yield_throw_after_nested_handler() );
|
||||
BOOST_CHECK_NO_THROW(ioc.run_one() ); // yield_throw_after_nested_handler suspend
|
||||
BOOST_CHECK_THROW(ioc.run_one(), std::runtime_error); // resume + throw
|
||||
BOOST_CHECK_EQUAL(1, ioc.poll() ); // yield_handler resume
|
||||
BOOST_CHECK(ioc.stopped() );
|
||||
}
|
||||
|
||||
boost::unit_test::test_suite * init_unit_test_suite( int, char* []) {
|
||||
boost::unit_test::test_suite * test =
|
||||
BOOST_TEST_SUITE("Boost.Context: spawn test suite");
|
||||
test->add( BOOST_TEST_CASE( & spawnFunction) );
|
||||
test->add( BOOST_TEST_CASE( & spawnBoundFunction) );
|
||||
test->add( BOOST_TEST_CASE( & spawnFunctionStackAllocator) );
|
||||
test->add( BOOST_TEST_CASE( & spawnHandler) );
|
||||
test->add( BOOST_TEST_CASE( & spawnHandlerStackAllocator) );
|
||||
test->add( BOOST_TEST_CASE( & spawnYieldContext) );
|
||||
test->add( BOOST_TEST_CASE( & spawnYieldContextStackAllocator) );
|
||||
test->add( BOOST_TEST_CASE( & spawnExecutor) );
|
||||
test->add( BOOST_TEST_CASE( & spawnExecutorStackAllocator) );
|
||||
test->add( BOOST_TEST_CASE( & spawnStrand) );
|
||||
test->add( BOOST_TEST_CASE( & spawnStrandStackAllocator) );
|
||||
test->add( BOOST_TEST_CASE( & spawnExecutionContext) );
|
||||
test->add( BOOST_TEST_CASE( & spawnExecutionContextStackAllocator) );
|
||||
test->add( BOOST_TEST_CASE( & spawnTimer) );
|
||||
test->add( BOOST_TEST_CASE( & spawnTimerDestruct) );
|
||||
test->add( BOOST_TEST_CASE( & returnSingleTuple) );
|
||||
test->add( BOOST_TEST_CASE( & returnMultiple2) );
|
||||
test->add( BOOST_TEST_CASE( & returnMultiple2MoveOnly) );
|
||||
test->add( BOOST_TEST_CASE( & returnMultiple3) );
|
||||
test->add( BOOST_TEST_CASE( & returnNonDefaultConstructible) );
|
||||
test->add( BOOST_TEST_CASE( & returnMultipleNonDefaultConstructible) );
|
||||
test->add( BOOST_TEST_CASE( & spawnThrowInHelper) );
|
||||
test->add( BOOST_TEST_CASE( & spawnHandlerThrowInHelper) );
|
||||
test->add( BOOST_TEST_CASE( & spawnThrowAfterYield) );
|
||||
test->add( BOOST_TEST_CASE( & spawnHandlerThrowAfterYield) );
|
||||
test->add( BOOST_TEST_CASE( & spawnThrowInNestedHelper) );
|
||||
test->add( BOOST_TEST_CASE( & spawnThrowAfterNestedYield) );
|
||||
test->add( BOOST_TEST_CASE( & spawnThrowAfterNestedSpawn) );
|
||||
return test;
|
||||
}
|
||||
Reference in New Issue
Block a user