|
26 | 26 | #include <random> |
27 | 27 | #endif |
28 | 28 |
|
| 29 | +// IMPORTANT NOTE about the local static variables initialized in the multithreaded |
| 30 | +// environment: |
| 31 | +// 1. C++11 has added a guarantee that a local static variable is always safely |
| 32 | +// initialized, with a deadlock-free guarnatee. |
| 33 | +// 2. C++03 STANDARD doesn't give any such guarantees, however compilers do: |
| 34 | +// - gcc, clang, Intel: They do implement the guarantees required by C++11 and |
| 35 | +// apply these guarantees even in C++03, unless overridden by -fno-threadsafe-statics |
| 36 | +// - Microsoft Visual Studio: this is supported since VS 2019 |
| 37 | +// |
| 38 | +// Therefore this code relies everywhere on that the static locals are initialized |
| 39 | +// safely in the multithreaded environment and pthread_once() is not in use. |
| 40 | + |
29 | 41 | using namespace srt::logging; |
30 | 42 | using namespace std; |
31 | 43 |
|
@@ -331,74 +343,67 @@ bool CGlobEvent::waitForEvent() |
331 | 343 | //////////////////////////////////////////////////////////////////////////////// |
332 | 344 |
|
333 | 345 | #if HAVE_CXX11 |
334 | | -static std::mt19937& randomGen() |
| 346 | +int getRandomInt(int minval, int maxval) |
335 | 347 | { |
336 | | - static std::random_device s_RandomDevice; |
337 | | - static std::mt19937 s_GenMT19937(s_RandomDevice()); |
338 | | - return s_GenMT19937; |
| 348 | + thread_local std::random_device s_RandomDevice; |
| 349 | + thread_local std::mt19937 s_GenMT19937(s_RandomDevice()); |
| 350 | + uniform_int_distribution<> dis(minVal, maxVal); |
| 351 | + return dis(s_GenMT19937); |
339 | 352 | } |
340 | | -#elif defined(_WIN32) && defined(__MINGW32__) |
341 | | -static void initRandSeed() |
| 353 | + |
| 354 | +#else |
| 355 | + |
| 356 | +#if SRT_HAVE_RAND_R |
| 357 | +static int randWithSeed() |
342 | 358 | { |
343 | | - const int64_t seed = sync::steady_clock::now().time_since_epoch().count(); |
344 | | - srand((unsigned int) seed); |
| 359 | + static unsigned int s_uRandSeed = sync::steady_clock::now().time_since_epoch().count(); |
| 360 | + return rand_r(&s_uRandSeed); |
345 | 361 | } |
346 | | -static pthread_once_t s_InitRandSeedOnce = PTHREAD_ONCE_INIT; |
347 | 362 | #else |
| 363 | +// Mainly MinGW has no rand_r(). |
| 364 | +// IMPORTNAT: This poses a risk for applications that use SRT |
| 365 | +// and they use rand() themselves, or some included library does. |
348 | 366 |
|
349 | | -static unsigned int genRandSeed() |
| 367 | +// We need a wrapper for srand() because it returns void so we need |
| 368 | +// to fake that it has produced some return value to initialize a static variable. |
| 369 | +static inline unsigned int srandWrapper(unsigned int seed) |
350 | 370 | { |
351 | | - // Duration::count() does not depend on any global objects, |
352 | | - // therefore it is preferred over count_microseconds(..). |
353 | | - const int64_t seed = sync::steady_clock::now().time_since_epoch().count(); |
354 | | - return (unsigned int) seed; |
| 371 | + srand(seed); |
| 372 | + return seed; |
355 | 373 | } |
356 | | - |
357 | | -static unsigned int* getRandSeed() |
| 374 | +static int randWithSeed() |
358 | 375 | { |
359 | | - static unsigned int s_uRandSeed = genRandSeed(); |
360 | | - return &s_uRandSeed; |
| 376 | + static unsigned int s_copyseed = srandWrapper(sync::steady_clock::now().time_since_epoch().count()); |
| 377 | + (void)s_copyseed; // fake it is used |
| 378 | + return rand(); |
361 | 379 | } |
362 | | - |
363 | | -#endif |
| 380 | +#endif // Mingw || others |
364 | 381 |
|
365 | 382 | int genRandomInt(int minVal, int maxVal) |
366 | 383 | { |
367 | | - // This Meyers singleton initialization is thread-safe since C++11, but is not thread-safe in C++03. |
| 384 | + // This Meyers singleton initialization is thread-safe since C++11, but is not thread-safe in C++03 |
| 385 | + // (still, see static initialization note in the beginning). |
368 | 386 | // A mutex to protect simultaneous access to the random device. |
369 | 387 | // Thread-local storage could be used here instead to store the seed / random device. |
370 | 388 | // However the generator is not used often (Initial Socket ID, Initial sequence number, FileCC), |
371 | 389 | // so sharing a single seed among threads should not impact the performance. |
372 | 390 | static sync::Mutex s_mtxRandomDevice; |
373 | 391 | sync::ScopedLock lck(s_mtxRandomDevice); |
374 | | -#if HAVE_CXX11 |
375 | | - uniform_int_distribution<> dis(minVal, maxVal); |
376 | | - return dis(randomGen()); |
377 | | -#else |
378 | | -#if defined(__MINGW32__) |
379 | | - // No rand_r(..) for MinGW. |
380 | | - pthread_once(&s_InitRandSeedOnce, initRandSeed); |
381 | | - // rand() returns a pseudo-random integer in the range 0 to RAND_MAX inclusive |
382 | | - // (i.e., the mathematical range [0, RAND_MAX]). |
383 | | - // Therefore, rand_0_1 belongs to [0.0, 1.0]. |
384 | | - const double rand_0_1 = double(rand()) / RAND_MAX; |
385 | | -#else // not __MINGW32__ |
386 | | - // rand_r(..) returns a pseudo-random integer in the range 0 to RAND_MAX inclusive |
| 392 | + |
| 393 | + // randWithSeed() returns a pseudo-random integer in the range 0 to RAND_MAX inclusive |
387 | 394 | // (i.e., the mathematical range [0, RAND_MAX]). |
388 | 395 | // Therefore, rand_0_1 belongs to [0.0, 1.0]. |
389 | | - const double rand_0_1 = double(rand_r(getRandSeed())) / RAND_MAX; |
390 | | -#endif |
391 | | - |
| 396 | + double rand_0_1 = double(randWithSeed()) / RAND_MAX; |
392 | 397 | // Map onto [minVal, maxVal]. |
393 | 398 | // Note. There is a minuscule probablity to get maxVal+1 as the result. |
394 | 399 | // So we have to use long long to handle cases when maxVal = INT32_MAX. |
395 | 400 | // Also we must check 'res' does not exceed maxVal, |
396 | 401 | // which may happen if rand_0_1 = 1, even though the chances are low. |
397 | 402 | const long long llMaxVal = maxVal; |
398 | 403 | const int res = minVal + static_cast<int>((llMaxVal + 1 - minVal) * rand_0_1); |
399 | | - return min(res, maxVal); |
400 | | -#endif // HAVE_CXX11 |
| 404 | + return std::min(res, maxVal); |
401 | 405 | } |
| 406 | +#endif |
402 | 407 |
|
403 | 408 | #if defined(SRT_ENABLE_STDCXX_SYNC) && HAVE_CXX17 |
404 | 409 |
|
|
0 commit comments