Skip to content

Commit 30649d8

Browse files
author
Mikolaj Malecki
committed
[MAINT][code] Removed use of pthread_once. Fixed Haivision#3115
1 parent 06b048a commit 30649d8

File tree

4 files changed

+61
-82
lines changed

4 files changed

+61
-82
lines changed

CMakeLists.txt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ include(FindPThreadGetSetName)
2626
include(CheckGCCAtomicIntrinsics)
2727
include(CheckCXXAtomic)
2828
include(CheckCXXStdPutTime)
29+
include(CheckSourceCompiles)
2930

3031
# This is required in some projects that add some other sources
3132
# to the SRT library to be compiled together (aka "virtual library").
@@ -490,7 +491,6 @@ else()
490491
endif()
491492
message(STATUS "WARNING OPTIONS: ${SRT_GCC_WARN}")
492493

493-
494494
# --------------------------------------------
495495
# Post-option variable synchronization
496496
# and option-to-preprocessor-macro transition
@@ -1174,6 +1174,22 @@ endmacro()
11741174
srt_set_stdcxx(srt_virtual "${USE_CXX_STD_LIB}")
11751175
srt_set_stdc(srt_virtual "99")
11761176

1177+
check_source_compiles(CXX
1178+
"#include <stdlib.h>
1179+
int main(){
1180+
int randsr = 31337;
1181+
int v = rand_r(&randr);
1182+
return 0;
1183+
}"
1184+
HAVE_RAND_R
1185+
)
1186+
if (HAVE_RAND_R)
1187+
target_compile_definitions(srt_virtual PRIVATE SRT_HAVE_RAND_R=1)
1188+
else()
1189+
target_compile_definitions(srt_virtual PRIVATE SRT_HAVE_RAND_R=0)
1190+
endif()
1191+
1192+
11771193
if (ENABLE_ENCRYPTION)
11781194
target_include_directories(srt_virtual PRIVATE ${SSL_INCLUDE_DIRS})
11791195

srtcore/core.cpp

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -233,33 +233,12 @@ struct SrtOptionAction
233233

234234
const SrtOptionAction s_sockopt_action;
235235

236-
#if HAVE_CXX11
237-
238236
CUDTUnited& CUDT::uglobal()
239237
{
240238
static CUDTUnited instance;
241239
return instance;
242240
}
243241

244-
#else // !HAVE_CXX11
245-
246-
static pthread_once_t s_UDTUnitedOnce = PTHREAD_ONCE_INIT;
247-
248-
static CUDTUnited *getInstance()
249-
{
250-
static CUDTUnited instance;
251-
return &instance;
252-
}
253-
254-
CUDTUnited& CUDT::uglobal()
255-
{
256-
// We don't want lock each time, pthread_once can be faster than mutex.
257-
pthread_once(&s_UDTUnitedOnce, reinterpret_cast<void (*)()>(getInstance));
258-
return *getInstance();
259-
}
260-
261-
#endif
262-
263242
SRT_TSA_DISABLED
264243
void CUDT::construct()
265244
{

srtcore/packetfilter.cpp

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -279,33 +279,12 @@ PacketFilter::Factory::~Factory()
279279
{
280280
}
281281

282-
#if HAVE_CXX11
283-
284282
PacketFilter::Internal& PacketFilter::internal()
285283
{
286284
static PacketFilter::Internal instance;
287285
return instance;
288286
}
289287

290-
#else // !HAVE_CXX11
291-
292-
static pthread_once_t s_PacketFactoryOnce = PTHREAD_ONCE_INIT;
293-
294-
static PacketFilter::Internal *getInstance()
295-
{
296-
static PacketFilter::Internal instance;
297-
return &instance;
298-
}
299-
300-
PacketFilter::Internal& PacketFilter::internal()
301-
{
302-
// We don't want lock each time, pthread_once can be faster than mutex.
303-
pthread_once(&s_PacketFactoryOnce, reinterpret_cast<void (*)()>(getInstance));
304-
return *getInstance();
305-
}
306-
307-
#endif
308-
309288
PacketFilter::Internal::Internal()
310289
{
311290
// Add here builtin packet filters and mark them

srtcore/sync.cpp

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@
2626
#include <random>
2727
#endif
2828

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+
2941
using namespace srt::logging;
3042
using namespace std;
3143

@@ -331,74 +343,67 @@ bool CGlobEvent::waitForEvent()
331343
////////////////////////////////////////////////////////////////////////////////
332344

333345
#if HAVE_CXX11
334-
static std::mt19937& randomGen()
346+
int getRandomInt(int minval, int maxval)
335347
{
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);
339352
}
340-
#elif defined(_WIN32) && defined(__MINGW32__)
341-
static void initRandSeed()
353+
354+
#else
355+
356+
#if SRT_HAVE_RAND_R
357+
static int randWithSeed()
342358
{
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);
345361
}
346-
static pthread_once_t s_InitRandSeedOnce = PTHREAD_ONCE_INIT;
347362
#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.
348366

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)
350370
{
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;
355373
}
356-
357-
static unsigned int* getRandSeed()
374+
static int randWithSeed()
358375
{
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();
361379
}
362-
363-
#endif
380+
#endif // Mingw || others
364381

365382
int genRandomInt(int minVal, int maxVal)
366383
{
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).
368386
// A mutex to protect simultaneous access to the random device.
369387
// Thread-local storage could be used here instead to store the seed / random device.
370388
// However the generator is not used often (Initial Socket ID, Initial sequence number, FileCC),
371389
// so sharing a single seed among threads should not impact the performance.
372390
static sync::Mutex s_mtxRandomDevice;
373391
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
387394
// (i.e., the mathematical range [0, RAND_MAX]).
388395
// 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;
392397
// Map onto [minVal, maxVal].
393398
// Note. There is a minuscule probablity to get maxVal+1 as the result.
394399
// So we have to use long long to handle cases when maxVal = INT32_MAX.
395400
// Also we must check 'res' does not exceed maxVal,
396401
// which may happen if rand_0_1 = 1, even though the chances are low.
397402
const long long llMaxVal = maxVal;
398403
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);
401405
}
406+
#endif
402407

403408
#if defined(SRT_ENABLE_STDCXX_SYNC) && HAVE_CXX17
404409

0 commit comments

Comments
 (0)