LogCabin
Core/Random.cc
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines