diff --git a/include/exec/detail/xorshift.hpp b/include/exec/detail/xorshift.hpp index 9371e7920..28a91405d 100644 --- a/include/exec/detail/xorshift.hpp +++ b/include/exec/detail/xorshift.hpp @@ -54,7 +54,7 @@ namespace experimental::execution friend auto operator==(xorshift const &, xorshift const &) -> bool = default; xorshift() - : m_seed(0xc1f651c67c62c6e0ull) + : m_seed(default_seed) {} explicit xorshift(std::random_device &rd) @@ -63,12 +63,16 @@ namespace experimental::execution } explicit xorshift(std::uint64_t seed) - : m_seed(seed) + : m_seed(seed == 0 ? default_seed : seed) {} void seed(std::random_device &rd) { m_seed = std::uint64_t(rd()) << 31 | std::uint64_t(rd()); + if (m_seed == 0) + { + m_seed = default_seed; + } } auto operator()() -> result_type @@ -87,7 +91,8 @@ namespace experimental::execution } private: - std::uint64_t m_seed; + static constexpr std::uint64_t default_seed = 0xc1f651c67c62c6e0ull; + std::uint64_t m_seed; }; } // namespace experimental::execution diff --git a/test/exec/CMakeLists.txt b/test/exec/CMakeLists.txt index 8b24f960b..1d967bd96 100644 --- a/test/exec/CMakeLists.txt +++ b/test/exec/CMakeLists.txt @@ -45,6 +45,7 @@ set(exec_test_sources test_scheduler_concept.cpp test_sequence_senders.cpp test_static_thread_pool.cpp + test_xorshift.cpp test_just_from.cpp test_fork_join.cpp test_cpo_split.cpp diff --git a/test/exec/test_xorshift.cpp b/test/exec/test_xorshift.cpp new file mode 100644 index 000000000..c4c004a80 --- /dev/null +++ b/test/exec/test_xorshift.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026 stdexec contributors + * + * 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 + +TEST_CASE("xorshift does not get stuck with a zero seed", "[xorshift]") +{ + exec::xorshift zero_seed{std::uint64_t{0}}; + exec::xorshift default_seed{}; + + bool produced_nonzero = false; + for (int i = 0; i < 8; ++i) + { + auto const sample = zero_seed(); + CHECK(sample == default_seed()); + produced_nonzero = produced_nonzero || sample != 0; + } + CHECK(produced_nonzero); +} + +TEST_CASE("xorshift preserves nonzero seed behavior", "[xorshift]") +{ + exec::xorshift nonzero_seed{std::uint64_t{1}}; + CHECK(nonzero_seed() == 0xd989bcacU); +}