mirror of
				https://github.com/ossrs/srs.git
				synced 2025-03-09 15:49:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			947 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			947 lines
		
	
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * 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/.
 | 
						|
 *
 | 
						|
 */
 | 
						|
#pragma once
 | 
						|
#ifndef INC_SRT_SYNC_H
 | 
						|
#define INC_SRT_SYNC_H
 | 
						|
 | 
						|
#include "platform_sys.h"
 | 
						|
 | 
						|
#include <cstdlib>
 | 
						|
#include <limits>
 | 
						|
#ifdef ENABLE_STDCXX_SYNC
 | 
						|
#include <chrono>
 | 
						|
#include <thread>
 | 
						|
#include <mutex>
 | 
						|
#include <condition_variable>
 | 
						|
#include <atomic>
 | 
						|
#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_STDCXX_STEADY
 | 
						|
#define SRT_SYNC_CLOCK_STR "STDCXX_STEADY"
 | 
						|
#else
 | 
						|
#include <pthread.h>
 | 
						|
 | 
						|
// Defile clock type to use
 | 
						|
#ifdef IA32
 | 
						|
#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_IA32_RDTSC
 | 
						|
#define SRT_SYNC_CLOCK_STR "IA32_RDTSC"
 | 
						|
#elif defined(IA64)
 | 
						|
#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_IA64_ITC
 | 
						|
#define SRT_SYNC_CLOCK_STR "IA64_ITC"
 | 
						|
#elif defined(AMD64)
 | 
						|
#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_AMD64_RDTSC
 | 
						|
#define SRT_SYNC_CLOCK_STR "AMD64_RDTSC"
 | 
						|
#elif defined(_WIN32)
 | 
						|
#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_WINQPC
 | 
						|
#define SRT_SYNC_CLOCK_STR "WINQPC"
 | 
						|
#elif TARGET_OS_MAC
 | 
						|
#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_MACH_ABSTIME
 | 
						|
#define SRT_SYNC_CLOCK_STR "MACH_ABSTIME"
 | 
						|
#elif defined(ENABLE_MONOTONIC_CLOCK)
 | 
						|
#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_GETTIME_MONOTONIC
 | 
						|
#define SRT_SYNC_CLOCK_STR "GETTIME_MONOTONIC"
 | 
						|
#else
 | 
						|
#define SRT_SYNC_CLOCK SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY
 | 
						|
#define SRT_SYNC_CLOCK_STR "POSIX_GETTIMEOFDAY"
 | 
						|
#endif
 | 
						|
 | 
						|
#endif // ENABLE_STDCXX_SYNC
 | 
						|
 | 
						|
#include "srt.h"
 | 
						|
#include "utilities.h"
 | 
						|
#include "srt_attr_defs.h"
 | 
						|
 | 
						|
 | 
						|
