From e1ffa594a1444e2b05dc1f4a6768d1753dc6a034 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 11 Oct 2017 17:42:15 +0200 Subject: [PATCH] Replace qrand() engine with C++11 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 . 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 --- src/corelib/global/qrandom.cpp | 142 ++++++++++++++------------------- 1 file 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 AndroidRandomStorage; -Q_GLOBAL_STATIC(AndroidRandomStorage, randomTLS) +namespace { +struct QRandEngine +{ + std::minstd_rand engine; + QRandEngine() : engine(1) {} -#elif defined(Q_OS_UNIX) && !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0) -using SeedStorageType = QtPrivate::FunctionPointer::Arguments::Car; + int generate() + { + std::minstd_rand::result_type v = engine(); + if (std::numeric_limits::max() != RAND_MAX) + v %= uint(RAND_MAX) + 1; -typedef QThreadStorage SeedStorage; -Q_GLOBAL_STATIC(SeedStorage, randTLS) // Thread Local Storage for seed value + return int(v); + } + void seed(std::minstd_rand::result_type q) + { + engine.seed(q); + } +}; +} + +#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, g_randTLS) +static inline QRandEngine *randTLS() +{ + auto tls = g_randTLS(); + if (!tls) + return nullptr; + return &tls->localData(); + +} #endif /*! \relates + \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("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 + \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) + auto prng = randTLS(); + if (prng) + return prng->generate(); + else return rand(); - - if (randomStorage->hasLocalData()) { - return randomStorage->localData().callMethod("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("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() - 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