[exec.bulk]

# 33 Execution control library [[exec]](./#exec)

## 33.9 Senders [[exec.snd]](exec.snd#exec.bulk)

### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.bulk)

#### 33.9.12.11 execution​::​bulk, execution​::​bulk_chunked, and execution​::​bulk_unchunked [exec.bulk]

[1](#1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4109)

bulk, bulk_chunked, and bulk_unchunked run a task repeatedly for every index in an index space[.](#1.sentence-1)

[2](#2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4113)

The names bulk, bulk_chunked, and bulk_unchunked denote pipeable sender adaptor objects[.](#2.sentence-1)

Let *bulk-algo* be eitherbulk, bulk_chunked, or bulk_unchunked[.](#2.sentence-2)

For subexpressions sndr, policy, shape, and f,
letPolicy be remove_cvref_t<decltype(policy)>,Shape be decltype(auto(shape)), andFunc be decay_t<decltype((f))>[.](#2.sentence-3)

If

- [(2.1)](#2.1)

decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), or

- [(2.2)](#2.2)

is_execution_policy_v<Policy> is false, or

- [(2.3)](#2.3)

Shape does not satisfy [integral](concepts.arithmetic#concept:integral "18.4.7 Arithmetic concepts [concepts.arithmetic]"), or

- [(2.4)](#2.4)

Func does not model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]"),

*bulk-algo*(sndr, policy, shape, f) is ill-formed[.](#2.sentence-4)

[3](#3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4136)

Otherwise,
the expression *bulk-algo*(sndr, policy, shape, f) is expression-equivalent to:

transform_sender(*get-domain-early*(sndr), *make-sender*(*bulk-algo*, *product-type*<*see below*, Shape, Func>{policy, shape, f}, sndr)) except that sndr is evaluated only once[.](#3.sentence-2)

The first template argument of *product-type* is Policy if Policy models [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]"), andconst Policy& otherwise[.](#3.sentence-3)

[4](#4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4150)

Let sndr and env be subexpressions such thatSndr is decltype((sndr))[.](#4.sentence-1)

If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, bulk_t> is false, then
the expression bulk.transform_sender(sndr, env) is ill-formed;
otherwise, it is equivalent to:auto [_, data, child] = sndr;auto& [policy, shape, f] = data;auto new_f = [func = std::move(f)](Shape begin, Shape end, auto&&... vs)noexcept(noexcept(f(begin, vs...))) {while (begin != end) func(begin++, vs...);}return bulk_chunked(std::move(child), policy, shape, std::move(new_f));

[*Note [1](#note-1)*:

This causes the bulk(sndr, policy, shape, f) sender to be
expressed in terms of bulk_chunked(sndr, policy, shape, f) when
it is connected to a receiver whose
execution domain does not customize bulk[.](#4.sentence-2)

â *end note*]

[5](#5)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4172)

The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
is specialized for bulk_chunked_t as follows:

[ð](#lib:impls-for%3cbulk_chunked_t%3e)

namespace std::execution {template<>struct *impls-for*<bulk_chunked_t> : *default-impls* {static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*(); };}

The member *impls-for*<bulk_chunked_t>​::​*complete* is initialized with a callable object equivalent to the following lambda:[]<class Index, class State, class Rcvr, class Tag, class... Args>(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")<Tag, set_value_t>) {auto& [policy, shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...)); *TRY-EVAL*(rcvr, [&]() noexcept(nothrow) { f(static_cast<decltype(auto(shape))>(0), auto(shape), args...);
 Tag()(std::move(rcvr), std::forward<Args>(args)...); }()); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}

The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") of the lambda above istrue if and only
if Tag denotes a type other than set_value_t or
if the expression f(auto(shape), auto(shape), args...) is well-formed[.](#5.sentence-3)

[6](#6)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4210)

The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
is specialized for bulk_unchunked_t as follows:namespace std::execution {template<>struct *impls-for*<bulk_unchunked_t> : *default-impls* {static constexpr auto *complete* = *see below*; };}

The member *impls-for*<bulk_unchunked_t>​::​*complete* is initialized with a callable object equivalent to the following lambda:[]<class Index, class State, class Rcvr, class Tag, class... Args>(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_­as [concept.same]")<Tag, set_value_t>) {auto& [shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), args...)); *TRY-EVAL*(rcvr, [&]() noexcept(nothrow) {for (decltype(auto(shape)) i = 0; i < shape; ++i) { f(auto(i), args...); } Tag()(std::move(rcvr), std::forward<Args>(args)...); }()); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}

The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") of the lambda above
is true if and only
if Tag denotes a type other than set_value_t or
if the expression f(auto(shape), args...) is well-formed[.](#6.sentence-3)

[ð](#lib:check-types,impls-for%3cbulk_t%3e)

`template<class Sndr, class... Env>
  static consteval void check-types();
`

[7](#7)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4253)

*Effects*: Equivalent to:auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>();auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {if constexpr (![invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]")<remove_cvref_t<*data-type*<Sndr>>, Ts&...>)throw *unspecified-exception*();};
cs.*for-each*(*overload-set*(fn, [](auto){})); where *unspecified-exception* is
a type derived from exception[.](#7.sentence-1)

[8](#8)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4268)

Let the subexpression out_sndr denote
the result of the invocation*bulk-algo*(sndr, policy, shape, f) or
an object equal to such, and
let the subexpression rcvr denote a receiver
such that the expression connect(out_sndr, rcvr) is well-formed[.](#8.sentence-1)

The expression connect(out_sndr, rcvr) has undefined behavior
unless it creates an asynchronous operation ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) that,
when started:

- [(8.1)](#8.1)

  If sndr has a successful completion, whereargs is a pack of lvalue subexpressions
referring to the value completion result datums of sndr, or
decayed copies of those values if they model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_­constructible [concept.copyconstructible]"),
then:

  * [(8.1.1)](#8.1.1)

      If out_sndr also completes successfully, then:

    +
          [(8.1.1.1)](#8.1.1.1)
          for bulk,
 invokes f(i, args...) for every i of type Shape from 0 to shape;

    +
          [(8.1.1.2)](#8.1.1.2)
          for bulk_unchunked,
 invokes f(i, args...) for every i of type Shape from 0 to shape;
          *Recommended practice*: The underlying scheduler should execute each iteration
 on a distinct execution agent[.](#8.1.1.2.sentence-2)

    +
          [(8.1.1.3)](#8.1.1.3)
          for bulk_chunked,
 invokes f(b, e, args...) zero or more times
 with pairs of b and e of type Shape in range [0, shape],
 such that b<e and
 for every i of type Shape from 0 to shape,
 there is exactly one invocation with a pair b and e,
 such that i is in the range [b, e)[.](#8.1.1.3.sentence-1)

  * [(8.1.2)](#8.1.2)

      If out_sndr completes with set_error(rcvr, eptr), then
 the asynchronous operation may invoke a subset of
 the invocations of f before the error completion handler is called, and eptr is an exception_ptr containing either:
    +
          [(8.1.2.1)](#8.1.2.1)
an exception thrown by an invocation of f, or

    +
          [(8.1.2.2)](#8.1.2.2)
a bad_alloc exception if
 the implementation fails to allocate required resources, or

    +
          [(8.1.2.3)](#8.1.2.3)
an exception derived from runtime_error[.](#8.1.2.sentence-1)

  * [(8.1.3)](#8.1.3)

      If out_sndr completes with set_stopped(rcvr), then
 the asynchronous operation may invoke a subset of
 the invocations of f before the stopped completion handler[.](#8.1.3.sentence-1)

- [(8.2)](#8.2)

  If sndr does not complete with set_value, then
the completion is forwarded to recv[.](#8.2.sentence-1)

- [(8.3)](#8.3)

  For *bulk-algo*,
the parameter policy describes
the manner in which
the execution of the asynchronous operations corresponding to these algorithms
may be parallelized and
the manner in which
they apply f[.](#8.3.sentence-1)
  Permissions and requirements
on parallel algorithm element access functions ([[algorithms.parallel.exec]](algorithms.parallel.exec "26.3.3 Effect of execution policies on algorithm execution"))
apply to f[.](#8.3.sentence-2)

[9](#9)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4358)

[*Note [2](#note-2)*:

The asynchronous operation corresponding to*bulk-algo*(sndr, policy, shape, f) can complete with set_stopped if cancellation is requested or
ignore cancellation requests[.](#9.sentence-1)

â *end note*]
