Skip to content

Commit 7bc448a

Browse files
authored
Merge branch 'main' into fix/read-with-default-connect-noexcept
2 parents 5027b79 + e92245d commit 7bc448a

23 files changed

Lines changed: 828 additions & 134 deletions

.github/workflows/ci.cpu.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,46 @@ jobs:
142142
path: /tmp/sccache*.log
143143
compression-level: 9
144144

145+
valgrind-run-loop:
146+
runs-on: ubuntu-latest
147+
name: CPU (gcc 14, Valgrind run_loop)
148+
container:
149+
options: -u root
150+
image: rapidsai/devcontainers:26.08-cpp-gcc14-cuda12.9
151+
steps:
152+
- name: Checkout stdexec
153+
uses: actions/checkout@v4
154+
with:
155+
persist-credentials: false
156+
157+
- name: Install Valgrind
158+
run: |
159+
apt-get update
160+
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends valgrind
161+
162+
- name: Build run_loop reproducer
163+
run: |
164+
cmake -S . -B build -GNinja \
165+
-DCMAKE_BUILD_TYPE=Debug \
166+
-DCMAKE_CXX_STANDARD=20 \
167+
-DCMAKE_CXX_EXTENSIONS=OFF \
168+
-DSTDEXEC_BUILD_TESTS=ON
169+
cmake --build build --target test.run_loop_finish_repro -v
170+
171+
- name: Run run_loop reproducer under Valgrind
172+
run: |
173+
timeout 60s valgrind \
174+
--fair-sched=no \
175+
--error-exitcode=1 \
176+
--quiet \
177+
./build/test/test.run_loop_finish_repro
178+
145179
ci-cpu:
146180
runs-on: ubuntu-latest
147181
name: CI (CPU)
148182
needs:
149183
- build-cpu
184+
- valgrind-run-loop
150185
steps:
151186
- run: echo "CI (CPU) success"
152187

include/exec/env.hpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,19 @@ namespace experimental::execution
6161
struct __without_t
6262
{
6363
template <class _Env, class _Query>
64-
constexpr auto operator()(_Env&& __env, _Query) const noexcept
64+
requires STDEXEC::__queryable_with<_Env, _Query>
65+
constexpr auto operator()(_Env&& __env, _Query) const
66+
noexcept(STDEXEC::__nothrow_constructible_from<__without<_Env, _Query>, _Env>)
67+
-> __without<_Env, _Query>
6568
{
66-
if constexpr (STDEXEC::__queryable_with<_Env, _Query>)
67-
{
68-
return __without<_Env, _Query>{static_cast<_Env&&>(__env)};
69-
}
70-
else
71-
{
72-
return static_cast<_Env&&>(__env);
73-
}
69+
return __without<_Env, _Query>{static_cast<_Env&&>(__env)};
70+
}
71+
72+
template <class _Env, class _Query>
73+
constexpr auto operator()(_Env&& __env, _Query) const
74+
noexcept(STDEXEC::__nothrow_move_constructible<_Env>) -> _Env
75+
{
76+
return static_cast<_Env&&>(__env);
7477
}
7578
};
7679

include/exec/just_from.hpp

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,17 @@ namespace experimental::execution
3030

3131
namespace detail
3232
{
33-
auto _just_from(just_from_t*) -> STDEXEC::set_value_t;
34-
auto _just_from(just_error_from_t*) -> STDEXEC::set_error_t;
35-
auto _just_from(just_stopped_from_t*) -> STDEXEC::set_stopped_t;
33+
auto _just_from(just_from_t *) -> STDEXEC::set_value_t;
34+
auto _just_from(just_error_from_t *) -> STDEXEC::set_error_t;
35+
auto _just_from(just_stopped_from_t *) -> STDEXEC::set_stopped_t;
3636
} // namespace detail
3737

