diff options
-rw-r--r-- | libstdc++-v3/ChangeLog | 11 | ||||
-rw-r--r-- | libstdc++-v3/src/c++11/random.cc | 26 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/26_numerics/random/random_device/94087.cc | 63 |
3 files changed, 98 insertions, 2 deletions
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index ae1ec875..67ca5b7 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,14 @@ +2020-05-19 Jonathan Wakely <jwakely@redhat.com> + + PR libstdc++/94087 + * src/c++11/random.cc (__x86_rdseed): Allow fallback function to be + passed in. + (__x86_rdseed_rdrand): New function that uses rdseed with rdrand + fallback. + (random_device::_M_init): Use __x86_rdseed_rdrand when both + instructions are available. + * testsuite/26_numerics/random/random_device/94087.cc: New test. + 2020-05-19 Patrick Palka <ppalka@redhat.com> PR c++/66439 diff --git a/libstdc++-v3/src/c++11/random.cc b/libstdc++-v3/src/c++11/random.cc index 236eccf..62ed274 100644 --- a/libstdc++-v3/src/c++11/random.cc +++ b/libstdc++-v3/src/c++11/random.cc @@ -97,7 +97,7 @@ namespace std _GLIBCXX_VISIBILITY(default) #if USE_RDSEED unsigned int __attribute__ ((target("rdseed"))) - __x86_rdseed(void*) + __x86_rdseed(void* fallback) { unsigned int retries = 100; unsigned int val; @@ -105,12 +105,25 @@ namespace std _GLIBCXX_VISIBILITY(default) while (__builtin_ia32_rdseed_si_step(&val) == 0) { if (--retries == 0) - std::__throw_runtime_error(__N("random_device: rdseed failed")); + { + if (auto f = reinterpret_cast<unsigned int(*)(void*)>(fallback)) + return f(nullptr); + std::__throw_runtime_error(__N("random_device: rdseed failed")); + } __builtin_ia32_pause(); } return val; } + +#if USE_RDRAND + unsigned int + __attribute__ ((target("rdseed,rdrnd"))) + __x86_rdseed_rdrand(void*) + { + return __x86_rdseed(reinterpret_cast<void*>(&__x86_rdrand)); + } +#endif #endif #ifdef _GLIBCXX_USE_CRT_RAND_S @@ -205,6 +218,15 @@ namespace std _GLIBCXX_VISIBILITY(default) __cpuid_count(7, 0, eax, ebx, ecx, edx); if (ebx & bit_RDSEED) { +#ifdef USE_RDRAND + // CPUID.01H:ECX.RDRAND[bit 30] + __cpuid(1, eax, ebx, ecx, edx); + if (ecx & bit_RDRND) + { + _M_func = &__x86_rdseed_rdrand; + return; + } +#endif _M_func = &__x86_rdseed; return; } diff --git a/libstdc++-v3/testsuite/26_numerics/random/random_device/94087.cc b/libstdc++-v3/testsuite/26_numerics/random/random_device/94087.cc new file mode 100644 index 0000000..cfcc261 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/random_device/94087.cc @@ -0,0 +1,63 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do run } +// { dg-options "-pthread" } +// { dg-require-effective-target c++11 } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include <random> +#include <memory> +#include <thread> +#include <cstdio> + +bool +random_device_available(const char* token) noexcept +{ + try { + std::random_device dev(token); + return true; + } catch (...) { + std::printf("random_device(\"%s\") not available\n", token); + return false; + } +} + +void read_random_device(const char* token, int iterations) +{ + std::random_device dev(token); + for (int i = 0; i != iterations; ++i) + (void) dev(); +} + +int main() { + std::thread workers[8]; + + // N.B. don't test /dev/random as it might block, and /dev/urandom + // "can incur an appreciable delay when requesting large amounts of data". + for (const char* dev : { "default", "rdrand", "rdseed", "rand_s" }) + { + if (random_device_available(dev)) + { + for (auto& w : workers) + w = std::thread{read_random_device, dev, 1000}; + for (auto& w : workers) + w.join(); + } + } +} |