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 #pragma once
11 #ifndef INC_SRT_SYNC_H
12 #define INC_SRT_SYNC_H
13
14 #include <cstdlib>
15 #include <limits>
16 #ifdef ENABLE_STDCXX_SYNC
17 #include <chrono>
18 #include <thread>
19 #include <mutex>
20 #include <condition_variable>
21 #include <atomic>
22 #define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_STDCXX_STEADY
23 #define SRT_SYNC_CLOCK_STR "STDCXX_STEADY"
24 #else
25 #include <pthread.h>
26
27 // Defile clock type to use
28 #ifdef IA32
29 #define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_IA32_RDTSC
30 #define SRT_SYNC_CLOCK_STR "IA32_RDTSC"
31 #elif defined(IA64)
32 #define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_IA64_ITC
33 #define SRT_SYNC_CLOCK_STR "IA64_ITC"
34 #elif defined(AMD64)
35 #define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_AMD64_RDTSC
36 #define SRT_SYNC_CLOCK_STR "AMD64_RDTSC"
37 #elif defined(_WIN32)
38 #define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_WINQPC
39 #define SRT_SYNC_CLOCK_STR "WINQPC"
40 #elif TARGET_OS_MAC
41 #define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_MACH_ABSTIME
42 #define SRT_SYNC_CLOCK_STR "MACH_ABSTIME"
43 #elif defined(ENABLE_MONOTONIC_CLOCK)
44 #define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_GETTIME_MONOTONIC
45 #define SRT_SYNC_CLOCK_STR "GETTIME_MONOTONIC"
46 #else
47 #define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY
48 #define SRT_SYNC_CLOCK_STR "POSIX_GETTIMEOFDAY"
49 #endif
50
51 #endif // ENABLE_STDCXX_SYNC
52
53 #include "srt.h"
54 #include "utilities.h"
55 #include "srt_attr_defs.h"
56
57 class CUDTException; // defined in common.h
58
59 namespace srt
60 {
61 namespace sync
62 {
63
64 ///////////////////////////////////////////////////////////////////////////////
65 //
66 // Duration class
67 //
68 ///////////////////////////////////////////////////////////////////////////////
69
70 #if ENABLE_STDCXX_SYNC
71
72 template <class Clock>
73 using Duration = std::chrono::duration<Clock>;
74
75 #else
76
77 /// Class template srt::sync::Duration represents a time interval.
78 /// It consists of a count of ticks of _Clock.
79 /// It is a wrapper of system timers in case of non-C++11 chrono build.
80 template <class Clock>
81 class Duration
82 {
83 public:
84 Duration()
85 : m_duration(0)
86 {
87 }
88
89 explicit Duration(int64_t d)
90 : m_duration(d)
91 {
92 }
93
94 public:
95 inline int64_t count() const { return m_duration; }
96
97 static Duration zero() { return Duration(); }
98
99 public: // Relational operators
100 inline bool operator>=(const Duration& rhs) const { return m_duration >= rhs.m_duration; }
101 inline bool operator>(const Duration& rhs) const { return m_duration > rhs.m_duration; }
102 inline bool operator==(const Duration& rhs) const { return m_duration == rhs.m_duration; }
103 inline bool operator!=(const Duration& rhs) const { return m_duration != rhs.m_duration; }
104 inline bool operator<=(const Duration& rhs) const { return m_duration <= rhs.m_duration; }
105 inline bool operator<(const Duration& rhs) const { return m_duration < rhs.m_duration; }
106
107 public: // Assignment operators
108 inline void operator*=(const int64_t mult) { m_duration = static_cast<int64_t>(m_duration * mult); }
109 inline void operator+=(const Duration& rhs) { m_duration += rhs.m_duration; }
110 inline void operator-=(const Duration& rhs) { m_duration -= rhs.m_duration; }
111
112 inline Duration operator+(const Duration& rhs) const { return Duration(m_duration + rhs.m_duration); }
113 inline Duration operator-(const Duration& rhs) const { return Duration(m_duration - rhs.m_duration); }
114 inline Duration operator*(const int64_t& rhs) const { return Duration(m_duration * rhs); }
115 inline Duration operator/(const int64_t& rhs) const { return Duration(m_duration / rhs); }
116
117 private:
118 // int64_t range is from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
119 int64_t m_duration;
120 };
121
122 #endif // ENABLE_STDCXX_SYNC
123
124 ///////////////////////////////////////////////////////////////////////////////
125 //
126 // TimePoint and steadt_clock classes
127 //
128 ///////////////////////////////////////////////////////////////////////////////
129
130 #if ENABLE_STDCXX_SYNC
131
132 using steady_clock = std::chrono::steady_clock;
133
134 template <class Clock, class Duration = typename Clock::duration>
135 using time_point = std::chrono::time_point<Clock, Duration>;
136
137 template <class Clock>
138 using TimePoint = std::chrono::time_point<Clock>;
139
140 template <class Clock, class Duration = typename Clock::duration>
is_zero(const time_point<Clock,Duration> & tp)141 inline bool is_zero(const time_point<Clock, Duration> &tp)
142 {
143 return tp.time_since_epoch() == Clock::duration::zero();
144 }
145
is_zero(const steady_clock::time_point & t)146 inline bool is_zero(const steady_clock::time_point& t)
147 {
148 return t == steady_clock::time_point();
149 }
150
151 #else
152 template <class Clock>
153 class TimePoint;
154
155 class steady_clock
156 {
157 public:
158 typedef Duration<steady_clock> duration;
159 typedef TimePoint<steady_clock> time_point;
160
161 public:
162 static time_point now();
163 };
164
165 /// Represents a point in time
166 template <class Clock>
167 class TimePoint
168 {
169 public:
TimePoint()170 TimePoint()
171 : m_timestamp(0)
172 {
173 }
174
TimePoint(uint64_t tp)175 explicit TimePoint(uint64_t tp)
176 : m_timestamp(tp)
177 {
178 }
179
TimePoint(const TimePoint<Clock> & other)180 TimePoint(const TimePoint<Clock>& other)
181 : m_timestamp(other.m_timestamp)
182 {
183 }
184
TimePoint(const Duration<Clock> & duration_since_epoch)185 TimePoint(const Duration<Clock>& duration_since_epoch)
186 : m_timestamp(duration_since_epoch.count())
187 {
188 }
189
~TimePoint()190 ~TimePoint() {}
191
192 public: // Relational operators
193 inline bool operator<(const TimePoint<Clock>& rhs) const { return m_timestamp < rhs.m_timestamp; }
194 inline bool operator<=(const TimePoint<Clock>& rhs) const { return m_timestamp <= rhs.m_timestamp; }
195 inline bool operator==(const TimePoint<Clock>& rhs) const { return m_timestamp == rhs.m_timestamp; }
196 inline bool operator!=(const TimePoint<Clock>& rhs) const { return m_timestamp != rhs.m_timestamp; }
197 inline bool operator>=(const TimePoint<Clock>& rhs) const { return m_timestamp >= rhs.m_timestamp; }
198 inline bool operator>(const TimePoint<Clock>& rhs) const { return m_timestamp > rhs.m_timestamp; }
199
200 public: // Arithmetic operators
201 inline Duration<Clock> operator-(const TimePoint<Clock>& rhs) const
202 {
203 return Duration<Clock>(m_timestamp - rhs.m_timestamp);
204 }
205 inline TimePoint operator+(const Duration<Clock>& rhs) const { return TimePoint(m_timestamp + rhs.count()); }
206 inline TimePoint operator-(const Duration<Clock>& rhs) const { return TimePoint(m_timestamp - rhs.count()); }
207
208 public: // Assignment operators
209 inline void operator=(const TimePoint<Clock>& rhs) { m_timestamp = rhs.m_timestamp; }
210 inline void operator+=(const Duration<Clock>& rhs) { m_timestamp += rhs.count(); }
211 inline void operator-=(const Duration<Clock>& rhs) { m_timestamp -= rhs.count(); }
212
213 public: //
min()214 static inline ATR_CONSTEXPR TimePoint min() { return TimePoint(std::numeric_limits<uint64_t>::min()); }
max()215 static inline ATR_CONSTEXPR TimePoint max() { return TimePoint(std::numeric_limits<uint64_t>::max()); }
216
217 public:
218 Duration<Clock> time_since_epoch() const;
219
220 private:
221 uint64_t m_timestamp;
222 };
223
224 template <>
225 srt::sync::Duration<srt::sync::steady_clock> srt::sync::TimePoint<srt::sync::steady_clock>::time_since_epoch() const;
226
227 inline Duration<steady_clock> operator*(const int& lhs, const Duration<steady_clock>& rhs)
228 {
229 return rhs * lhs;
230 }
231
232 #endif // ENABLE_STDCXX_SYNC
233
234 // NOTE: Moved the following class definitons to "atomic_clock.h"
235 // template <class Clock>
236 // class AtomicDuration;
237 // template <class Clock>
238 // class AtomicClock;
239
240 ///////////////////////////////////////////////////////////////////////////////
241 //
242 // Duration and timepoint conversions
243 //
244 ///////////////////////////////////////////////////////////////////////////////
245
246 /// Function return number of decimals in a subsecond precision.
247 /// E.g. for a microsecond accuracy of steady_clock the return would be 6.
248 /// For a nanosecond accuracy of the steady_clock the return value would be 9.
249 int clockSubsecondPrecision();
250
251 #if ENABLE_STDCXX_SYNC
252
count_microseconds(const steady_clock::duration & t)253 inline long long count_microseconds(const steady_clock::duration &t)
254 {
255 return std::chrono::duration_cast<std::chrono::microseconds>(t).count();
256 }
257
count_microseconds(const steady_clock::time_point tp)258 inline long long count_microseconds(const steady_clock::time_point tp)
259 {
260 return std::chrono::duration_cast<std::chrono::microseconds>(tp.time_since_epoch()).count();
261 }
262
count_milliseconds(const steady_clock::duration & t)263 inline long long count_milliseconds(const steady_clock::duration &t)
264 {
265 return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
266 }
267
count_seconds(const steady_clock::duration & t)268 inline long long count_seconds(const steady_clock::duration &t)
269 {
270 return std::chrono::duration_cast<std::chrono::seconds>(t).count();
271 }
272
microseconds_from(int64_t t_us)273 inline steady_clock::duration microseconds_from(int64_t t_us)
274 {
275 return std::chrono::microseconds(t_us);
276 }
277
milliseconds_from(int64_t t_ms)278 inline steady_clock::duration milliseconds_from(int64_t t_ms)
279 {
280 return std::chrono::milliseconds(t_ms);
281 }
282
seconds_from(int64_t t_s)283 inline steady_clock::duration seconds_from(int64_t t_s)
284 {
285 return std::chrono::seconds(t_s);
286 }
287
288 #else
289
290 int64_t count_microseconds(const steady_clock::duration& t);
291 int64_t count_milliseconds(const steady_clock::duration& t);
292 int64_t count_seconds(const steady_clock::duration& t);
293
294 Duration<steady_clock> microseconds_from(int64_t t_us);
295 Duration<steady_clock> milliseconds_from(int64_t t_ms);
296 Duration<steady_clock> seconds_from(int64_t t_s);
297
is_zero(const TimePoint<steady_clock> & t)298 inline bool is_zero(const TimePoint<steady_clock>& t)
299 {
300 return t == TimePoint<steady_clock>();
301 }
302
303 #endif // ENABLE_STDCXX_SYNC
304
305
306 ///////////////////////////////////////////////////////////////////////////////
307 //
308 // Mutex section
309 //
310 ///////////////////////////////////////////////////////////////////////////////
311
312 #if ENABLE_STDCXX_SYNC
313 using Mutex = std::mutex;
314 using UniqueLock = std::unique_lock<std::mutex>;
315 using ScopedLock = std::lock_guard<std::mutex>;
316 #else
317 /// Mutex is a class wrapper, that should mimic the std::chrono::mutex class.
318 /// At the moment the extra function ref() is temporally added to allow calls
319 /// to pthread_cond_timedwait(). Will be removed by introducing CEvent.
320 class SRT_ATTR_CAPABILITY("mutex") Mutex
321 {
322 friend class SyncEvent;
323
324 public:
325 Mutex();
326 ~Mutex();
327
328 public:
329 int lock() SRT_ATTR_ACQUIRE();
330 int unlock() SRT_ATTR_RELEASE();
331
332 /// @return true if the lock was acquired successfully, otherwise false
333 bool try_lock() SRT_ATTR_TRY_ACQUIRE(true);
334
335 // TODO: To be removed with introduction of the CEvent.
ref()336 pthread_mutex_t& ref() { return m_mutex; }
337
338 private:
339 pthread_mutex_t m_mutex;
340 };
341
342 /// A pthread version of std::chrono::scoped_lock<mutex> (or lock_guard for C++11)
343 class SRT_ATTR_SCOPED_CAPABILITY ScopedLock
344 {
345 public:
346 SRT_ATTR_ACQUIRE(m)
347 ScopedLock(Mutex& m);
348
349 SRT_ATTR_RELEASE()
350 ~ScopedLock();
351
352 private:
353 Mutex& m_mutex;
354 };
355
356 /// A pthread version of std::chrono::unique_lock<mutex>
357 class SRT_ATTR_SCOPED_CAPABILITY UniqueLock
358 {
359 friend class SyncEvent;
360
361 public:
362 SRT_ATTR_ACQUIRE(m_Mutex)
363 UniqueLock(Mutex &m);
364
365 SRT_ATTR_RELEASE(m_Mutex)
366 ~UniqueLock();
367
368 public:
369 SRT_ATTR_ACQUIRE(m_Mutex)
370 void lock();
371
372 SRT_ATTR_RELEASE(m_Mutex)
373 void unlock();
374
375 SRT_ATTR_RETURN_CAPABILITY(m_Mutex)
376 Mutex* mutex(); // reflects C++11 unique_lock::mutex()
377
378 private:
379 int m_iLocked;
380 Mutex& m_Mutex;
381 };
382 #endif // ENABLE_STDCXX_SYNC
383
enterCS(Mutex & m)384 inline void enterCS(Mutex& m) SRT_ATTR_EXCLUDES(m) SRT_ATTR_ACQUIRE(m) { m.lock(); }
385
tryEnterCS(Mutex & m)386 inline bool tryEnterCS(Mutex& m) SRT_ATTR_EXCLUDES(m) SRT_ATTR_TRY_ACQUIRE(true, m) { return m.try_lock(); }
387
leaveCS(Mutex & m)388 inline void leaveCS(Mutex& m) SRT_ATTR_REQUIRES(m) SRT_ATTR_RELEASE(m) { m.unlock(); }
389
390 class InvertedLock
391 {
392 Mutex& m_mtx;
393
394 public:
SRT_ATTR_REQUIRES(m)395 SRT_ATTR_REQUIRES(m) SRT_ATTR_RELEASE(m)
396 InvertedLock(Mutex& m)
397 : m_mtx(m)
398 {
399 m_mtx.unlock();
400 }
401
402 SRT_ATTR_ACQUIRE(m_mtx)
~InvertedLock()403 ~InvertedLock()
404 {
405 m_mtx.lock();
406 }
407 };
408
setupMutex(Mutex &,const char *)409 inline void setupMutex(Mutex&, const char*) {}
releaseMutex(Mutex &)410 inline void releaseMutex(Mutex&) {}
411
412 ////////////////////////////////////////////////////////////////////////////////
413 //
414 // Condition section
415 //
416 ////////////////////////////////////////////////////////////////////////////////
417
418 class Condition
419 {
420 public:
421 Condition();
422 ~Condition();
423
424 public:
425 /// These functions do not align with C++11 version. They are here hopefully as a temporal solution
426 /// to avoud issues with static initialization of CV on windows.
427 void init();
428 void destroy();
429
430 public:
431 /// Causes the current thread to block until the condition variable is notified
432 /// or a spurious wakeup occurs.
433 ///
434 /// @param lock Corresponding mutex locked by UniqueLock
435 void wait(UniqueLock& lock);
436
437 /// Atomically releases lock, blocks the current executing thread,
438 /// and adds it to the list of threads waiting on *this.
439 /// The thread will be unblocked when notify_all() or notify_one() is executed,
440 /// or when the relative timeout rel_time expires.
441 /// It may also be unblocked spuriously. When unblocked, regardless of the reason,
442 /// lock is reacquired and wait_for() exits.
443 ///
444 /// @returns false if the relative timeout specified by rel_time expired,
445 /// true otherwise (signal or spurious wake up).
446 ///
447 /// @note Calling this function if lock.mutex()
448 /// is not locked by the current thread is undefined behavior.
449 /// Calling this function if lock.mutex() is not the same mutex as the one
450 /// used by all other threads that are currently waiting on the same
451 /// condition variable is undefined behavior.
452 bool wait_for(UniqueLock& lock, const steady_clock::duration& rel_time);
453
454 /// Causes the current thread to block until the condition variable is notified,
455 /// a specific time is reached, or a spurious wakeup occurs.
456 ///
457 /// @param[in] lock an object of type UniqueLock, which must be locked by the current thread
458 /// @param[in] timeout_time an object of type time_point representing the time when to stop waiting
459 ///
460 /// @returns false if the relative timeout specified by timeout_time expired,
461 /// true otherwise (signal or spurious wake up).
462 bool wait_until(UniqueLock& lock, const steady_clock::time_point& timeout_time);
463
464 /// Calling notify_one() unblocks one of the waiting threads,
465 /// if any threads are waiting on this CV.
466 void notify_one();
467
468 /// Unblocks all threads currently waiting for this CV.
469 void notify_all();
470
471 private:
472 #if ENABLE_STDCXX_SYNC
473 std::condition_variable m_cv;
474 #else
475 pthread_cond_t m_cv;
476 #endif
477 };
478
setupCond(Condition & cv,const char *)479 inline void setupCond(Condition& cv, const char*) { cv.init(); }
releaseCond(Condition & cv)480 inline void releaseCond(Condition& cv) { cv.destroy(); }
481
482 ///////////////////////////////////////////////////////////////////////////////
483 //
484 // Event (CV) section
485 //
486 ///////////////////////////////////////////////////////////////////////////////
487
488 // This class is used for condition variable combined with mutex by different ways.
489 // This should provide a cleaner API around locking with debug-logging inside.
490 class CSync
491 {
492 Condition* m_cond;
493 UniqueLock* m_locker;
494
495 public:
496 // Locked version: must be declared only after the declaration of UniqueLock,
497 // which has locked the mutex. On this delegate you should call only
498 // signal_locked() and pass the UniqueLock variable that should remain locked.
499 // Also wait() and wait_for() can be used only with this socket.
CSync(Condition & cond,UniqueLock & g)500 CSync(Condition& cond, UniqueLock& g)
501 : m_cond(&cond), m_locker(&g)
502 {
503 // XXX it would be nice to check whether the owner is also current thread
504 // but this can't be done portable way.
505
506 // When constructed by this constructor, the user is expected
507 // to only call signal_locked() function. You should pass the same guard
508 // variable that you have used for construction as its argument.
509 }
510
511 // COPY CONSTRUCTOR: DEFAULT!
512
513 // Wait indefinitely, until getting a signal on CV.
wait()514 void wait()
515 {
516 m_cond->wait(*m_locker);
517 }
518
519 /// Block the call until either @a timestamp time achieved
520 /// or the conditional is signaled.
521 /// @param [in] delay Maximum time to wait since the moment of the call
522 /// @retval false if the relative timeout specified by rel_time expired,
523 /// @retval true if condition is signaled or spurious wake up.
wait_for(const steady_clock::duration & delay)524 bool wait_for(const steady_clock::duration& delay)
525 {
526 return m_cond->wait_for(*m_locker, delay);
527 }
528
529 // Wait until the given time is achieved.
530 /// @param [in] exptime The target time to wait until.
531 /// @retval false if the target wait time is reached.
532 /// @retval true if condition is signal or spurious wake up.
wait_until(const steady_clock::time_point & exptime)533 bool wait_until(const steady_clock::time_point& exptime)
534 {
535 return m_cond->wait_until(*m_locker, exptime);
536 }
537
538 // Static ad-hoc version
lock_signal(Condition & cond,Mutex & m)539 static void lock_signal(Condition& cond, Mutex& m)
540 {
541 ScopedLock lk(m); // XXX with thread logging, don't use ScopedLock directly!
542 cond.notify_one();
543 }
544
lock_broadcast(Condition & cond,Mutex & m)545 static void lock_broadcast(Condition& cond, Mutex& m)
546 {
547 ScopedLock lk(m); // XXX with thread logging, don't use ScopedLock directly!
548 cond.notify_all();
549 }
550
signal_locked(UniqueLock & lk SRT_ATR_UNUSED)551 void signal_locked(UniqueLock& lk SRT_ATR_UNUSED)
552 {
553 // EXPECTED: lk.mutex() is LOCKED.
554 m_cond->notify_one();
555 }
556
557 // The signal_relaxed and broadcast_relaxed functions are to be used in case
558 // when you don't care whether the associated mutex is locked or not (you
559 // accept the case that a mutex isn't locked and the signal gets effectively
560 // missed), or you somehow know that the mutex is locked, but you don't have
561 // access to the associated UniqueLock object. This function, although it does
562 // the same thing as signal_locked() and broadcast_locked(), is here for
563 // the user to declare explicitly that the signal/broadcast is done without
564 // being prematurely certain that the associated mutex is locked.
565 //
566 // It is then expected that whenever these functions are used, an extra
567 // comment is provided to explain, why the use of the relaxed signaling is
568 // correctly used.
569
signal_relaxed()570 void signal_relaxed() { signal_relaxed(*m_cond); }
signal_relaxed(Condition & cond)571 static void signal_relaxed(Condition& cond) { cond.notify_one(); }
broadcast_relaxed(Condition & cond)572 static void broadcast_relaxed(Condition& cond) { cond.notify_all(); }
573 };
574
575 ////////////////////////////////////////////////////////////////////////////////
576 //
577 // CEvent class
578 //
579 ////////////////////////////////////////////////////////////////////////////////
580
581 class CEvent
582 {
583 public:
584 CEvent();
585 ~CEvent();
586
587 public:
mutex()588 Mutex& mutex() { return m_lock; }
589
590 public:
591 /// Causes the current thread to block until
592 /// a specific time is reached.
593 ///
594 /// @return true if condition occured or spuriously woken up
595 /// false on timeout
596 bool lock_wait_until(const steady_clock::time_point& tp);
597
598 /// Blocks the current executing thread,
599 /// and adds it to the list of threads waiting on* this.
600 /// The thread will be unblocked when notify_all() or notify_one() is executed,
601 /// or when the relative timeout rel_time expires.
602 /// It may also be unblocked spuriously.
603 /// Uses internal mutex to lock.
604 ///
605 /// @return true if condition occured or spuriously woken up
606 /// false on timeout
607 bool lock_wait_for(const steady_clock::duration& rel_time);
608
609 /// Atomically releases lock, blocks the current executing thread,
610 /// and adds it to the list of threads waiting on* this.
611 /// The thread will be unblocked when notify_all() or notify_one() is executed,
612 /// or when the relative timeout rel_time expires.
613 /// It may also be unblocked spuriously.
614 /// When unblocked, regardless of the reason, lock is reacquiredand wait_for() exits.
615 ///
616 /// @return true if condition occured or spuriously woken up
617 /// false on timeout
618 bool wait_for(UniqueLock& lk, const steady_clock::duration& rel_time);
619
620 void lock_wait();
621
622 void wait(UniqueLock& lk);
623
624 void notify_one();
625
626 void notify_all();
627
628 private:
629 Mutex m_lock;
630 Condition m_cond;
631 };
632
633
634 class CTimer
635 {
636 public:
637 CTimer();
638 ~CTimer();
639
640 public:
641 /// Causes the current thread to block until
642 /// the specified time is reached.
643 /// Sleep can be interrupted by calling interrupt()
644 /// or woken up to recheck the scheduled time by tick()
645 /// @param tp target time to sleep until
646 ///
647 /// @return true if the specified time was reached
648 /// false should never happen
649 bool sleep_until(steady_clock::time_point tp);
650
651 /// Resets target wait time and interrupts waiting
652 /// in sleep_until(..)
653 void interrupt();
654
655 /// Wakes up waiting thread (sleep_until(..)) without
656 /// changing the target waiting time to force a recheck
657 /// of the current time in comparisson to the target time.
658 void tick();
659
660 private:
661 CEvent m_event;
662 steady_clock::time_point m_tsSchedTime;
663 };
664
665
666 /// Print steady clock timepoint in a human readable way.
667 /// days HH:MM:SS.us [STD]
668 /// Example: 1D 02:12:56.123456
669 ///
670 /// @param [in] steady clock timepoint
671 /// @returns a string with a formatted time representation
672 std::string FormatTime(const steady_clock::time_point& time);
673
674 /// Print steady clock timepoint relative to the current system time
675 /// Date HH:MM:SS.us [SYS]
676 /// @param [in] steady clock timepoint
677 /// @returns a string with a formatted time representation
678 std::string FormatTimeSys(const steady_clock::time_point& time);
679
680 enum eDurationUnit {DUNIT_S, DUNIT_MS, DUNIT_US};
681
682 template <eDurationUnit u>
683 struct DurationUnitName;
684
685 template<>
686 struct DurationUnitName<DUNIT_US>
687 {
688 static const char* name() { return "us"; }
689 static double count(const steady_clock::duration& dur) { return static_cast<double>(count_microseconds(dur)); }
690 };
691
692 template<>
693 struct DurationUnitName<DUNIT_MS>
694 {
695 static const char* name() { return "ms"; }
696 static double count(const steady_clock::duration& dur) { return static_cast<double>(count_microseconds(dur))/1000.0; }
697 };
698
699 template<>
700 struct DurationUnitName<DUNIT_S>
701 {
702 static const char* name() { return "s"; }
703 static double count(const steady_clock::duration& dur) { return static_cast<double>(count_microseconds(dur))/1000000.0; }
704 };
705
706 template<eDurationUnit UNIT>
707 inline std::string FormatDuration(const steady_clock::duration& dur)
708 {
709 return Sprint(DurationUnitName<UNIT>::count(dur)) + DurationUnitName<UNIT>::name();
710 }
711
712 inline std::string FormatDuration(const steady_clock::duration& dur)
713 {
714 return FormatDuration<DUNIT_US>(dur);
715 }
716
717 ////////////////////////////////////////////////////////////////////////////////
718 //
719 // CGlobEvent class
720 //
721 ////////////////////////////////////////////////////////////////////////////////
722
723 class CGlobEvent
724 {
725 public:
726 /// Triggers the event and notifies waiting threads.
727 /// Simply calls notify_one().
728 static void triggerEvent();
729
730 /// Waits for the event to be triggered with 10ms timeout.
731 /// Simply calls wait_for().
732 static bool waitForEvent();
733 };
734
735 ////////////////////////////////////////////////////////////////////////////////
736 //
737 // CThread class
738 //
739 ////////////////////////////////////////////////////////////////////////////////
740
741 #ifdef ENABLE_STDCXX_SYNC
742 typedef std::system_error CThreadException;
743 using CThread = std::thread;
744 namespace this_thread = std::this_thread;
745 #else // pthreads wrapper version
746 typedef ::CUDTException CThreadException;
747
748 class CThread
749 {
750 public:
751 CThread();
752 /// @throws std::system_error if the thread could not be started.
753 CThread(void *(*start_routine) (void *), void *arg);
754
755 #if HAVE_FULL_CXX11
756 CThread& operator=(CThread &other) = delete;
757 CThread& operator=(CThread &&other);
758 #else
759 CThread& operator=(CThread &other);
760 /// To be used only in StartThread function.
761 /// Creates a new stread and assigns to this.
762 /// @throw CThreadException
763 void create_thread(void *(*start_routine) (void *), void *arg);
764 #endif
765
766 public: // Observers
767 /// Checks if the CThread object identifies an active thread of execution.
768 /// A default constructed thread is not joinable.
769 /// A thread that has finished executing code, but has not yet been joined
770 /// is still considered an active thread of execution and is therefore joinable.
771 bool joinable() const;
772
773 struct id
774 {
775 explicit id(const pthread_t t)
776 : value(t)
777 {}
778
779 const pthread_t value;
780 inline bool operator==(const id& second) const
781 {
782 return pthread_equal(value, second.value) != 0;
783 }
784 };
785
786 /// Returns the id of the current thread.
787 /// In this implementation the ID is the pthread_t.
788 const id get_id() const { return id(m_thread); }
789
790 public:
791 /// Blocks the current thread until the thread identified by *this finishes its execution.
792 /// If that thread has already terminated, then join() returns immediately.
793 ///
794 /// @throws std::system_error if an error occurs
795 void join();
796
797 public: // Internal
798 /// Calls pthread_create, throws exception on failure.
799 /// @throw CThreadException
800 void create(void *(*start_routine) (void *), void *arg);
801
802 private:
803 pthread_t m_thread;
804 };
805
806 template <class Stream>
807 inline Stream& operator<<(Stream& str, const CThread::id& cid)
808 {
809 #if defined(_WIN32) && (defined(PTW32_VERSION) || defined (__PTW32_VERSION))
810 // This is a version specific for pthread-win32 implementation
811 // Here pthread_t type is a structure that is not convertible
812 // to a number at all.
813 return str << pthread_getw32threadid_np(cid.value);
814 #else
815 return str << cid.value;
816 #endif
817 }
818
819 namespace this_thread
820 {
821 const inline CThread::id get_id() { return CThread::id (pthread_self()); }
822
823 inline void sleep_for(const steady_clock::duration& t)
824 {
825 #if !defined(_WIN32)
826 usleep(count_microseconds(t)); // microseconds
827 #else
828 Sleep((DWORD) count_milliseconds(t));
829 #endif
830 }
831 }
832
833 #endif
834
835 /// StartThread function should be used to do CThread assignments:
836 /// @code
837 /// CThread a();
838 /// a = CThread(func, args);
839 /// @endcode
840 ///
841 /// @returns true if thread was started successfully,
842 /// false on failure
843 ///
844 #ifdef ENABLE_STDCXX_SYNC
845 typedef void* (&ThreadFunc) (void*);
846 bool StartThread(CThread& th, ThreadFunc&& f, void* args, const std::string& name);
847 #else
848 bool StartThread(CThread& th, void* (*f) (void*), void* args, const std::string& name);
849 #endif
850
851 ////////////////////////////////////////////////////////////////////////////////
852 //
853 // CThreadError class - thread local storage wrapper
854 //
855 ////////////////////////////////////////////////////////////////////////////////
856
857 /// Set thread local error
858 /// @param e new CUDTException
859 void SetThreadLocalError(const CUDTException& e);
860
861 /// Get thread local error
862 /// @returns CUDTException pointer
863 CUDTException& GetThreadLocalError();
864
865 ////////////////////////////////////////////////////////////////////////////////
866 //
867 // Random distribution functions.
868 //
869 ////////////////////////////////////////////////////////////////////////////////
870
871 /// Generate a uniform-distributed random integer from [minVal; maxVal].
872 /// If HAVE_CXX11, uses std::uniform_distribution(std::random_device).
873 /// @param[in] minVal minimum allowed value of the resulting random number.
874 /// @param[in] maxVal maximum allowed value of the resulting random number.
875 int genRandomInt(int minVal, int maxVal);
876
877 } // namespace sync
878 } // namespace srt
879
880 #include "atomic_clock.h"
881
882 #endif // INC_SRT_SYNC_H
883