summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2017-10-11 17:42:15 +0200
committerThiago Macieira <thiago.macieira@intel.com>2017-11-30 15:54:15 +0000
commite1ffa594a1444e2b05dc1f4a6768d1753dc6a034 (patch)
tree334892f5a47defc76543692fe0d32307563bb62d
parent09c151006005d80e6343ecdd72d668a2f0cd95f4 (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.cpp142
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