3838
template <class JustTag>
3939
struct _just_from
4040
{ // NOLINT(bugprone-crtp-constructor-accessibility)
4141
private:
4242
friend JustTag;
43-
using _set_tag_t = decltype(detail::_just_from(static_cast<JustTag*>(nullptr)));
43+
using _set_tag_t = decltype(detail::_just_from(static_cast<JustTag *>(nullptr)));
4444

4545
using _diag_t = STDEXEC::__if_c<STDEXEC_IS_SAME(_set_tag_t, STDEXEC::set_error_t),
4646
AN_ERROR_COMPLETION_MUST_HAVE_EXACTLY_ONE_ERROR_ARGUMENT,
@@ -54,11 +54,11 @@ namespace experimental::execution
5454
struct _probe_fn
5555
{
5656
template <class... Ts>
57-
auto operator()(Ts&&... ts) const noexcept -> _error_t<Ts...>;
57+
auto operator()(Ts &&...ts) const noexcept -> _error_t<Ts...>;
5858

5959
template <class... Ts>
6060
requires STDEXEC::__cmplsigs::__is_compl_sig<_set_tag_t(Ts...)>
61-
auto operator()(Ts&&...) const noexcept -> STDEXEC::completion_signatures<_set_tag_t(Ts...)>
61+
auto operator()(Ts &&...) const noexcept -> STDEXEC::completion_signatures<_set_tag_t(Ts...)>
6262
{
6363
return {};
6464
}
@@ -67,13 +67,13 @@ namespace experimental::execution
6767
template <class Rcvr>
6868
struct _complete_fn
6969
{
70-
Rcvr& _rcvr;
70+
Rcvr &_rcvr;
7171

7272
template <class... Ts>
7373
STDEXEC_ATTRIBUTE(always_inline, host, device)
74-
void operator()(Ts&&... ts) const noexcept
74+
void operator()(Ts &&...ts) const noexcept
7575
{
76-
_set_tag_t()(static_cast<Rcvr&&>(_rcvr), static_cast<Ts&&>(ts)...);
76+
_set_tag_t()(static_cast<Rcvr &&>(_rcvr), static_cast<Ts &&>(ts)...);
7777
}
7878
};
7979

@@ -89,17 +89,17 @@ namespace experimental::execution
8989
{
9090
if constexpr (STDEXEC::__nothrow_callable<Fn, _complete_fn<Rcvr>>)
9191
{
92-
static_cast<Fn&&>(_fn)(_complete_fn<Rcvr>{_rcvr});
92+
static_cast<Fn &&>(_fn)(_complete_fn<Rcvr>{_rcvr});
9393
}
9494
else
9595
{
9696
STDEXEC_TRY
9797
{
98-
static_cast<Fn&&>(_fn)(_complete_fn<Rcvr>{_rcvr});
98+
static_cast<Fn &&>(_fn)(_complete_fn<Rcvr>{_rcvr});
9999
}
100100
STDEXEC_CATCH_ALL
101101
{
102-
STDEXEC::set_error(static_cast<Rcvr&&>(_rcvr), std::current_exception());
102+
STDEXEC::set_error(static_cast<Rcvr &&>(_rcvr), std::current_exception());
103103
}
104104
}
105105
}
@@ -149,15 +149,15 @@ namespace experimental::execution
149149
auto connect(Rcvr rcvr) && noexcept(STDEXEC::__nothrow_decay_copyable<Rcvr, Fn>)
150150
-> _opstate<Rcvr, Fn>
151151
{
152-
return _opstate<Rcvr, Fn>{static_cast<Rcvr&&>(rcvr), static_cast<Fn&&>(_fn)};
152+
return _opstate<Rcvr, Fn>{static_cast<Rcvr &&>(rcvr), static_cast<Fn &&>(_fn)};
153153
}
154154

155155
template <class Rcvr>
156156
STDEXEC_ATTRIBUTE(host, device)
157157
auto connect(Rcvr rcvr) const & noexcept(STDEXEC::__nothrow_decay_copyable<Rcvr, Fn const &>)
158158
-> _opstate<Rcvr, Fn>
159159
{
160-
return _opstate<Rcvr, Fn>{static_cast<Rcvr&&>(rcvr), _fn};
160+
return _opstate<Rcvr, Fn>{static_cast<Rcvr &&>(rcvr), _fn};
161161
}
162162

163163
[[nodiscard]]
@@ -172,17 +172,17 @@ namespace experimental::execution
172172

173173
template <class Rcvr>
174174
STDEXEC_ATTRIBUTE(host, device)
175-
auto submit(Rcvr rcvr) && noexcept -> void
175+
auto submit(Rcvr rcvr) && noexcept(STDEXEC::__nothrow_decay_copyable<Fn>) -> void
176176
{
177-
auto op = static_cast<_sndr_base&&>(*this).connect(static_cast<Rcvr&&>(rcvr));
177+
auto op = static_cast<_sndr_base &&>(*this).connect(static_cast<Rcvr &&>(rcvr));
178178
STDEXEC::start(op);
179179
}
180180

181181
template <class Rcvr>
182182
STDEXEC_ATTRIBUTE(host, device)
183-
auto submit(Rcvr rcvr) const & noexcept -> void
183+
auto submit(Rcvr rcvr) const & noexcept(STDEXEC::__nothrow_decay_copyable<Fn const &>) -> void
184184
{
185-
auto op = this->connect(static_cast<Rcvr&&>(rcvr));
185+
auto op = this->connect(static_cast<Rcvr &&>(rcvr));
186186
STDEXEC::start(op);
187187
}
188188
};
@@ -203,7 +203,7 @@ namespace experimental::execution
203203
"must return an instance of a specialization of " STDEXEC_PP_STRINGIZE(
204204
STDEXEC) "::completion_signatures<>.");
205205
return _sndr<Fn>{
206-
{{}, static_cast<Fn&&>(fn)}
206+
{{}, static_cast<Fn &&>(fn)}
207207
};
208208
}
209209
else

