<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 2932: Constraints on parallel algorithm implementations are underspecified</title>
<meta property="og:title" content="Issue 2932: Constraints on parallel algorithm implementations are underspecified">
<meta property="og:description" content="C++ library issue. Status: C++20">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue2932.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#C++20">C++20</a> status.</em></p>
<h3 id="2932"><a href="lwg-defects.html#2932">2932</a>. Constraints on parallel algorithm implementations are underspecified</h3>
<p><b>Section:</b> 26.3.3 <a href="https://wg21.link/algorithms.parallel.exec">[algorithms.parallel.exec]</a> <b>Status:</b> <a href="lwg-active.html#C++20">C++20</a>
 <b>Submitter:</b> Dietmar K&uuml;hl <b>Opened:</b> 2017-02-05 <b>Last modified:</b> 2021-02-25</p>
<p><b>Priority: </b>Not Prioritized
</p>
<p><b>View all other</b> <a href="lwg-index.html#algorithms.parallel.exec">issues</a> in [algorithms.parallel.exec].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#C++20">C++20</a> status.</p>
<p><b>Discussion:</b></p>
<p>
Section 26.3.3 <a href="https://wg21.link/algorithms.parallel.exec">[algorithms.parallel.exec]</a> specifies constraints a user of the parallel algorithms has to obey. Notably, 
it specifies in paragraph 3 that executions of element access functions are indeterminately sequenced with respect to each other. Correspondingly, it is the user's obligation to ensure that these calls do not introduce data races (this is also clarified in 
a note on this section).
<p/>
Unfortunately, there is no constraint that, at least, mutating element access functions like <code>operator++()</code> on an 
iterator are called on different objects. An odd implementation of a parallel algorithm could increment a shared iterator 
from two threads without synchronisation of its own and the user would be obliged to make sure there is no data race!
<p/>
For example:
</p>
<blockquote><pre>
template &lt;typename FwdIt&gt;
FwdIt adjacent_find(std::execution::parallel_policy, FwdIt it, FwdIt end) 
{
  if (2 &lt;= std::distance(it, end)) {
    FwdIt tmp(it);
    auto f1 = std::async([&amp;tmp](){ ++tmp; });
    auto f2 = std::async([&amp;tmp](){ ++tmp; });
    f1.get();
    f2.get();
  }
  return std::adjancent_find(it, end);
}
</pre></blockquote>
<p>
This code is, obviously, a contrived example but with the current specification a legal implementation of <code>adjacent_find()</code>. 
The problem is that, e.g., for pointers there is a data race when incrementing <code>tmp</code>, i.e., the function can't be used 
on pointers. I don't think any of the containers makes a guarantee that their iterators can be incremented without synchronisation, 
i.e., the standard library doesn't have any iterators which could be used with this algorithm!
<p/>
Of course, no sane implementation would do anything like that. However, they are allowed to be implemented as above, making it unnecessarily hard and probably inefficient to use the algorithms: in portable code any user of the parallel algorithms needs 
to deal with the possibility that mutating operations on the same object are called from different threads. There is a constraint 
missing for the parallel algorithm implementations which limits calls to, at least, some element access functions to be applied 
only to different objects if there is synchronisation done by the algorithm. At least, obviously mutating operations like the 
iterator increment/decrement operators need to be correspondingly constrained.
<p/>
On the other hand, it seems reasonable that a shared data structure stores, e.g., a predicate used concurrently by all threads. 
This use is quite reasonable and there is nothing wrong. That is, demanding that all element access functions are called on different objects between different threads would possibly adversely over-constraining the algorithms. Only the element access functions deliberately changing the object need to be constrained.
<p/>
Based on checking all algorithms in the standard library taking an <code>ExecutionPolicy</code> template parameter there are broadly 
four groups of template parameters:
</p>
<ol>
<li><p>Parameters with a known set of possible arguments: <code>ExecutionPolicy</code> (execution policies listed in 
26.3.6 <a href="https://wg21.link/execpol">[execpol]</a>).</p></li>
<li><p>Parameters specifying types of objects which are expected not to change: <code>BinaryOperation</code>, <code>BinaryPredicate</code>,
<code>Compare</code>, <code>Function</code>, <code>Predicate</code>, <code>UnaryFunction</code>, <code>UnaryOperation</code>, and <code>T</code> (all but 
the last one are function objects although I couldn't locate concepts for some of them &mdash; that may be a separate issue).</p></li>
<li><p>Parameters of mutable types which are also meant to be mutated: <code>InputIterator</code>, <code>OutputIterator</code>, 
<code>ForwardIterator</code>, <code>BidirectionalIterator</code>, <code>RandomAccessIterator</code>, and <code>Size</code> (the concept for 
<code>Size</code> also seems to be unspecified).</p></li>
<li><p>Some algorithms use <code>Generator</code> which seems to be a mutable function object. However, I couldn't locate a concept 
for this parameter.</p></li>
</ol>
<p>
The problematic group is 3 and possibly 4: mutations on the objects are expected. It seem the general approach of disallowing 
calling non-const functions without synchronisation applies. Note, however, that prohibiting calling of any non-const function 
from the algorithms would put undue burden on the implementation of algorithms: any of the accessor functions may be non-const 
although the concept assume that the function would be const. The constraint should, thus, only apply to functions which may 
mutate the object according to their respective concept.
</p>
<p>
Suggested Resolution:
</p>
<p>
Add a statement prohibiting unsequenced calls to element access functions on the same object which are not applicable to 
const objects according to the corresponding concept. I'm not sure how to best specify the constraint in general, though.
<p/>
Since the current algorithms use relatively few concepts there are fairly few operations actually affected. It may be 
reasonable at least for the initial version (and until we could refer to constructs in concepts in the language) to 
explicitly list the affected operations. I haven't done a full audit but iterator <code>++</code>, <code>--</code>, <code>@=</code> (for 
<code>@</code> being any of the operators which can be combined with an assignment), and assignments on all objects may be 
the set of affected element access functions whose use needs to be constrained.
<p/>
Here is a concrete proposal for the change: In 26.3.2 <a href="https://wg21.link/algorithms.parallel.user">[algorithms.parallel.user]</a> add a paragraph:
<p/>
Parallel algorithms are constrained when calling mutating element access functions without synchronisation: if any mutating 
element access function is called on an object there shall be no other unsynchronised accesses to this object. The mutating 
element access functions are those which are specified as mutating object in the concept, notably assignment on any object, 
operators <code>++</code>, <code>--</code>, <code>+=</code>, and <code>-=</code> on any of the iterator or <code>Size</code> parameters, and 
any <code>@=</code> operators on the <code>Size</code> parameters.
</p>

