Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.cpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
- { name: "CPU (clang 16, Release, ASAN)", build: "Release", tag: llvm16-cuda12.9, cxxstd: "20", cxxflags: "-stdlib=libc++ -fsanitize=address -fsanitize-ignorelist=/home/coder/stdexec/sanitizer-ignorelist.txt" }
- { name: "CPU (clang 22, Debug)", build: "Debug", tag: llvm22-cuda13.2, cxxstd: "23", cxxflags: "-stdlib=libc++" }
- { name: "CPU (clang 22, Release)", build: "Release", tag: llvm22-cuda13.2, cxxstd: "23", cxxflags: "-stdlib=libc++" }
- { name: "CPU (clang 22, Release, noexcept)", build: "Release", tag: llvm22-cuda13.2, cxxstd: "23", cxxflags: "-stdlib=libc++ -fno-exceptions" }
- { name: "CPU (gcc 12, Debug)", build: "Debug", tag: gcc12-cuda12.9, cxxstd: "20", cxxflags: "", }
- { name: "CPU (gcc 12, Release)", build: "Release", tag: gcc12-cuda12.9, cxxstd: "20", cxxflags: "", }
# With the following config, 2 tests mysteriously time out, but only in CI and not locally.
Expand All @@ -39,6 +40,7 @@ jobs:
- { name: "CPU (gcc 14, Release, LEAK)", build: "Release", tag: gcc14-cuda12.9, cxxstd: "20", cxxflags: "-fsanitize=leak", }
- { name: "CPU (gcc 14, Release, c++23)", build: "Release", tag: gcc14-cuda12.9, cxxstd: "23", cxxflags: "", }
- { name: "CPU (gcc 15, Release, c++23)", build: "Release", tag: gcc15-cuda12.9, cxxstd: "23", cxxflags: "", }
- { name: "CPU (gcc 15, Release, c++23,noexcept)", build: "Release", tag: gcc15-cuda12.9, cxxstd: "23", cxxflags: "-fno-exceptions", }
container:
options: -u root
image: rapidsai/devcontainers:26.06-cpp-${{ matrix.tag }}
Expand Down
13 changes: 11 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,13 @@ endif()
# Additionally, we require GCC-12 or newer for Relacy, at __atomic.hpp has fallback
# when std::atomic<std::shared_ptr<>> is not provided, and GCC-12 is the first
# libstdc++ that provides it.
# Additionally, realacy is not ready for -fno-exceptions.
if(
${STDEXEC_BUILD_TESTS} AND
CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND
CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12 AND
NOT CMAKE_CXX_FLAGS MATCHES "-fsanitize")
NOT CMAKE_CXX_FLAGS MATCHES "-fsanitize" AND
NOT CMAKE_CXX_FLAGS MATCHES "-fno-exceptions")
set(STDEXEC_BUILD_RELACY_TESTS_DEFAULT ON)
else()
set(STDEXEC_BUILD_RELACY_TESTS_DEFAULT OFF)
Expand Down Expand Up @@ -272,9 +274,16 @@ add_library(STDEXEC::stdexec ALIAS stdexec)
# Support target for examples and tests
add_library(stdexec_executable_flags INTERFACE)

# suppress Wunused-parameter in cased of fno-exceptions due to Catch2 bug https://github.com/catchorg/Catch2/issues/3114
if(CMAKE_CXX_FLAGS MATCHES "-fno-exceptions")
set(SUPPRESS_WUNUSED_PARAMETER 1)
else()
set(SUPPRESS_WUNUSED_PARAMETER 0)
endif()

# Enable warnings
target_compile_options(stdexec_executable_flags INTERFACE
$<$<STREQUAL:${stdexec_compiler_frontend},GNU>:-Wall;-Werror=unused-parameter>
$<$<STREQUAL:${stdexec_compiler_frontend},GNU>:-Wall;$<IF:${SUPPRESS_WUNUSED_PARAMETER},-Wno-unused-parameter,-Werror=unused-parameter>>
$<$<STREQUAL:${stdexec_compiler_frontend},AppleClang>:-Wall>
$<$<STREQUAL:${stdexec_compiler_frontend},MSVC>:/W4>)

Expand Down
2 changes: 2 additions & 0 deletions include/exec/asio/asio_thread_pool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#include <exec/asio/asio_config.hpp>

#include "noexcept_boost_throw.hpp"

#include "../thread_pool_base.hpp"

namespace experimental::execution::asio
Expand Down
45 changes: 45 additions & 0 deletions include/exec/asio/noexcept_boost_throw.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2026 NVIDIA Corporation
*
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://llvm.org/LICENSE.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include <exec/asio/asio_config.hpp>
#include <stdexec/__detail/__config.hpp>

/**
* This is a boost::throw_exception implementation provided for fno-exceptions builds
Comment thread
BartolomeyKant marked this conversation as resolved.
*/
#if STDEXEC_ASIO_USES_BOOST && STDEXEC_NO_STDCPP_EXCEPTIONS()

# include <boost/asio/thread_pool.hpp>
# include <boost/throw_exception.hpp>