include/exec/libdispatch_queue.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,8 @@ namespace experimental::execution
537537

538538
bulk_op_state(libdispatch_queue &queue, Shape shape, Fun fun, CvSender &&sndr, Receiver rcvr)
539539
: shared_state_(std::move(rcvr), shape, fun)
540-
, inner_op_{STDEXEC::connect(std::move(sndr), bulk_rcvr{shared_state_, queue})}
540+
, inner_op_{
541+
STDEXEC::connect(static_cast<CvSender &&>(sndr), bulk_rcvr{shared_state_, queue})}
541542
{}
542543

543544
void start() & noexcept

include/exec/sequence/merge_each.hpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -383,16 +383,12 @@ namespace experimental::execution
383383
}
384384
void set_break() noexcept
385385
{
386-
switch (__completion_.exchange(__completion_t::__stopped))
386+
auto __expected = __completion_t::__started;
387+
if (__completion_.compare_exchange_strong(__expected, __completion_t::__stopped))
387388
{
388-
case __completion_t::__started:
389-
// We must request stop. When the previous state is __error or __stopped, then stop has
390-
// already been requested.
389+
// We transitioned from started to stopped, so we must request stop. When the state is
390+
// already error or stopped, stop has already been requested.
391391
__nested_stop_.request_stop();
392-
break;
393-
case __completion_t::__stopped:
394-
[[fallthrough]]; // We're already in the "stopped" state. Ignore the break.
395-
case __completion_t::__error:; // We're already in the "error" state. Ignore the break.
396392
}
397393
}
398394

@@ -455,8 +451,9 @@ namespace experimental::execution
455451
}
456452
void error_complete() noexcept override
457453
{
458-
// do not double report error
459-
STDEXEC::set_stopped(static_cast<_Receiver&&>(__rcvr_));
454+
// The error has been delivered as an item. Complete the sequence so the consumer can
455+
// publish it, unless the operation was independently stopped.
456+
exec::__set_value_unless_stopped(static_cast<_Receiver&&>(__rcvr_));
460457
}
461458

462459
void complete_if_none_active() noexcept

include/exec/static_thread_pool.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -930,8 +930,10 @@ namespace experimental::execution
930930
for (std::uint32_t i = 0; i < tasks.size(); ++i)
931931
{
932932
std::uint32_t index = i % this->available_parallelism();
933-
queue.queues_[index].push_front(&tasks[i]);
934-
thread_states_[index]->notify();
933+
if (queue.queues_[index].push_front(&tasks[i]))
934+
{
935+
thread_states_[index]->notify();
936+
}
935937
}
936938
// At this point the calling thread can exit and the pool will take over.
937939
// Ultimately, the last completing thread passes the result forward.

include/exec/when_any.hpp

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,39 +34,39 @@ namespace experimental::execution
3434
template <class _Env>
3535
using __env_t = __join_env_t<prop<get_stop_token_t, inplace_stop_token>, _Env>;
3636

37-
template <class... _Ts>
38-
using __nothrow_decay_copyable_and_move_constructible_t = __mbool<(
39-
(__nothrow_decay_copyable<_Ts> && __nothrow_move_constructible<__decay_t<_Ts>>) && ...)>;
40-
4137
template <class... Args>
4238
using __as_rvalues = set_value_t (*)(__decay_t<Args>...);
4339

4440
template <class... E>
45-
using __as_error = set_error_t (*)(E...);
41+
using __as_error = set_error_t (*)(__decay_t<E>...);
4642

