diff --git a/modules/juce_core/maths/juce_Random.cpp b/modules/juce_core/maths/juce_Random.cpp index a11bd8693f..32a4d6af7b 100644 --- a/modules/juce_core/maths/juce_Random.cpp +++ b/modules/juce_core/maths/juce_Random.cpp @@ -46,26 +46,29 @@ Random::Random() : seed (1) void Random::setSeed (const int64 newSeed) noexcept { - if (this == &getSystemRandom()) - { - // Resetting the system Random risks messing up - // JUCE's internal state. If you need a predictable - // stream of random numbers you should use a local - // Random object. - jassertfalse; - return; - } + // Resetting the system Random risks messing up JUCE's internal state. + // If you need a predictable stream of random numbers you should use a + // local Random object. + jassert (! isSystemRandom); seed = newSeed; } void Random::combineSeed (const int64 seedValue) noexcept { + // Resetting the system Random risks messing up JUCE's internal state. + // Consider using a local Random object instead. + jassert (! isSystemRandom); + seed ^= nextInt64() ^ seedValue; } void Random::setSeedRandomly() { + // Resetting the system Random risks messing up JUCE's internal state. + // Consider using a local Random object instead. + jassert (! isSystemRandom); + static std::atomic globalSeed { 0 }; combineSeed (globalSeed ^ (int64) (pointer_sized_int) this); @@ -78,13 +81,28 @@ void Random::setSeedRandomly() Random& Random::getSystemRandom() noexcept { - static Random sysRand; + thread_local Random sysRand = std::invoke ([] + { + Random r; + #if JUCE_ASSERTIONS_ENABLED_OR_LOGGED + r.isSystemRandom = true; + #endif + return r; + }); + return sysRand; } //============================================================================== int Random::nextInt() noexcept { + // If you encounter this assertion you've likely stored a reference to the + // system random object and are accessing it from a thread other than the + // one it was first created on. This may lead to race conditions on the + // random object. To avoid this assertion call Random::getSystemRandom() + // directly instead of storing a reference. + jassert (! isSystemRandom || this == &getSystemRandom()); + seed = (int64) (((((uint64) seed) * 0x5deece66dLL) + 11) & 0xffffffffffffLL); return (int) (seed >> 16); diff --git a/modules/juce_core/maths/juce_Random.h b/modules/juce_core/maths/juce_Random.h index d991bea036..e495923921 100644 --- a/modules/juce_core/maths/juce_Random.h +++ b/modules/juce_core/maths/juce_Random.h @@ -128,11 +128,12 @@ public: */ void setSeedRandomly(); - /** The overhead of creating a new Random object is fairly small, but if you want to avoid - it, you can call this method to get a global shared Random object. + /** The overhead of creating a new Random object is fairly small, but if you want + to avoid it, you can call this method to get a global shared Random object. - It's not thread-safe though, so threads should use their own Random object, otherwise - you run the risk of your random numbers becoming.. erm.. randomly corrupted.. + Note this will return a different object per thread it's accessed from, + making it thread safe. However, it's therefore important not store a reference + to this object that will later be accessed from other threads. */ static Random& getSystemRandom() noexcept; @@ -140,6 +141,10 @@ private: //============================================================================== int64 seed; + #if JUCE_ASSERTIONS_ENABLED_OR_LOGGED + bool isSystemRandom = false; + #endif + JUCE_LEAK_DETECTOR (Random) };