<!DOCTYPE html>
<html lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<style>
pre {font-family: "Consolas", "Lucida Console", monospace; margin-left:20pt; }
code {font-family: "Consolas", "Lucida Console", monospace; }
pre > i   { font-family: "Consolas", "Lucida Console", monospace;  font-style:italic; }
code > i  { font-family: "Consolas", "Lucida Console", monospace;  font-style:italic; }
pre > em  { font-family: "Consolas", "Lucida Console", monospace;  font-style:italic; }
code > em { font-family: "Consolas", "Lucida Console", monospace;  font-style:italic; }
dl > dt { font-style:italic; }
body { font-family: "Calibri" }

@media (prefers-color-scheme: dark) {
	body { background: #111; color:  #ccc; }
	a { color:  #38f; }
	a:visited { color:  #a4d; }
	.sect { color:  #ccc; }
    del { text-decoration: line-through; color: #EE9999; }
    ins { text-decoration: underline; color: #99EE99; }
    blockquote.std    { color: #ccc; background-color: #2A2A2A;  border: 1px solid #3A3A3A;  padding-left: 0.5em; padding-right: 0.5em; }
    blockquote.stddel { text-decoration: line-through;  color: #ccc; background-color: #221820;  border: 1px solid #332228;  padding-left: 0.5em; padding-right: 0.5em; ; }
    blockquote.stdins { text-decoration: underline;  color: #ccc; background-color: #182220;  border: 1px solid #223328; padding: 0.5em; }
    table { border: 1px solid #ccc; border-spacing: 0px;  margin-left: auto; margin-right: auto; }
}

@media (prefers-color-scheme: light) {
	body { background:  white; color: black; }
    del { text-decoration: line-through; color: #8B0040; }
    ins { text-decoration: underline; color: #005100; }
    blockquote.std    { color: #000000; background-color: #F1F1F1;  border: 1px solid #D1D1D1;  padding-left: 0.5em; padding-right: 0.5em; }
    blockquote.stddel { text-decoration: line-through;  color: #000000; background-color: #FFEBFF;  border: 1px solid #ECD7EC;  padding-left: 0.5em; padding-right: 0.5em; ; }
    blockquote.stdins { text-decoration: underline;  color: #000000; background-color: #C8FFC8;  border: 1px solid #B3EBB3; padding: 0.5em; }
    table { border: 1px solid black; border-spacing: 1px;  margin-left: auto; margin-right: auto; }
}


.comment em { font-family: "Calibri"; font-style:italic; }
p.example   { margin-left: 2em; }
pre.example { margin-left: 2em; }
div.example { margin-left: 2em; }
div.poll { margin-left: 2em; }

code.extract { background-color: #F5F6A2; }
pre.extract  { margin-left: 2em; background-color: #F5F6A2;  border: 1px solid #E1E28E; }

p.function    { }
.attribute    { margin-left: 2em; }
.attribute dt { float: left; font-style: italic;  padding-right: 1ex; }
.attribute dd { margin-left: 0em; }

.editor { color: #4444BB; font-style: normal; background-color: #DDDDDD; }

tab { padding-left: 4em; }
tab3 { padding-left: 3em; }

.link { float: right; font-family: "Consolas", "Lucida Console", monospace; font-size:80% }


table.header { border: none; border-spacing: 0;  margin-left: 0px; font-style: normal; }
td.header { border: none; border-spacing: 0;  margin-left: 0px; font-style: normal; }
.header { border: none; border-spacing: 0;  margin-left: 0px; font-style: normal; }
table.poll { border: 1px solid black; border-spacing: 0px;  margin-left: 0px; font-style: normal; }

th { text-align: left; vertical-align: top;  padding-left: 0.4em;  /*padding-right: 0.4em; border-bottom:1px dashed;*/ }
td { text-align: left;  padding-left: 0.4em; padding-right: 0.4em; /*border-right:1px dashed; */}
tr { border: solid; border-width: 1px 0; border-bottom:1px solid blue }


.revision   { /*color: #005599;*/ }
.grammar { list-style-type:none }

</style>

<title>Make fiber_context::can_resume() const</title>

</head>
<body>  

<table class="header"><tbody>
  <tr>
    <th>Document number:&nbsp;&nbsp;</th><th> </th><td class="header">P3472R1</td>
  </tr>
  <tr>
    <th>Date:&nbsp;&nbsp;</th><th> </th><td class="header">2025-01-28</td>
  </tr>
  <tr>
    <th>Audience:&nbsp;&nbsp;</th><th> </th><td class="header">LEWG</td>
  </tr>
  <tr>
    <th>Reply-to:&nbsp;&nbsp;</th><th> </th><td class="header">
        <address>Andrzej Krzemieński &lt;akrzemi1 at gmail dot com&gt;</address>
    </td>
  </tr>
</tbody></table>



<h1>Make <code>fiber_context::can_resume()</code> <code>const</code></h1>


<h2>Revision history</h2>

<ul>
<li>R1 &mdash; Added wording (relative to <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p0876r19.pdf"
       title="Oliver Kowalke, Nat Goodspeed, &ldquo;fiber_context - fibers without scheduler&rdquo;">[P0876R19]</a>).</li>
</ul>

<h2>Discussion</h2>


<p> We propose to change the signature of function <code>fiber_context::can_resume()</code>
    so that it is a <code>const</code> member function.       
    </p>

<p> <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p0876r19.pdf"
       title="Oliver Kowalke, Nat Goodspeed, &ldquo;fiber_context - fibers without scheduler&rdquo;">[P0876R19]</a>
    proposes a great library, <code>fiber_context</code>, providing an API for stackful
    context switching, based on 
    <a href="https://www.boost.org/doc/libs/1_86_0/libs/context/doc/html/index.html"
      >Boost.Context</a>.
    It has one unusual design point in its interface. Member function 
    <code>fiber_context::can_resume()</code>, which tells if the given fiber can be resumed
    by the calling thread, which is used as a precondition for other 
    member functions in <code>fiber_context</code> is <em>not</em> declared 
    <code>const</code>, implying that it cannot be called concurrently, without external
    synchronization, from multiple threads.</p>
    
<p> The older revision of the paper, 
    <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0876r6.pdf"
       title="Oliver Kowalke, Nat Goodspeed, &ldquo;fiber_context - fibers without scheduler&rdquo;">[P0876R6]</a>,
    gives the rationale for making this function non-<code>const</code>.
    </p>
    
<blockquote>
<p> <code>can_resume()</code> is not marked <code>const</code> because in at least one 
    implementation, it requires an internal context switch. However, the stack operations
    are effectively read-only. Nonetheless, if it is possible for more than one thread to
    call <code>can_resume()</code> concurrently on the same non-empty 
    <code>std::fiber_context</code> instance, locking is the caller’s responsibility.
    </p>
</blockquote>

<p> Given this potential data race, it is 
    conceptually difficult to accept that it is a <em>predicate</em> suitable for use as a function
    precondition. Further, in the context of the proposed contract support framework (<a href="https://isocpp.org/files/papers/P2900R9.pdf"
       title="Joshua Berne, Timur Doumler, Andrzej Krzemieński et al., &ldquo;Contracts for C++&rdquo;">[P2900R9]</a>),
    function <code>fiber_context::can_resume()</code> would not be usable in contract assertions,
    as they always treat objects used in the predicates as <code>const</code>.
    </p>


<p> We believe that this "not const" provision is not necessary. The functionality of 
    <code>can_resume()</code> can be implemented under a hosted implementation by storing
    the result of <code>std::this_thread::get_id()</code> upon the first fiber resumption
    and comparing it against the result of <code>std::this_thread::get_id()</code> performed
    inside <code>can_resume()</code>. This technique is used in the 
    <a href="https://github.com/boostorg/context/blob/p0876r13/include/boost/context/fiber_context_fcontext.hpp#L326">reference implementation</a>.
    Under a freestanding implementation <code>can_resume()</code> can be implemented as
    <code>!empty()</code>.
    </p>
    
<h2>Proposed wording</h2>

<p> The proposed wording is relative to <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p0876r19.pdf"
       title="Oliver Kowalke, Nat Goodspeed, &ldquo;fiber_context - fibers without scheduler&rdquo;">[P0876R19]</a>,
    which has not yet been added to the Working Paper.
    </p> 

<p> Modify [fiber.context.class] as follows.</p>

<blockquote class="std">
<pre>
namespace std {

class fiber_context {
public:
  <em>// [fiber.context.cons], constructors, move and assignment</em>
  fiber_context() noexcept = default;
  
  template&lt;class F&gt;
  explicit fiber_context(F&amp;&amp; entry);
  
  template&lt;class F, class D&gt;
  fiber_context(F&amp;&amp; entry, span&lt;byte&gt; stack, D&amp;&amp; deleter);

  ~fiber_context();
  
  fiber_context(fiber_context&& other) noexcept;
  fiber_context&amp; operator=(fiber_context&& other) noexcept;

  <em>// [fiber.context.mem], members</em>
  fiber_context resume() &amp;&amp;;
  template&lt;class Fn&gt;
  fiber_context resume_with(Fn&amp;&amp; fn) &amp;&amp;;

  bool can_resume() <ins>const</ins> noexcept;

  explicit operator bool() const noexcept;
  bool empty() const noexcept;

  void swap(fiber_context& other) noexcept;

  <em>// [fiber.context.special], specialized algorithms</em>
  friend void swap(fiber_context&amp; lhs, fiber_context&amp; rhs) noexcept;
  
private:
  void* state = nullptr; <em>// exposition only</em>
};

} <em>// namespace std</em>
</pre>
</blockquote>


<p>Modify the description of function <code>can_resume</code> in section [fiber.context.mem]
   as follows.</p>


<blockquote class="std">
<p><code>
bool can_resume() <ins>const</ins> noexcept ;</code></p>

<p>Returns:</p>
<ul>
<li><code>false</code> if <code>empty()</code> is <code>true</code></li>
<li><code>true</code> if the fiber represented by <code>*this</code> is in the prepared state (has no owning thread)</li>
<li><code>true</code> if the calling thread is the owning thread of the fiber represented by <code>*this</code></li>
<li><code>false</code> otherwise.</li>
</ul>
</blockquote>   
   

<h2>References</h2>


<ul>

<li>[P0876R6] — Oliver Kowalke, Nat Goodspeed,
    "<em>fiber_context</em> - fibers without scheduler", <br>
    (<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0876r6.pdf">"https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0876r6.pdf"</a>).
    </li>
    <!--
    <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0876r6.pdf"
       title="Oliver Kowalke, Nat Goodspeed, &ldquo;fiber_context - fibers without scheduler&rdquo;">[P0876R6]</a>
    -->
    
<li>[P0876R19] — Oliver Kowalke, Nat Goodspeed,
    "<em>fiber_context</em> - fibers without scheduler", <br>
    (<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p0876r19.pdf">"https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p0876r19.pdf"</a>).
    </li>
    <!--
    <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p0876r19.pdf"
       title="Oliver Kowalke, Nat Goodspeed, &ldquo;fiber_context - fibers without scheduler&rdquo;">[P0876R19]</a>
    -->
    
<li>[P2900R9] — Joshua Berne, Timur Doumler, Andrzej Krzemieński et al.,
    "Contracts for C++", <br>
    (<a href="https://isocpp.org/files/papers/P2900R9.pdf">"https://isocpp.org/files/papers/P2900R9.pdf"</a>).
    </li>
    <!--
    <a href="https://isocpp.org/files/papers/P2900R9.pdf"
       title="Joshua Berne, Timur Doumler, Andrzej Krzemieński et al., &ldquo;Contracts for C++&rdquo;">[P2900R9]</a>
    -->

<!--
<li>[N4988] — Thomas Köppe, "Working Draft Programming Languages — C++" <br>
    (<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/n4988.pdf">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/n4988.pdf</a>).
    </li>
    -->
    <!--
    <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/n4988.pdf"
       title="Thomas Köppe, &ldquo;Working Draft Programming Languages — C++&rdquo;">[N4988]</a>
    -->	  

</ul>



</body></html>