namespace srt
 | 
						|
{
 | 
						|
 | 
						|
class CUDTException;    // defined in common.h
 | 
						|
 | 
						|
namespace sync
 | 
						|
{
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Duration class
 | 
						|
//
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
#if ENABLE_STDCXX_SYNC
 | 
						|
 | 
						|
template <class Clock>
 | 
						|
using Duration = std::chrono::duration<Clock>;
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
/// Class template srt::sync::Duration represents a time interval.
 | 
						|
/// It consists of a count of ticks of _Clock.
 | 
						|
/// It is a wrapper of system timers in case of non-C++11 chrono build.
 | 
						|
template <class Clock>
 | 
						|
class Duration
 | 
						|
{
 | 
						|
public:
 | 
						|
    Duration()
 | 
						|
        : m_duration(0)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    explicit Duration(int64_t d)
 | 
						|
        : m_duration(d)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
public:
 | 
						|
    inline int64_t count() const { return m_duration; }
 | 
						|
 | 
						|
    static Duration zero() { return Duration(); }
 | 
						|
 | 
						|
public: // Relational operators
 | 
						|
    inline bool operator>=(const Duration& rhs) const { return m_duration >= rhs.m_duration; }
 | 
						|
    inline bool operator>(const Duration& rhs) const { return m_duration > rhs.m_duration; }
 | 
						|
    inline bool operator==(const Duration& rhs) const { return m_duration == rhs.m_duration; }
 | 
						|
    inline bool operator!=(const Duration& rhs) const { return m_duration != rhs.m_duration; }
 | 
						|
    inline bool operator<=(const Duration& rhs) const { return m_duration <= rhs.m_duration; }
 | 
						|
    inline bool operator<(const Duration& rhs) const { return m_duration < rhs.m_duration; }
 | 
						|
 | 
						|
public: // Assignment operators
 | 
						|
    inline void operator*=(const int64_t mult) { m_duration = static_cast<int64_t>(m_duration * mult); }
 | 
						|
    inline void operator+=(const Duration& rhs) { m_duration += rhs.m_duration; }
 | 
						|
    inline void operator-=(const Duration& rhs) { m_duration -= rhs.m_duration; }
 | 
						|
 | 
						|
    inline Duration operator+(const Duration& rhs) const { return Duration(m_duration + rhs.m_duration); }
 | 
						|
    inline Duration operator-(const Duration& rhs) const { return Duration(m_duration - rhs.m_duration); }
 | 
						|
    inline Duration operator*(const int64_t& rhs) const { return Duration(m_duration * rhs); }
 | 
						|
    inline Duration operator/(const int64_t& rhs) const { return Duration(m_duration / rhs); }
 | 
						|
 | 
						|
private:
 | 
						|
    // int64_t range is from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
 | 
						|
    int64_t m_duration;
 | 
						|
};
 | 
						|
 | 
						|
#endif // ENABLE_STDCXX_SYNC
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// TimePoint and steadt_clock classes
 | 
						|
//
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
#if ENABLE_STDCXX_SYNC
 | 
						|
 | 
						|
using steady_clock = std::chrono::steady_clock;
 | 
						|
 | 
						|
template <class Clock, class Duration = typename Clock::duration>
 | 
						|
using time_point = std::chrono::time_point<Clock, Duration>;
 | 
						|
 | 
						|
template <class Clock>
 | 
						|
using TimePoint = std::chrono::time_point<Clock>;
 | 
						|
 | 
						|
template <class Clock, class Duration = typename Clock::duration>
 | 
						|
inline bool is_zero(const time_point<Clock, Duration> &tp)
 | 
						|
{
 | 
						|
    return tp.time_since_epoch() == Clock::duration::zero();
 | 
						|
}
 | 
						|
 | 
						|
inline bool is_zero(const steady_clock::time_point& t)
 | 
						|
{
 | 
						|
    return t == steady_clock::time_point();
 | 
						|
}
 | 
						|
 | 
						|
#else
 | 
						|
template <class Clock>
 | 
						|
class TimePoint;
 | 
						|
 | 
						|
class steady_clock
 | 
						|
{
 | 
						|
public:
 | 
						|
    typedef Duration<steady_clock>  duration;
 | 
						|
    typedef TimePoint<steady_clock> time_point;
 | 
						|
 | 
						|
public:
 | 
						|
    static time_point now();
 | 
						|
};
 | 
						|
 | 
						|
/// Represents a point in time
 | 
						|
template <class Clock>
 | 
						|
class TimePoint
 | 
						|
{
 | 
						|
public:
 | 
						|
    TimePoint()
 | 
						|
        : m_timestamp(0)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    explicit TimePoint(uint64_t tp)
 | 
						|
        : m_timestamp(tp)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    TimePoint(const TimePoint<Clock>& other)
 | 
						|
        : m_timestamp(other.m_timestamp)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    TimePoint(const Duration<Clock>& duration_since_epoch)
 | 
						|
        : m_timestamp(duration_since_epoch.count())
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    ~TimePoint() {}
 | 
						|
 | 
						|
public: // Relational operators
 | 
						|
    inline bool operator<(const TimePoint<Clock>& rhs) const { return m_timestamp < rhs.m_timestamp; }
 | 
						|
    inline bool operator<=(const TimePoint<Clock>& rhs) const { return m_timestamp <= rhs.m_timestamp; }
 | 
						|
    inline bool operator==(const TimePoint<Clock>& rhs) const { return m_timestamp == rhs.m_timestamp; }
 | 
						|
    inline bool operator!=(const TimePoint<Clock>& rhs) const { return m_timestamp != rhs.m_timestamp; }
 | 
						|
    inline bool operator>=(const TimePoint<Clock>& rhs) const { return m_timestamp >= rhs.m_timestamp; }
 | 
						|
    inline bool operator>(const TimePoint<Clock>& rhs) const { return m_timestamp > rhs.m_timestamp; }
 | 
						|
 | 
						|
public: // Arithmetic operators
 | 
						|
    inline Duration<Clock> operator-(const TimePoint<Clock>& rhs) const
 | 
						|
    {
 | 
						|
        return Duration<Clock>(m_timestamp - rhs.m_timestamp);
 | 
						|
    }
 | 
						|
    inline TimePoint operator+(const Duration<Clock>& rhs) const { return TimePoint(m_timestamp + rhs.count()); }
 | 
						|
    inline TimePoint operator-(const Duration<Clock>& rhs) const { return TimePoint(m_timestamp - rhs.count()); }
 | 
						|
 | 
						|
public: // Assignment operators
 | 
						|
    inline void operator=(const TimePoint<Clock>& rhs) { m_timestamp = rhs.m_timestamp; }
 | 
						|
    inline void operator+=(const Duration<Clock>& rhs) { m_timestamp += rhs.count(); }
 | 
						|
    inline void operator-=(const Duration<Clock>& rhs) { m_timestamp -= rhs.count(); }
 | 
						|
 | 
						|
public: //
 | 
						|
    static inline ATR_CONSTEXPR TimePoint min() { return TimePoint(std::numeric_limits<uint64_t>::min()); }
 | 
						|
    static inline ATR_CONSTEXPR TimePoint max() { return TimePoint(std::numeric_limits<uint64_t>::max()); }
 | 
						|
 | 
						|
public:
 | 
						|
    Duration<Clock> time_since_epoch() const;
 | 
						|
 | 
						|
private:
 | 
						|
    uint64_t m_timestamp;
 | 
						|
};
 | 
						|
 | 
						|
template <>
 | 
						|
srt::sync::Duration<srt::sync::steady_clock> srt::sync::TimePoint<srt::sync::steady_clock>::time_since_epoch() const;
 | 
						|
 | 
						|
inline Duration<steady_clock> operator*(const int& lhs, const Duration<steady_clock>& rhs)
 | 
						|
{
 | 
						|
    return rhs * lhs;
 | 
						|
}
 | 
						|
 | 
						|
#endif // ENABLE_STDCXX_SYNC
 | 
						|
 | 
						|
// NOTE: Moved the following class definitions to "atomic_clock.h"
 | 
						|
//   template <class Clock>
 | 
						|
//      class AtomicDuration;
 | 
						|
//   template <class Clock>
 | 
						|
//      class AtomicClock;
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Duration and timepoint conversions
 | 
						|
//
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
/// Function return number of decimals in a subsecond precision.
 | 
						|
/// E.g. for a microsecond accuracy of steady_clock the return would be 6.
 | 
						|
/// For a nanosecond accuracy of the steady_clock the return value would be 9.
 | 
						|
int clockSubsecondPrecision();
 | 
						|
 | 
						|
#if ENABLE_STDCXX_SYNC
 | 
						|
 | 
						|
inline long long count_microseconds(const steady_clock::duration &t)
 | 
						|
{
 | 
						|
    return std::chrono::duration_cast<std::chrono::microseconds>(t).count();
 | 
						|
}
 | 
						|
 | 
						|
inline long long count_microseconds(const steady_clock::time_point tp)
 | 
						|
{
 | 
						|
    return std::chrono::duration_cast<std::chrono::microseconds>(tp.time_since_epoch()).count();
 | 
						|
}
 | 
						|
 | 
						|
inline long long count_milliseconds(const steady_clock::duration &t)
 | 
						|
{
 | 
						|
    return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
 | 
						|
}
 | 
						|
 | 
						|
inline long long count_seconds(const steady_clock::duration &t)
 | 
						|
{
 | 
						|
    return std::chrono::duration_cast<std::chrono::seconds>(t).count();
 | 
						|
}
 | 
						|
 | 
						|
inline steady_clock::duration microseconds_from(int64_t t_us)
 | 
						|
{
 | 
						|
    return std::chrono::microseconds(t_us);
 | 
						|
}
 | 
						|
 | 
						|
inline steady_clock::duration milliseconds_from(int64_t t_ms)
 | 
						|
{
 | 
						|
    return std::chrono::milliseconds(t_ms);
 | 
						|
}
 | 
						|
 | 
						|
inline steady_clock::duration seconds_from(int64_t t_s)
 | 
						|
{
 | 
						|
    return std::chrono::seconds(t_s);
 | 
						|
}
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
int64_t count_microseconds(const steady_clock::duration& t);
 | 
						|
int64_t count_milliseconds(const steady_clock::duration& t);
 | 
						|
int64_t count_seconds(const steady_clock::duration& t);
 | 
						|
 | 
						|
Duration<steady_clock> microseconds_from(int64_t t_us);
 | 
						|
Duration<steady_clock> milliseconds_from(int64_t t_ms);
 | 
						|
Duration<steady_clock> seconds_from(int64_t t_s);
 | 
						|
 | 
						|
inline bool is_zero(const TimePoint<steady_clock>& t)
 | 
						|
{
 | 
						|
    return t == TimePoint<steady_clock>();
 | 
						|
}
 | 
						|
 | 
						|
#endif // ENABLE_STDCXX_SYNC
 | 
						|
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Mutex section
 | 
						|
//
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
#if ENABLE_STDCXX_SYNC
 | 
						|
using Mutex = std::mutex;
 | 
						|
using UniqueLock = std::unique_lock<std::mutex>;
 | 
						|
using ScopedLock = std::lock_guard<std::mutex>;
 | 
						|
#else
 | 
						|
/// Mutex is a class wrapper, that should mimic the std::chrono::mutex class.
 | 
						|
/// At the moment the extra function ref() is temporally added to allow calls
 | 
						|
/// to pthread_cond_timedwait(). Will be removed by introducing CEvent.
 | 
						|
class SRT_ATTR_CAPABILITY("mutex") Mutex
 | 
						|
{
 | 
						|
    friend class SyncEvent;
 | 
						|
 | 
						|
public:
 | 
						|
    Mutex();
 | 
						|
    ~Mutex();
 | 
						|
 | 
						|
public:
 | 
						|
    int lock() SRT_ATTR_ACQUIRE();
 | 
						|
    int unlock() SRT_ATTR_RELEASE();
 | 
						|
 | 
						|
    /// @return     true if the lock was acquired successfully, otherwise false
 | 
						|
    bool try_lock() SRT_ATTR_TRY_ACQUIRE(true);
 | 
						|
 | 
						|
    // TODO: To be removed with introduction of the CEvent.
 | 
						|
    pthread_mutex_t& ref() { return m_mutex; }
 | 
						|
 | 
						|
private:
 | 
						|
    pthread_mutex_t m_mutex;
 | 
						|
};
 | 
						|
 | 
						|
/// A pthread version of std::chrono::scoped_lock<mutex> (or lock_guard for C++11)
 | 
						|
class SRT_ATTR_SCOPED_CAPABILITY ScopedLock
 | 
						|
{
 | 
						|
public:
 | 
						|
    SRT_ATTR_ACQUIRE(m)
 | 
						|
    explicit ScopedLock(Mutex& m);
 | 
						|
 | 
						|
    SRT_ATTR_RELEASE()
 | 
						|
    ~ScopedLock();
 | 
						|
 | 
						|
private:
 | 
						|
    Mutex& m_mutex;
 | 
						|
};
 | 
						|
 | 
						|
/// A pthread version of std::chrono::unique_lock<mutex>
 | 
						|
class SRT_ATTR_SCOPED_CAPABILITY UniqueLock
 | 
						|
{
 | 
						|
    friend class SyncEvent;
 | 
						|
    int m_iLocked;
 | 
						|
    Mutex& m_Mutex;
 | 
						|
 | 
						|
public:
 | 
						|
    SRT_ATTR_ACQUIRE(m)
 | 
						|
    explicit UniqueLock(Mutex &m);
 | 
						|
 | 
						|
    SRT_ATTR_RELEASE()
 | 
						|
    ~UniqueLock();
 | 
						|
 | 
						|
public:
 | 
						|
    SRT_ATTR_ACQUIRE()
 | 
						|
    void lock();
 | 
						|
 | 
						|
    SRT_ATTR_RELEASE()
 | 
						|
    void unlock();
 | 
						|
 | 
						|
    SRT_ATTR_RETURN_CAPABILITY(m_Mutex)
 | 
						|
    Mutex* mutex(); // reflects C++11 unique_lock::mutex()
 | 
						|
};
 | 
						|
#endif // ENABLE_STDCXX_SYNC
 | 
						|
 | 
						|
inline void enterCS(Mutex& m) SRT_ATTR_EXCLUDES(m) SRT_ATTR_ACQUIRE(m) { m.lock(); }
 | 
						|
 | 
						|
inline bool tryEnterCS(Mutex& m) SRT_ATTR_EXCLUDES(m) SRT_ATTR_TRY_ACQUIRE(true, m) { return m.try_lock(); }
 | 
						|
 | 
						|
inline void leaveCS(Mutex& m) SRT_ATTR_REQUIRES(m) SRT_ATTR_RELEASE(m) { m.unlock(); }
 | 
						|
 | 
						|
class InvertedLock
 | 
						|
{
 | 
						|
    Mutex& m_mtx;
 | 
						|
 | 
						|
public:
 | 
						|
    SRT_ATTR_REQUIRES(m) SRT_ATTR_RELEASE(m)
 | 
						|
    InvertedLock(Mutex& m)
 | 
						|
        : m_mtx(m)
 | 
						|
    {
 | 
						|
        m_mtx.unlock();
 | 
						|
    }
 | 
						|
 | 
						|
    SRT_ATTR_ACQUIRE(m_mtx)
 | 
						|
    ~InvertedLock()
 | 
						|
    {
 | 
						|
        m_mtx.lock();
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
inline void setupMutex(Mutex&, const char*) {}
 | 
						|
inline void releaseMutex(Mutex&) {}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Condition section
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
class Condition
 | 
						|
{
 | 
						|
public:
 | 
						|
    Condition();
 | 
						|
    ~Condition();
 | 
						|
 | 
						|
public:
 | 
						|
    /// These functions do not align with C++11 version. They are here hopefully as a temporal solution
 | 
						|
    /// to avoud issues with static initialization of CV on windows.
 | 
						|
    void init();
 | 
						|
    void destroy();
 | 
						|
 | 
						|
public:
 | 
						|
    /// Causes the current thread to block until the condition variable is notified
 | 
						|
    /// or a spurious wakeup occurs.
 | 
						|
    ///
 | 
						|
    /// @param lock Corresponding mutex locked by UniqueLock
 | 
						|
    void wait(UniqueLock& lock);
 | 
						|
 | 
						|
    /// Atomically releases lock, blocks the current executing thread, 
 | 
						|
    /// and adds it to the list of threads waiting on *this.
 | 
						|
    /// The thread will be unblocked when notify_all() or notify_one() is executed,
 | 
						|
    /// or when the relative timeout rel_time expires.
 | 
						|
    /// It may also be unblocked spuriously. When unblocked, regardless of the reason,
 | 
						|
    /// lock is reacquired and wait_for() exits.
 | 
						|
    ///
 | 
						|
    /// @returns false if the relative timeout specified by rel_time expired,
 | 
						|
    ///          true otherwise (signal or spurious wake up).
 | 
						|
    ///
 | 
						|
    /// @note Calling this function if lock.mutex()
 | 
						|
    /// is not locked by the current thread is undefined behavior.
 | 
						|
    /// Calling this function if lock.mutex() is not the same mutex as the one
 | 
						|
    /// used by all other threads that are currently waiting on the same
 | 
						|
    /// condition variable is undefined behavior.
 | 
						|
    bool wait_for(UniqueLock& lock, const steady_clock::duration& rel_time);
 | 
						|
 | 
						|
    /// Causes the current thread to block until the condition variable is notified,
 | 
						|
    /// a specific time is reached, or a spurious wakeup occurs.
 | 
						|
    ///
 | 
						|
    /// @param[in] lock  an object of type UniqueLock, which must be locked by the current thread 
 | 
						|
    /// @param[in] timeout_time an object of type time_point representing the time when to stop waiting 
 | 
						|
    ///
 | 
						|
    /// @returns false if the relative timeout specified by timeout_time expired,
 | 
						|
    ///          true otherwise (signal or spurious wake up).
 | 
						|
    bool wait_until(UniqueLock& lock, const steady_clock::time_point& timeout_time);
 | 
						|
 | 
						|
    /// Calling notify_one() unblocks one of the waiting threads,
 | 
						|
    /// if any threads are waiting on this CV.
 | 
						|
    void notify_one();
 | 
						|
 | 
						|
    /// Unblocks all threads currently waiting for this CV.
 | 
						|
    void notify_all();
 | 
						|
 | 
						|
private:
 | 
						|
#if ENABLE_STDCXX_SYNC
 | 
						|
    std::condition_variable m_cv;
 | 
						|
#else
 | 
						|
    pthread_cond_t  m_cv;
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
inline void setupCond(Condition& cv, const char*) { cv.init(); }
 | 
						|
inline void releaseCond(Condition& cv) { cv.destroy(); }
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Event (CV) section
 | 
						|
//
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
// This class is used for condition variable combined with mutex by different ways.
 | 
						|
// This should provide a cleaner API around locking with debug-logging inside.
 | 
						|
class CSync
 | 
						|
{
 | 
						|
protected:
 | 
						|
    Condition* m_cond;
 | 
						|
    UniqueLock* m_locker;
 | 
						|
 | 
						|
public:
 | 
						|
    // Locked version: must be declared only after the declaration of UniqueLock,
 | 
						|
    // which has locked the mutex. On this delegate you should call only
 | 
						|
    // signal_locked() and pass the UniqueLock variable that should remain locked.
 | 
						|
    // Also wait() and wait_for() can be used only with this socket.
 | 
						|
    CSync(Condition& cond, UniqueLock& g)
 | 
						|
        : m_cond(&cond), m_locker(&g)
 | 
						|
    {
 | 
						|
        // XXX it would be nice to check whether the owner is also current thread
 | 
						|
        // but this can't be done portable way.
 | 
						|
 | 
						|
        // When constructed by this constructor, the user is expected
 | 
						|
        // to only call signal_locked() function. You should pass the same guard
 | 
						|
        // variable that you have used for construction as its argument.
 | 
						|
    }
 | 
						|
 | 
						|
    // COPY CONSTRUCTOR: DEFAULT!
 | 
						|
 | 
						|
    // Wait indefinitely, until getting a signal on CV.
 | 
						|
    void wait()
 | 
						|
    {
 | 
						|
        m_cond->wait(*m_locker);
 | 
						|
    }
 | 
						|
 | 
						|
    /// Block the call until either @a timestamp time achieved
 | 
						|
    /// or the conditional is signaled.
 | 
						|
    /// @param [in] delay Maximum time to wait since the moment of the call
 | 
						|
    /// @retval false if the relative timeout specified by rel_time expired,
 | 
						|
    /// @retval true if condition is signaled or spurious wake up.
 | 
						|
    bool wait_for(const steady_clock::duration& delay)
 | 
						|
    {
 | 
						|
        return m_cond->wait_for(*m_locker, delay);
 | 
						|
    }
 | 
						|
 | 
						|
    // Wait until the given time is achieved.
 | 
						|
    /// @param [in] exptime The target time to wait until.
 | 
						|
    /// @retval false if the target wait time is reached.
 | 
						|
    /// @retval true if condition is signal or spurious wake up.
 | 
						|
    bool wait_until(const steady_clock::time_point& exptime)
 | 
						|
    {
 | 
						|
        return m_cond->wait_until(*m_locker, exptime);
 | 
						|
    }
 | 
						|
 | 
						|
    // Static ad-hoc version
 | 
						|
    static void lock_notify_one(Condition& cond, Mutex& m)
 | 
						|
    {
 | 
						|
        ScopedLock lk(m); // XXX with thread logging, don't use ScopedLock directly!
 | 
						|
        cond.notify_one();
 | 
						|
    }
 | 
						|
 | 
						|
    static void lock_notify_all(Condition& cond, Mutex& m)
 | 
						|
    {
 | 
						|
        ScopedLock lk(m); // XXX with thread logging, don't use ScopedLock directly!
 | 
						|
        cond.notify_all();
 | 
						|
    }
 | 
						|
 | 
						|
    void notify_one_locked(UniqueLock& lk SRT_ATR_UNUSED)
 | 
						|
    {
 | 
						|
        // EXPECTED: lk.mutex() is LOCKED.
 | 
						|
        m_cond->notify_one();
 | 
						|
    }
 | 
						|
 | 
						|
    void notify_all_locked(UniqueLock& lk SRT_ATR_UNUSED)
 | 
						|
    {
 | 
						|
        // EXPECTED: lk.mutex() is LOCKED.
 | 
						|
        m_cond->notify_all();
 | 
						|
    }
 | 
						|
 | 
						|
    // The *_relaxed functions are to be used in case when you don't care
 | 
						|
    // whether the associated mutex is locked or not (you accept the case that
 | 
						|
    // a mutex isn't locked and the condition notification gets effectively
 | 
						|
    // missed), or you somehow know that the mutex is locked, but you don't
 | 
						|
    // have access to the associated UniqueLock object. This function, although
 | 
						|
    // it does the same thing as CSync::notify_one_locked etc. here for the
 | 
						|
    // user to declare explicitly that notifying is done without being
 | 
						|
    // prematurely certain that the associated mutex is locked.
 | 
						|
    //
 | 
						|
    // It is then expected that whenever these functions are used, an extra
 | 
						|
    // comment is provided to explain, why the use of the relaxed notification
 | 
						|
    // is correctly used.
 | 
						|
 | 
						|
    void notify_one_relaxed() { notify_one_relaxed(*m_cond); }
 | 
						|
    static void notify_one_relaxed(Condition& cond) { cond.notify_one(); }
 | 
						|
    static void notify_all_relaxed(Condition& cond) { cond.notify_all(); }
 | 
						|
};
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// CEvent class
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
// XXX Do not use this class now, there's an unknown issue
 | 
						|
// connected to object management with the use of release* functions.
 | 
						|
// Until this is solved, stay with separate *Cond and *Lock fields.
 | 
						|
class CEvent
 | 
						|
{
 | 
						|
public:
 | 
						|
    CEvent();
 | 
						|
    ~CEvent();
 | 
						|
 | 
						|
public:
 | 
						|
    Mutex& mutex() { return m_lock; }
 | 
						|
    Condition& cond() { return m_cond; }
 | 
						|
 | 
						|
public:
 | 
						|
    /// Causes the current thread to block until
 | 
						|
    /// a specific time is reached.
 | 
						|
    ///
 | 
						|
    /// @return true  if condition occurred or spuriously woken up
 | 
						|
    ///         false on timeout
 | 
						|
    bool lock_wait_until(const steady_clock::time_point& tp);
 | 
						|
 | 
						|
    /// Blocks the current executing thread,
 | 
						|
    /// and adds it to the list of threads waiting on* this.
 | 
						|
    /// The thread will be unblocked when notify_all() or notify_one() is executed,
 | 
						|
    /// or when the relative timeout rel_time expires.
 | 
						|
    /// It may also be unblocked spuriously.
 | 
						|
    /// Uses internal mutex to lock.
 | 
						|
    ///
 | 
						|
    /// @return true  if condition occurred or spuriously woken up
 | 
						|
    ///         false on timeout
 | 
						|
    bool lock_wait_for(const steady_clock::duration& rel_time);
 | 
						|
 | 
						|
    /// Atomically releases lock, blocks the current executing thread,
 | 
						|
    /// and adds it to the list of threads waiting on* this.
 | 
						|
    /// The thread will be unblocked when notify_all() or notify_one() is executed,
 | 
						|
    /// or when the relative timeout rel_time expires.
 | 
						|
    /// It may also be unblocked spuriously.
 | 
						|
    /// When unblocked, regardless of the reason, lock is reacquiredand wait_for() exits.
 | 
						|
    ///
 | 
						|
    /// @return true  if condition occurred or spuriously woken up
 | 
						|
    ///         false on timeout
 | 
						|
    bool wait_for(UniqueLock& lk, const steady_clock::duration& rel_time);
 | 
						|
 | 
						|
    void lock_wait();
 | 
						|
 | 
						|
    void wait(UniqueLock& lk);
 | 
						|
 | 
						|
    void notify_one();
 | 
						|
 | 
						|
    void notify_all();
 | 
						|
 | 
						|
    void lock_notify_one()
 | 
						|
    {
 | 
						|
        ScopedLock lk(m_lock); // XXX with thread logging, don't use ScopedLock directly!
 | 
						|
        m_cond.notify_one();
 | 
						|
    }
 | 
						|
 | 
						|
    void lock_notify_all()
 | 
						|
    {
 | 
						|
        ScopedLock lk(m_lock); // XXX with thread logging, don't use ScopedLock directly!
 | 
						|
        m_cond.notify_all();
 | 
						|
    }
 | 
						|
 | 
						|
private:
 | 
						|
    Mutex      m_lock;
 | 
						|
    Condition  m_cond;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
// This class binds together the functionality of
 | 
						|
// UniqueLock and CSync. It provides a simple interface of CSync
 | 
						|
// while having already the UniqueLock applied in the scope,
 | 
						|
// so a safe statement can be made about the mutex being locked
 | 
						|
// when signalling or waiting.
 | 
						|
class CUniqueSync: public CSync
 | 
						|
{
 | 
						|
    UniqueLock m_ulock;
 | 
						|
 | 
						|
public:
 | 
						|
 | 
						|
    UniqueLock& locker() { return m_ulock; }
 | 
						|
 | 
						|
    CUniqueSync(Mutex& mut, Condition& cnd)
 | 
						|
        : CSync(cnd, m_ulock)
 | 
						|
        , m_ulock(mut)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    CUniqueSync(CEvent& event)
 | 
						|
        : CSync(event.cond(), m_ulock)
 | 
						|
        , m_ulock(event.mutex())
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    // These functions can be used safely because
 | 
						|
    // this whole class guarantees that whatever happens
 | 
						|
    // while its object exists is that the mutex is locked.
 | 
						|
 | 
						|
    void notify_one()
 | 
						|
    {
 | 
						|
        m_cond->notify_one();
 | 
						|
    }
 | 
						|
 | 
						|
    void notify_all()
 | 
						|
    {
 | 
						|
        m_cond->notify_all();
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
class CTimer
 | 
						|
{
 | 
						|
public:
 | 
						|
    CTimer();
 | 
						|
    ~CTimer();
 | 
						|
 | 
						|
public:
 | 
						|
    /// Causes the current thread to block until
 | 
						|
    /// the specified time is reached.
 | 
						|
    /// Sleep can be interrupted by calling interrupt()
 | 
						|
    /// or woken up to recheck the scheduled time by tick()
 | 
						|
    /// @param tp target time to sleep until
 | 
						|
    ///
 | 
						|
    /// @return true  if the specified time was reached
 | 
						|
    ///         false should never happen
 | 
						|
    bool sleep_until(steady_clock::time_point tp);
 | 
						|
 | 
						|
    /// Resets target wait time and interrupts waiting
 | 
						|
    /// in sleep_until(..)
 | 
						|
    void interrupt();
 | 
						|
 | 
						|
    /// Wakes up waiting thread (sleep_until(..)) without
 | 
						|
    /// changing the target waiting time to force a recheck
 | 
						|
    /// of the current time in comparisson to the target time.
 | 
						|
    void tick();
 | 
						|
 | 
						|
private:
 | 
						|
    CEvent m_event;
 | 
						|
    steady_clock::time_point m_tsSchedTime;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/// Print steady clock timepoint in a human readable way.
 | 
						|
/// days HH:MM:SS.us [STD]
 | 
						|
/// Example: 1D 02:12:56.123456
 | 
						|
///
 | 
						|
/// @param [in] steady clock timepoint
 | 
						|
/// @returns a string with a formatted time representation
 | 
						|
std::string FormatTime(const steady_clock::time_point& time);
 | 
						|
 | 
						|
/// Print steady clock timepoint relative to the current system time
 | 
						|
/// Date HH:MM:SS.us [SYS]
 | 
						|
/// @param [in] steady clock timepoint
 | 
						|
/// @returns a string with a formatted time representation
 | 
						|
std::string FormatTimeSys(const steady_clock::time_point& time);
 | 
						|
 | 
						|
enum eDurationUnit {DUNIT_S, DUNIT_MS, DUNIT_US};
 | 
						|
 | 
						|
template <eDurationUnit u>
 | 
						|
struct DurationUnitName;
 | 
						|
 | 
						|
template<>
 | 
						|
struct DurationUnitName<DUNIT_US>
 | 
						|
{
 | 
						|
    static const char* name() { return "us"; }
 | 
						|
    static double count(const steady_clock::duration& dur) { return static_cast<double>(count_microseconds(dur)); }
 | 
						|
};
 | 
						|
 | 
						|
template<>
 | 
						|
struct DurationUnitName<DUNIT_MS>
 | 
						|
{
 | 
						|
    static const char* name() { return "ms"; }
 | 
						|
    static double count(const steady_clock::duration& dur) { return static_cast<double>(count_microseconds(dur))/1000.0; }
 | 
						|
};
 | 
						|
 | 
						|
template<>
 | 
						|
struct DurationUnitName<DUNIT_S>
 | 
						|
{
 | 
						|
    static const char* name() { return "s"; }
 | 
						|
    static double count(const steady_clock::duration& dur) { return static_cast<double>(count_microseconds(dur))/1000000.0; }
 | 
						|
};
 | 
						|
 | 
						|
template<eDurationUnit UNIT>
 | 
						|
inline std::string FormatDuration(const steady_clock::duration& dur)
 | 
						|
{
 | 
						|
    return Sprint(DurationUnitName<UNIT>::count(dur)) + DurationUnitName<UNIT>::name();
 | 
						|
}
 | 
						|
 | 
						|
inline std::string FormatDuration(const steady_clock::duration& dur)
 | 
						|
{
 | 
						|
    return FormatDuration<DUNIT_US>(dur);
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// CGlobEvent class
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
class CGlobEvent
 | 
						|
{
 | 
						|
public:
 | 
						|
    /// Triggers the event and notifies waiting threads.
 | 
						|
    /// Simply calls notify_one().
 | 
						|
    static void triggerEvent();
 | 
						|
 | 
						|
    /// Waits for the event to be triggered with 10ms timeout.
 | 
						|
    /// Simply calls wait_for().
 | 
						|
    static bool waitForEvent();
 | 
						|
};
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// CThread class
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
#ifdef ENABLE_STDCXX_SYNC
 | 
						|
typedef std::system_error CThreadException;
 | 
						|
using CThread = std::thread;
 | 
						|
namespace this_thread = std::this_thread;
 | 
						|
#else // pthreads wrapper version
 | 
						|
typedef CUDTException CThreadException;
 | 
						|
 | 
						|
class CThread
 | 
						|
{
 | 
						|
public:
 | 
						|
    CThread();
 | 
						|
    /// @throws std::system_error if the thread could not be started.
 | 
						|
    CThread(void *(*start_routine) (void *), void *arg);
 | 
						|
 | 
						|
#if HAVE_FULL_CXX11
 | 
						|
    CThread& operator=(CThread &other) = delete;
 | 
						|
    CThread& operator=(CThread &&other);
 | 
						|
#else
 | 
						|
    CThread& operator=(CThread &other);
 | 
						|
    /// To be used only in StartThread function.
 | 
						|
    /// Creates a new stread and assigns to this.
 | 
						|
    /// @throw CThreadException
 | 
						|
    void create_thread(void *(*start_routine) (void *), void *arg);
 | 
						|
#endif
 | 
						|
 | 
						|
public: // Observers
 | 
						|
    /// Checks if the CThread object identifies an active thread of execution.
 | 
						|
    /// A default constructed thread is not joinable.
 | 
						|
    /// A thread that has finished executing code, but has not yet been joined
 | 
						|
    /// is still considered an active thread of execution and is therefore joinable.
 | 
						|
    bool joinable() const;
 | 
						|
 | 
						|
    struct id
 | 
						|
    {
 | 
						|
        explicit id(const pthread_t t)
 | 
						|
            : value(t)
 | 
						|
        {}
 | 
						|
 | 
						|
        const pthread_t value;
 | 
						|
        inline bool operator==(const id& second) const
 | 
						|
        {
 | 
						|
            return pthread_equal(value, second.value) != 0;
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
    /// Returns the id of the current thread.
 | 
						|
    /// In this implementation the ID is the pthread_t.
 | 
						|
    const id get_id() const { return id(m_thread); }
 | 
						|
 | 
						|
public:
 | 
						|
    /// Blocks the current thread until the thread identified by *this finishes its execution.
 | 
						|
    /// If that thread has already terminated, then join() returns immediately.
 | 
						|
    ///
 | 
						|
    /// @throws std::system_error if an error occurs
 | 
						|
    void join();
 | 
						|
 | 
						|
public: // Internal
 | 
						|
    /// Calls pthread_create, throws exception on failure.
 | 
						|
    /// @throw CThreadException
 | 
						|
    void create(void *(*start_routine) (void *), void *arg);
 | 
						|
 | 
						|
private:
 | 
						|
    pthread_t m_thread;
 | 
						|
};
 | 
						|
 | 
						|
template <class Stream>
 | 
						|
inline Stream& operator<<(Stream& str, const CThread::id& cid)
 | 
						|
{
 | 
						|
#if defined(_WIN32) && (defined(PTW32_VERSION) || defined (__PTW32_VERSION))
 | 
						|
    // This is a version specific for pthread-win32 implementation
 | 
						|
    // Here pthread_t type is a structure that is not convertible
 | 
						|
    // to a number at all.
 | 
						|
    return str << pthread_getw32threadid_np(cid.value);
 | 
						|
#else
 | 
						|
    return str << cid.value;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
namespace this_thread
 | 
						|
{
 | 
						|
    const inline CThread::id get_id() { return CThread::id (pthread_self()); }
 | 
						|
 | 
						|
    inline void sleep_for(const steady_clock::duration& t)
 | 
						|
    {
 | 
						|
#if !defined(_WIN32)
 | 
						|
        usleep(count_microseconds(t)); // microseconds
 | 
						|
#else
 | 
						|
        Sleep((DWORD) count_milliseconds(t));
 | 
						|
#endif
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
/// StartThread function should be used to do CThread assignments:
 | 
						|
/// @code
 | 
						|
/// CThread a();
 | 
						|
/// a = CThread(func, args);
 | 
						|
/// @endcode
 | 
						|
///
 | 
						|
/// @returns true if thread was started successfully,
 | 
						|
///          false on failure
 | 
						|
///
 | 
						|
#ifdef ENABLE_STDCXX_SYNC
 | 
						|
typedef void* (&ThreadFunc) (void*);
 | 
						|
bool StartThread(CThread& th, ThreadFunc&& f, void* args, const std::string& name);
 | 
						|
#else
 | 
						|
bool StartThread(CThread& th, void* (*f) (void*), void* args, const std::string& name);
 | 
						|
#endif
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// CThreadError class - thread local storage wrapper
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
/// Set thread local error
 | 
						|
/// @param e new CUDTException
 | 
						|
void SetThreadLocalError(const CUDTException& e);
 | 
						|
 | 
						|
/// Get thread local error
 | 
						|
/// @returns CUDTException pointer
 | 
						|
CUDTException& GetThreadLocalError();
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//
 | 
						|
// Random distribution functions.
 | 
						|
//
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
/// Generate a uniform-distributed random integer from [minVal; maxVal].
 | 
						|
/// If HAVE_CXX11, uses std::uniform_distribution(std::random_device).
 | 
						|
/// @param[in] minVal minimum allowed value of the resulting random number.
 | 
						|
/// @param[in] maxVal maximum allowed value of the resulting random number.
 | 
						|
int genRandomInt(int minVal, int maxVal);
 | 
						|
 | 
						|
} // namespace sync
 | 
						|
} // namespace srt
 | 
						|
 | 
						|
#include "atomic_clock.h"
 | 
						|
 | 
						|
#endif // INC_SRT_SYNC_H
 |