Skip to content

Commit b3b76ea

Browse files
authored
Merge branch 'main' into use_sender
2 parents 940502e + f0998dd commit b3b76ea

12 files changed

Lines changed: 121 additions & 76 deletions

File tree

cmake/clangd_compile_info.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
2121
# Symlink the compile command output to the source dir, where clangd will find it.
2222
set(compile_commands_file "${CMAKE_BINARY_DIR}/compile_commands.json")
2323
set(compile_commands_link "${CMAKE_SOURCE_DIR}/compile_commands.json")
24-
message(STATUS "Creating symlink from ${compile_commands_link} to ${compile_commands_file}...")
24+
message(STATUS "Creating symlink from \"${compile_commands_file}\" to \"${compile_commands_link}\"...")
2525
stdexec_execute_non_fatal_process(COMMAND
2626
"${CMAKE_COMMAND}" -E rm -f "${compile_commands_link}")
2727
stdexec_execute_non_fatal_process(COMMAND

include/exec/static_thread_pool.hpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ namespace exec {
697697
threads_.reserve(threadCount);
698698

699699
try {
700-
numActive_.store(threadCount << 16, std::memory_order_relaxed);
700+
numActive_.store(threadCount << 16u, std::memory_order_relaxed);
701701
for (std::uint32_t i = 0; i < threadCount; ++i) {
702702
threads_.emplace_back([this, i] { run(i); });
703703
}
@@ -942,28 +942,29 @@ namespace exec {
942942
}
943943

944944
inline void static_thread_pool_::thread_state::set_sleeping() {
945-
pool_->numActive_.fetch_sub(1 << 16, std::memory_order_relaxed);
945+
pool_->numActive_.fetch_sub(1u << 16u, std::memory_order_relaxed);
946946
}
947947

948948
// wakeup a worker thread and maintain the invariant that we always one active thief as long as a potential victim is awake
949949
inline void static_thread_pool_::thread_state::clear_sleeping() {
950-
const std::uint32_t numActive = pool_->numActive_.fetch_add(1 << 16, std::memory_order_relaxed);
950+
const std::uint32_t numActive =
951+
pool_->numActive_.fetch_add(1u << 16u, std::memory_order_relaxed);
951952
if (numActive == 0) {
952953
notify_one_sleeping();
953954
}
954955
}
955956

956957
inline void static_thread_pool_::thread_state::set_stealing() {
957-
const std::uint32_t diff = 1 - (1 << 16);
958+
const std::uint32_t diff = 1u - (1u << 16u);
958959
pool_->numActive_.fetch_add(diff, std::memory_order_relaxed);
959960
}
960961

961962
// put a thief to sleep but maintain the invariant that we always have one active thief as long as a potential victim is awake
962963
inline void static_thread_pool_::thread_state::clear_stealing() {
963-
constexpr std::uint32_t diff = 1 - (1 << 16);
964+
constexpr std::uint32_t diff = 1 - (1u << 16u);
964965
const std::uint32_t numActive = pool_->numActive_.fetch_sub(diff, std::memory_order_relaxed);
965-
const std::uint32_t numVictims = numActive >> 16;
966-
const std::uint32_t numThiefs = numActive & 0xffff;
966+
const std::uint32_t numVictims = numActive >> 16u;
967+
const std::uint32_t numThiefs = numActive & 0xffffu;
967968
if (numThiefs == 1 && numVictims != 0) {
968969
notify_one_sleeping();
969970
}

include/nvexec/stream_context.cuh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ namespace nvexec {
150150
using __id = env;
151151
context_state_t context_state_;
152152

153-
template <class CPO>
154-
auto query(get_completion_scheduler_t<CPO>) const noexcept -> stream_scheduler {
153+
[[nodiscard]]
154+
auto query(get_completion_scheduler_t<set_value_t>) const noexcept -> stream_scheduler {
155155
return stream_scheduler{context_state_};
156156
}
157157
};

include/stdexec/__detail/__basic_sender.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ namespace stdexec {
140140
};
141141

142142
static constexpr auto get_completion_signatures = //
143-
[]<class _Sender>(_Sender&& __sndr, __ignore = {}) noexcept {
143+
[]<class _Sender>(_Sender&&, auto&&...) noexcept {
144144
static_assert(
145145
__mnever<tag_of_t<_Sender>>,
146146
"No customization of get_completion_signatures for this sender tag type.");

include/stdexec/__detail/__completion_signatures.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ namespace stdexec {
4646
concept __valid_completion_signatures = //
4747
__same_as<__ok_t<_Completions>, __msuccess> && __sigs::__is_completion_signatures<_Completions>;
4848

49-
template <class _Sender, class _Env>
49+
template <class _Sender, class... _Env>
5050
using __unrecognized_sender_error = //
51-
__mexception<_UNRECOGNIZED_SENDER_TYPE_<>, _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
51+
__mexception<_UNRECOGNIZED_SENDER_TYPE_<>, _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>...>;
5252
} // namespace stdexec

include/stdexec/__detail/__continues_on.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ namespace stdexec {
6363
}
6464

6565
template <class _Sender, class _Env>
66-
static auto transform_sender(_Sender&& __sndr, const _Env& __env) {
66+
static auto transform_sender(_Sender&& __sndr, const _Env&) {
6767
return __sexpr_apply(static_cast<_Sender&&>(__sndr), __transform_sender_fn());
6868
}
6969
};

include/stdexec/__detail/__env.hpp

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
#include "__tag_invoke.hpp"
2525
#include "__tuple.hpp"
2626

27-
#include <type_traits>
2827
#include <functional> // IWYU pragma: keep for unwrap_reference_t
28+
#include <type_traits>
2929
#include <utility>
3030

3131
STDEXEC_PRAGMA_PUSH()
@@ -211,7 +211,7 @@ namespace stdexec {
211211
struct get_domain_t {
212212
template <class _Ty>
213213
requires tag_invocable<get_domain_t, const _Ty&>
214-
constexpr auto operator()(const _Ty& __ty) const noexcept
214+
constexpr auto operator()(const _Ty&) const noexcept
215215
-> __decay_t<tag_invoke_result_t<get_domain_t, const _Ty&>> {
216216
static_assert(
217217
nothrow_tag_invocable<get_domain_t, const _Ty&>,
@@ -361,8 +361,6 @@ namespace stdexec {
361361
STDEXEC_ATTRIBUTE((nodiscard)) constexpr auto query(_Query) const noexcept -> const _Value& {
362362
return __value;
363363
}
364-
365-
auto operator=(const prop&) -> prop& = delete;
366364
};
367365

368366
template <class _Query, class _Value>
@@ -398,8 +396,6 @@ namespace stdexec {
398396
return tag_invoke(
399397
__q, env::__get_1st<_Query, _Args...>(*this), static_cast<_Args&&>(__args)...);
400398
}
401-
402-
auto operator=(const env&) -> env& = delete;
403399
};
404400

405401
// specialization for two envs to avoid warnings about elided braces
@@ -433,8 +429,6 @@ namespace stdexec {
433429
return tag_invoke(
434430
__q, env::__get_1st<_Query, _Args...>(*this), static_cast<_Args&&>(__args)...);
435431
}
436-
437-
auto operator=(const env&) -> env& = delete;
438432
};
439433

440434
template <class... _Envs>
@@ -461,8 +455,6 @@ namespace stdexec {
461455
auto query(_Key) const noexcept -> const _Value& {
462456
return __value_;
463457
}
464-
465-
auto operator=(const __with&) -> __with& = delete;
466458
};
467459

468460
template <class _Value, class _Tag, class... _Tags>
@@ -494,8 +486,6 @@ namespace stdexec {
494486
-> tag_invoke_result_t<_Tag, __cvref_env_t> {
495487
return tag_invoke(_Tag(), __env_);
496488
}
497-
498-
auto operator=(const __t&) -> __t& = delete;
499489
};
500490
};
501491

@@ -539,8 +529,6 @@ namespace stdexec {
539529
query(_Key) const noexcept(nothrow_tag_invocable<_Key, __cvref_env_t>) -> decltype(auto) {
540530
return tag_invoke(_Key(), __env_);
541531
}
542-
543-
auto operator=(const __t&) -> __t& = delete;
544532
};
545533
};
546534

@@ -573,8 +561,6 @@ namespace stdexec {
573561
-> __call_result_t<const _Fun&, _Tag> {
574562
return __fun_(_Tag());
575563
}
576-
577-
auto operator=(const __from&) -> __from& = delete;
578564
};
579565

580566
template <class _Fun>

include/stdexec/__detail/__senders.hpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "__transform_completion_signatures.hpp"
3131
#include "__transform_sender.hpp"
3232
#include "__type_traits.hpp"
33+
#include <type_traits>
3334

3435
namespace stdexec {
3536
/////////////////////////////////////////////////////////////////////////////
@@ -335,4 +336,54 @@ namespace stdexec {
335336
template <class _Sender>
336337
concept __well_formed_sender = __detail::__well_formed_sender<
337338
__minvoke<__with_default_q<__completion_signatures_of_t, dependent_completions>, _Sender>>;
339+
340+
// Used to report a meaningful error message when the sender_in<Sndr, Env> concept check fails.
341+
template <class _Sender, class... _Env>
342+
auto __diagnose_sender_concept_failure() {
343+
if constexpr (!enable_sender<_Sender>) {
344+
static_assert(
345+
enable_sender<_Sender>,
346+
"The given type is not a sender because stdexec::enable_sender<Sender> is false. Either "
347+
"give the type a nested ::sender_concept typedef that is an alias for stdexec::sender_t, "
348+
"or else specialize the stdexec::enable_sender boolean trait for this type to true.");
349+
} else if constexpr (!__detail::__consistent_completion_domains<_Sender>) {
350+
static_assert(
351+
__detail::__consistent_completion_domains<_Sender>,
352+
"The completion schedulers of the sender do not have consistent domains. This is likely a "
353+
"bug in the sender implementation.");
354+
} else if constexpr (!move_constructible<__decay_t<_Sender>>) {
355+
static_assert(
356+
move_constructible<__decay_t<_Sender>>, //
357+
"The sender type is not move-constructible.");
358+
} else if constexpr (!constructible_from<__decay_t<_Sender>, _Sender>) {
359+
static_assert(
360+
constructible_from<__decay_t<_Sender>, _Sender>,
361+
"The sender cannot be decay-copied. Did you forget a std::move?");
362+
} else {
363+
using _Completions = __completion_signatures_of_t<_Sender, _Env...>;
364+
if constexpr (__same_as<_Completions, __unrecognized_sender_error<_Sender, _Env...>>) {
365+
static_assert(
366+
__mnever<_Completions>,
367+
"The sender type was not able to report its completion signatures when asked. This is "
368+
"either because it lacks the necessary member functions, or because the member functions "
369+
"were ill-formed.\n\nA sender can declare its completion signatures in one of two ways:\n"
370+
" 1. By defining a nested type alias named `completion_signatures` that is a\n"
371+
" specialization of stdexec::completion_signatures<...>.\n"
372+
" 2. By defining a member function named `get_completion_signatures` that returns a\n"
373+
" specialization of stdexec::completion_signatures<...>.");
374+
} else if constexpr (__merror<_Completions>) {
375+
static_assert(
376+
!__merror<_Completions>,
377+
"Trying to compute the sender's completion signatures resulted in an error. See the rest "
378+
"of the compiler diagnostic for clues. Look for the string \"_ERROR_\".");
379+
// MSVC needs more encouragement to print the type of the error.
380+
STDEXEC_MSVC(_Completions __what = 0;)
381+
} else {
382+
static_assert(
383+
__valid_completion_signatures<_Completions>,
384+
"The stdexec::sender_in<Sender, Environment> concept check has failed. This is likely a "
385+
"bug in the sender implementation.");
386+
}
387+
}
388+
}
338389
} // namespace stdexec

include/stdexec/__detail/__sync_wait.hpp

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -178,55 +178,61 @@ namespace stdexec {
178178
_Sender,
179179
__env>>;
180180

181-
#if STDEXEC_EDG()
182-
// It requires some hoop-jumping to get the NVHPC compiler to report a meaningful
183-
// diagnostic for SFINAE failures.
184-
template <class _Sender>
185-
auto __diagnose_error() {
186-
if constexpr (!sender_in<_Sender, __env>) {
187-
using _Completions = __completion_signatures_of_t<_Sender, __env>;
188-
if constexpr (__merror<_Completions>) {
189-
return _Completions();
190-
} else {
191-
constexpr __mstring __diag =
192-
"The stdexec::sender_in<Sender, Environment> concept check has failed."_mstr;
193-
return __sync_wait_error<__diag, _Sender>();
194-
}
195-
} else if constexpr (!__valid_sync_wait_argument<_Sender>) {
196-
return __sync_wait_error<__too_many_successful_completions_diag, _Sender>();
197-
} else if constexpr (!sender_to<_Sender, __sync_receiver_for_t<_Sender>>) {
198-
constexpr __mstring __diag =
199-
"Failed to connect the given sender to sync_wait's internal receiver. "
200-
"The stdexec::connect(Sender, Receiver) expression is ill-formed."_mstr;
201-
return __sync_wait_error<__diag, _Sender>();
202-
} else {
203-
constexpr __mstring __diag = "Unknown concept check failure."_mstr;
204-
return __sync_wait_error<__diag, _Sender>();
205-
}
206-
}
207-
208-
template <class _Sender>
209-
using __error_description_t = decltype(__sync_wait::__diagnose_error<_Sender>());
210-
#endif
211-
212181
////////////////////////////////////////////////////////////////////////////
213182
// [execution.senders.consumers.sync_wait]
214183
struct sync_wait_t {
215-
template <sender_in<__env> _Sender>
216-
requires __valid_sync_wait_argument<_Sender>
217-
&& __has_implementation_for<sync_wait_t, __early_domain_of_t<_Sender>, _Sender>
218-
auto operator()(_Sender&& __sndr) const -> std::optional<__value_tuple_for_t<_Sender>> {
219-
using _Domain = __late_domain_of_t<_Sender, __env>;
220-
return stdexec::apply_sender(_Domain(), *this, static_cast<_Sender&&>(__sndr));
184+
template <class _Sender>
185+
auto operator()(_Sender&& __sndr) const {
186+
if constexpr (!sender_in<_Sender, __env>) {
187+
stdexec::__diagnose_sender_concept_failure<_Sender, __env>();
188+
} else {
189+
using __domain_t = __late_domain_of_t<_Sender, __env>;
190+
constexpr auto __success_completion_count =
191+
__v<value_types_of_t<_Sender, __env, __types, __msize::__f>>;
192+
static_assert(
193+
__success_completion_count != 0,
194+
"The argument to stdexec::sync_wait() is a sender that cannot complete successfully. "
195+
"stdexec::sync_wait() requires a sender that can complete successfully in exactly one "
196+
"way. In other words, the sender's completion signatures must include exactly one "
197+
"signature of the form `set_value_t(value-types...)`.");
198+
static_assert(
199+
__success_completion_count <= 1,
200+
"The sender passed to stdexec::sync_wait() can complete successfully in "
201+
"more than one way. Use stdexec::sync_wait_with_variant() instead.");
202+
if constexpr (1 == __success_completion_count) {
203+
using __sync_wait_receiver = __receiver_t<_Sender>;
204+
constexpr bool __no_custom_sync_wait = __same_as<__domain_t, default_domain>;
205+
if constexpr (__no_custom_sync_wait && sender_to<_Sender, __sync_wait_receiver>) {
206+
// using __connect_result = connect_result_t<_Sender, __sync_wait_receiver>;
207+
// if constexpr (!operation_state<__connect_result>) {
208+
// static_assert(
209+
// operation_state<__connect_result>,
210+
// "The `connect` member function of the sender passed to stdexec::sync_wait() does "
211+
// "not return an operation state. An operation state is required to have a "
212+
// "no-throw .start() member function.");
213+
// } else
214+
{
215+
// success path, dispatch to the default domain's sync_wait
216+
return default_domain().apply_sender(*this, static_cast<_Sender&&>(__sndr));
217+
}
218+
} else if constexpr (__no_custom_sync_wait) {
219+
static_assert(
220+
sender_to<_Sender, __sync_wait_receiver>,
221+
"The sender passed to stdexec::sync_wait() does not have a .connect(<receiver>) "
222+
"member function that accepts sync_wait's receiver.");
223+
} else if constexpr (!__has_implementation_for<sync_wait_t, __domain_t, _Sender>) {
224+
static_assert(
225+
__has_implementation_for<sync_wait_t, __domain_t, _Sender>,
226+
"The sender passed to stdexec::sync_wait() has a domain that does not provide a "
227+
"usable implementation for sync_wait().");
228+
} else {
229+
// success path, dispatch to the custom domain's sync_wait
230+
return stdexec::apply_sender(__domain_t(), *this, static_cast<_Sender&&>(__sndr));
231+
}
232+
}
233+
}
221234
}
222235

223-
#if STDEXEC_EDG()
224-
// This is needed to get sensible diagnostics from nvc++
225-
template <class _Sender, class _Error = __error_description_t<_Sender>>
226-
auto operator()(_Sender&&, [[maybe_unused]] _Error __diagnostic = {}) const
227-
-> std::optional<std::tuple<int>> = delete;
228-
#endif
229-
230236
// clang-format off
231237
/// @brief Synchronously wait for the result of a sender, blocking the
232238
/// current thread.

test/stdexec/algos/adaptors/test_then.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ namespace {
184184
struct then_test_domain {
185185
template <class Sender, class... Env>
186186
requires std::same_as<ex::tag_of_t<Sender>, ex::then_t>
187-
static auto transform_sender(Sender&& sndr, Env&&...) {
187+
static auto transform_sender(Sender&&, Env&&...) {
188188
return ex::just(std::string{"ciao"});
189189
}
190190
};

0 commit comments

Comments
 (0)