47-
// Here we convert all set_value(Args...) to set_value(__decay_t<Args>...). Note, we keep all
48-
// error types as they are and unconditionally add set_stopped(). The indirection through the
49-
// __completions_fn is to avoid a pack expansion bug in nvc++.
43+
// Here we convert all set_value(Args...) and set_error(Args...) to use decayed arguments and
44+
// unconditionally add set_stopped(). The indirection through the __completions_fn is to avoid
45+
// a pack expansion bug in nvc++.
5046
template <class... _Env>
5147
struct __completions_fn
5248
{
49+
template <class _CvSender>
50+
using __raw_completions_t = __completion_signatures_of_t<_CvSender, __env_t<_Env>...>;
51+
52+
template <class _CvSender>
53+
using __decayed_completions_t =
54+
__transform_reduce_completion_signatures_t<__raw_completions_t<_CvSender>,
55+
__as_rvalues,
56+
__as_error,
57+
set_stopped_t (*)(),
58+
__completion_signature_ptrs_t>;
59+
5360
template <class... _CvSenders>
54-
using __all_value_args_nothrow_decay_copyable =
55-
__minvoke_q<__mand_t,
56-
__value_types_t<__completion_signatures_of_t<_CvSenders, __env_t<_Env>...>,
57-
__qq<__nothrow_decay_copyable_and_move_constructible_t>,
58-
__qq<__mand_t>>...>;
61+
using __all_nothrow_decay_copyable_results =
62+
__mand<__nothrow_decay_copyable_results_t<__raw_completions_t<_CvSenders>>...,
63+
__nothrow_decay_copyable_results_t<__decayed_completions_t<_CvSenders>>...>;
5964

6065
template <class... _CvSenders>
6166
using __f = __mtry_q<__concat_completion_signatures_t>::__f<
62-
__eptr_completion_unless_t<__all_value_args_nothrow_decay_copyable<_CvSenders...>>,
67+
__eptr_completion_unless_t<__all_nothrow_decay_copyable_results<_CvSenders...>>,
6368
completion_signatures<set_stopped_t()>,
64-
__transform_reduce_completion_signatures_t<
65-
__completion_signatures_of_t<_CvSenders, __env_t<_Env>...>,
66-
__as_rvalues,
67-
__as_error,
68-
set_stopped_t (*)(),
69-
__completion_signature_ptrs_t>...>;
69+
__decayed_completions_t<_CvSenders>...>;
7070
};
7171

7272
template <class _Env, class... _CvSenders>

include/nvexec/nvtx.cuh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ namespace nv::execution
110110
static_cast<Self&&>(self).sndr_,
111111
static_cast<Receiver&&>(rcvr),
112112
[&](_strm::opstate_base<Receiver>& stream_provider) -> receiver_t<Receiver>
113-
{ return receiver_t<Receiver>(stream_provider, std::move(self.name_)); });
113+
{ return receiver_t<Receiver>(stream_provider, static_cast<Self&&>(self).name_); });
114114
}
115115
STDEXEC_EXPLICIT_THIS_END(connect)
116116

include/stdexec/__detail/__any.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import stdexec;
4848
STDEXEC_PRAGMA_IGNORE_GNU("-Wredundant-consteval-if")
4949
STDEXEC_PRAGMA_IGNORE_GNU("-Warray-bounds")
5050

51-
// NOLINTBEGIN(moderize-use-override)
51+
// NOLINTBEGIN(modernize-use-override)
5252

5353
STDEXEC_MODULE_EXPORT_AUTHORING
5454
namespace STDEXEC::__any
@@ -2283,7 +2283,7 @@ namespace STDEXEC::__any
22832283

22842284
} // namespace STDEXEC::__any
22852285

2286-
// NOLINTEND(moderize-use-override)
2286+
// NOLINTEND(modernize-use-override)
22872287

22882288
# include "__epilogue.hpp"
22892289
#endif // !STDEXEC_USE_MODULES() || defined(STDEXEC_IN_MODULE_PURVIEW)

include/stdexec/__detail/__bulk.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,10 @@ namespace STDEXEC
281281

282282
STDEXEC_TRY
283283
{
284-
__state.__data_.__fun_(__shape_t(), __shape_t(__state.__data_.__shape_), __args...);
284+
if (__shape_t{} < __state.__data_.__shape_)
285+
{
286+
__state.__data_.__fun_(__shape_t(), __shape_t(__state.__data_.__shape_), __args...);
287+
}
285288
STDEXEC::set_value(static_cast<_State&&>(__state).__rcvr_,
286289
static_cast<_Args&&>(__args)...);
287290
}

0 commit comments

Comments
 (0)