|
39 | 39 | #include "tiledb/common/random/seeder.h" |
40 | 40 |
|
41 | 41 | namespace tiledb::common { |
| 42 | + |
| 43 | +/** |
| 44 | + * Marker class for a test-only PRNG constructor |
| 45 | + */ |
| 46 | +class RandomSeedT {}; |
| 47 | +/** |
| 48 | + * Marker constant |
| 49 | + */ |
| 50 | +static constexpr RandomSeedT RandomSeed; |
| 51 | + |
| 52 | +/** |
| 53 | + * A random number generator suitable for both production and testing. |
| 54 | + * |
| 55 | + * @section Requirements |
| 56 | + * |
| 57 | + * This PRNG must support two very different kinds of situations: |
| 58 | + * |
| 59 | + * 1. In production use (the ordinary case) the seed must be _actually_ random |
| 60 | + * so that the random sequences in different processes are distinct. |
| 61 | + * 2. During most testing the seed must be deterministic to ensure that |
| 62 | + * different test runs execute the same sequence of operations. This ensures |
| 63 | + * that test failures can be replicated for diagnosis and correction. |
| 64 | + * a. In particular, the seed in Catch2 test runs should be deterministic. |
| 65 | + * 3. Certain tests, however, require actual randomness. |
| 66 | + * a. One such test verifies that actual randomness is available per (1). Such |
| 67 | + * tests necessarily have the possibility of failures, i.e. of false |
| 68 | + * positives, but the actual likelihood can be made extremely low. |
| 69 | + * b. Stress tests execute large number of test runs searching for defects. |
| 70 | + * Such tests do not generate new information when run with previously- |
| 71 | + * used PRNG sequences. |
| 72 | + * |
| 73 | + * This class satisfies these requirements with the following implementation |
| 74 | + * choices: |
| 75 | + * 1. If the user has not called `set_seed()` on the global seeder (from |
| 76 | + * `Seeder::get`), then the seed is taken from `std::random_device`. |
| 77 | + * 2. If the user has called `set_seed()` on the global seeder, that seed is |
| 78 | + * used. |
| 79 | + * 3. This class uses a global seeder in order to support Catch2. An event |
| 80 | + * handler that executes at the start of the test run calls `set_seed()`. |
| 81 | + * |
| 82 | + * @section Maturity |
| 83 | + * |
| 84 | + * This class only has a default constructor. It does not have constructors that |
| 85 | + * take seeds nor seeders. Such constructors would be useful for replicating |
| 86 | + * test runs, but would also be premature at present. There's further test |
| 87 | + * infrastructure required to replicate a specific test in isolation. As that |
| 88 | + * test infrastructure matures, so also should this class. In the interim, in |
| 89 | + * order to replicate a specific test with a specific seed, the function |
| 90 | + * `initial_prng()` can be temporarily changed. |
| 91 | + * |
| 92 | + * This class uses a seeded PRNG to implement the random sequence. The |
| 93 | + * requirement is that sequences in different processes be distinct, not that |
| 94 | + * they be actually random. A randomly-seeded PRNG satisfies this requirement. |
| 95 | + * The motivation for this implementation choice is as follows: |
| 96 | + * 1. There is no standard hardware requirement for random number generation. |
| 97 | + * While it's generally available, there are unknown variations in |
| 98 | + * significant quality parameters such as the rate of random generation, |
| 99 | + * duration of an RNG call, and randomness of generation (e.g. n-gram |
| 100 | + * entropies). |
| 101 | + * 2. In order not to stress a potentially inadequate RNG, we only call it for |
| 102 | + * seeding and not for every number. |
| 103 | + * 3. Qualifying a potential RNG implementation requires engineering resources |
| 104 | + * that have not been committed as yet. |
| 105 | + * |
| 106 | + * @section Caveat |
| 107 | + * |
| 108 | + * This class uses `std::random_device` to seed the PRNG if no explicit seed is |
| 109 | + * set. The standard library does not require that this class use an actual RNG, |
| 110 | + * i.e. RNG from hardware of some kind. Indeed, certain earlier implementations |
| 111 | + * did not do so and were deterministic. In order to validate that this device |
| 112 | + * is actually random, it's necessary to run a multiprocess test to observe |
| 113 | + * initialization in different processes. The test suite does not contain such |
| 114 | + * a validation test at present. |
| 115 | + */ |
42 | 116 | class PRNG { |
43 | 117 | public: |
44 | 118 | /* ********************************* */ |
45 | 119 | /* CONSTRUCTORS & DESTRUCTORS */ |
46 | 120 | /* ********************************* */ |
47 | 121 |
|
48 | 122 | /** |
49 | | - * Constructor. |
| 123 | + * Default constructor. |
50 | 124 | * |
51 | | - * Constructs an mt19937 engine for random number generation. |
52 | | - * If Seeder has been seeded, the seed will be set on the engine. |
53 | | - * Otherwise, it is default-constructed. |
| 125 | + * If `Seeder` has been seeded, the seed will be set on the engine. Otherwise, |
| 126 | + * the generator is constructed with a random seed. |
54 | 127 | */ |
55 | 128 | PRNG(); |
56 | 129 |
|
| 130 | + /** |
| 131 | + * Constructor for random seeding. |
| 132 | + * |
| 133 | + * This constructor makes an object that is always constructed with a random |
| 134 | + * seed. |
| 135 | + * |
| 136 | + * @warning This constructor is only for testing. It must not be used in |
| 137 | + * production code, where it would thwart the ability to run tests |
| 138 | + * deterministically. |
| 139 | + */ |
| 140 | + PRNG(RandomSeedT); |
| 141 | + |
57 | 142 | /** Copy constructor is deleted. */ |
58 | 143 | PRNG(const PRNG&) = delete; |
59 | 144 |
|
@@ -89,13 +174,6 @@ class PRNG { |
89 | 174 |
|
90 | 175 | /** Mutex which protects against simultaneous access to operator() body. */ |
91 | 176 | std::mutex mtx_; |
92 | | - |
93 | | - /* ********************************* */ |
94 | | - /* PRIVATE METHODS */ |
95 | | - /* ********************************* */ |
96 | | - |
97 | | - /** Default-constructs an mt19937 engine and optionally sets the seed. */ |
98 | | - std::mt19937_64 prng_initial(); |
99 | 177 | }; |
100 | 178 | } // namespace tiledb::common |
101 | 179 |
|
|
0 commit comments