<p><strong>Previous resolution [SUPERSEDED]:</strong></p>
<blockquote class="note">
<p>This wording is relative to <a href="https://wg21.link/n4640">N4640</a>.</p>

<ol>
<li><p>Modify 26.3.2 <a href="https://wg21.link/algorithms.parallel.user">[algorithms.parallel.user]</a> as indicated:</p>
<blockquote>
<p>
-1- Function objects passed into parallel algorithms as objects of type <code>Predicate</code>, <code>BinaryPredicate</code>, 
<code>Compare</code>, and <code>BinaryOperation</code> shall not directly or indirectly modify objects via their arguments.
<p/>
<ins>-?- Parallel algorithms are constrained when calling mutating element access functions without synchronisation: If 
any mutating element access function is called on an object there shall be no other unsynchronised accesses to this object. 
The mutating element access functions are those which are specified as mutating object in the concept, notably assignment 
on any object, operators <code>++</code>, <code>--</code>, <code>+=</code>, and <code>-=</code> on any of the iterator or <code>Size</code> 
parameters, and any <code>@=</code> operators on the <code>Size</code> parameters.</ins>
</p>
</blockquote>
</li>

</ol>
</blockquote>

<p><i>[2017-03-03, Kona]</i></p>

<p>
Dietmar provides improved wording. Issues with the PR before the change:
</p>
<ul>
<li><p>The part before the colon is redundant: we don't need to state that.</p></li>
<li><p>Replace "notably" with "specifically"</p></li>
<li><p><code>swap()</code> needs to be in the list.</p></li>
<li><p>Not sure what "called on an object means"</p></li>
<li><p>The assignment side is overconstrained: the right hand side is allowed.</p></li>
</ul>

<p><strong>Previous resolution [SUPERSEDED]:</strong></p>
<blockquote class="note">
<p>This wording is relative to <a href="https://wg21.link/n4640">N4640</a>.</p>

<ol>
<li><p>Modify 26.3.2 <a href="https://wg21.link/algorithms.parallel.user">[algorithms.parallel.user]</a> as indicated:</p>
<blockquote>
<p>
-1- Function objects passed into parallel algorithms as objects of type <code>Predicate</code>, <code>BinaryPredicate</code>, 
<code>Compare</code>, and <code>BinaryOperation</code> shall not directly or indirectly modify objects via their arguments.
<p/>
<ins>-?- If an object is mutated by an element access function the algorithm will perform no other unsynchronized 
accesses to that object. The mutating element access functions are those which are specified as mutating the object 
in the relevant concept, such as <code>swap()</code>, <code>++</code>, <code>--</code>, <code>@=</code>, and assignments. For the assignment 
and <code>@=</code> operators only the left argument is mutated.</ins>
</p>
</blockquote>
</li>

</ol>
</blockquote>

<p><i>[2017-03-03, Kona]</i></p>

<p>Dietmar finetunes wording after review by SG1.</p>

<p><i>[2017-03-03, Kona]</i></p>

<p>Move to Ready</p>


<p id="res-2932"><b>Proposed resolution:</b></p>
<p>This wording is relative to <a href="https://wg21.link/n4640">N4640</a>.</p>

<ol>
<li><p>Add a new paragraph following 26.3.3 <a href="https://wg21.link/algorithms.parallel.exec">[algorithms.parallel.exec]</a> p1 as indicated:</p>
<blockquote>
<p>
-1- Parallel algorithms have template parameters named <code>ExecutionPolicy</code> (20.19) which describe the manner in
which the execution of these algorithms may be parallelized and the manner in which they apply the element
access functions.
<p/>
<ins>-?- If an object is modified by an element access function the algorithm will perform no other unsynchronized accesses 
to that object. The modifying element access functions are those which are specified as modifying the object in the relevant 
concept [<i>Note:</i> For example, <code>swap()</code>, <code>++</code>, <code>--</code>, <code>@=</code>, and assignments modify the object. 
For the assignment and <code>@=</code> operators only the left argument is modified. &mdash; <i>end note</i>].</ins>
<p/>
-2- [&hellip;]
</p>
</blockquote>
</li>

</ol>






</body>
</html>
