diff --git a/include/exec/detail/atomic_intrusive_queue.hpp b/include/exec/detail/atomic_intrusive_queue.hpp index b205a7aa9..3fd53d040 100644 --- a/include/exec/detail/atomic_intrusive_queue.hpp +++ b/include/exec/detail/atomic_intrusive_queue.hpp @@ -65,6 +65,11 @@ namespace experimental::execution void prepend(STDEXEC::__intrusive_queue<_NextPtr> queue) noexcept { + if (queue.empty()) + { + return; + } + __node_pointer __new_head = queue.front(); __node_pointer __tail = queue.back(); __node_pointer __old_head = __head_.load(STDEXEC::__std::memory_order_relaxed); diff --git a/test/exec/CMakeLists.txt b/test/exec/CMakeLists.txt index 1d967bd96..9489a47df 100644 --- a/test/exec/CMakeLists.txt +++ b/test/exec/CMakeLists.txt @@ -17,6 +17,7 @@ set(exec_test_sources ../test_main.cpp test_bwos_lifo_queue.cpp + test_atomic_intrusive_queue.cpp test_any_sender.cpp test_task.cpp test_timed_thread_scheduler.cpp diff --git a/test/exec/test_atomic_intrusive_queue.cpp b/test/exec/test_atomic_intrusive_queue.cpp new file mode 100644 index 000000000..279ca9072 --- /dev/null +++ b/test/exec/test_atomic_intrusive_queue.cpp @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#include + +#include + +#include + +namespace +{ + struct test_node + { + test_node* next_{nullptr}; + }; + + using test_queue = STDEXEC::__intrusive_queue<&test_node::next_>; + using atomic_queue = exec::__atomic_intrusive_queue<&test_node::next_>; + + TEST_CASE("atomic_intrusive_queue::prepend handles empty input", "[atomic_intrusive_queue]") + { + atomic_queue queue; + queue.prepend(test_queue{}); + CHECK(queue.empty()); + + test_node existing{}; + REQUIRE(queue.push_front(&existing)); + queue.prepend(test_queue{}); + CHECK_FALSE(queue.empty()); + + auto contents = queue.pop_all(); + REQUIRE(contents.pop_front() == &existing); + CHECK(contents.empty()); + } + + TEST_CASE("atomic_intrusive_queue::prepend preserves queue order", "[atomic_intrusive_queue]") + { + atomic_queue queue; + test_node existing{}; + test_node first{}; + test_node second{}; + REQUIRE(queue.push_front(&existing)); + + test_queue incoming; + incoming.push_back(&first); + incoming.push_back(&second); + queue.prepend(std::move(incoming)); + + auto contents = queue.pop_all(); + CHECK(contents.pop_front() == &first); + CHECK(contents.pop_front() == &second); + CHECK(contents.pop_front() == &existing); + CHECK(contents.empty()); + } +} // namespace