From 4d7afb8c36398afc1ff674ef50fedb70d8e39c2f Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sun, 18 Oct 2026 17:41:26 +0200 Subject: [PATCH] Fix empty atomic intrusive queue prepend --- .../exec/detail/atomic_intrusive_queue.hpp | 5 ++ test/exec/CMakeLists.txt | 1 + test/exec/test_atomic_intrusive_queue.cpp | 68 +++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 test/exec/test_atomic_intrusive_queue.cpp diff --git a/include/exec/detail/atomic_intrusive_queue.hpp b/include/exec/detail/atomic_intrusive_queue.hpp index b205a7aa97..3fd53d0403 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 1d967bd964..9489a47df5 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 0000000000..279ca90726 --- /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