diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2017-10-11 17:42:15 +0200 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2017-11-30 15:54:15 +0000 |
commit | e1ffa594a1444e2b05dc1f4a6768d1753dc6a034 (patch) | |
tree | 334892f5a47defc76543692fe0d32307563bb62d | |
parent | 09c151006005d80e6343ecdd72d668a2f0cd95f4 (diff) |
Replace qrand() engine with C++11 <random> LCG and deprecate
Instead of trying to adapt to whatever the C library may have and using
QThreadLocalStorage, let's use a simple linear congruential generator
engine from <random>. We can't use a single instance because qsrand()
is documented to work per thread.
I thought of using QRandomEngine, but had to make the choice between
growing the QtCore code size and growing the per-thread data size. Code
is sharable and is actually smaller than the sizeof(QRandomEngine),
which is over 2500 bytes. sizeof(std::minstd_rand) is just
sizeof(uint_fast32_t).
Change-Id: I0a103569c81b4711a649fffd14ec8e641d02bf20
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
-rw-r--r-- | src/corelib/global/qrandom.cpp | 142 |
1 files changed, 59 insertions, 83 deletions
diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp index 72ac8d332b..7da86188bb 100644 --- a/src/corelib/global/qrandom.cpp +++ b/src/corelib/global/qrandom.cpp @@ -1262,20 +1262,56 @@ void QRandomGenerator::_fillRange(void *buffer, void *bufferEnd) std::generate(begin, end, [this]() { return storage.engine()(); }); } -#if defined(Q_OS_ANDROID) && (__ANDROID_API__ < 21) -typedef QThreadStorage<QJNIObjectPrivate> AndroidRandomStorage; -Q_GLOBAL_STATIC(AndroidRandomStorage, randomTLS) +namespace { +struct QRandEngine +{ + std::minstd_rand engine; + QRandEngine() : engine(1) {} + + int generate() + { + std::minstd_rand::result_type v = engine(); + if (std::numeric_limits<int>::max() != RAND_MAX) + v %= uint(RAND_MAX) + 1; + + return int(v); + } -#elif defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0) -using SeedStorageType = QtPrivate::FunctionPointer<decltype(&srand)>::Arguments::Car; + void seed(std::minstd_rand::result_type q) + { + engine.seed(q); + } +}; +} -typedef QThreadStorage<SeedStorageType *> SeedStorage; -Q_GLOBAL_STATIC(SeedStorage, randTLS) // Thread Local Storage for seed value +#if defined(QT_NO_THREAD) || defined(Q_OS_WIN) +// On Windows srand() and rand() already use Thread-Local-Storage +// to store the seed between calls +static inline QRandEngine *randTLS() +{ + return nullptr; +} +#elif defined(Q_COMPILER_THREAD_LOCAL) +static inline QRandEngine *randTLS() +{ + thread_local QRandEngine r; + return &r; +} +#else +Q_GLOBAL_STATIC(QThreadStorage<QRandEngine>, g_randTLS) +static inline QRandEngine *randTLS() +{ + auto tls = g_randTLS(); + if (!tls) + return nullptr; + return &tls->localData(); +} #endif /*! \relates <QtGlobal> + \deprecated \since 4.2 Thread-safe version of the standard C++ \c srand() function. @@ -1287,49 +1323,23 @@ Q_GLOBAL_STATIC(SeedStorage, randTLS) // Thread Local Storage for seed value if two threads call qsrand(1) and subsequently call qrand(), the threads will get the same random number sequence. + \note This function is deprecated. In new applications, use + QRandomGenerator instead. + \sa qrand(), QRandomGenerator */ void qsrand(uint seed) { -#if defined(Q_OS_ANDROID) && (__ANDROID_API__ < 21) - if (randomTLS->hasLocalData()) { - randomTLS->localData().callMethod<void>("setSeed", "(J)V", jlong(seed)); - return; - } - - QJNIObjectPrivate random("java/util/Random", - "(J)V", - jlong(seed)); - if (!random.isValid()) { + auto prng = randTLS(); + if (prng) + prng->seed(seed); + else srand(seed); - return; - } - - randomTLS->setLocalData(random); -#elif defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0) - SeedStorage *seedStorage = randTLS(); - if (seedStorage) { - SeedStorageType *pseed = seedStorage->localData(); - if (!pseed) - seedStorage->setLocalData(pseed = new SeedStorageType); - *pseed = seed; - } else { - //global static seed storage should always exist, - //except after being deleted by QGlobalStaticDeleter. - //But since it still can be called from destructor of another - //global static object, fallback to srand(seed) - srand(seed); - } -#else - // On Windows srand() and rand() already use Thread-Local-Storage - // to store the seed between calls - // this is also valid for QT_NO_THREAD - srand(seed); -#endif } /*! \relates <QtGlobal> + \deprecated \since 4.2 Thread-safe version of the standard C++ \c rand() function. @@ -1343,52 +1353,18 @@ void qsrand(uint seed) step is skipped, then the sequence will be pre-seeded with a constant value. - \sa qsrand(), QRandomGenerator + \note This function is deprecated. In new applications, use + QRandomGenerator instead. + + \sa qrand(), QRandomGenerator */ int qrand() { -#if defined(Q_OS_ANDROID) && (__ANDROID_API__ < 21) - AndroidRandomStorage *randomStorage = randomTLS(); - if (!randomStorage) - return rand(); - - if (randomStorage->hasLocalData()) { - return randomStorage->localData().callMethod<jint>("nextInt", - "(I)I", - RAND_MAX); - } - - QJNIObjectPrivate random("java/util/Random", - "(J)V", - jlong(1)); - - if (!random.isValid()) - return rand(); - - randomStorage->setLocalData(random); - return random.callMethod<jint>("nextInt", "(I)I", RAND_MAX); -#elif defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0) - SeedStorage *seedStorage = randTLS(); - if (seedStorage) { - SeedStorageType *pseed = seedStorage->localData(); - if (!pseed) { - seedStorage->setLocalData(pseed = new SeedStorageType); - *pseed = 1; - } - return rand_r(pseed); - } else { - //global static seed storage should always exist, - //except after being deleted by QGlobalStaticDeleter. - //But since it still can be called from destructor of another - //global static object, fallback to rand() + auto prng = randTLS(); + if (prng) + return prng->generate(); + else return rand(); - } -#else - // On Windows srand() and rand() already use Thread-Local-Storage - // to store the seed between calls - // this is also valid for QT_NO_THREAD - return rand(); -#endif } QT_END_NAMESPACE |