2
0
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:
Oliver Kowalke
2021-10-16 10:52:47 +02:00
parent cbc3eb1076
commit f409fa8f59
13 changed files with 21 additions and 1855 deletions

View File

@@ -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

View File

@@ -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]

View File

@@ -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">&lt;&lt;</phrase> <phrase role="string">&quot;f1: returned second time: &quot;</phrase> <phrase role="special">&lt;&lt;</phrase> <phrase role="identifier">data</phrase> <phrase role="special">&lt;&lt;</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">([&amp;</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">&amp;&amp;</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">([&amp;</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">&amp;&amp;</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">&lt;&lt;</phrase> <phrase role="string">&quot;f2: entered: &quot;</phrase> <phrase role="special">&lt;&lt;</phrase> <phrase role="identifier">data</phrase> <phrase role="special">&lt;&lt;</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">&quot;Parsed: %c\n&quot;</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">&amp;</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">&lt;</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">&gt;</phrase>
<phrase role="keyword">template</phrase><phrase role="special">&lt;</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">&gt;</phrase>
<phrase role="keyword">auto</phrase> <phrase role="identifier">spawn</phrase><phrase role="special">(</phrase> <phrase role="identifier">Function</phrase> <phrase role="special">&amp;&amp;</phrase> <phrase role="identifier">fn</phrase><phrase role="special">,</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">&amp;&amp;</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">&lt;</phrase><phrase role="identifier">Handler</phrase><phrase
role="special">&gt;)</phrase></code>.
</para>
</listitem>
</varlistentry>
</variablelist>
<programlisting><phrase role="keyword">template</phrase><phrase role="special">&lt;</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">&gt;</phrase>
<phrase role="keyword">auto</phrase> <phrase role="identifier">spawn</phrase><phrase role="special">(</phrase> <phrase role="identifier">Handler</phrase> <phrase role="special">&amp;&amp;</phrase> <phrase role="identifier">hndlr</phrase><phrase role="special">,</phrase> <phrase role="identifier">Function</phrase> <phrase role="special">&amp;&amp;</phrase> <phrase role="identifier">fn</phrase><phrase role="special">,</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">&amp;&amp;</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">&lt;</phrase><phrase role="identifier">Handler</phrase><phrase
role="special">&gt;)</phrase></code>.
</para>
</listitem>
</varlistentry>
</variablelist>
<programlisting><phrase role="keyword">template</phrase><phrase role="special">&lt;</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">&gt;</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">&lt;</phrase> <phrase role="identifier">Handler</phrase> <phrase role="special">&gt;</phrase> <phrase role="identifier">ctx</phrase><phrase role="special">,</phrase> <phrase role="identifier">Function</phrase> <phrase role="special">&amp;&amp;</phrase> <phrase role="identifier">function</phrase><phrase role="special">,</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">&amp;&amp;</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">&lt;</phrase><phrase role="identifier">Handler</phrase><phrase
role="special">&gt;)</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">&lt;</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">&gt;</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">&amp;</phrase> <phrase role="identifier">ex</phrase><phrase role="special">,</phrase> <phrase role="identifier">Function</phrase> <phrase role="special">&amp;&amp;</phrase> <phrase role="identifier">function</phrase><phrase role="special">,</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">&amp;&amp;</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">&lt;</phrase><phrase role="identifier">Handler</phrase><phrase
role="special">&gt;)</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">&lt;</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">&gt;</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">&lt;</phrase> <phrase role="identifier">Executor</phrase> <phrase role="special">&gt;</phrase> <phrase role="keyword">const</phrase><phrase role="special">&amp;</phrase> <phrase role="identifier">ex</phrase><phrase role="special">,</phrase> <phrase role="identifier">Function</phrase> <phrase role="special">&amp;&amp;</phrase> <phrase role="identifier">function</phrase><phrase role="special">,</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">&amp;&amp;</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">&lt;</phrase><phrase role="identifier">Handler</phrase><phrase
role="special">&gt;)</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">&lt;</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">&gt;</phrase>
<phrase role="keyword">auto</phrase> <phrase role="identifier">spawn</phrase><phrase role="special">(</phrase> <phrase role="identifier">ExecutionContext</phrase> <phrase role="special">&amp;</phrase> <phrase role="identifier">ctx</phrase><phrase role="special">,</phrase> <phrase role="identifier">Function</phrase> <phrase role="special">&amp;&amp;</phrase> <phrase role="identifier">function</phrase><phrase role="special">,</phrase> <phrase role="identifier">StackAllocator</phrase> <phrase role="special">&amp;&amp;</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">&lt;</phrase><phrase role="identifier">Handler</phrase><phrase
role="special">&gt;)</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>

View File

@@ -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]

View File

@@ -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
;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 :

View File

@@ -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;
}