Skip to content

Commit 6d5a06e

Browse files
committed
Make random number generation thread safe and optimized.
1 parent 76aeb51 commit 6d5a06e

2 files changed

Lines changed: 32 additions & 31 deletions

File tree

include/bitcoin/bitcoin/utility/random.hpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,6 @@ BC_API uint64_t pseudo_random();
3939
*/
4040
BC_API uint64_t pseudo_random(uint64_t begin, uint64_t end);
4141

42-
/**
43-
* Generate a non-zero pseudo random number within the domain.
44-
* @return The 64 bit number.
45-
*/
46-
BC_API uint64_t nonzero_pseudo_random();
47-
48-
/**
49-
* Generate a non-zero pseudo random number within (0, end].
50-
* @return The 64 bit number.
51-
*/
52-
BC_API uint64_t nonzero_pseudo_random(uint64_t end);
53-
5442
/**
5543
* Fill a buffer with randomness using the default random engine.
5644
* @param[in] chunk The buffer to fill with randomness.

src/utility/random.cpp

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,50 +21,66 @@
2121

2222
#include <chrono>
2323
#include <cstdint>
24-
#include <stdexcept>
2524
#include <random>
26-
#include <thread>
25+
#include <boost/thread.hpp>
2726
#include <bitcoin/bitcoin/utility/asio.hpp>
28-
#include <bitcoin/bitcoin/utility/assert.hpp>
2927
#include <bitcoin/bitcoin/utility/data.hpp>
28+
#include <bitcoin/bitcoin/utility/thread.hpp>
3029
#include <bitcoin/bitcoin/constants.hpp>
3130

3231
namespace libbitcoin {
32+
33+
using namespace bc::asio;
34+
using namespace std::chrono;
3335

3436
// DO NOT USE srand() and rand() on MSVC as srand must be called per thread.
3537
// Values may be truly random depending on the underlying device.
36-
// TODO: convert pseudo_random/nonzero_pseudo_random methods to templates.
3738

38-
uint64_t pseudo_random()
39+
static uint32_t get_clock_seed()
3940
{
40-
return pseudo_random(0, max_uint64);
41+
const auto now = high_resolution_clock::now();
42+
return static_cast<uint32_t>(now.time_since_epoch().count());
4143
}
4244

43-
uint64_t pseudo_random(uint64_t begin, uint64_t end)
45+
static std::mt19937& get_twister()
4446
{
45-
std::uniform_int_distribution<uint64_t> distribution(begin, end);
46-
std::random_device device;
47-
return distribution(device);
47+
// Boost.thread will clean up the thread statics using this function.
48+
const auto deleter = [](std::mt19937* twister)
49+
{
50+
delete twister;
51+
};
52+
53+
// Maintain thread static state space.
54+
static boost::thread_specific_ptr<std::mt19937> twister(deleter);
55+
56+
// This is thread safe because the instance is static.
57+
if (twister.get() == nullptr)
58+
{
59+
// Seed with high resolution clock.
60+
twister.reset(new std::mt19937(get_clock_seed()));
61+
}
62+
63+
return *twister;
4864
}
4965

50-
uint64_t nonzero_pseudo_random()
66+
uint64_t pseudo_random()
5167
{
52-
return pseudo_random(1, max_uint64);
68+
return pseudo_random(0, max_uint64);
5369
}
5470

55-
uint64_t nonzero_pseudo_random(uint64_t end)
71+
uint64_t pseudo_random(uint64_t begin, uint64_t end)
5672
{
57-
return pseudo_random(1, end);
73+
std::uniform_int_distribution<uint64_t> distribution(begin, end);
74+
return distribution(get_twister());
5875
}
5976

6077
void pseudo_random_fill(data_chunk& chunk)
6178
{
6279
// uniform_int_distribution is undefined for sizes < 16 bits.
6380
std::uniform_int_distribution<uint16_t> distribution(0, max_uint8);
64-
std::random_device device;
6581

6682
for (auto& byte: chunk)
67-
byte = static_cast<uint8_t>(distribution(device));
83+
byte = static_cast<uint8_t>(distribution(get_twister()));
6884
}
6985

7086
// Randomly select a time duration in the range:
@@ -73,9 +89,6 @@ void pseudo_random_fill(data_chunk& chunk)
7389
asio::duration pseudo_randomize(const asio::duration& expiration,
7490
uint8_t ratio)
7591
{
76-
using namespace bc::asio;
77-
using namespace std::chrono;
78-
7992
if (ratio == 0)
8093
return expiration;
8194

0 commit comments

Comments
 (0)