# include <cstdlib>
# include <exception>

namespace boost
{
void throw_exception(std::exception const &)
{
std::abort();
}

void throw_exception(std::exception const &, boost::source_location const &)
{
std::abort();
}
} // namespace boost
#endif
64 changes: 32 additions & 32 deletions include/stdexec/__detail/__config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -658,38 +658,6 @@ namespace STDEXEC
# define STDEXEC_IF_NOT_CONSTEVAL STDEXEC_IF_CONSTEVAL {} else
#endif

#if defined(STDEXEC_ASSERT)
// nothing to do, user has provided their own assertion macro
#elif defined(STDEXEC_ASSERT_FN)
// legacy way to customize assertions, still supported for backward compatibility
# define STDEXEC_ASSERT(_XP) STDEXEC_ASSERT_FN(_XP)
#else
# define STDEXEC_ASSERT(_XP) \
do \
{ \
STDEXEC_IF_CONSTEVAL \
{ \
if (!(_XP)) \
STDEXEC::__throw_assertion_failure(); \
} \
else \
{ \
assert(_XP); \
} \
} while (false)
#endif

namespace STDEXEC
{
struct __assertion_failure
{};

inline void __throw_assertion_failure()
{
throw __assertion_failure{};
}
} // namespace STDEXEC

#define STDEXEC_AUTO_RETURN(...) \
noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) { \
return __VA_ARGS__; \
Expand Down Expand Up @@ -810,6 +778,38 @@ namespace STDEXEC
}
} // namespace STDEXEC

#if defined(STDEXEC_ASSERT)
// nothing to do, user has provided their own assertion macro
#elif defined(STDEXEC_ASSERT_FN)
// legacy way to customize assertions, still supported for backward compatibility
# define STDEXEC_ASSERT(_XP) STDEXEC_ASSERT_FN(_XP)
#else
# define STDEXEC_ASSERT(_XP) \
do \
{ \
STDEXEC_IF_CONSTEVAL \
{ \
if (!(_XP)) \
STDEXEC::__throw_assertion_failure(); \
} \
else \
{ \
assert(_XP); \
} \
} while (false)
#endif

namespace STDEXEC
{
struct __assertion_failure
{};

inline void __throw_assertion_failure()
{
STDEXEC_THROW(__assertion_failure{});
}
} // namespace STDEXEC

///////////////////////////////////////////////////////////////////////////////
/// To hook a customization point like STDEXEC::connect, define a member
/// function like this:
Expand Down
2 changes: 2 additions & 0 deletions test/exec/asio/test_asio_thread_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ namespace
CHECK(k == 5);
}

#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
TEST_CASE("asio_thread_pool exceptions", "[asio_thread_pool]")
{
using namespace STDEXEC;
Expand All @@ -158,6 +159,7 @@ namespace
CHECK(tbb_result == other_result);
}
}
#endif

TEST_CASE("asio_thread_pool async_inclusive_scan", "[asio_thread_pool]")
{
Expand Down
8 changes: 8 additions & 0 deletions test/exec/asio/test_completion_token.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ namespace
CHECK(ctx.stopped());
}

#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
template <typename CompletionToken>
decltype(auto) async_throw_from_initiation(CompletionToken&& token)
{
Expand Down Expand Up @@ -262,6 +263,7 @@ namespace
CHECK(ctx.stopped());
CHECK(ex);
}
#endif

TEST_CASE("When an operation is abandoned this is reported via a stopped "
"signal",
Expand Down Expand Up @@ -346,6 +348,7 @@ namespace
CHECK(ptr.use_count() == 1U);
}

#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
template <typename Executor, typename CompletionToken>
decltype(auto) async_indirect_completion_handler_throw_from_completion(Executor const & ex,
std::shared_ptr<void>& ptr,
Expand Down Expand Up @@ -417,6 +420,7 @@ namespace
ptr.reset();
CHECK(ex);
}
#endif

template <typename CompletionToken>
decltype(auto) async_multishot(CompletionToken&& token)
Expand Down Expand Up @@ -568,6 +572,7 @@ namespace
b.join();
}

#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
TEST_CASE("When the initiating function posts and then throws, and the "
"posted operation simply abandons the completion handler, the operation "
"completes after the post with the thrown error",
Expand Down Expand Up @@ -695,6 +700,7 @@ namespace
start_shared(std::move(b));
CHECK(ex);
}
#endif

struct value_category_receiver
{
Expand Down Expand Up @@ -978,6 +984,7 @@ namespace
CHECK(!cancelled);
}

#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
TEST_CASE("Upon exception the stop token is no longer in use", "[asioexec][use_sender]")
{
bool cancelled = false;
Expand Down Expand Up @@ -1079,6 +1086,7 @@ namespace
CHECK(ctx.stopped());
CHECK(ex);
}
#endif

TEST_CASE("I/O objects may be transformed to use senders as their default vocabulary with only "
"minimal "
Expand Down
4 changes: 3 additions & 1 deletion test/exec/asio/test_use_sender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ namespace
token);
}

