123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- /*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2019 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
- #include "platform_sys.h"
- #include <iomanip>
- #include <stdexcept>
- #include <cmath>
- #include "sync.h"
- #include "srt.h"
- #include "srt_compat.h"
- #include "logging.h"
- #include "common.h"
- // HAVE_CXX11 is defined in utilities.h, included with common.h.
- // The following conditional inclusion must go after common.h.
- #if HAVE_CXX11
- #include <random>
- #endif
- namespace srt_logging
- {
- extern Logger inlog;
- }
- using namespace srt_logging;
- using namespace std;
- namespace srt
- {
- namespace sync
- {
- std::string FormatTime(const steady_clock::time_point& timestamp)
- {
- if (is_zero(timestamp))
- {
- // Use special string for 0
- return "00:00:00.000000 [STDY]";
- }
- const int decimals = clockSubsecondPrecision();
- const uint64_t total_sec = count_seconds(timestamp.time_since_epoch());
- const uint64_t days = total_sec / (60 * 60 * 24);
- const uint64_t hours = total_sec / (60 * 60) - days * 24;
- const uint64_t minutes = total_sec / 60 - (days * 24 * 60) - hours * 60;
- const uint64_t seconds = total_sec - (days * 24 * 60 * 60) - hours * 60 * 60 - minutes * 60;
- ostringstream out;
- if (days)
- out << days << "D ";
- out << setfill('0') << setw(2) << hours << ":"
- << setfill('0') << setw(2) << minutes << ":"
- << setfill('0') << setw(2) << seconds << "."
- << setfill('0') << setw(decimals) << (timestamp - seconds_from(total_sec)).time_since_epoch().count() << " [STDY]";
- return out.str();
- }
- std::string FormatTimeSys(const steady_clock::time_point& timestamp)
- {
- const time_t now_s = ::time(NULL); // get current time in seconds
- const steady_clock::time_point now_timestamp = steady_clock::now();
- const int64_t delta_us = count_microseconds(timestamp - now_timestamp);
- const int64_t delta_s =
- static_cast<int64_t>(floor((static_cast<double>(count_microseconds(now_timestamp.time_since_epoch()) % 1000000) + delta_us) / 1000000.0));
- const time_t tt = now_s + delta_s;
- struct tm tm = SysLocalTime(tt); // in seconds
- char tmp_buf[512];
- strftime(tmp_buf, 512, "%X.", &tm);
- ostringstream out;
- out << tmp_buf << setfill('0') << setw(6) << (count_microseconds(timestamp.time_since_epoch()) % 1000000) << " [SYST]";
- return out.str();
- }
- #ifdef ENABLE_STDCXX_SYNC
- bool StartThread(CThread& th, ThreadFunc&& f, void* args, const string& name)
- #else
- bool StartThread(CThread& th, void* (*f) (void*), void* args, const string& name)
- #endif
- {
- ThreadName tn(name);
- try
- {
- #if HAVE_FULL_CXX11 || defined(ENABLE_STDCXX_SYNC)
- th = CThread(f, args);
- #else
- // No move semantics in C++03, therefore using a dedicated function
- th.create_thread(f, args);
- #endif
- }
- #if ENABLE_HEAVY_LOGGING
- catch (const CThreadException& e)
- #else
- catch (const CThreadException&)
- #endif
- {
- HLOGC(inlog.Debug, log << name << ": failed to start thread. " << e.what());
- return false;
- }
- return true;
- }
- } // namespace sync
- } // namespace srt
- ////////////////////////////////////////////////////////////////////////////////
- //
- // CEvent class
- //
- ////////////////////////////////////////////////////////////////////////////////
- srt::sync::CEvent::CEvent()
- {
- #ifndef _WIN32
- m_cond.init();
- #endif
- }
- srt::sync::CEvent::~CEvent()
- {
- #ifndef _WIN32
- m_cond.destroy();
- #endif
- }
- bool srt::sync::CEvent::lock_wait_until(const TimePoint<steady_clock>& tp)
- {
- UniqueLock lock(m_lock);
- return m_cond.wait_until(lock, tp);
- }
- void srt::sync::CEvent::notify_one()
- {
- return m_cond.notify_one();
- }
- void srt::sync::CEvent::notify_all()
- {
- return m_cond.notify_all();
- }
- bool srt::sync::CEvent::lock_wait_for(const steady_clock::duration& rel_time)
- {
- UniqueLock lock(m_lock);
- return m_cond.wait_for(lock, rel_time);
- }
- bool srt::sync::CEvent::wait_for(UniqueLock& lock, const steady_clock::duration& rel_time)
- {
- return m_cond.wait_for(lock, rel_time);
- }
- void srt::sync::CEvent::lock_wait()
- {
- UniqueLock lock(m_lock);
- return wait(lock);
- }
- void srt::sync::CEvent::wait(UniqueLock& lock)
- {
- return m_cond.wait(lock);
- }
- namespace srt {
- namespace sync {
- srt::sync::CEvent g_Sync;
- } // namespace sync
- } // namespace srt
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Timer
- //
- ////////////////////////////////////////////////////////////////////////////////
- srt::sync::CTimer::CTimer()
- {
- }
- srt::sync::CTimer::~CTimer()
- {
- }
- bool srt::sync::CTimer::sleep_until(TimePoint<steady_clock> tp)
- {
- // The class member m_sched_time can be used to interrupt the sleep.
- // Refer to Timer::interrupt().
- enterCS(m_event.mutex());
- m_tsSchedTime = tp;
- leaveCS(m_event.mutex());
- #if USE_BUSY_WAITING
- #if defined(_WIN32)
- // 10 ms on Windows: bad accuracy of timers
- const steady_clock::duration
- td_threshold = milliseconds_from(10);
- #else
- // 1 ms on non-Windows platforms
- const steady_clock::duration
- td_threshold = milliseconds_from(1);
- #endif
- #endif // USE_BUSY_WAITING
- TimePoint<steady_clock> cur_tp = steady_clock::now();
-
- while (cur_tp < m_tsSchedTime)
- {
- #if USE_BUSY_WAITING
- steady_clock::duration td_wait = m_tsSchedTime - cur_tp;
- if (td_wait <= 2 * td_threshold)
- break;
- td_wait -= td_threshold;
- m_event.lock_wait_for(td_wait);
- #else
- m_event.lock_wait_until(m_tsSchedTime);
- #endif // USE_BUSY_WAITING
- cur_tp = steady_clock::now();
- }
- #if USE_BUSY_WAITING
- while (cur_tp < m_tsSchedTime)
- {
- #ifdef IA32
- __asm__ volatile ("pause; rep; nop; nop; nop; nop; nop;");
- #elif IA64
- __asm__ volatile ("nop 0; nop 0; nop 0; nop 0; nop 0;");
- #elif AMD64
- __asm__ volatile ("nop; nop; nop; nop; nop;");
- #elif defined(_WIN32) && !defined(__MINGW32__)
- __nop();
- __nop();
- __nop();
- __nop();
- __nop();
- #endif
- cur_tp = steady_clock::now();
- }
- #endif // USE_BUSY_WAITING
- return cur_tp >= m_tsSchedTime;
- }
- void srt::sync::CTimer::interrupt()
- {
- UniqueLock lck(m_event.mutex());
- m_tsSchedTime = steady_clock::now();
- m_event.notify_all();
- }
- void srt::sync::CTimer::tick()
- {
- m_event.notify_one();
- }
- void srt::sync::CGlobEvent::triggerEvent()
- {
- return g_Sync.notify_one();
- }
- bool srt::sync::CGlobEvent::waitForEvent()
- {
- return g_Sync.lock_wait_for(milliseconds_from(10));
- }
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Random
- //
- ////////////////////////////////////////////////////////////////////////////////
- namespace srt
- {
- #if HAVE_CXX11
- static std::mt19937& randomGen()
- {
- static std::random_device s_RandomDevice;
- static std::mt19937 s_GenMT19937(s_RandomDevice());
- return s_GenMT19937;
- }
- #elif defined(_WIN32) && defined(__MINGW32__)
- static void initRandSeed()
- {
- const int64_t seed = sync::steady_clock::now().time_since_epoch().count();
- srand((unsigned int) seed);
- }
- static pthread_once_t s_InitRandSeedOnce = PTHREAD_ONCE_INIT;
- #else
- static unsigned int genRandSeed()
- {
- // Duration::count() does not depend on any global objects,
- // therefore it is preferred over count_microseconds(..).
- const int64_t seed = sync::steady_clock::now().time_since_epoch().count();
- return (unsigned int) seed;
- }
- static unsigned int* getRandSeed()
- {
- static unsigned int s_uRandSeed = genRandSeed();
- return &s_uRandSeed;
- }
- #endif
- }
- int srt::sync::genRandomInt(int minVal, int maxVal)
- {
- // This Meyers singleton initialization is thread-safe since C++11, but is not thread-safe in C++03.
- // A mutex to protect simultaneous access to the random device.
- // Thread-local storage could be used here instead to store the seed / random device.
- // However the generator is not used often (Initial Socket ID, Initial sequence number, FileCC),
- // so sharing a single seed among threads should not impact the performance.
- static sync::Mutex s_mtxRandomDevice;
- sync::ScopedLock lck(s_mtxRandomDevice);
- #if HAVE_CXX11
- uniform_int_distribution<> dis(minVal, maxVal);
- return dis(randomGen());
- #else
- #if defined(__MINGW32__)
- // No rand_r(..) for MinGW.
- pthread_once(&s_InitRandSeedOnce, initRandSeed);
- // rand() returns a pseudo-random integer in the range 0 to RAND_MAX inclusive
- // (i.e., the mathematical range [0, RAND_MAX]).
- // Therefore, rand_0_1 belongs to [0.0, 1.0].
- const double rand_0_1 = double(rand()) / RAND_MAX;
- #else // not __MINGW32__
- // rand_r(..) returns a pseudo-random integer in the range 0 to RAND_MAX inclusive
- // (i.e., the mathematical range [0, RAND_MAX]).
- // Therefore, rand_0_1 belongs to [0.0, 1.0].
- const double rand_0_1 = double(rand_r(getRandSeed())) / RAND_MAX;
- #endif
- // Map onto [minVal, maxVal].
- // Note. There is a minuscule probablity to get maxVal+1 as the result.
- // So we have to use long long to handle cases when maxVal = INT32_MAX.
- // Also we must check 'res' does not exceed maxVal,
- // which may happen if rand_0_1 = 1, even though the chances are low.
- const long long llMaxVal = maxVal;
- const int res = minVal + static_cast<int>((llMaxVal + 1 - minVal) * rand_0_1);
- return min(res, maxVal);
- #endif // HAVE_CXX11
- }
|