Skip to content

Commit 9518708

Browse files
author
MarcoFalke
committed
Merge bitcoin#18781: Add templated GetRandDuration<>
0000ea3 test: Add test for GetRandMillis and GetRandMicros (MarcoFalke) fa0e5b8 Add templated GetRandomDuration<> (MarcoFalke) Pull request description: A naive implementation of this template is dangerous, because the call site might accidentally omit the template parameter: ```cpp template <typename D> D GetRandDur(const D& duration_max) { return D{GetRand(duration_max.count())}; } BOOST_AUTO_TEST_CASE(util_time_GetRandTime) { std::chrono::seconds rand_hour = GetRandDur(std::chrono::hours{1}); // Want seconds to be in range [0..1hour), but always get zero :(((( BOOST_CHECK_EQUAL(rand_hour.count(), 0); } ``` Luckily `std::common_type` is already specialised in the standard lib for `std::chrono::duration` (https://en.cppreference.com/w/cpp/chrono/duration/common_type). And its effect seem to be that the call site must always specify the template argument explicitly. So instead of implementing the function for each duration type by hand, replace it with a templated version that is safe to use. ACKs for top commit: laanwj: Code review ACK 0000ea3 promag: Code review ACK 0000ea3. jonatack: ACK 0000ea3 thanks for the improved documentation. Code review, built, ran `src/test/test_bitcoin -t random_tests -l test_suite` for the new unit tests, `git diff fa05a4c 0000ea3` since previous review: hebasto: ACK 0000ea3 with non-blocking [nit](bitcoin#18781 (comment)). Tree-SHA512: e89d46e31452be6ea14269ecbbb2cdd9ae83b4412cd14dff7d1084283092722a2f847cb501e8054394e4a3eff852f9c87f6d694fd008b3f7e8458cb5a3068af7
2 parents e2f6866 + 0000ea3 commit 9518708

File tree

3 files changed

+21
-18
lines changed

3 files changed

+21
-18
lines changed

src/random.cpp

+2-14
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,14 @@
1414
#include <wincrypt.h>
1515
#endif
1616
#include <logging.h> // for LogPrintf()
17+
#include <randomenv.h>
18+
#include <support/allocators/secure.h>
1719
#include <sync.h> // for Mutex
1820
#include <util/time.h> // for GetTimeMicros()
1921

2022
#include <stdlib.h>
2123
#include <thread>
2224

23-
#include <randomenv.h>
24-
25-
#include <support/allocators/secure.h>
26-
2725
#ifndef WIN32
2826
#include <fcntl.h>
2927
#include <sys/time.h>
@@ -590,16 +588,6 @@ uint64_t GetRand(uint64_t nMax) noexcept
590588
return FastRandomContext(g_mock_deterministic_tests).randrange(nMax);
591589
}
592590

593-
std::chrono::microseconds GetRandMicros(std::chrono::microseconds duration_max) noexcept
594-
{
595-
return std::chrono::microseconds{GetRand(duration_max.count())};
596-
}
597-
598-
std::chrono::milliseconds GetRandMillis(std::chrono::milliseconds duration_max) noexcept
599-
{
600-
return std::chrono::milliseconds{GetRand(duration_max.count())};
601-
}
602-
603591
int GetRandInt(int nMax) noexcept
604592
{
605593
return GetRand(nMax);

src/random.h

+14-2
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,21 @@
6767
* Thread-safe.
6868
*/
6969
void GetRandBytes(unsigned char* buf, int num) noexcept;
70+
/** Generate a uniform random integer in the range [0..range). Precondition: range > 0 */
7071
uint64_t GetRand(uint64_t nMax) noexcept;
71-
std::chrono::microseconds GetRandMicros(std::chrono::microseconds duration_max) noexcept;
72-
std::chrono::milliseconds GetRandMillis(std::chrono::milliseconds duration_max) noexcept;
72+
/** Generate a uniform random duration in the range [0..max). Precondition: max.count() > 0 */
73+
template <typename D>
74+
D GetRandomDuration(typename std::common_type<D>::type max) noexcept
75+
// Having the compiler infer the template argument from the function argument
76+
// is dangerous, because the desired return value generally has a different
77+
// type than the function argument. So std::common_type is used to force the
78+
// call site to specify the type of the return value.
79+
{
80+
assert(max.count() > 0);
81+
return D{GetRand(max.count())};
82+
};
83+
constexpr auto GetRandMicros = GetRandomDuration<std::chrono::microseconds>;
84+
constexpr auto GetRandMillis = GetRandomDuration<std::chrono::milliseconds>;
7385
int GetRandInt(int nMax) noexcept;
7486
uint256 GetRandHash() noexcept;
7587

src/test/random_tests.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
2828
for (int i = 10; i > 0; --i) {
2929
BOOST_CHECK_EQUAL(GetRand(std::numeric_limits<uint64_t>::max()), uint64_t{10393729187455219830U});
3030
BOOST_CHECK_EQUAL(GetRandInt(std::numeric_limits<int>::max()), int{769702006});
31+
BOOST_CHECK_EQUAL(GetRandMicros(std::chrono::hours{1}).count(), 2917185654);
32+
BOOST_CHECK_EQUAL(GetRandMillis(std::chrono::hours{1}).count(), 2144374);
3133
}
3234
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
3335
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
@@ -47,6 +49,8 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
4749
for (int i = 10; i > 0; --i) {
4850
BOOST_CHECK(GetRand(std::numeric_limits<uint64_t>::max()) != uint64_t{10393729187455219830U});
4951
BOOST_CHECK(GetRandInt(std::numeric_limits<int>::max()) != int{769702006});
52+
BOOST_CHECK(GetRandMicros(std::chrono::hours{1}) != std::chrono::microseconds{2917185654});
53+
BOOST_CHECK(GetRandMillis(std::chrono::hours{1}) != std::chrono::milliseconds{2144374});
5054
}
5155
{
5256
FastRandomContext ctx3, ctx4;
@@ -87,7 +91,7 @@ BOOST_AUTO_TEST_CASE(stdrandom_test)
8791
BOOST_CHECK(x >= 3);
8892
BOOST_CHECK(x <= 9);
8993

90-
std::vector<int> test{1,2,3,4,5,6,7,8,9,10};
94+
std::vector<int> test{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
9195
std::shuffle(test.begin(), test.end(), ctx);
9296
for (int j = 1; j <= 10; ++j) {
9397
BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
@@ -97,7 +101,6 @@ BOOST_AUTO_TEST_CASE(stdrandom_test)
97101
BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
98102
}
99103
}
100-
101104
}
102105

103106
/** Test that Shuffle reaches every permutation with equal probability. */

0 commit comments

Comments
 (0)