#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
TEST_CASE("Error codes native to the version of Asio used are transformed "
"into a system_error",
"[asioexec][use_sender]")
Expand Down Expand Up @@ -238,6 +239,7 @@ namespace
REQUIRE(ex);
CHECK_THROWS_AS(std::rethrow_exception(std::move(ex)), std::system_error);
}
#endif

TEST_CASE("I/O objects may be transformed to use senders as their default vocabulary",
"[asioexec][use_sender]")
Expand Down Expand Up @@ -270,7 +272,7 @@ namespace
CHECK(ctx.stopped());
}

#if !STDEXEC_NO_STDCPP_COROUTINES()
#if !STDEXEC_NO_STDCPP_COROUTINES() && !STDEXEC_NO_STDCPP_EXCEPTIONS()
template <typename Timer>
::STDEXEC::task<void> test_awaitable_in_stdexec_task(Timer& timer)
{
Expand Down
2 changes: 1 addition & 1 deletion test/exec/sequence/test_ignore_all_values.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ namespace
STDEXEC::completion_signatures_of_t<Sender, STDEXEC::env<>>>);
CHECK_THROWS(STDEXEC::sync_wait(sndr));
}
#endif // !STDEXEC_NO_STDCPP_EXCEPTIONS()

struct sequence_op
{
Expand Down Expand Up @@ -114,4 +113,5 @@ namespace
STDEXEC::set_error_t(std::exception_ptr)>;
STATIC_REQUIRE(STDEXEC::__mset_eq<ExpectedSigs, ActualSigs>);
}
#endif // !STDEXEC_NO_STDCPP_EXCEPTIONS()
} // namespace
16 changes: 9 additions & 7 deletions test/exec/test_any_sender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ STDEXEC_PRAGMA_IGNORE_EDG(deprecated_entity_with_custom_message)
namespace
{
struct get_address_t : ex::__query<get_address_t>
{};
{ };

inline constexpr get_address_t get_address;

Expand Down Expand Up @@ -149,12 +149,12 @@ namespace
ex::set_value(static_cast<receiver_ref &&>(ref), 42);
CHECK(value.value_.index() == 1);
CHECK(std::get<1>(value.value_) == 42);
#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
// Check set error
CHECK(error.value_.index() == 0);
ref = error;
ex::set_error(static_cast<receiver_ref &&>(ref), std::make_exception_ptr(42));
CHECK(error.value_.index() == 2);
#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
// MSVC issues a warning about unreachable code in this block, hence the warning
// suppression at the top of the file.
CHECK_THROWS_AS(std::rethrow_exception(std::get<2>(error.value_)), int);
Expand Down Expand Up @@ -199,7 +199,7 @@ namespace
{
int value = 0;
any_sender_of<ex::set_value_t()> sndr = ex::just(42)
| ex::then([&](int v) noexcept { value = v; });
| ex::then([&](int v) noexcept { value = v; });
CHECK(std::same_as<ex::completion_signatures_of_t<any_sender_of<ex::set_value_t()>>,
ex::completion_signatures<ex::set_value_t()>>);
ex::sync_wait(std::move(sndr));
Expand Down Expand Up @@ -319,7 +319,7 @@ namespace

explicit constexpr stopped_token(bool stopped) noexcept
: stopped_{stopped}
{}
{ }

template <class>
using callback_type = __callback_type;
Expand Down Expand Up @@ -362,7 +362,7 @@ namespace
stopped_receiver(Token token, bool expect_stop)
: stopped_receiver_base<Token>{token}
, expect_stop_{expect_stop}
{}
{ }

bool expect_stop_{false};

Expand Down Expand Up @@ -610,11 +610,13 @@ namespace
auto op = ex::connect(ex::schedule(scheduler), expect_stopped_receiver{});
ex::start(op);
}
#if !STDEXEC_NO_STDCPP_EXCEPTIONS()
scheduler = error_scheduler<>{std::make_exception_ptr(std::logic_error("test"))};
{
auto op = ex::connect(ex::schedule(scheduler), expect_error_receiver<>{});
ex::start(op);
}
#endif // !STDEXEC_NO_STDCPP_EXCEPTIONS()
}

TEST_CASE("any_scheduler sender lifetime", "[types][any_scheduler][any_sender]")
Expand Down Expand Up @@ -799,14 +801,14 @@ namespace
TEST_CASE("any_receiver", "[types][any_sender]")
{
using completions_t = ex::completion_signatures<ex::set_value_t(int),
ex::set_error_t(std::exception_ptr),
ex::set_error_t(std::string_view),
ex::set_stopped_t()>;
using queries_t = exec::queries<ex::inplace_stop_token(ex::get_stop_token_t) noexcept>;

using any_receiver_t = exec::any_receiver<completions_t, queries_t>;
any_receiver_t rcvr = sink<completions_t>{};
std::move(rcvr).set_value(42);
std::move(rcvr).set_error(std::make_exception_ptr(std::runtime_error("error")));
std::move(rcvr).set_error(std::string_view{"error"});
std::move(rcvr).set_stopped();

[[maybe_unused]]
Expand Down
Loading