LogCabin
|
00001 /* Copyright (c) 2009-2014 Stanford University 00002 * Copyright (c) 2015 Diego Ongaro 00003 * Copyright (c) 2015 Scale Computing 00004 * 00005 * Permission to use, copy, modify, and distribute this software for any 00006 * purpose with or without fee is hereby granted, provided that the above 00007 * copyright notice and this permission notice appear in all copies. 00008 * 00009 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES 00010 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 00011 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR 00012 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 00013 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 00014 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 00015 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 00016 * 00017 * 00018 * Some of this code is copied from RAMCloud src/Common.cc _generateRandom(), 00019 * Copyright (c) 2009-2014 Stanford University also under the same ISC license. 00020 */ 00021 00022 #include <cassert> 00023 #include <cmath> 00024 #include <cstring> 00025 #include <fcntl.h> 00026 #include <limits> 00027 #include <mutex> 00028 #include <pthread.h> 00029 #include <sys/stat.h> 00030 #include <unistd.h> 00031 00032 #include "Core/Debug.h" 00033 #include "Core/Random.h" 00034 00035 namespace LogCabin { 00036 namespace Core { 00037 namespace Random { 00038 00039 // forward declaration 00040 void acquireMutex(); 00041 void releaseMutex(); 00042 00043 namespace { 00044 00045 // forward declaration 00046 void resetRandomState(); 00047 00048 /** 00049 * Keeps state needed by the random number generator, protected by a mutex. 00050 */ 00051 class RandomState { 00052 public: 00053 RandomState() 00054 : mutex() 00055 , init(false) 00056 , statebuf() 00057 , randbuf() 00058 { 00059 reset(); 00060 int err = pthread_atfork(LogCabin::Core::Random::acquireMutex, 00061 LogCabin::Core::Random::releaseMutex, 00062 resetRandomState); 00063 if (err != 0) { 00064 // too early to call ERROR in here 00065 fprintf(stderr, "Failed to set up pthread_atfork() handler to " 00066 "reset random number generator seed in child processes. " 00067 "As a result, child processes will generate the same " 00068 "sequence of random values as the parent they were forked " 00069 "from. Error: %s\n", 00070 strerror(err)); 00071 } 00072 } 00073 00074 void reset() { 00075 std::lock_guard<std::mutex> lockGuard(mutex); 00076 int fd = open("/dev/urandom", O_RDONLY); 00077 if (fd < 0) { 00078 // too early to call PANIC in here 00079 fprintf(stderr, "Couldn't open /dev/urandom: %s\n", 00080 strerror(errno)); 00081 abort(); 00082 } 00083 unsigned int seed; 00084 ssize_t bytesRead = read(fd, &seed, sizeof(seed)); 00085 close(fd); 00086 if (bytesRead != sizeof(seed)) { 00087 // too early to call PANIC in here 00088 fprintf(stderr, "Couldn't read full seed from /dev/urandom\n"); 00089 abort(); 00090 } 00091 initstate_r(seed, statebuf, STATE_BYTES, &randbuf); 00092 static_assert(RAND_MAX >= (1L << 30), "RAND_MAX too small"); 00093 init = true; 00094 } 00095 00096 uint64_t random64() { 00097 std::lock_guard<std::mutex> lockGuard(mutex); 00098 if (!init) { 00099 // probably too early to call PANIC in here 00100 fprintf(stderr, "Looks like you hit the so-called static " 00101 "initialization order fiasco in Core::Random\n"); 00102 abort(); 00103 } 00104 00105 // Each call to random returns 31 bits of randomness, 00106 // so we need three to get 64 bits of randomness. 00107 int32_t lo, mid, hi; 00108 random_r(&randbuf, &lo); 00109 random_r(&randbuf, &mid); 00110 random_r(&randbuf, &hi); 00111 uint64_t r = (((uint64_t(hi) & 0x7FFFFFFF) << 33) | // NOLINT 00112 ((uint64_t(mid) & 0x7FFFFFFF) << 2) | // NOLINT 00113 (uint64_t(lo) & 0x00000003)); // NOLINT 00114 return r; 00115 } 00116 00117 friend void LogCabin::Core::Random::acquireMutex(); 00118 friend void LogCabin::Core::Random::releaseMutex(); 00119 00120 private: 00121 00122 /** 00123 * Protect following members from concurrent access. 00124 */ 00125 std::mutex mutex; 00126 00127 /** 00128 * Set to true when the constructor completes. 00129 */ 00130 bool init; 00131 00132 /** 00133 * Size of 'statebuf'. 128 is the same size as initstate() uses for regular 00134 * random(), see manpages for details. 00135 */ 00136 enum { STATE_BYTES = 128 }; 00137 00138 /** 00139 * Internal scratch state used by random_r. 00140 */ 00141 char statebuf[STATE_BYTES]; 00142 00143 /** 00144 * random_r's state. Must be handed to each call, and seems to refer to 00145 * statebuf in some undocumented way. 00146 */ 00147 random_data randbuf; 00148 } randomState; 00149 00150 /** 00151 * Called in child after fork() to reset random seed. 00152 */ 00153 void 00154 resetRandomState() 00155 { 00156 // we will have grabbed the mutex in pthread_atfork prepare, need 00157 // to release here 00158 releaseMutex(); 00159 randomState.reset(); 00160 } 00161 00162 /** 00163 * Fill a variable of type T with some random bytes. 00164 */ 00165 template<typename T> 00166 T 00167 getRandomBytes() 00168 { 00169 T buf {}; 00170 size_t offset = 0; 00171 while (offset < sizeof(buf)) { 00172 uint64_t r = randomState.random64(); 00173 size_t copy = std::min(sizeof(r), sizeof(buf) - offset); 00174 memcpy(reinterpret_cast<char*>(&buf) + offset, &r, copy); 00175 offset += copy; 00176 } 00177 return buf; 00178 } 00179 00180 /// Return a random number between 0 and 1. 00181 double 00182 randomUnit() 00183 { 00184 return (double(random64()) / 00185 double(std::numeric_limits<uint64_t>::max())); 00186 } 00187 00188 } // anonymous namespace 00189 00190 /** 00191 * Called before fork() to grab the mutex used in pthread_atfork. This 00192 * function is outside of the anonymous namespace so it can be called 00193 * from RandomTest. 00194 */ 00195 void 00196 acquireMutex() 00197 { 00198 randomState.mutex.lock(); 00199 } 00200 00201 /** 00202 * Called in the parent post fork(). This function is outside of the 00203 * anonymous namespace so it can be called from RandomTest. 00204 */ 00205 void 00206 releaseMutex() 00207 { 00208 randomState.mutex.unlock(); 00209 } 00210 00211 uint8_t 00212 random8() 00213 { 00214 return getRandomBytes<uint8_t>(); 00215 } 00216 00217 uint16_t 00218 random16() 00219 { 00220 return getRandomBytes<uint16_t>(); 00221 } 00222 00223 uint32_t 00224 random32() 00225 { 00226 return getRandomBytes<uint32_t>(); 00227 } 00228 00229 uint64_t 00230 random64() 00231 { 00232 return randomState.random64(); 00233 } 00234 00235 double 00236 randomRangeDouble(double start, double end) 00237 { 00238 return start + randomUnit() * (end - start); 00239 } 00240 00241 uint64_t 00242 randomRange(uint64_t start, uint64_t end) 00243 { 00244 return uint64_t(lround(randomRangeDouble(double(start), double(end)))); 00245 } 00246 00247 } // namespace LogCabin::Core::Random 00248 } // namespace LogCabin::Core 00249 } // namespace LogCabin