sync.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*
  2. * SRT - Secure, Reliable, Transport
  3. * Copyright (c) 2019 Haivision Systems Inc.
  4. *
  5. * This Source Code Form is subject to the terms of the Mozilla Public
  6. * License, v. 2.0. If a copy of the MPL was not distributed with this
  7. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  8. *
  9. */
  10. #include "platform_sys.h"
  11. #include <iomanip>
  12. #include <stdexcept>
  13. #include <cmath>
  14. #include "sync.h"
  15. #include "srt.h"
  16. #include "srt_compat.h"
  17. #include "logging.h"
  18. #include "common.h"
  19. // HAVE_CXX11 is defined in utilities.h, included with common.h.
  20. // The following conditional inclusion must go after common.h.
  21. #if HAVE_CXX11
  22. #include <random>
  23. #endif
  24. namespace srt_logging
  25. {
  26. extern Logger inlog;
  27. }
  28. using namespace srt_logging;
  29. using namespace std;
  30. namespace srt
  31. {
  32. namespace sync
  33. {
  34. std::string FormatTime(const steady_clock::time_point& timestamp)
  35. {
  36. if (is_zero(timestamp))
  37. {
  38. // Use special string for 0
  39. return "00:00:00.000000 [STDY]";
  40. }
  41. const int decimals = clockSubsecondPrecision();
  42. const uint64_t total_sec = count_seconds(timestamp.time_since_epoch());
  43. const uint64_t days = total_sec / (60 * 60 * 24);
  44. const uint64_t hours = total_sec / (60 * 60) - days * 24;
  45. const uint64_t minutes = total_sec / 60 - (days * 24 * 60) - hours * 60;
  46. const uint64_t seconds = total_sec - (days * 24 * 60 * 60) - hours * 60 * 60 - minutes * 60;
  47. ostringstream out;
  48. if (days)
  49. out << days << "D ";
  50. out << setfill('0') << setw(2) << hours << ":"
  51. << setfill('0') << setw(2) << minutes << ":"
  52. << setfill('0') << setw(2) << seconds << "."
  53. << setfill('0') << setw(decimals) << (timestamp - seconds_from(total_sec)).time_since_epoch().count() << " [STDY]";
  54. return out.str();
  55. }
  56. std::string FormatTimeSys(const steady_clock::time_point& timestamp)
  57. {
  58. const time_t now_s = ::time(NULL); // get current time in seconds
  59. const steady_clock::time_point now_timestamp = steady_clock::now();
  60. const int64_t delta_us = count_microseconds(timestamp - now_timestamp);
  61. const int64_t delta_s =
  62. static_cast<int64_t>(floor((static_cast<double>(count_microseconds(now_timestamp.time_since_epoch()) % 1000000) + delta_us) / 1000000.0));
  63. const time_t tt = now_s + delta_s;
  64. struct tm tm = SysLocalTime(tt); // in seconds
  65. char tmp_buf[512];
  66. strftime(tmp_buf, 512, "%X.", &tm);
  67. ostringstream out;
  68. out << tmp_buf << setfill('0') << setw(6) << (count_microseconds(timestamp.time_since_epoch()) % 1000000) << " [SYST]";
  69. return out.str();
  70. }
  71. #ifdef ENABLE_STDCXX_SYNC
  72. bool StartThread(CThread& th, ThreadFunc&& f, void* args, const string& name)
  73. #else
  74. bool StartThread(CThread& th, void* (*f) (void*), void* args, const string& name)
  75. #endif
  76. {
  77. ThreadName tn(name);
  78. try
  79. {
  80. #if HAVE_FULL_CXX11 || defined(ENABLE_STDCXX_SYNC)
  81. th = CThread(f, args);
  82. #else
  83. // No move semantics in C++03, therefore using a dedicated function
  84. th.create_thread(f, args);
  85. #endif
  86. }
  87. #if ENABLE_HEAVY_LOGGING
  88. catch (const CThreadException& e)
  89. #else
  90. catch (const CThreadException&)
  91. #endif
  92. {
  93. HLOGC(inlog.Debug, log << name << ": failed to start thread. " << e.what());
  94. return false;
  95. }
  96. return true;
  97. }
  98. } // namespace sync
  99. } // namespace srt
  100. ////////////////////////////////////////////////////////////////////////////////
  101. //
  102. // CEvent class
  103. //
  104. ////////////////////////////////////////////////////////////////////////////////
  105. srt::sync::CEvent::CEvent()
  106. {
  107. #ifndef _WIN32
  108. m_cond.init();
  109. #endif
  110. }
  111. srt::sync::CEvent::~CEvent()
  112. {
  113. #ifndef _WIN32
  114. m_cond.destroy();
  115. #endif
  116. }
  117. bool srt::sync::CEvent::lock_wait_until(const TimePoint<steady_clock>& tp)
  118. {
  119. UniqueLock lock(m_lock);
  120. return m_cond.wait_until(lock, tp);
  121. }
  122. void srt::sync::CEvent::notify_one()
  123. {
  124. return m_cond.notify_one();
  125. }
  126. void srt::sync::CEvent::notify_all()
  127. {
  128. return m_cond.notify_all();
  129. }
  130. bool srt::sync::CEvent::lock_wait_for(const steady_clock::duration& rel_time)
  131. {
  132. UniqueLock lock(m_lock);
  133. return m_cond.wait_for(lock, rel_time);
  134. }
  135. bool srt::sync::CEvent::wait_for(UniqueLock& lock, const steady_clock::duration& rel_time)
  136. {
  137. return m_cond.wait_for(lock, rel_time);
  138. }
  139. void srt::sync::CEvent::lock_wait()
  140. {
  141. UniqueLock lock(m_lock);
  142. return wait(lock);
  143. }
  144. void srt::sync::CEvent::wait(UniqueLock& lock)
  145. {
  146. return m_cond.wait(lock);
  147. }
  148. namespace srt {
  149. namespace sync {
  150. srt::sync::CEvent g_Sync;
  151. } // namespace sync
  152. } // namespace srt
  153. ////////////////////////////////////////////////////////////////////////////////
  154. //
  155. // Timer
  156. //
  157. ////////////////////////////////////////////////////////////////////////////////
  158. srt::sync::CTimer::CTimer()
  159. {
  160. }
  161. srt::sync::CTimer::~CTimer()
  162. {
  163. }
  164. bool srt::sync::CTimer::sleep_until(TimePoint<steady_clock> tp)
  165. {
  166. // The class member m_sched_time can be used to interrupt the sleep.
  167. // Refer to Timer::interrupt().
  168. enterCS(m_event.mutex());
  169. m_tsSchedTime = tp;
  170. leaveCS(m_event.mutex());
  171. #if USE_BUSY_WAITING
  172. #if defined(_WIN32)
  173. // 10 ms on Windows: bad accuracy of timers
  174. const steady_clock::duration
  175. td_threshold = milliseconds_from(10);
  176. #else
  177. // 1 ms on non-Windows platforms
  178. const steady_clock::duration
  179. td_threshold = milliseconds_from(1);
  180. #endif
  181. #endif // USE_BUSY_WAITING
  182. TimePoint<steady_clock> cur_tp = steady_clock::now();
  183. while (cur_tp < m_tsSchedTime)
  184. {
  185. #if USE_BUSY_WAITING
  186. steady_clock::duration td_wait = m_tsSchedTime - cur_tp;
  187. if (td_wait <= 2 * td_threshold)
  188. break;
  189. td_wait -= td_threshold;
  190. m_event.lock_wait_for(td_wait);
  191. #else
  192. m_event.lock_wait_until(m_tsSchedTime);
  193. #endif // USE_BUSY_WAITING
  194. cur_tp = steady_clock::now();
  195. }
  196. #if USE_BUSY_WAITING
  197. while (cur_tp < m_tsSchedTime)
  198. {
  199. #ifdef IA32
  200. __asm__ volatile ("pause; rep; nop; nop; nop; nop; nop;");
  201. #elif IA64
  202. __asm__ volatile ("nop 0; nop 0; nop 0; nop 0; nop 0;");
  203. #elif AMD64
  204. __asm__ volatile ("nop; nop; nop; nop; nop;");
  205. #elif defined(_WIN32) && !defined(__MINGW32__)
  206. __nop();
  207. __nop();
  208. __nop();
  209. __nop();
  210. __nop();
  211. #endif
  212. cur_tp = steady_clock::now();
  213. }
  214. #endif // USE_BUSY_WAITING
  215. return cur_tp >= m_tsSchedTime;
  216. }
  217. void srt::sync::CTimer::interrupt()
  218. {
  219. UniqueLock lck(m_event.mutex());
  220. m_tsSchedTime = steady_clock::now();
  221. m_event.notify_all();
  222. }
  223. void srt::sync::CTimer::tick()
  224. {
  225. m_event.notify_one();
  226. }
  227. void srt::sync::CGlobEvent::triggerEvent()
  228. {
  229. return g_Sync.notify_one();
  230. }
  231. bool srt::sync::CGlobEvent::waitForEvent()
  232. {
  233. return g_Sync.lock_wait_for(milliseconds_from(10));
  234. }
  235. ////////////////////////////////////////////////////////////////////////////////
  236. //
  237. // Random
  238. //
  239. ////////////////////////////////////////////////////////////////////////////////
  240. namespace srt
  241. {
  242. #if HAVE_CXX11
  243. static std::mt19937& randomGen()
  244. {
  245. static std::random_device s_RandomDevice;
  246. static std::mt19937 s_GenMT19937(s_RandomDevice());
  247. return s_GenMT19937;
  248. }
  249. #elif defined(_WIN32) && defined(__MINGW32__)
  250. static void initRandSeed()
  251. {
  252. const int64_t seed = sync::steady_clock::now().time_since_epoch().count();
  253. srand((unsigned int) seed);
  254. }
  255. static pthread_once_t s_InitRandSeedOnce = PTHREAD_ONCE_INIT;
  256. #else
  257. static unsigned int genRandSeed()
  258. {
  259. // Duration::count() does not depend on any global objects,
  260. // therefore it is preferred over count_microseconds(..).
  261. const int64_t seed = sync::steady_clock::now().time_since_epoch().count();
  262. return (unsigned int) seed;
  263. }
  264. static unsigned int* getRandSeed()
  265. {
  266. static unsigned int s_uRandSeed = genRandSeed();
  267. return &s_uRandSeed;
  268. }
  269. #endif
  270. }
  271. int srt::sync::genRandomInt(int minVal, int maxVal)
  272. {
  273. // This Meyers singleton initialization is thread-safe since C++11, but is not thread-safe in C++03.
  274. // A mutex to protect simultaneous access to the random device.
  275. // Thread-local storage could be used here instead to store the seed / random device.
  276. // However the generator is not used often (Initial Socket ID, Initial sequence number, FileCC),
  277. // so sharing a single seed among threads should not impact the performance.
  278. static sync::Mutex s_mtxRandomDevice;
  279. sync::ScopedLock lck(s_mtxRandomDevice);
  280. #if HAVE_CXX11
  281. uniform_int_distribution<> dis(minVal, maxVal);
  282. return dis(randomGen());
  283. #else
  284. #if defined(__MINGW32__)
  285. // No rand_r(..) for MinGW.
  286. pthread_once(&s_InitRandSeedOnce, initRandSeed);
  287. // rand() returns a pseudo-random integer in the range 0 to RAND_MAX inclusive
  288. // (i.e., the mathematical range [0, RAND_MAX]).
  289. // Therefore, rand_0_1 belongs to [0.0, 1.0].
  290. const double rand_0_1 = double(rand()) / RAND_MAX;
  291. #else // not __MINGW32__
  292. // rand_r(..) returns a pseudo-random integer in the range 0 to RAND_MAX inclusive
  293. // (i.e., the mathematical range [0, RAND_MAX]).
  294. // Therefore, rand_0_1 belongs to [0.0, 1.0].
  295. const double rand_0_1 = double(rand_r(getRandSeed())) / RAND_MAX;
  296. #endif
  297. // Map onto [minVal, maxVal].
  298. // Note. There is a minuscule probablity to get maxVal+1 as the result.
  299. // So we have to use long long to handle cases when maxVal = INT32_MAX.
  300. // Also we must check 'res' does not exceed maxVal,
  301. // which may happen if rand_0_1 = 1, even though the chances are low.
  302. const long long llMaxVal = maxVal;
  303. const int res = minVal + static_cast<int>((llMaxVal + 1 - minVal) * rand_0_1);
  304. return min(res, maxVal);
  305. #endif // HAVE_CXX11
  306. }