1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

SRT: Upgrade libsrt from 1.4.1 to 1.5.1. v6.0.12 (#3362)

Co-authored-by: winlin <winlin@vip.126.com>
This commit is contained in:
john 2023-01-04 19:56:33 +08:00 committed by GitHub
parent 7a56208f2f
commit fe086dfc31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
143 changed files with 38185 additions and 15108 deletions

View file

@ -1,360 +0,0 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 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/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 02/21/2013
modified by
Haivision Systems Inc.
*****************************************************************************/
#include "core.h"
#include "ccc.h"
#include <cmath>
#include <cstring>
CCC::CCC():
m_iSYNInterval(CUDT::m_iSYNInterval),
m_dPktSndPeriod(1.0),
m_dCWndSize(16.0),
m_iBandwidth(),
m_dMaxCWndSize(),
m_iMSS(),
m_iSndCurrSeqNo(),
m_iRcvRate(),
m_iRTT(),
m_pcParam(NULL),
m_iPSize(0),
m_UDT(),
m_iACKPeriod(0),
m_iACKInterval(0),
m_bUserDefinedRTO(false),
m_iRTO(-1),
m_PerfInfo()
{
}
CCC::~CCC()
{
delete [] m_pcParam;
}
void CCC::setACKTimer(int msINT)
{
m_iACKPeriod = msINT > m_iSYNInterval ? m_iSYNInterval : msINT;
}
void CCC::setACKInterval(int pktINT)
{
m_iACKInterval = pktINT;
}
void CCC::setRTO(int usRTO)
{
m_bUserDefinedRTO = true;
m_iRTO = usRTO;
}
void CCC::sendCustomMsg(CPacket& pkt) const
{
CUDT* u = CUDT::getUDTHandle(m_UDT);
if (NULL != u)
{
pkt.m_iID = u->m_PeerID;
#ifdef SRT_ENABLE_CTRLTSTAMP
pkt.m_iTimeStamp = int(CTimer::getTime() - u->m_StartTime);
#endif
u->m_pSndQueue->sendto(u->m_pPeerAddr, pkt);
}
}
const CPerfMon* CCC::getPerfInfo()
{
try
{
CUDT* u = CUDT::getUDTHandle(m_UDT);
if (NULL != u)
u->sample(&m_PerfInfo, false);
}
catch (...)
{
return NULL;
}
return &m_PerfInfo;
}
void CCC::setMSS(int mss)
{
m_iMSS = mss;
}
void CCC::setBandwidth(int bw)
{
m_iBandwidth = bw;
}
void CCC::setSndCurrSeqNo(int32_t seqno)
{
m_iSndCurrSeqNo = seqno;
}
void CCC::setRcvRate(int rcvrate)
{
m_iRcvRate = rcvrate;
}
void CCC::setMaxCWndSize(int cwnd)
{
m_dMaxCWndSize = cwnd;
}
void CCC::setRTT(int rtt)
{
m_iRTT = rtt;
}
void CCC::setUserParam(const char* param, int size)
{
delete [] m_pcParam;
m_pcParam = new char[size];
memcpy(m_pcParam, param, size);
m_iPSize = size;
}
//
CUDTCC::CUDTCC():
m_iRCInterval(),
m_LastRCTime(),
m_bSlowStart(),
m_iLastAck(),
m_bLoss(),
m_iLastDecSeq(),
m_dLastDecPeriod(),
m_iNAKCount(),
m_iDecRandom(),
m_iAvgNAKNum(),
m_iDecCount()
{
}
void CUDTCC::init()
{
m_iRCInterval = m_iSYNInterval;
m_LastRCTime = CTimer::getTime();
setACKTimer(m_iRCInterval);
m_bSlowStart = true;
m_iLastAck = m_iSndCurrSeqNo;
m_bLoss = false;
m_iLastDecSeq = CSeqNo::decseq(m_iLastAck);
m_dLastDecPeriod = 1;
m_iAvgNAKNum = 0;
m_iNAKCount = 0;
m_iDecRandom = 1;
m_dCWndSize = 16;
m_dPktSndPeriod = 1;
}
void CUDTCC::onACK(int32_t ack)
{
int64_t B = 0;
double inc = 0;
// Note: 1/24/2012
// The minimum increase parameter is increased from "1.0 / m_iMSS" to 0.01
// because the original was too small and caused sending rate to stay at low level
// for long time.
const double min_inc = 0.01;
uint64_t currtime = CTimer::getTime();
if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval)
return;
m_LastRCTime = currtime;
#ifdef SRT_ENABLE_BSTATS
//m_iRcvRate is bytes/sec
if (m_bSlowStart)
{
m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack);
m_iLastAck = ack;
if (m_dCWndSize > m_dMaxCWndSize)
{
m_bSlowStart = false;
if (m_iRcvRate > 0)
m_dPktSndPeriod = 1000000.0 / ((m_iRcvRate + m_iMSS - 1) / m_iMSS);
else
m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize;
}
}
else
m_dCWndSize = ((m_iRcvRate + m_iMSS -1) / m_iMSS) / 1000000.0 * (m_iRTT + m_iRCInterval) + 16;
#else
if (m_bSlowStart)
{
m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack);
m_iLastAck = ack;
if (m_dCWndSize > m_dMaxCWndSize)
{
m_bSlowStart = false;
if (m_iRcvRate > 0)
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
else
m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize;
}
}
else
m_dCWndSize = m_iRcvRate / 1000000.0 * (m_iRTT + m_iRCInterval) + 16;
#endif
// During Slow Start, no rate increase
if (m_bSlowStart)
return;
if (m_bLoss)
{
m_bLoss = false;
return;
}
//m_iBandwidth is pkts/sec
B = (int64_t)(m_iBandwidth - 1000000.0 / m_dPktSndPeriod);
if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((m_iBandwidth / 9) < B))
B = m_iBandwidth / 9;
if (B <= 0)
inc = min_inc;
else
{
// inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS)
// Beta = 1.5 * 10^(-6)
inc = pow(10.0, ceil(log10(B * m_iMSS * 8.0))) * 0.0000015 / m_iMSS;
if (inc < min_inc)
inc = min_inc;
}
m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval);
}
void CUDTCC::onLoss(const int32_t* losslist, int)
{
//Slow Start stopped, if it hasn't yet
if (m_bSlowStart)
{
m_bSlowStart = false;
if (m_iRcvRate > 0)
{
// Set the sending rate to the receiving rate.
#ifdef SRT_ENABLE_BSTATS
//Need average packet size here for better send period
m_dPktSndPeriod = 1000000.0 / ((m_iRcvRate + m_iMSS - 1) / m_iMSS);
#else
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
#endif
return;
}
// If no receiving rate is observed, we have to compute the sending
// rate according to the current window size, and decrease it
// using the method below.
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);
}
m_bLoss = true;
if (CSeqNo::seqcmp(losslist[0] & 0x7FFFFFFF, m_iLastDecSeq) > 0)
{
m_dLastDecPeriod = m_dPktSndPeriod;
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125);
m_iAvgNAKNum = (int)ceil(m_iAvgNAKNum * 0.875 + m_iNAKCount * 0.125);
m_iNAKCount = 1;
m_iDecCount = 1;
m_iLastDecSeq = m_iSndCurrSeqNo;
// remove global synchronization using randomization
srand(m_iLastDecSeq);
m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX));
if (m_iDecRandom < 1)
m_iDecRandom = 1;
}
else if ((m_iDecCount ++ < 5) && (0 == (++ m_iNAKCount % m_iDecRandom)))
{
// 0.875^5 = 0.51, rate should not be decreased by more than half within a congestion period
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125);
m_iLastDecSeq = m_iSndCurrSeqNo;
}
}
void CUDTCC::onTimeout()
{
if (m_bSlowStart)
{
m_bSlowStart = false;
if (m_iRcvRate > 0)
#ifdef SRT_ENABLE_BSTATS
// Need average packet size here
m_dPktSndPeriod = 1000000.0 / ((m_iRcvRate + m_iMSS - 1) / m_iMSS);
#else
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
#endif
else
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);
}
else
{
/*
m_dLastDecPeriod = m_dPktSndPeriod;
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 2);
m_iLastDecSeq = m_iLastAck;
*/
}
}

View file

@ -1,219 +0,0 @@
/*****************************************************************************
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Yunhong Gu, last updated 02/28/2012
*****************************************************************************/
#ifndef __UDT_CCC_H__
#define __UDT_CCC_H__
#include "udt.h"
#include "packet.h"
class UDT_API CCC
{
friend class CUDT;
public:
CCC();
virtual ~CCC();
private:
CCC(const CCC&);
CCC& operator=(const CCC&) {return *this;}
public:
/// Callback function to be called (only) at the start of a UDT connection.
/// note that this is different from CCC(), which is always called.
virtual void init() {}
/// Callback function to be called when a UDT connection is closed.
virtual void close() {}
/// Callback function to be called when an ACK packet is received.
/// @param [in] ackno the data sequence number acknowledged by this ACK.
virtual void onACK(int32_t) {}
/// Callback function to be called when a loss report is received.
/// @param [in] losslist list of sequence number of packets, in the format describled in packet.cpp.
/// @param [in] size length of the loss list.
virtual void onLoss(const int32_t*, int) {}
/// Callback function to be called when a timeout event occurs.
virtual void onTimeout() {}
/// Callback function to be called when a data is sent.
/// @param [in] seqno the data sequence number.
/// @param [in] size the payload size.
virtual void onPktSent(const CPacket*) {}
/// Callback function to be called when a data is received.
/// @param [in] seqno the data sequence number.
/// @param [in] size the payload size.
virtual void onPktReceived(const CPacket*) {}
/// Callback function to Process a user defined packet.
/// @param [in] pkt the user defined packet.
virtual void processCustomMsg(const CPacket*) {}
protected:
/// Set periodical acknowldging and the ACK period.
/// @param [in] msINT the period to send an ACK.
void setACKTimer(int msINT);
/// Set packet-based acknowldging and the number of packets to send an ACK.
/// @param [in] pktINT the number of packets to send an ACK.
void setACKInterval(int pktINT);
/// Set RTO value.
/// @param [in] msRTO RTO in macroseconds.
void setRTO(int usRTO);
/// Send a user defined control packet.
/// @param [in] pkt user defined packet.
void sendCustomMsg(CPacket& pkt) const;
/// retrieve performance information.
/// @return Pointer to a performance info structure.
const CPerfMon* getPerfInfo();
/// Set user defined parameters.
/// @param [in] param the paramters in one buffer.
/// @param [in] size the size of the buffer.
void setUserParam(const char* param, int size);
private:
void setMSS(int mss);
void setMaxCWndSize(int cwnd);
void setBandwidth(int bw);
void setSndCurrSeqNo(int32_t seqno);
void setRcvRate(int rcvrate);
void setRTT(int rtt);
protected:
const int32_t& m_iSYNInterval; // UDT constant parameter, SYN
double m_dPktSndPeriod; // Packet sending period, in microseconds
double m_dCWndSize; // Congestion window size, in packets
int m_iBandwidth; // estimated bandwidth, packets per second
double m_dMaxCWndSize; // maximum cwnd size, in packets
int m_iMSS; // Maximum Packet Size, including all packet headers
int32_t m_iSndCurrSeqNo; // current maximum seq no sent out
int m_iRcvRate; // packet arrive rate at receiver side, packets per second
int m_iRTT; // current estimated RTT, microsecond
char* m_pcParam; // user defined parameter
int m_iPSize; // size of m_pcParam
private:
UDTSOCKET m_UDT; // The UDT entity that this congestion control algorithm is bound to
int m_iACKPeriod; // Periodical timer to send an ACK, in milliseconds
int m_iACKInterval; // How many packets to send one ACK, in packets
bool m_bUserDefinedRTO; // if the RTO value is defined by users
int m_iRTO; // RTO value, microseconds
CPerfMon m_PerfInfo; // protocol statistics information
};
class CCCVirtualFactory
{
public:
virtual ~CCCVirtualFactory() {}
virtual CCC* create() = 0;
virtual CCCVirtualFactory* clone() = 0;
};
template <class T>
class CCCFactory: public CCCVirtualFactory
{
public:
virtual ~CCCFactory() {}
virtual CCC* create() {return new T;}
virtual CCCVirtualFactory* clone() {return new CCCFactory<T>;}
};
class CUDTCC: public CCC
{
public:
CUDTCC();
public:
virtual void init();
virtual void onACK(int32_t);
virtual void onLoss(const int32_t*, int);
virtual void onTimeout();
private:
int m_iRCInterval; // UDT Rate control interval
uint64_t m_LastRCTime; // last rate increase time
bool m_bSlowStart; // if in slow start phase
int32_t m_iLastAck; // last ACKed seq no
bool m_bLoss; // if loss happened since last rate increase
int32_t m_iLastDecSeq; // max pkt seq no sent out when last decrease happened
double m_dLastDecPeriod; // value of pktsndperiod when last decrease happened
int m_iNAKCount; // NAK counter
int m_iDecRandom; // random threshold on decrease by number of loss events
int m_iAvgNAKNum; // average number of NAKs per congestion
int m_iDecCount; // number of decreases in a congestion epoch
};
#endif

View file

@ -0,0 +1,79 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2020 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/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_F_ACCESS_CONTROL_H
#define INC_F_ACCESS_CONTROL_H
// A list of rejection codes that are SRT specific.
#define SRT_REJX_FALLBACK 1000 // A code used in case when the application wants to report some problem, but can't precisely specify it.
#define SRT_REJX_KEY_NOTSUP 1001 // The key used in the StreamID keyed string is not supported by the service.
#define SRT_REJX_FILEPATH 1002 // The resource type designates a file and the path is either wrong syntax or not found
#define SRT_REJX_HOSTNOTFOUND 1003 // The `h` host specification was not recognized by the service
// The list of http codes adopted for SRT.
// An example C++ header for HTTP codes can be found at:
// https://github.com/j-ulrich/http-status-codes-cpp
// Some of the unused code can be revived in the future, if there
// happens to be a good reason for it.
#define SRT_REJX_BAD_REQUEST 1400 // General syntax error in the SocketID specification (also a fallback code for undefined cases)
#define SRT_REJX_UNAUTHORIZED 1401 // Authentication failed, provided that the user was correctly identified and access to the required resource would be granted
#define SRT_REJX_OVERLOAD 1402 // The server is too heavily loaded, or you have exceeded credits for accessing the service and the resource.
#define SRT_REJX_FORBIDDEN 1403 // Access denied to the resource by any kind of reason.
#define SRT_REJX_NOTFOUND 1404 // Resource not found at this time.
#define SRT_REJX_BAD_MODE 1405 // The mode specified in `m` key in StreamID is not supported for this request.
#define SRT_REJX_UNACCEPTABLE 1406 // The requested parameters specified in SocketID cannot be satisfied for the requested resource. Also when m=publish and the data format is not acceptable.
// CODE NOT IN USE 407: unused: proxy functionality not predicted
// CODE NOT IN USE 408: unused: no timeout predicted for listener callback
#define SRT_REJX_CONFLICT 1409 // The resource being accessed is already locked for modification. This is in case of m=publish and the specified resource is currently read-only.
// CODE NOT IN USE 410: unused: treated as a specific case of 404
// CODE NOT IN USE 411: unused: no reason to include lenght in the protocol
// CODE NOT IN USE 412: unused: preconditions not predicted in AC
// CODE NOT IN USE 413: unused: AC size is already defined as 512
// CODE NOT IN USE 414: unused: AC size is already defined as 512
#define SRT_REJX_NOTSUP_MEDIA 1415 // The media type is not supported by the application. This is the `t` key that specifies the media type as stream, file and auth, possibly extended by the application.
// CODE NOT IN USE 416: unused: no detailed specification defined
// CODE NOT IN USE 417: unused: expectations not supported
// CODE NOT IN USE 418: unused: sharks do not drink tea
// CODE NOT IN USE 419: not defined in HTTP
// CODE NOT IN USE 420: not defined in HTTP
// CODE NOT IN USE 421: unused: misdirection not supported
// CODE NOT IN USE 422: unused: aligned to general 400
#define SRT_REJX_LOCKED 1423 // The resource being accessed is locked for any access.
#define SRT_REJX_FAILED_DEPEND 1424 // The request failed because it specified a dependent session ID that has been disconnected.
// CODE NOT IN USE 425: unused: replaying not supported
// CODE NOT IN USE 426: unused: tempting, but it requires resend in connected
// CODE NOT IN USE 427: not defined in HTTP
// CODE NOT IN USE 428: unused: renders to 409
// CODE NOT IN USE 429: unused: renders to 402
// CODE NOT IN USE 451: unused: renders to 403
#define SRT_REJX_ISE 1500 // Unexpected internal server error
#define SRT_REJX_UNIMPLEMENTED 1501 // The request was recognized, but the current version doesn't support it.
#define SRT_REJX_GW 1502 // The server acts as a gateway and the target endpoint rejected the connection.
#define SRT_REJX_DOWN 1503 // The service has been temporarily taken over by a stub reporting this error. The real service can be down for maintenance or crashed.
// CODE NOT IN USE 504: unused: timeout not supported
#define SRT_REJX_VERSION 1505 // SRT version not supported. This might be either unsupported backward compatibility, or an upper value of a version.
// CODE NOT IN USE 506: unused: negotiation and references not supported
#define SRT_REJX_NOROOM 1507 // The data stream cannot be archived due to lacking storage space. This is in case when the request type was to send a file or the live stream to be archived.
// CODE NOT IN USE 508: unused: no redirection supported
// CODE NOT IN USE 509: not defined in HTTP
// CODE NOT IN USE 510: unused: extensions not supported
// CODE NOT IN USE 511: unused: intercepting proxies not supported
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 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/.
*
*
*/
/*****************************************************************************
@ -45,14 +45,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/*****************************************************************************
written by
Yunhong Gu, last updated 09/28/2010
Yunhong Gu, last updated 09/28/2010
modified by
Haivision Systems Inc.
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_API_H__
#define __UDT_API_H__
#ifndef INC_SRT_API_H
#define INC_SRT_API_H
#include <map>
#include <vector>
@ -64,237 +63,425 @@ modified by
#include "cache.h"
#include "epoll.h"
#include "handshake.h"
#include "core.h"
#if ENABLE_BONDING
#include "group.h"
#endif
// Please refer to structure and locking information provided in the
// docs/dev/low-level-info.md document.
namespace srt
{
class CUDT;
/// @brief Class CUDTSocket is a control layer on top of the CUDT core functionality layer.
/// CUDTSocket owns CUDT.
class CUDTSocket
{
public:
CUDTSocket();
~CUDTSocket();
CUDTSocket()
: m_Status(SRTS_INIT)
, m_SocketID(0)
, m_ListenSocket(0)
, m_PeerID(0)
#if ENABLE_BONDING
, m_GroupMemberData()
, m_GroupOf()
#endif
, m_iISN(0)
, m_UDT(this)
, m_AcceptCond()
, m_AcceptLock()
, m_uiBackLog(0)
, m_iMuxID(-1)
{
construct();
}
SRT_SOCKSTATUS m_Status; //< current socket state
CUDTSocket(const CUDTSocket& ancestor)
: m_Status(SRTS_INIT)
, m_SocketID(0)
, m_ListenSocket(0)
, m_PeerID(0)
#if ENABLE_BONDING
, m_GroupMemberData()
, m_GroupOf()
#endif
, m_iISN(0)
, m_UDT(this, ancestor.m_UDT)
, m_AcceptCond()
, m_AcceptLock()
, m_uiBackLog(0)
, m_iMuxID(-1)
{
construct();
}
/// Time when the socket is closed.
/// When the socket is closed, it is not removed immediately from the list
/// of sockets in order to prevent other methods from accessing invalid address.
/// A timer is started and the socket will be removed after approximately
/// 1 second (see CUDTUnited::checkBrokenSockets()).
uint64_t m_ClosureTimeStamp;
~CUDTSocket();
int m_iIPversion; //< IP version
sockaddr* m_pSelfAddr; //< pointer to the local address of the socket
sockaddr* m_pPeerAddr; //< pointer to the peer address of the socket
void construct();
SRTSOCKET m_SocketID; //< socket ID
SRTSOCKET m_ListenSocket; //< ID of the listener socket; 0 means this is an independent socket
SRT_ATTR_GUARDED_BY(m_ControlLock)
sync::atomic<SRT_SOCKSTATUS> m_Status; //< current socket state
SRTSOCKET m_PeerID; //< peer socket ID
int32_t m_iISN; //< initial sequence number, used to tell different connection from same IP:port
/// Time when the socket is closed.
/// When the socket is closed, it is not removed immediately from the list
/// of sockets in order to prevent other methods from accessing invalid address.
/// A timer is started and the socket will be removed after approximately
/// 1 second (see CUDTUnited::checkBrokenSockets()).
sync::steady_clock::time_point m_tsClosureTimeStamp;
CUDT* m_pUDT; //< pointer to the UDT entity
sockaddr_any m_SelfAddr; //< local address of the socket
sockaddr_any m_PeerAddr; //< peer address of the socket
std::set<SRTSOCKET>* m_pQueuedSockets; //< set of connections waiting for accept()
std::set<SRTSOCKET>* m_pAcceptSockets; //< set of accept()ed connections
SRTSOCKET m_SocketID; //< socket ID
SRTSOCKET m_ListenSocket; //< ID of the listener socket; 0 means this is an independent socket
pthread_cond_t m_AcceptCond; //< used to block "accept" call
pthread_mutex_t m_AcceptLock; //< mutex associated to m_AcceptCond
SRTSOCKET m_PeerID; //< peer socket ID
#if ENABLE_BONDING
groups::SocketData* m_GroupMemberData; //< Pointer to group member data, or NULL if not a group member
CUDTGroup* m_GroupOf; //< Group this socket is a member of, or NULL if it isn't
#endif
unsigned int m_uiBackLog; //< maximum number of connections in queue
int m_iMuxID; //< multiplexer ID
pthread_mutex_t m_ControlLock; //< lock this socket exclusively for control APIs: bind/listen/connect
static int64_t getPeerSpec(SRTSOCKET id, int32_t isn)
{
return (id << 30) + isn;
}
int64_t getPeerSpec()
{
return getPeerSpec(m_PeerID, m_iISN);
}
int32_t m_iISN; //< initial sequence number, used to tell different connection from same IP:port
private:
CUDTSocket(const CUDTSocket&);
CUDTSocket& operator=(const CUDTSocket&);
CUDT m_UDT; //< internal SRT socket logic
public:
std::set<SRTSOCKET> m_QueuedSockets; //< set of connections waiting for accept()
sync::Condition m_AcceptCond; //< used to block "accept" call
sync::Mutex m_AcceptLock; //< mutex associated to m_AcceptCond
unsigned int m_uiBackLog; //< maximum number of connections in queue
// XXX A refactoring might be needed here.
// There are no reasons found why the socket can't contain a list iterator to a
// multiplexer INSTEAD of m_iMuxID. There's no danger in this solution because
// the multiplexer is never deleted until there's at least one socket using it.
//
// The multiplexer may even physically be contained in the CUDTUnited object,
// just track the multiple users of it (the listener and the accepted sockets).
// When deleting, you simply "unsubscribe" yourself from the multiplexer, which
// will unref it and remove the list element by the iterator kept by the
// socket.
int m_iMuxID; //< multiplexer ID
sync::Mutex m_ControlLock; //< lock this socket exclusively for control APIs: bind/listen/connect
CUDT& core() { return m_UDT; }
const CUDT& core() const { return m_UDT; }
static int64_t getPeerSpec(SRTSOCKET id, int32_t isn) { return (int64_t(id) << 30) + isn; }
int64_t getPeerSpec() { return getPeerSpec(m_PeerID, m_iISN); }
SRT_SOCKSTATUS getStatus();
/// This function shall be called always wherever
/// you'd like to call cudtsocket->m_pUDT->close(),
/// from within the GC thread only (that is, only when
/// the socket should be no longer visible in the
/// connection, including for sending remaining data).
void breakSocket_LOCKED();
/// This makes the socket no longer capable of performing any transmission
/// operation, but continues to be responsive in the connection in order
/// to finish sending the data that were scheduled for sending so far.
void setClosed();
/// This does the same as setClosed, plus sets the m_bBroken to true.
/// Such a socket can still be read from so that remaining data from
/// the receiver buffer can be read, but no longer sends anything.
void setBrokenClosed();
void removeFromGroup(bool broken);
// Instrumentally used by select() and also required for non-blocking
// mode check in groups
bool readReady();
bool writeReady() const;
bool broken() const;
private:
CUDTSocket& operator=(const CUDTSocket&);
};
////////////////////////////////////////////////////////////////////////////////
class CUDTUnited
{
friend class CUDT;
friend class CRendezvousQueue;
friend class CUDT;
friend class CUDTGroup;
friend class CRendezvousQueue;
friend class CCryptoControl;
public:
CUDTUnited();
~CUDTUnited();
CUDTUnited();
~CUDTUnited();
// Public constants
static const int32_t MAX_SOCKET_VAL = SRTGROUP_MASK - 1; // maximum value for a regular socket
public:
static std::string CONID(SRTSOCKET sock);
/// initialize the UDT library.
/// @return 0 if success, otherwise -1 is returned.
int startup();
/// release the UDT library.
/// @return 0 if success, otherwise -1 is returned.
int cleanup();
/// Create a new UDT socket.
/// @param [in] af IP version, IPv4 (AF_INET) or IPv6 (AF_INET6).
/// @param [in] type (ignored)
/// @return The new UDT socket ID, or INVALID_SOCK.
SRTSOCKET newSocket(int af, int );
/// Create a new UDT connection.
/// @param [in] listen the listening UDT socket;
/// @param [in] peer peer address.
/// @param [in,out] hs handshake information from peer side (in), negotiated value (out);
/// @return If the new connection is successfully created: 1 success, 0 already exist, -1 error.
int newConnection(const SRTSOCKET listen, const sockaddr* peer, CHandShake* hs, const CPacket& hspkt,
ref_t<SRT_REJECT_REASON> r_error);
int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq);
/// look up the UDT entity according to its ID.
/// @param [in] u the UDT socket ID.
/// @return Pointer to the UDT entity.
CUDT* lookup(const SRTSOCKET u);
/// Check the status of the UDT socket.
/// @param [in] u the UDT socket ID.
/// @return UDT socket status, or NONEXIST if not found.
SRT_SOCKSTATUS getStatus(const SRTSOCKET u);
// socket APIs
int bind(const SRTSOCKET u, const sockaddr* name, int namelen);
int bind(const SRTSOCKET u, UDPSOCKET udpsock);
int listen(const SRTSOCKET u, int backlog);
SRTSOCKET accept(const SRTSOCKET listen, sockaddr* addr, int* addrlen);
int connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn);
int close(const SRTSOCKET u);
int getpeername(const SRTSOCKET u, sockaddr* name, int* namelen);
int getsockname(const SRTSOCKET u, sockaddr* name, int* namelen);
int select(ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout);
int selectEx(const std::vector<SRTSOCKET>& fds, std::vector<SRTSOCKET>* readfds, std::vector<SRTSOCKET>* writefds, std::vector<SRTSOCKET>* exceptfds, int64_t msTimeOut);
int epoll_create();
int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
int epoll_remove_usock(const int eid, const SRTSOCKET u);
int epoll_remove_ssock(const int eid, const SYSSOCKET s);
int epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
int epoll_wait(const int eid, std::set<SRTSOCKET>* readfds, std::set<SRTSOCKET>* writefds, int64_t msTimeOut, std::set<SYSSOCKET>* lrfds = NULL, std::set<SYSSOCKET>* lwfds = NULL);
int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
int32_t epoll_set(const int eid, int32_t flags);
int epoll_release(const int eid);
/// record the UDT exception.
/// @param [in] e pointer to a UDT exception instance.
void setError(CUDTException* e);
/// look up the most recent UDT exception.
/// @return pointer to a UDT exception instance.
CUDTException* getError();
private:
// void init();
private:
std::map<SRTSOCKET, CUDTSocket*> m_Sockets; // stores all the socket structures
pthread_mutex_t m_ControlLock; // used to synchronize UDT API
pthread_mutex_t m_IDLock; // used to synchronize ID generation
SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID
std::map<int64_t, std::set<SRTSOCKET> > m_PeerRec;// record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn
private:
pthread_key_t m_TLSError; // thread local error record (last error)
static void TLSDestroy(void* e) {if (NULL != e) delete (CUDTException*)e;}
private:
CUDTSocket* locate(const SRTSOCKET u);
CUDTSocket* locate(const sockaddr* peer, const SRTSOCKET id, int32_t isn);
void updateMux(CUDTSocket* s, const sockaddr* addr = NULL, const UDPSOCKET* = NULL);
void updateListenerMux(CUDTSocket* s, const CUDTSocket* ls);
private:
std::map<int, CMultiplexer> m_mMultiplexer; // UDP multiplexer
pthread_mutex_t m_MultiplexerLock;
private:
CCache<CInfoBlock>* m_pCache; // UDT network information cache
private:
volatile bool m_bClosing;
pthread_mutex_t m_GCStopLock;
pthread_cond_t m_GCStopCond;
pthread_mutex_t m_InitLock;
int m_iInstanceCount; // number of startup() called by application
bool m_bGCStatus; // if the GC thread is working (true)
pthread_t m_GCThread;
static void* garbageCollect(void*);
std::map<SRTSOCKET, CUDTSocket*> m_ClosedSockets; // temporarily store closed sockets
void checkBrokenSockets();
void removeSocket(const SRTSOCKET u);
CEPoll m_EPoll; // handling epoll data structures and events
private:
CUDTUnited(const CUDTUnited&);
CUDTUnited& operator=(const CUDTUnited&);
};
// Debug support
inline std::string SockaddrToString(const sockaddr* sadr)
{
void* addr =
sadr->sa_family == AF_INET ?
(void*)&((sockaddr_in*)sadr)->sin_addr
: sadr->sa_family == AF_INET6 ?
(void*)&((sockaddr_in6*)sadr)->sin6_addr
: 0;
// (cast to (void*) is required because otherwise the 2-3 arguments
// of ?: operator would have different types, which isn't allowed in C++.
if ( !addr )
return "unknown:0";
std::ostringstream output;
char hostbuf[1024];
int flags;
#if ENABLE_GETNAMEINFO
flags = NI_NAMEREQD;
#else
flags = NI_NUMERICHOST | NI_NUMERICSERV;
#endif
if (!getnameinfo(sadr, sizeof(*sadr), hostbuf, 1024, NULL, 0, flags))
enum ErrorHandling
{
output << hostbuf;
ERH_RETURN,
ERH_THROW,
ERH_ABORT
};
static std::string CONID(SRTSOCKET sock);
/// initialize the UDT library.
/// @return 0 if success, otherwise -1 is returned.
int startup();
/// release the UDT library.
/// @return 0 if success, otherwise -1 is returned.
int cleanup();
/// Create a new UDT socket.
/// @param [out] pps Variable (optional) to which the new socket will be written, if succeeded
/// @return The new UDT socket ID, or INVALID_SOCK.
SRTSOCKET newSocket(CUDTSocket** pps = NULL);
/// Create (listener-side) a new socket associated with the incoming connection request.
/// @param [in] listen the listening socket ID.
/// @param [in] peer peer address.
/// @param [in,out] hs handshake information from peer side (in), negotiated value (out);
/// @param [out] w_error error code in case of failure.
/// @param [out] w_acpu reference to the existing associated socket if already exists.
/// @return 1: if the new connection was successfully created (accepted), @a w_acpu is NULL;
/// 0: the connection already exists (reference to the corresponding socket is returned in @a w_acpu).
/// -1: The connection processing failed due to memory alloation error, exceeding listener's backlog,
/// any error propagated from CUDT::open and CUDT::acceptAndRespond.
int newConnection(const SRTSOCKET listen,
const sockaddr_any& peer,
const CPacket& hspkt,
CHandShake& w_hs,
int& w_error,
CUDT*& w_acpu);
int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq);
int installConnectHook(const SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq);
/// Check the status of the UDT socket.
/// @param [in] u the UDT socket ID.
/// @return UDT socket status, or NONEXIST if not found.
SRT_SOCKSTATUS getStatus(const SRTSOCKET u);
// socket APIs
int bind(CUDTSocket* u, const sockaddr_any& name);
int bind(CUDTSocket* u, UDPSOCKET udpsock);
int listen(const SRTSOCKET u, int backlog);
SRTSOCKET accept(const SRTSOCKET listen, sockaddr* addr, int* addrlen);
SRTSOCKET accept_bond(const SRTSOCKET listeners[], int lsize, int64_t msTimeOut);
int connect(SRTSOCKET u, const sockaddr* srcname, const sockaddr* tarname, int tarlen);
int connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn);
int connectIn(CUDTSocket* s, const sockaddr_any& target, int32_t forced_isn);
#if ENABLE_BONDING
int groupConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG targets[], int arraysize);
int singleMemberConnect(CUDTGroup* g, SRT_SOCKGROUPCONFIG* target);
#endif
int close(const SRTSOCKET u);
int close(CUDTSocket* s);
void getpeername(const SRTSOCKET u, sockaddr* name, int* namelen);
void getsockname(const SRTSOCKET u, sockaddr* name, int* namelen);
int select(UDT::UDSET* readfds, UDT::UDSET* writefds, UDT::UDSET* exceptfds, const timeval* timeout);
int selectEx(const std::vector<SRTSOCKET>& fds,
std::vector<SRTSOCKET>* readfds,
std::vector<SRTSOCKET>* writefds,
std::vector<SRTSOCKET>* exceptfds,
int64_t msTimeOut);
int epoll_create();
int epoll_clear_usocks(int eid);
int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
int epoll_add_usock_INTERNAL(const int eid, CUDTSocket* s, const int* events);
int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
int epoll_remove_usock(const int eid, const SRTSOCKET u);
template <class EntityType>
int epoll_remove_entity(const int eid, EntityType* ent);
int epoll_remove_socket_INTERNAL(const int eid, CUDTSocket* ent);
#if ENABLE_BONDING
int epoll_remove_group_INTERNAL(const int eid, CUDTGroup* ent);
#endif
int epoll_remove_ssock(const int eid, const SYSSOCKET s);
int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
int32_t epoll_set(const int eid, int32_t flags);
int epoll_release(const int eid);
#if ENABLE_BONDING
// [[using locked(m_GlobControlLock)]]
CUDTGroup& addGroup(SRTSOCKET id, SRT_GROUP_TYPE type)
{
// This only ensures that the element exists.
// If the element was newly added, it will be NULL.
CUDTGroup*& g = m_Groups[id];
if (!g)
{
// This is a reference to the cell, so it will
// rewrite it into the map.
g = new CUDTGroup(type);
}
// Now we are sure that g is not NULL,
// and persistence of this object is in the map.
// The reference to the object can be safely returned here.
return *g;
}
output << ":" << ntohs(((sockaddr_in*)sadr)->sin_port); // TRICK: sin_port and sin6_port have the same offset and size
return output.str();
}
void deleteGroup(CUDTGroup* g);
void deleteGroup_LOCKED(CUDTGroup* g);
// [[using locked(m_GlobControlLock)]]
CUDTGroup* findPeerGroup_LOCKED(SRTSOCKET peergroup)
{
for (groups_t::iterator i = m_Groups.begin(); i != m_Groups.end(); ++i)
{
if (i->second->peerid() == peergroup)
return i->second;
}
return NULL;
}
#endif
CEPoll& epoll_ref() { return m_EPoll; }
private:
/// Generates a new socket ID. This function starts from a randomly
/// generated value (at initialization time) and goes backward with
/// with next calls. The possible values come from the range without
/// the SRTGROUP_MASK bit, and the group bit is set when the ID is
/// generated for groups. It is also internally checked if the
/// newly generated ID isn't already used by an existing socket or group.
///
/// Socket ID value range.
/// - [0]: reserved for handshake procedure. If the destination Socket ID is 0
/// (destination Socket ID unknown) the packet will be sent to the listening socket
/// or to a socket that is in the rendezvous connection phase.
/// - [1; 2 ^ 30): single socket ID range.
/// - (2 ^ 30; 2 ^ 31): group socket ID range. Effectively any positive number
/// from [1; 2 ^ 30) with bit 30 set to 1. Bit 31 is zero.
/// The most significant bit 31 (sign bit) is left unused so that checking for a value <= 0 identifies an invalid
/// socket ID.
///
/// @param group The socket id should be for socket group.
/// @return The new socket ID.
/// @throw CUDTException if after rolling over all possible ID values nothing can be returned
SRTSOCKET generateSocketID(bool group = false);
private:
typedef std::map<SRTSOCKET, CUDTSocket*> sockets_t; // stores all the socket structures
sockets_t m_Sockets;
#if ENABLE_BONDING
typedef std::map<SRTSOCKET, CUDTGroup*> groups_t;
groups_t m_Groups;
#endif
sync::Mutex m_GlobControlLock; // used to synchronize UDT API
sync::Mutex m_IDLock; // used to synchronize ID generation
SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID
SRTSOCKET m_SocketIDGenerator_init; // Keeps track of the very first one
std::map<int64_t, std::set<SRTSOCKET> >
m_PeerRec; // record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn
private:
friend struct FLookupSocketWithEvent_LOCKED;
CUDTSocket* locateSocket(SRTSOCKET u, ErrorHandling erh = ERH_RETURN);
// This function does the same as locateSocket, except that:
// - lock on m_GlobControlLock is expected (so that you don't unlock between finding and using)
// - only return NULL if not found
CUDTSocket* locateSocket_LOCKED(SRTSOCKET u);
CUDTSocket* locatePeer(const sockaddr_any& peer, const SRTSOCKET id, int32_t isn);
#if ENABLE_BONDING
CUDTGroup* locateAcquireGroup(SRTSOCKET u, ErrorHandling erh = ERH_RETURN);
CUDTGroup* acquireSocketsGroup(CUDTSocket* s);
struct GroupKeeper
{
CUDTGroup* group;
// This is intended for API functions to lock the group's existence
// for the lifetime of their call.
GroupKeeper(CUDTUnited& glob, SRTSOCKET id, ErrorHandling erh) { group = glob.locateAcquireGroup(id, erh); }
// This is intended for TSBPD thread that should lock the group's
// existence until it exits.
GroupKeeper(CUDTUnited& glob, CUDTSocket* s) { group = glob.acquireSocketsGroup(s); }
~GroupKeeper()
{
if (group)
{
// We have a guarantee that if `group` was set
// as non-NULL here, it is also acquired and will not
// be deleted until this busy flag is set back to false.
sync::ScopedLock cgroup(*group->exp_groupLock());
group->apiRelease();
// Only now that the group lock is lifted, can the
// group be now deleted and this pointer potentially dangling
}
}
};
#endif
void updateMux(CUDTSocket* s, const sockaddr_any& addr, const UDPSOCKET* = NULL);
bool updateListenerMux(CUDTSocket* s, const CUDTSocket* ls);
// Utility functions for updateMux
void configureMuxer(CMultiplexer& w_m, const CUDTSocket* s, int af);
uint16_t installMuxer(CUDTSocket* w_s, CMultiplexer& sm);
/// @brief Checks if channel configuration matches the socket configuration.
/// @param cfgMuxer multiplexer configuration.
/// @param cfgSocket socket configuration.
/// @return tru if configurations match, false otherwise.
static bool channelSettingsMatch(const CSrtMuxerConfig& cfgMuxer, const CSrtConfig& cfgSocket);
private:
std::map<int, CMultiplexer> m_mMultiplexer; // UDP multiplexer
sync::Mutex m_MultiplexerLock;
private:
CCache<CInfoBlock>* m_pCache; // UDT network information cache
private:
srt::sync::atomic<bool> m_bClosing;
sync::Mutex m_GCStopLock;
sync::Condition m_GCStopCond;
sync::Mutex m_InitLock;
int m_iInstanceCount; // number of startup() called by application
bool m_bGCStatus; // if the GC thread is working (true)
sync::CThread m_GCThread;
static void* garbageCollect(void*);
sockets_t m_ClosedSockets; // temporarily store closed sockets
#if ENABLE_BONDING
groups_t m_ClosedGroups;
#endif
void checkBrokenSockets();
void removeSocket(const SRTSOCKET u);
CEPoll m_EPoll; // handling epoll data structures and events
private:
CUDTUnited(const CUDTUnited&);
CUDTUnited& operator=(const CUDTUnited&);
};
} // namespace srt
#endif

View file

@ -0,0 +1,311 @@
//----------------------------------------------------------------------------
// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or distribute
// this software, either in source code form or as a compiled binary, for any
// purpose, commercial or non-commercial, and by any means.
//
// In jurisdictions that recognize copyright laws, the author or authors of
// this software dedicate any and all copyright interest in the software to the
// public domain. We make this dedication for the benefit of the public at
// large and to the detriment of our heirs and successors. We intend this
// dedication to be an overt act of relinquishment in perpetuity of all present
// and future rights to this software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to <http://unlicense.org/>
//-----------------------------------------------------------------------------
// SRT Project information:
// This file was adopted from a Public Domain project from
// https://github.com/mbitsnbites/atomic
// Only namespaces were changed to adopt it for SRT project.
#ifndef SRT_SYNC_ATOMIC_H_
#define SRT_SYNC_ATOMIC_H_
// Macro for disallowing copying of an object.
#if __cplusplus >= 201103L
#define ATOMIC_DISALLOW_COPY(T) \
T(const T&) = delete; \
T& operator=(const T&) = delete;
#else
#define ATOMIC_DISALLOW_COPY(T) \
T(const T&); \
T& operator=(const T&);
#endif
// A portable static assert.
#if __cplusplus >= 201103L
#define ATOMIC_STATIC_ASSERT(condition, message) \
static_assert((condition), message)
#else
// Based on: http://stackoverflow.com/a/809465/5778708
#define ATOMIC_STATIC_ASSERT(condition, message) \
_impl_STATIC_ASSERT_LINE(condition, __LINE__)
#define _impl_PASTE(a, b) a##b
#ifdef __GNUC__
#define _impl_UNUSED __attribute__((__unused__))
#else
#define _impl_UNUSED
#endif
#define _impl_STATIC_ASSERT_LINE(condition, line) \
typedef char _impl_PASTE( \
STATIC_ASSERT_failed_, \
line)[(2 * static_cast<int>(!!(condition))) - 1] _impl_UNUSED
#endif
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
// NOTE: Defined at the top level.
#elif __cplusplus >= 201103L
// NOTE: Prefer to use the c++11 std::atomic.
#define ATOMIC_USE_CPP11_ATOMIC
#elif (defined(__clang__) && defined(__clang_major__) && (__clang_major__ > 5)) \
|| defined(__xlc__)
// NOTE: Clang <6 does not support GCC __atomic_* intrinsics. I am unsure
// about Clang6. Since Clang sets __GNUC__ and __GNUC_MINOR__ of this era
// to <4.5, older Clang will catch the setting below to use the
// POSIX Mutex Implementation.
#define ATOMIC_USE_GCC_INTRINSICS
#elif defined(__GNUC__) \
&& ( (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)) )
// NOTE: The __atomic_* family of intrisics were introduced in GCC-4.7.0.
// NOTE: This follows #if defined(__clang__), because most if, not all,
// versions of Clang define __GNUC__ and __GNUC_MINOR__ but often define
// them to 4.4 or an even earlier version. Most of the newish versions
// of Clang also support GCC Atomic Intrisics even if they set GCC version
// macros to <4.7.
#define ATOMIC_USE_GCC_INTRINSICS
#elif defined(__GNUC__) && !defined(ATOMIC_USE_SRT_SYNC_MUTEX)
// NOTE: GCC compiler built-ins for atomic operations are pure
// compiler extensions prior to GCC-4.7 and were grouped into the
// the __sync_* family of functions. GCC-4.7, both the c++11 and C11
// standards had been finalized, and GCC updated their built-ins to
// better reflect the new memory model and the new functions grouped
// into the __atomic_* family. Also the memory models were defined
// differently, than in pre 4.7.
// TODO: PORT to the pre GCC-4.7 __sync_* intrinsics. In the meantime use
// the POSIX Mutex Implementation.
#define ATOMIC_USE_SRT_SYNC_MUTEX 1
#elif defined(_MSC_VER)
#define ATOMIC_USE_MSVC_INTRINSICS
#include "atomic_msvc.h"
#else
#error Unsupported compiler / system.
#endif
// Include any necessary headers for the selected Atomic Implementation.
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
#include "sync.h"
#endif
#if defined(ATOMIC_USE_CPP11_ATOMIC)
#include <atomic>
#endif
namespace srt {
namespace sync {
template <typename T>
class atomic {
public:
ATOMIC_STATIC_ASSERT(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 ||
sizeof(T) == 8,
"Only types of size 1, 2, 4 or 8 are supported");
atomic()
: value_(static_cast<T>(0))
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
, mutex_()
#endif
{
// No-Op
}
explicit atomic(const T value)
: value_(value)
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
, mutex_()
#endif
{
// No-Op
}
~atomic()
{
// No-Op
}
/// @brief Performs an atomic increment operation (value + 1).
/// @returns The new value of the atomic object.
T operator++() {
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
ScopedLock lg_(mutex_);
const T t = ++value_;
return t;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
return __atomic_add_fetch(&value_, 1, __ATOMIC_SEQ_CST);
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
return msvc::interlocked<T>::increment(&value_);
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
return ++value_;
#else
#error "Implement Me."
#endif
}
/// @brief Performs an atomic decrement operation (value - 1).
/// @returns The new value of the atomic object.
T operator--() {
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
ScopedLock lg_(mutex_);
const T t = --value_;
return t;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
return __atomic_sub_fetch(&value_, 1, __ATOMIC_SEQ_CST);
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
return msvc::interlocked<T>::decrement(&value_);
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
return --value_;
#else
#error "Implement Me."
#endif
}
/// @brief Performs an atomic compare-and-swap (CAS) operation.
///
/// The value of the atomic object is only updated to the new value if the
/// old value of the atomic object matches @c expected_val.
///
/// @param expected_val The expected value of the atomic object.
/// @param new_val The new value to write to the atomic object.
/// @returns True if new_value was written to the atomic object.
bool compare_exchange(const T expected_val, const T new_val) {
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
ScopedLock lg_(mutex_);
bool result = false;
if (expected_val == value_)
{
value_ = new_val;
result = true;
}
return result;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
T e = expected_val;
return __atomic_compare_exchange_n(
&value_, &e, new_val, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
const T old_val =
msvc::interlocked<T>::compare_exchange(&value_, new_val, expected_val);
return (old_val == expected_val);
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
T e = expected_val;
return value_.compare_exchange_weak(e, new_val);
#else
#error "Implement Me."
#endif
}
/// @brief Performs an atomic set operation.
///
/// The value of the atomic object is unconditionally updated to the new
/// value.
///
/// @param new_val The new value to write to the atomic object.
void store(const T new_val) {
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
ScopedLock lg_(mutex_);
value_ = new_val;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
__atomic_store_n(&value_, new_val, __ATOMIC_SEQ_CST);
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
(void)msvc::interlocked<T>::exchange(&value_, new_val);
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
value_.store(new_val);
#else
#error "Implement Me."
#endif
}
/// @returns the current value of the atomic object.
/// @note Be careful about how this is used, since any operations on the
/// returned value are inherently non-atomic.
T load() const {
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
ScopedLock lg_(mutex_);
const T t = value_;
return t;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
return __atomic_load_n(&value_, __ATOMIC_SEQ_CST);
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
// TODO(m): Is there a better solution for MSVC?
return value_;
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
return value_;
#else
#error "Implement Me."
#endif
}
/// @brief Performs an atomic exchange operation.
///
/// The value of the atomic object is unconditionally updated to the new
/// value, and the old value is returned.
///
/// @param new_val The new value to write to the atomic object.
/// @returns the old value.
T exchange(const T new_val) {
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
ScopedLock lg_(mutex_);
const T t = value_;
value_ = new_val;
return t;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
return __atomic_exchange_n(&value_, new_val, __ATOMIC_SEQ_CST);
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
return msvc::interlocked<T>::exchange(&value_, new_val);
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
return value_.exchange(new_val);
#else
#error "Implement Me."
#endif
}
T operator=(const T new_value) {
store(new_value);
return new_value;
}
operator T() const {
return load();
}
private:
#if defined(ATOMIC_USE_SRT_SYNC_MUTEX) && (ATOMIC_USE_SRT_SYNC_MUTEX == 1)
T value_;
mutable Mutex mutex_;
#elif defined(ATOMIC_USE_GCC_INTRINSICS)
volatile T value_;
#elif defined(ATOMIC_USE_MSVC_INTRINSICS)
volatile T value_;
#elif defined(ATOMIC_USE_CPP11_ATOMIC)
std::atomic<T> value_;
#else
#error "Implement Me. (value_ type)"
#endif
ATOMIC_DISALLOW_COPY(atomic)
};
} // namespace sync
} // namespace srt
// Undef temporary defines.
#undef ATOMIC_USE_GCC_INTRINSICS
#undef ATOMIC_USE_MSVC_INTRINSICS
#undef ATOMIC_USE_CPP11_ATOMIC
#endif // ATOMIC_ATOMIC_H_

View file

@ -0,0 +1,91 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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/.
*
*/
#ifndef INC_SRT_SYNC_ATOMIC_CLOCK_H
#define INC_SRT_SYNC_ATOMIC_CLOCK_H
#include "sync.h"
#include "atomic.h"
namespace srt
{
namespace sync
{
template <class Clock>
class AtomicDuration
{
atomic<int64_t> dur;
typedef typename Clock::duration duration_type;
typedef typename Clock::time_point time_point_type;
public:
AtomicDuration() ATR_NOEXCEPT : dur(0) {}
duration_type load()
{
int64_t val = dur.load();
return duration_type(val);
}
void store(const duration_type& d)
{
dur.store(d.count());
}
AtomicDuration<Clock>& operator=(const duration_type& s)
{
dur = s.count();
return *this;
}
operator duration_type() const
{
return duration_type(dur);
}
};
template <class Clock>
class AtomicClock
{
atomic<uint64_t> dur;
typedef typename Clock::duration duration_type;
typedef typename Clock::time_point time_point_type;
public:
AtomicClock() ATR_NOEXCEPT : dur(0) {}
time_point_type load() const
{
int64_t val = dur.load();
return time_point_type(duration_type(val));
}
void store(const time_point_type& d)
{
dur.store(uint64_t(d.time_since_epoch().count()));
}
AtomicClock& operator=(const time_point_type& s)
{
dur = s.time_since_epoch().count();
return *this;
}
operator time_point_type() const
{
return time_point_type(duration_type(dur.load()));
}
};
} // namespace sync
} // namespace srt
#endif // INC_SRT_SYNC_ATOMIC_CLOCK_H

View file

@ -0,0 +1,245 @@
//-----------------------------------------------------------------------------
// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or distribute
// this software, either in source code form or as a compiled binary, for any
// purpose, commercial or non-commercial, and by any means.
//
// In jurisdictions that recognize copyright laws, the author or authors of
// this software dedicate any and all copyright interest in the software to the
// public domain. We make this dedication for the benefit of the public at
// large and to the detriment of our heirs and successors. We intend this
// dedication to be an overt act of relinquishment in perpetuity of all present
// and future rights to this software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to <http://unlicense.org/>
//-----------------------------------------------------------------------------
// SRT Project information:
// This file was adopted from a Public Domain project from
// https://github.com/mbitsnbites/atomic
// Only namespaces were changed to adopt it for SRT project.
#ifndef SRT_SYNC_ATOMIC_MSVC_H_
#define SRT_SYNC_ATOMIC_MSVC_H_
// Define which functions we need (don't include <intrin.h>).
extern "C" {
short _InterlockedIncrement16(short volatile*);
long _InterlockedIncrement(long volatile*);
__int64 _InterlockedIncrement64(__int64 volatile*);
short _InterlockedDecrement16(short volatile*);
long _InterlockedDecrement(long volatile*);
__int64 _InterlockedDecrement64(__int64 volatile*);
char _InterlockedExchange8(char volatile*, char);
short _InterlockedExchange16(short volatile*, short);
long __cdecl _InterlockedExchange(long volatile*, long);
__int64 _InterlockedExchange64(__int64 volatile*, __int64);
char _InterlockedCompareExchange8(char volatile*, char, char);
short _InterlockedCompareExchange16(short volatile*, short, short);
long __cdecl _InterlockedCompareExchange(long volatile*, long, long);
__int64 _InterlockedCompareExchange64(__int64 volatile*, __int64, __int64);
};
// Define which functions we want to use as inline intriniscs.
#pragma intrinsic(_InterlockedIncrement)
#pragma intrinsic(_InterlockedIncrement16)
#pragma intrinsic(_InterlockedDecrement)
#pragma intrinsic(_InterlockedDecrement16)
#pragma intrinsic(_InterlockedCompareExchange)
#pragma intrinsic(_InterlockedCompareExchange8)
#pragma intrinsic(_InterlockedCompareExchange16)
#pragma intrinsic(_InterlockedExchange)
#pragma intrinsic(_InterlockedExchange8)
#pragma intrinsic(_InterlockedExchange16)
#if defined(_M_X64)
#pragma intrinsic(_InterlockedIncrement64)
#pragma intrinsic(_InterlockedDecrement64)
#pragma intrinsic(_InterlockedCompareExchange64)
#pragma intrinsic(_InterlockedExchange64)
#endif // _M_X64
namespace srt {
namespace sync {
namespace msvc {
template <typename T, size_t N = sizeof(T)>
struct interlocked {
};
template <typename T>
struct interlocked<T, 1> {
static inline T increment(T volatile* x) {
// There's no _InterlockedIncrement8().
char old_val, new_val;
do {
old_val = static_cast<char>(*x);
new_val = old_val + static_cast<char>(1);
} while (_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(x),
new_val,
old_val) != old_val);
return static_cast<T>(new_val);
}
static inline T decrement(T volatile* x) {
// There's no _InterlockedDecrement8().
char old_val, new_val;
do {
old_val = static_cast<char>(*x);
new_val = old_val - static_cast<char>(1);
} while (_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(x),
new_val,
old_val) != old_val);
return static_cast<T>(new_val);
}
static inline T compare_exchange(T volatile* x,
const T new_val,
const T expected_val) {
return static_cast<T>(
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(x),
static_cast<const char>(new_val),
static_cast<const char>(expected_val)));
}
static inline T exchange(T volatile* x, const T new_val) {
return static_cast<T>(_InterlockedExchange8(
reinterpret_cast<volatile char*>(x), static_cast<const char>(new_val)));
}
};
template <typename T>
struct interlocked<T, 2> {
static inline T increment(T volatile* x) {
return static_cast<T>(
_InterlockedIncrement16(reinterpret_cast<volatile short*>(x)));
}
static inline T decrement(T volatile* x) {
return static_cast<T>(
_InterlockedDecrement16(reinterpret_cast<volatile short*>(x)));
}
static inline T compare_exchange(T volatile* x,
const T new_val,
const T expected_val) {
return static_cast<T>(
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(x),
static_cast<const short>(new_val),
static_cast<const short>(expected_val)));
}
static inline T exchange(T volatile* x, const T new_val) {
return static_cast<T>(
_InterlockedExchange16(reinterpret_cast<volatile short*>(x),
static_cast<const short>(new_val)));
}
};
template <typename T>
struct interlocked<T, 4> {
static inline T increment(T volatile* x) {
return static_cast<T>(
_InterlockedIncrement(reinterpret_cast<volatile long*>(x)));
}
static inline T decrement(T volatile* x) {
return static_cast<T>(
_InterlockedDecrement(reinterpret_cast<volatile long*>(x)));
}
static inline T compare_exchange(T volatile* x,
const T new_val,
const T expected_val) {
return static_cast<T>(
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(x),
static_cast<const long>(new_val),
static_cast<const long>(expected_val)));
}
static inline T exchange(T volatile* x, const T new_val) {
return static_cast<T>(_InterlockedExchange(
reinterpret_cast<volatile long*>(x), static_cast<const long>(new_val)));
}
};
template <typename T>
struct interlocked<T, 8> {
static inline T increment(T volatile* x) {
#if defined(_M_X64)
return static_cast<T>(
_InterlockedIncrement64(reinterpret_cast<volatile __int64*>(x)));
#else
// There's no _InterlockedIncrement64() for 32-bit x86.
__int64 old_val, new_val;
do {
old_val = static_cast<__int64>(*x);
new_val = old_val + static_cast<__int64>(1);
} while (_InterlockedCompareExchange64(
reinterpret_cast<volatile __int64*>(x), new_val, old_val) !=
old_val);
return static_cast<T>(new_val);
#endif // _M_X64
}
static inline T decrement(T volatile* x) {
#if defined(_M_X64)
return static_cast<T>(
_InterlockedDecrement64(reinterpret_cast<volatile __int64*>(x)));
#else
// There's no _InterlockedDecrement64() for 32-bit x86.
__int64 old_val, new_val;
do {
old_val = static_cast<__int64>(*x);
new_val = old_val - static_cast<__int64>(1);
} while (_InterlockedCompareExchange64(
reinterpret_cast<volatile __int64*>(x), new_val, old_val) !=
old_val);
return static_cast<T>(new_val);
#endif // _M_X64
}
static inline T compare_exchange(T volatile* x,
const T new_val,
const T expected_val) {
return static_cast<T>(_InterlockedCompareExchange64(
reinterpret_cast<volatile __int64*>(x),
static_cast<const __int64>(new_val),
static_cast<const __int64>(expected_val)));
}
static inline T exchange(T volatile* x, const T new_val) {
#if defined(_M_X64)
return static_cast<T>(
_InterlockedExchange64(reinterpret_cast<volatile __int64*>(x),
static_cast<const __int64>(new_val)));
#else
// There's no _InterlockedExchange64 for 32-bit x86.
__int64 old_val;
do {
old_val = static_cast<__int64>(*x);
} while (_InterlockedCompareExchange64(
reinterpret_cast<volatile __int64*>(x), new_val, old_val) !=
old_val);
return static_cast<T>(old_val);
#endif // _M_X64
}
};
} // namespace msvc
} // namespace sync
} // namespace srt
#endif // ATOMIC_ATOMIC_MSVC_H_

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 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/.
*
*
*/
/*****************************************************************************
@ -50,461 +50,562 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_BUFFER_H__
#define __UDT_BUFFER_H__
#ifndef INC_SRT_BUFFER_H
#define INC_SRT_BUFFER_H
#include "udt.h"
#include "list.h"
#include "queue.h"
#include "tsbpd_time.h"
#include "utilities.h"
#include <fstream>
class CSndBuffer
// The notation used for "circular numbers" in comments:
// The "cicrular numbers" are numbers that when increased up to the
// maximum become zero, and similarly, when the zero value is decreased,
// it turns into the maximum value minus one. This wrapping works the
// same for adding and subtracting. Circular numbers cannot be multiplied.
// Operations done on these numbers are marked with additional % character:
// a %> b : a is later than b
// a ++% (++%a) : shift a by 1 forward
// a +% b : shift a by b
// a == b : equality is same as for just numbers
namespace srt {
/// The AvgBufSize class is used to calculate moving average of the buffer (RCV or SND)
class AvgBufSize
{
public:
// XXX There's currently no way to access the socket ID set for
// whatever the buffer is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
CSndBuffer(int size = 32, int mss = 1500);
~CSndBuffer();
typedef sync::steady_clock::time_point time_point;
public:
AvgBufSize()
: m_dBytesCountMAvg(0.0)
, m_dCountMAvg(0.0)
, m_dTimespanMAvg(0.0)
{
}
/// Insert a user buffer into the sending list.
/// @param [in] data pointer to the user data block.
/// @param [in] len size of the block.
/// @param [in] ttl time to live in milliseconds
/// @param [in] order if the block should be delivered in order, for DGRAM only
void addBuffer(const char* data, int len, int ttl, bool order, uint64_t srctime, ref_t<int32_t> r_msgno);
/// Read a block of data from file and insert it into the sending list.
/// @param [in] ifs input file stream.
/// @param [in] len size of the block.
/// @return actual size of data added from the file.
int addBufferFromFile(std::fstream& ifs, int len);
/// Find data position to pack a DATA packet from the furthest reading point.
/// @param [out] data the pointer to the data position.
/// @param [out] msgno message number of the packet.
/// @param [out] origintime origin time stamp of the message
/// @param [in] kflags Odd|Even crypto key flag
/// @return Actual length of data read.
int readData(char** data, int32_t& msgno, uint64_t& origintime, int kflgs);
/// Find data position to pack a DATA packet for a retransmission.
/// @param [out] data the pointer to the data position.
/// @param [in] offset offset from the last ACK point.
/// @param [out] msgno message number of the packet.
/// @param [out] origintime origin time stamp of the message
/// @param [out] msglen length of the message
/// @return Actual length of data read.
int readData(char** data, const int offset, int32_t& msgno, uint64_t& origintime, int& msglen);
/// Update the ACK point and may release/unmap/return the user data according to the flag.
/// @param [in] offset number of packets acknowledged.
void ackData(int offset);
/// Read size of data still in the sending list.
/// @return Current size of the data in the sending list.
int getCurrBufSize() const;
int dropLateData(int &bytes, uint64_t latetime);
#ifdef SRT_ENABLE_SNDBUFSZ_MAVG
void updAvgBufSize(uint64_t time);
int getAvgBufSize(ref_t<int> bytes, ref_t<int> timespan);
#endif /* SRT_ENABLE_SNDBUFSZ_MAVG */
int getCurrBufSize(ref_t<int> bytes, ref_t<int> timespan);
uint64_t getInRatePeriod() const { return m_InRatePeriod; }
/// Retrieve input bitrate in bytes per second
int getInputRate() const { return m_iInRateBps; }
/// Update input rate calculation.
/// @param [in] time current time in microseconds
/// @param [in] pkts number of packets newly added to the buffer
/// @param [in] bytes number of payload bytes in those newly added packets
///
/// @return Current size of the data in the sending list.
void updateInputRate(uint64_t time, int pkts = 0, int bytes = 0);
void resetInputRateSmpPeriod(bool disable = false)
{
setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US);
}
public:
bool isTimeToUpdate(const time_point& now) const;
void update(const time_point& now, int pkts, int bytes, int timespan_ms);
public:
inline double pkts() const { return m_dCountMAvg; }
inline double timespan_ms() const { return m_dTimespanMAvg; }
inline double bytes() const { return m_dBytesCountMAvg; }
private:
time_point m_tsLastSamplingTime;
double m_dBytesCountMAvg;
double m_dCountMAvg;
double m_dTimespanMAvg;
};
void increase();
void setInputRateSmpPeriod(int period);
/// The class to estimate source bitrate based on samples submitted to the buffer.
/// Is currently only used by the CSndBuffer.
class CRateEstimator
{
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
public:
CRateEstimator();
private: // Constants
public:
uint64_t getInRatePeriod() const { return m_InRatePeriod; }
static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms
static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms
static const int64_t INPUTRATE_MAX_PACKETS = 2000; // ~ 21 Mbps of 1316 bytes payload
/// Retrieve input bitrate in bytes per second
int getInputRate() const { return m_iInRateBps; }
void setInputRateSmpPeriod(int period);
/// Update input rate calculation.
/// @param [in] time current time in microseconds
/// @param [in] pkts number of packets newly added to the buffer
/// @param [in] bytes number of payload bytes in those newly added packets
///
/// @return Current size of the data in the sending list.
void updateInputRate(const time_point& time, int pkts = 0, int bytes = 0);
void resetInputRateSmpPeriod(bool disable = false) { setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US); }
private: // Constants
static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms
static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms
static const int64_t INPUTRATE_MAX_PACKETS = 2000; // ~ 21 Mbps of 1316 bytes payload
static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE;
private:
pthread_mutex_t m_BufLock; // used to synchronize buffer operation
int m_iInRatePktsCount; // number of payload bytes added since InRateStartTime
int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime
time_point m_tsInRateStartTime;
uint64_t m_InRatePeriod; // usec
int m_iInRateBps; // Input Rate in Bytes/sec
};
struct Block
{
char* m_pcData; // pointer to the data block
int m_iLength; // length of the block
class CSndBuffer
{
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
int32_t m_iMsgNoBitset; // message number
uint64_t m_ullOriginTime_us; // original request time
uint64_t m_ullSourceTime_us;
int m_iTTL; // time to live (milliseconds)
public:
// XXX There's currently no way to access the socket ID set for
// whatever the buffer is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
Block* m_pNext; // next block
/// @brief CSndBuffer constructor.
/// @param size initial number of blocks (each block to store one packet payload).
/// @param maxpld maximum packet payload.
CSndBuffer(int size = 32, int maxpld = 1500);
~CSndBuffer();
int32_t getMsgSeq()
{
// NOTE: this extracts message ID with regard to REXMIT flag.
// This is valid only for message ID that IS GENERATED in this instance,
// not provided by the peer. This can be otherwise sent to the peer - it doesn't matter
// for the peer that it uses LESS bits to represent the message.
return m_iMsgNoBitset & MSGNO_SEQ::mask;
}
public:
/// Insert a user buffer into the sending list.
/// For @a w_mctrl the following fields are used:
/// INPUT:
/// - msgttl: timeout for retransmitting the message, if lost
/// - inorder: request to deliver the message in order of sending
/// - srctime: local time as a base for packet's timestamp (0 if unused)
/// - pktseq: sequence number to be stamped on the packet (-1 if unused)
/// - msgno: message number to be stamped on the packet (-1 if unused)
/// OUTPUT:
/// - srctime: local time stamped on the packet (same as input, if input wasn't 0)
/// - pktseq: sequence number to be stamped on the next packet
/// - msgno: message number stamped on the packet
/// @param [in] data pointer to the user data block.
/// @param [in] len size of the block.
/// @param [inout] w_mctrl Message control data
SRT_ATTR_EXCLUDES(m_BufLock)
void addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl);
} *m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock;
/// Read a block of data from file and insert it into the sending list.
/// @param [in] ifs input file stream.
/// @param [in] len size of the block.
/// @return actual size of data added from the file.
SRT_ATTR_EXCLUDES(m_BufLock)
int addBufferFromFile(std::fstream& ifs, int len);
// m_pBlock: The head pointer
// m_pFirstBlock: The first block
// m_pCurrBlock: The current block
// m_pLastBlock: The last block (if first == last, buffer is empty)
/// Find data position to pack a DATA packet from the furthest reading point.
/// @param [out] packet the packet to read.
/// @param [out] origintime origin time stamp of the message
/// @param [in] kflags Odd|Even crypto key flag
/// @param [out] seqnoinc the number of packets skipped due to TTL, so that seqno should be incremented.
/// @return Actual length of data read.
SRT_ATTR_EXCLUDES(m_BufLock)
int readData(CPacket& w_packet, time_point& w_origintime, int kflgs, int& w_seqnoinc);
struct Buffer
{
char* m_pcData; // buffer
int m_iSize; // size
Buffer* m_pNext; // next buffer
} *m_pBuffer; // physical buffer
/// Peek an information on the next original data packet to send.
/// @return origin time stamp of the next packet; epoch start time otherwise.
SRT_ATTR_EXCLUDES(m_BufLock)
time_point peekNextOriginal() const;
int32_t m_iNextMsgNo; // next message number
/// Find data position to pack a DATA packet for a retransmission.
/// @param [in] offset offset from the last ACK point (backward sequence number difference)
/// @param [out] packet the packet to read.
/// @param [out] origintime origin time stamp of the message
/// @param [out] msglen length of the message
/// @return Actual length of data read (return 0 if offset too large, -1 if TTL exceeded).
SRT_ATTR_EXCLUDES(m_BufLock)
int readData(const int offset, CPacket& w_packet, time_point& w_origintime, int& w_msglen);
int m_iSize; // buffer size (number of packets)
int m_iMSS; // maximum seqment/packet size
/// Get the time of the last retransmission (if any) of the DATA packet.
/// @param [in] offset offset from the last ACK point (backward sequence number difference)
///
/// @return Last time of the last retransmission event for the corresponding DATA packet.
SRT_ATTR_EXCLUDES(m_BufLock)
time_point getPacketRexmitTime(const int offset);
int m_iCount; // number of used blocks
/// Update the ACK point and may release/unmap/return the user data according to the flag.
/// @param [in] offset number of packets acknowledged.
int32_t getMsgNoAt(const int offset);
int m_iBytesCount; // number of payload bytes in queue
uint64_t m_ullLastOriginTime_us;
void ackData(int offset);
#ifdef SRT_ENABLE_SNDBUFSZ_MAVG
uint64_t m_LastSamplingTime;
int m_iCountMAvg;
int m_iBytesCountMAvg;
int m_TimespanMAvg;
#endif /* SRT_ENABLE_SNDBUFSZ_MAVG */
/// Read size of data still in the sending list.
/// @return Current size of the data in the sending list.
int getCurrBufSize() const;
int m_iInRatePktsCount; // number of payload bytes added since InRateStartTime
int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime
uint64_t m_InRateStartTime;
uint64_t m_InRatePeriod; // usec
int m_iInRateBps; // Input Rate in Bytes/sec
int m_iAvgPayloadSz; // Average packet payload size
SRT_ATTR_EXCLUDES(m_BufLock)
int dropLateData(int& bytes, int32_t& w_first_msgno, const time_point& too_late_time);
void updAvgBufSize(const time_point& time);
int getAvgBufSize(int& bytes, int& timespan);
int getCurrBufSize(int& bytes, int& timespan);
/// @brief Get the buffering delay of the oldest message in the buffer.
/// @return the delay value.
SRT_ATTR_EXCLUDES(m_BufLock)
duration getBufferingDelay(const time_point& tnow) const;
uint64_t getInRatePeriod() const { return m_rateEstimator.getInRatePeriod(); }
/// Retrieve input bitrate in bytes per second
int getInputRate() const { return m_rateEstimator.getInputRate(); }
void resetInputRateSmpPeriod(bool disable = false) { m_rateEstimator.resetInputRateSmpPeriod(disable); }
const CRateEstimator& getRateEstimator() const { return m_rateEstimator; }
void setRateEstimator(const CRateEstimator& other) { m_rateEstimator = other; }
private:
CSndBuffer(const CSndBuffer&);
CSndBuffer& operator=(const CSndBuffer&);
void increase();
private:
mutable sync::Mutex m_BufLock; // used to synchronize buffer operation
struct Block
{
char* m_pcData; // pointer to the data block
int m_iLength; // payload length of the block.
int32_t m_iMsgNoBitset; // message number
int32_t m_iSeqNo; // sequence number for scheduling
time_point m_tsOriginTime; // block origin time (either provided from above or equals the time a message was submitted for sending.
time_point m_tsRexmitTime; // packet retransmission time
int m_iTTL; // time to live (milliseconds)
Block* m_pNext; // next block
int32_t getMsgSeq()
{
// NOTE: this extracts message ID with regard to REXMIT flag.
// This is valid only for message ID that IS GENERATED in this instance,
// not provided by the peer. This can be otherwise sent to the peer - it doesn't matter
// for the peer that it uses LESS bits to represent the message.
return m_iMsgNoBitset & MSGNO_SEQ::mask;
}
} * m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock;
// m_pBlock: The head pointer
// m_pFirstBlock: The first block
// m_pCurrBlock: The current block
// m_pLastBlock: The last block (if first == last, buffer is empty)
struct Buffer
{
char* m_pcData; // buffer
int m_iSize; // size
Buffer* m_pNext; // next buffer
} * m_pBuffer; // physical buffer
int32_t m_iNextMsgNo; // next message number
int m_iSize; // buffer size (number of packets)
const int m_iBlockLen; // maximum length of a block holding packet payload (excluding packet header).
int m_iCount; // number of used blocks
int m_iBytesCount; // number of payload bytes in queue
time_point m_tsLastOriginTime;
AvgBufSize m_mavg;
CRateEstimator m_rateEstimator;
private:
CSndBuffer(const CSndBuffer&);
CSndBuffer& operator=(const CSndBuffer&);
};
////////////////////////////////////////////////////////////////////////////////
#if (!ENABLE_NEW_RCVBUFFER)
class CRcvBuffer
{
public:
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
public:
// XXX There's currently no way to access the socket ID set for
// whatever the queue is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
/// Construct the buffer.
/// @param [in] queue CUnitQueue that actually holds the units (packets)
/// @param [in] bufsize_pkts in units (packets)
CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = 65536);
~CRcvBuffer();
static const int DEFAULT_SIZE = 65536;
/// Construct the buffer.
/// @param [in] queue CUnitQueue that actually holds the units (packets)
/// @param [in] bufsize_pkts in units (packets)
CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = DEFAULT_SIZE);
~CRcvBuffer();
public:
/// Write data into the buffer.
/// @param [in] unit pointer to a data unit containing new packet
/// @param [in] offset offset from last ACK point.
/// @return 0 is success, -1 if data is repeated.
int addData(CUnit* unit, int offset);
/// Write data into the buffer.
/// @param [in] unit pointer to a data unit containing new packet
/// @param [in] offset offset from last ACK point.
/// @return 0 is success, -1 if data is repeated.
/// Read data into a user buffer.
/// @param [in] data pointer to user buffer.
/// @param [in] len length of user buffer.
/// @return size of data read.
int readBuffer(char* data, int len);
int addData(CUnit* unit, int offset);
/// Read data directly into file.
/// @param [in] file C++ file stream.
/// @param [in] len expected length of data to write into the file.
/// @return size of data read.
int readBufferToFile(std::fstream& ofs, int len);
/// Read data into a user buffer.
/// @param [in] data pointer to user buffer.
/// @param [in] len length of user buffer.
/// @return size of data read.
/// Update the ACK point of the buffer.
/// @param [in] len number of units to be acknowledged.
/// @return 1 if a user buffer is fulfilled, otherwise 0.
int ackData(int len);
int readBuffer(char* data, int len);
/// Query how many buffer space left for data receiving.
/// Actually only acknowledged packets, that are still in the buffer,
/// are considered to take buffer space.
///
/// @return size of available buffer space (including user buffer) for data receiving.
/// Not counting unacknowledged packets.
int getAvailBufSize() const;
/// Read data directly into file.
/// @param [in] file C++ file stream.
/// @param [in] len expected length of data to write into the file.
/// @return size of data read.
/// Query how many data has been continuously received (for reading) and ready to play (tsbpdtime < now).
/// @return size of valid (continous) data for reading.
int getRcvDataSize() const;
int readBufferToFile(std::fstream& ofs, int len);
/// Query how many data was received and acknowledged.
/// @param [out] bytes bytes
/// @param [out] spantime spantime
/// @return size in pkts of acked data.
int getRcvDataSize(int& bytes, int& spantime);
/// Update the ACK point of the buffer.
/// @param [in] len number of units to be acknowledged.
/// @return 1 if a user buffer is fulfilled, otherwise 0.
/// Query a 1 sec moving average of how many data was received and acknowledged.
/// @param [out] bytes bytes
/// @param [out] spantime spantime
/// @return size in pkts of acked data.
int getRcvAvgDataSize(int& bytes, int& spantime);
void ackData(int len);
/// Query how many data of the receive buffer is acknowledged.
/// @param [in] now current time in us.
/// @return none.
void updRcvAvgDataSize(const time_point& now);
/// Query how many buffer space left for data receiving.
/// Actually only acknowledged packets, that are still in the buffer,
/// are considered to take buffer space.
///
/// @return size of available buffer space (including user buffer) for data receiving.
/// Not counting unacknowledged packets.
/// Query the received average payload size.
/// @return size (bytes) of payload size
unsigned getRcvAvgPayloadSize() const;
int getAvailBufSize() const;
struct ReadingState
{
time_point tsStart;
time_point tsLastAck;
time_point tsEnd;
int iNumAcknowledged;
int iNumUnacknowledged;
};
/// Query how many data has been continuously received (for reading) and ready to play (tsbpdtime < now).
/// @return size of valid (continous) data for reading.
ReadingState debugGetReadingState() const;
int getRcvDataSize() const;
/// Form a string of the current buffer fullness state.
/// number of packets acknowledged, TSBPD readiness, etc.
std::string strFullnessState(const time_point& tsNow) const;
/// Query how many data was received and acknowledged.
/// @param [out] bytes bytes
/// @param [out] spantime spantime
/// @return size in pkts of acked data.
/// Mark the message to be dropped from the message list.
/// @param [in] msgno message number.
/// @param [in] using_rexmit_flag whether the MSGNO field uses rexmit flag (if not, one more bit is part of the
/// msgno value)
void dropMsg(int32_t msgno, bool using_rexmit_flag);
int getRcvDataSize(int &bytes, int &spantime);
#if SRT_ENABLE_RCVBUFSZ_MAVG
/// Query a 1 sec moving average of how many data was received and acknowledged.
/// @param [out] bytes bytes
/// @param [out] spantime spantime
/// @return size in pkts of acked data.
int getRcvAvgDataSize(int &bytes, int &spantime);
/// Query how many data of the receive buffer is acknowledged.
/// @param [in] now current time in us.
/// @return none.
void updRcvAvgDataSize(uint64_t now);
#endif /* SRT_ENABLE_RCVBUFSZ_MAVG */
/// Query the received average payload size.
/// @return size (bytes) of payload size
int getRcvAvgPayloadSize() const;
/// Mark the message to be dropped from the message list.
/// @param [in] msgno message number.
/// @param [in] using_rexmit_flag whether the MSGNO field uses rexmit flag (if not, one more bit is part of the msgno value)
void dropMsg(int32_t msgno, bool using_rexmit_flag);
/// read a message.
/// @param [out] data buffer to write the message into.
/// @param [in] len size of the buffer.
/// @return actuall size of data read.
int readMsg(char* data, int len);
/// read a message.
/// @param [out] data buffer to write the message into.
/// @param [in] len size of the buffer.
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay
/// @return actuall size of data read.
int readMsg(char* data, int len, ref_t<SRT_MSGCTRL> mctrl);
/// Query if data is ready to read (tsbpdtime <= now if TsbPD is active).
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay
/// of next packet in recv buffer, ready or not.
/// @param [out] curpktseq Sequence number of the packet if there is one ready to play
/// @return true if ready to play, false otherwise (tsbpdtime may be !0 in
/// both cases).
bool isRcvDataReady(ref_t<uint64_t> tsbpdtime, ref_t<int32_t> curpktseq);
bool isRcvDataReady();
bool isRcvDataAvailable()
{
return m_iLastAckPos != m_iStartPos;
}
CPacket* getRcvReadyPacket();
/// Set TimeStamp-Based Packet Delivery Rx Mode
/// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay
/// @param [in] delay aggreed TsbPD delay
/// @return 0
int setRcvTsbPdMode(uint64_t timebase, uint32_t delay);
/// Add packet timestamp for drift caclculation and compensation
/// @param [in] timestamp packet time stamp
/// @param [ref] lock Mutex that should be locked for the operation
void addRcvTsbPdDriftSample(uint32_t timestamp, pthread_mutex_t& lock);
#ifdef SRT_DEBUG_TSBPD_DRIFT
void printDriftHistogram(int64_t iDrift);
void printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg);
#endif
/// Get information on the 1st message in queue.
// Parameters (of the 1st packet queue, ready to play or not):
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if none
/// @param [out] passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app)
/// @param [out] skipseqno -1 or seq number of 1st unacknowledged pkt ready to play preceeded by missing packets.
/// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true
/// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE:
/// IF skipseqno != -1, packet ready to play preceeded by missing packets.;
/// IF skipseqno == -1, no missing packet but 1st not ready to play.
bool getRcvFirstMsg(ref_t<uint64_t> tsbpdtime, ref_t<bool> passack, ref_t<int32_t> skipseqno, ref_t<int32_t> curpktseq);
/// Update the ACK point of the buffer.
/// @param [in] len size of data to be skip & acknowledged.
void skipData(int len);
/// read a message.
/// @param [out] data buffer to write the message into.
/// @param [in] len size of the buffer.
/// @return actuall size of data read.
int readMsg(char* data, int len);
#if ENABLE_HEAVY_LOGGING
void reportBufferStats(); // Heavy logging Debug only
void readMsgHeavyLogging(int p);
#endif
private:
/// Adjust receive queue to 1st ready to play message (tsbpdtime < now).
// Parameters (of the 1st packet queue, ready to play or not):
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if none
/// @retval true 1st packet ready to play without discontinuity (no hole)
/// @retval false tsbpdtime = 0: no packet ready to play
/// read a message.
/// @param [out] data buffer to write the message into.
/// @param [in] len size of the buffer.
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay
/// @return actuall size of data read.
int readMsg(char* data, int len, SRT_MSGCTRL& w_mctrl, int upto);
bool getRcvReadyMsg(ref_t<uint64_t> tsbpdtime, ref_t<int32_t> curpktseq);
public:
// (This is exposed as used publicly in logs)
/// Get packet delivery local time base (adjusted for wrap around)
/// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min
/// @return local delivery time (usec)
uint64_t getTsbPdTimeBase(uint32_t timestamp_us);
/// Get packet local delivery time
/// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min
/// @return local delivery time (usec)
public:
uint64_t getPktTsbPdTime(uint32_t timestamp);
int debugGetSize() const;
bool empty() const;
// Required by PacketFilter facility to use as a storage
// for provided packets
CUnitQueue* getUnitQueue()
{
return m_pUnitQueue;
}
private:
/// thread safe bytes counter of the Recv & Ack buffer
/// @param [in] pkts acked or removed pkts from rcv buffer (used with acked = true)
/// @param [in] bytes number of bytes added/delete (if negative) to/from rcv buffer.
/// @param [in] acked true when adding new pkt in RcvBuffer; false when acking/removing pkts to/from buffer
void countBytes(int pkts, int bytes, bool acked = false);
private:
bool scanMsg(ref_t<int> start, ref_t<int> end, ref_t<bool> passack);
private:
CUnit** m_pUnit; // pointer to the protocol buffer (array of CUnit* items)
const int m_iSize; // size of the array of CUnit* items
CUnitQueue* m_pUnitQueue; // the shared unit queue
int m_iStartPos; // the head position for I/O (inclusive)
int m_iLastAckPos; // the last ACKed position (exclusive)
// EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1
int m_iMaxPos; // the furthest data position
int m_iNotch; // the starting read point of the first unit
pthread_mutex_t m_BytesCountLock; // used to protect counters operations
int m_iBytesCount; // Number of payload bytes in the buffer
int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer
int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer
int m_iAvgPayloadSz; // Average payload size for dropped bytes estimation
bool m_bTsbPdMode; // true: apply TimeStamp-Based Rx Mode
uint32_t m_uTsbPdDelay; // aggreed delay
uint64_t m_ullTsbPdTimeBase; // localtime base for TsbPd mode
// Note: m_ullTsbPdTimeBase cumulates values from:
// 1. Initial SRT_CMD_HSREQ packet returned value diff to current time:
// == (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception
// 2. Timestamp overflow (@c CRcvBuffer::getTsbPdTimeBase), when overflow on packet detected
// += CPacket::MAX_TIMESTAMP+1 (it's a hex round value, usually 0x1*e8).
// 3. Time drift (CRcvBuffer::addRcvTsbPdDriftSample, executed exclusively
// from UMSG_ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE
// once the value of average drift exceeds this value in whatever direction.
// += (+/-)CRcvBuffer::TSBPD_DRIFT_MAX_VALUE
//
// XXX Application-supplied timestamps won't work therefore. This requires separate
// calculation of all these things above.
bool m_bTsbPdWrapCheck; // true: check packet time stamp wrap around
static const uint32_t TSBPD_WRAP_PERIOD = (30*1000000); //30 seconds (in usec)
static const int TSBPD_DRIFT_MAX_VALUE = 5000; // Max drift (usec) above which TsbPD Time Offset is adjusted
static const int TSBPD_DRIFT_MAX_SAMPLES = 1000; // Number of samples (UMSG_ACKACK packets) to perform drift caclulation and compensation
//int m_iTsbPdDrift; // recent drift in the packet time stamp
//int64_t m_TsbPdDriftSum; // Sum of sampled drift
//int m_iTsbPdDriftNbSamples; // Number of samples in sum and histogram
DriftTracer<TSBPD_DRIFT_MAX_SAMPLES, TSBPD_DRIFT_MAX_VALUE> m_DriftTracer;
#ifdef SRT_ENABLE_RCVBUFSZ_MAVG
uint64_t m_LastSamplingTime;
int m_TimespanMAvg;
int m_iCountMAvg;
int m_iBytesCountMAvg;
#endif /* SRT_ENABLE_RCVBUFSZ_MAVG */
#ifdef SRT_DEBUG_TSBPD_DRIFT
int m_TsbPdDriftHisto100us[22]; // Histogram of 100us TsbPD drift (-1.0 .. +1.0 ms in 0.1ms increment)
int m_TsbPdDriftHisto1ms[22]; // Histogram of TsbPD drift (-10.0 .. +10.0 ms, in 1.0 ms increment)
static const int TSBPD_DRIFT_PRT_SAMPLES = 200; // Number of samples (UMSG_ACKACK packets) to print hostogram
#endif /* SRT_DEBUG_TSBPD_DRIFT */
/// Query if data is ready to read (tsbpdtime <= now if TsbPD is active).
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay
/// of next packet in recv buffer, ready or not.
/// @param [out] curpktseq Sequence number of the packet if there is one ready to play
/// @return true if ready to play, false otherwise (tsbpdtime may be !0 in
/// both cases).
bool isRcvDataReady(time_point& w_tsbpdtime, int32_t& w_curpktseq, int32_t seqdistance);
#ifdef SRT_DEBUG_TSBPD_OUTJITTER
unsigned long m_ulPdHisto[4][10];
#endif /* SRT_DEBUG_TSBPD_OUTJITTER */
void debugTraceJitter(time_point t);
#else
void debugTraceJitter(time_point) {}
#endif /* SRT_DEBUG_TSBPD_OUTJITTER */
bool isRcvDataReady();
bool isRcvDataAvailable() { return m_iLastAckPos != m_iStartPos; }
CPacket* getRcvReadyPacket(int32_t seqdistance);
/// Set TimeStamp-Based Packet Delivery Rx Mode
/// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay
/// @param [in] delay aggreed TsbPD delay
void setRcvTsbPdMode(const time_point& timebase, const duration& delay);
/// Add packet timestamp for drift caclculation and compensation
/// @param [in] timestamp packet time stamp
/// @param [in] tsPktArrival arrival time of the packet used to extract the drift sample.
/// @param [in] rtt RTT sample
bool addRcvTsbPdDriftSample(uint32_t timestamp, const time_point& tsPktArrival, int rtt);
#ifdef SRT_DEBUG_TSBPD_DRIFT
void printDriftHistogram(int64_t iDrift);
void printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg);
#endif
/// Get information on the 1st message in queue.
// Parameters (of the 1st packet queue, ready to play or not):
/// @param [out] w_tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0
/// if none
/// @param [out] w_passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app)
/// @param [out] w_skipseqno SRT_SEQNO_NONE or seq number of 1st unacknowledged pkt ready to play preceeded by
/// missing packets.
/// @param base_seq SRT_SEQNO_NONE or desired, ignore seq smaller than base if exist packet ready-to-play
/// and larger than base
/// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true
/// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE:
/// IF skipseqno != SRT_SEQNO_NONE, packet ready to play preceeded by missing packets.;
/// IF skipseqno == SRT_SEQNO_NONE, no missing packet but 1st not ready to play.
bool getRcvFirstMsg(time_point& w_tsbpdtime,
bool& w_passack,
int32_t& w_skipseqno,
int32_t& w_curpktseq,
int32_t base_seq = SRT_SEQNO_NONE);
/// Update the ACK point of the buffer.
/// @param [in] len size of data to be skip & acknowledged.
void skipData(int len);
#if ENABLE_HEAVY_LOGGING
void reportBufferStats() const; // Heavy logging Debug only
#endif
bool empty() const
{
// This will not always return the intended value,
// that is, it may return false when the buffer really is
// empty - but it will return true then in one of next calls.
// This function will be always called again at some point
// if it returned false, and on true the connection
// is going to be broken - so this behavior is acceptable.
return m_iStartPos == m_iLastAckPos;
}
bool full() const { return m_iStartPos == (m_iLastAckPos + 1) % m_iSize; }
int capacity() const { return m_iSize; }
private:
CRcvBuffer();
CRcvBuffer(const CRcvBuffer&);
CRcvBuffer& operator=(const CRcvBuffer&);
/// This gives up unit at index p. The unit is given back to the
/// free unit storage for further assignment for the new incoming
/// data.
size_t freeUnitAt(size_t p)
{
CUnit* u = m_pUnit[p];
m_pUnit[p] = NULL;
size_t rmbytes = u->m_Packet.getLength();
m_pUnitQueue->makeUnitFree(u);
return rmbytes;
}
/// Adjust receive queue to 1st ready to play message (tsbpdtime < now).
/// Parameters (of the 1st packet queue, ready to play or not):
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if
/// none
/// @param base_seq SRT_SEQNO_NONE or desired, ignore seq smaller than base
/// @retval true 1st packet ready to play without discontinuity (no hole)
/// @retval false tsbpdtime = 0: no packet ready to play
bool getRcvReadyMsg(time_point& w_tsbpdtime, int32_t& w_curpktseq, int upto, int base_seq = SRT_SEQNO_NONE);
public:
/// @brief Get clock drift in microseconds.
int64_t getDrift() const { return m_tsbpd.drift(); }
public:
int32_t getTopMsgno() const;
void getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift);
void applyGroupTime(const time_point& timebase, bool wrapcheck, uint32_t delay, const duration& udrift);
void applyGroupDrift(const time_point& timebase, bool wrapcheck, const duration& udrift);
time_point getPktTsbPdTime(uint32_t timestamp);
int debugGetSize() const;
time_point debugGetDeliveryTime(int offset);
size_t dropData(int len);
private:
int extractData(char* data, int len, int p, int q, bool passack);
bool accessMsg(int& w_p, int& w_q, bool& w_passack, int64_t& w_playtime, int upto);
/// Describes the state of the first N packets
std::string debugTimeState(size_t first_n_pkts) const;
/// thread safe bytes counter of the Recv & Ack buffer
/// @param [in] pkts acked or removed pkts from rcv buffer (used with acked = true)
/// @param [in] bytes number of bytes added/delete (if negative) to/from rcv buffer.
/// @param [in] acked true when adding new pkt in RcvBuffer; false when acking/removing pkts to/from buffer
void countBytes(int pkts, int bytes, bool acked = false);
private:
bool scanMsg(int& w_start, int& w_end, bool& w_passack);
int shift(int basepos, int shift) const { return (basepos + shift) % m_iSize; }
/// Simplified versions with ++ and --; avoid using division instruction
int shiftFwd(int basepos) const
{
if (++basepos == m_iSize)
return 0;
return basepos;
}
int shiftBack(int basepos) const
{
if (basepos == 0)
return m_iSize - 1;
return --basepos;
}
private:
CUnit** m_pUnit; // Array of pointed units collected in the buffer
const int m_iSize; // Size of the internal array of CUnit* items
CUnitQueue* m_pUnitQueue; // the shared unit queue
int m_iStartPos; // HEAD: first packet available for reading
int m_iLastAckPos; // the last ACKed position (exclusive), follows the last readable
// EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1
int m_iMaxPos; // delta between acked-TAIL and reception-TAIL
int m_iNotch; // the starting read point of the first unit
// (this is required for stream reading mode; it's
// the position in the first unit in the list
// up to which data are already retrieved;
// in message reading mode it's unused and always 0)
sync::Mutex m_BytesCountLock; // used to protect counters operations
int m_iBytesCount; // Number of payload bytes in the buffer
int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer
int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer
unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation
CTsbpdTime m_tsbpd;
AvgBufSize m_mavg;
private:
CRcvBuffer();
CRcvBuffer(const CRcvBuffer&);
CRcvBuffer& operator=(const CRcvBuffer&);
};
#endif // !ENABLE_NEW_RCVBUFFER
} // namespace srt
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,367 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2020 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/.
*
*/
#ifndef INC_SRT_BUFFER_RCV_H
#define INC_SRT_BUFFER_RCV_H
#if ENABLE_NEW_RCVBUFFER
#include "buffer.h" // AvgBufSize
#include "common.h"
#include "queue.h"
#include "sync.h"
#include "tsbpd_time.h"
namespace srt
{
/*
* Circular receiver buffer.
*
* |<------------------- m_szSize ---------------------------->|
* | |<------------ m_iMaxPosInc ----------->| |
* | | | |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+
* | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 |...| 0 | m_pUnit[]
* +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+
* | |
* | \__last pkt received
* |
* \___ m_iStartPos: first message to read
*
* m_pUnit[i]->status_: 0: free, 1: good, 2: read, 3: dropped (can be combined with read?)
*
* thread safety:
* start_pos_: CUDT::m_RecvLock
* first_unack_pos_: CUDT::m_AckLock
* max_pos_inc_: none? (modified on add and ack
* first_nonread_pos_:
*/
class CRcvBufferNew
{
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
public:
CRcvBufferNew(int initSeqNo, size_t size, CUnitQueue* unitqueue, bool bMessageAPI);
~CRcvBufferNew();
public:
/// Insert a unit into the buffer.
/// Similar to CRcvBuffer::addData(CUnit* unit, int offset)
///
/// @param [in] unit pointer to a data unit containing new packet
/// @param [in] offset offset from last ACK point.
///
/// @return 0 on success, -1 if packet is already in buffer, -2 if packet is before m_iStartSeqNo.
/// -3 if a packet is offset is ahead the buffer capacity.
// TODO: Previously '-2' also meant 'already acknowledged'. Check usage of this value.
int insert(CUnit* unit);
/// Drop packets in the receiver buffer from the current position up to the seqno (excluding seqno).
/// @param [in] seqno drop units up to this sequence number
/// @return number of dropped packets.
int dropUpTo(int32_t seqno);
/// @brief Drop all the packets in the receiver buffer.
/// The starting position and seqno are shifted right after the last packet in the buffer.
/// @return the number of dropped packets.
int dropAll();
/// @brief Drop the whole message from the buffer.
/// If message number is 0, then use sequence numbers to locate sequence range to drop [seqnolo, seqnohi].
/// When one packet of the message is in the range of dropping, the whole message is to be dropped.
/// @param seqnolo sequence number of the first packet in the dropping range.
/// @param seqnohi sequence number of the last packet in the dropping range.
/// @param msgno message number to drop (0 if unknown)
/// @return the number of packets actually dropped.
int dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno);
/// Read the whole message from one or several packets.
///
/// @param [in,out] data buffer to write the message into.
/// @param [in] len size of the buffer.
/// @param [in,out] message control data
///
/// @return actual number of bytes extracted from the buffer.
/// 0 if nothing to read.
/// -1 on failure.
int readMessage(char* data, size_t len, SRT_MSGCTRL* msgctrl = NULL);
/// Read acknowledged data into a user buffer.
/// @param [in, out] dst pointer to the target user buffer.
/// @param [in] len length of user buffer.
/// @return size of data read. -1 on error.
int readBuffer(char* dst, int len);
/// Read acknowledged data directly into file.
/// @param [in] ofs C++ file stream.
/// @param [in] len expected length of data to write into the file.
/// @return size of data read. -1 on error.
int readBufferToFile(std::fstream& ofs, int len);
public:
/// Get the starting position of the buffer as a packet sequence number.
int getStartSeqNo() const { return m_iStartSeqNo; }
/// Sets the start seqno of the buffer.
/// Must be used with caution and only when the buffer is empty.
void setStartSeqNo(int seqno) { m_iStartSeqNo = seqno; }
/// Given the sequence number of the first unacknowledged packet
/// tells the size of the buffer available for packets.
/// Effective returns capacity of the buffer minus acknowledged packet still kept in it.
// TODO: Maybe does not need to return minus one slot now to distinguish full and empty buffer.
size_t getAvailSize(int iFirstUnackSeqNo) const
{
// Receiver buffer allows reading unacknowledged packets.
// Therefore if the first packet in the buffer is ahead of the iFirstUnackSeqNo
// then it does not have acknowledged packets and its full capacity is available.
// Otherwise subtract the number of acknowledged but not yet read packets from its capacity.
const int iRBufSeqNo = getStartSeqNo();
if (CSeqNo::seqcmp(iRBufSeqNo, iFirstUnackSeqNo) >= 0) // iRBufSeqNo >= iFirstUnackSeqNo
{
// Full capacity is available, still don't want to encourage extra packets to come.
// Note: CSeqNo::seqlen(n, n) returns 1.
return capacity() - CSeqNo::seqlen(iFirstUnackSeqNo, iRBufSeqNo) + 1;
}
// Note: CSeqNo::seqlen(n, n) returns 1.
return capacity() - CSeqNo::seqlen(iRBufSeqNo, iFirstUnackSeqNo) + 1;
}
/// @brief Checks if the buffer has packets available for reading regardless of the TSBPD.
/// @return true if there are packets available for reading, false otherwise.
bool hasAvailablePackets() const;
/// Query how many data has been continuously received (for reading) and available for reading out
/// regardless of the TSBPD.
/// TODO: Rename to countAvailablePackets().
/// @return size of valid (continous) data for reading.
int getRcvDataSize() const;
/// Get the number of packets, bytes and buffer timespan.
/// Differs from getRcvDataSize() that it counts all packets in the buffer, not only continious.
int getRcvDataSize(int& bytes, int& timespan) const;
struct PacketInfo
{
int seqno;
bool seq_gap; //< true if there are missing packets in the buffer, preceding current packet
time_point tsbpd_time;
};
/// Get information on the 1st message in queue.
/// Similar to CRcvBuffer::getRcvFirstMsg
/// Parameters (of the 1st packet queue, ready to play or not):
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if
/// none
/// @param [out] passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app)
/// @param [out] skipseqno -1 or seq number of 1st unacknowledged pkt ready to play preceeded by missing packets.
/// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true
/// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE:
/// IF skipseqno != -1, packet ready to play preceeded by missing packets.;
/// IF skipseqno == -1, no missing packet but 1st not ready to play.
PacketInfo getFirstValidPacketInfo() const;
PacketInfo getFirstReadablePacketInfo(time_point time_now) const;
/// Get information on packets available to be read.
/// @returns a pair of sequence numbers (first available; first unavailable).
///
/// @note CSeqNo::seqoff(first, second) is 0 if nothing to read.
std::pair<int, int> getAvailablePacketsRange() const;
size_t countReadable() const;
bool empty() const
{
return (m_iMaxPosInc == 0);
}
/// Return buffer capacity.
/// One slot had to be empty in order to tell the difference between "empty buffer" and "full buffer".
/// E.g. m_iFirstNonreadPos would again point to m_iStartPos if m_szSize entries are added continiously.
/// TODO: Old receiver buffer capacity returns the actual size. Check for conflicts.
size_t capacity() const
{
return m_szSize - 1;
}
int64_t getDrift() const { return m_tsbpd.drift(); }
// TODO: make thread safe?
int debugGetSize() const
{
return getRcvDataSize();
}
/// Zero time to include all available packets.
/// TODO: Rename to 'canRead`.
bool isRcvDataReady(time_point time_now = time_point()) const;
int getRcvAvgDataSize(int& bytes, int& timespan);
void updRcvAvgDataSize(const time_point& now);
unsigned getRcvAvgPayloadSize() const { return m_uAvgPayloadSz; }
void getInternalTimeBase(time_point& w_timebase, bool& w_wrp, duration& w_udrift)
{
return m_tsbpd.getInternalTimeBase(w_timebase, w_wrp, w_udrift);
}
public: // Used for testing
/// Peek unit in position of seqno
const CUnit* peek(int32_t seqno);
private:
inline int incPos(int pos, int inc = 1) const { return (pos + inc) % m_szSize; }
inline int decPos(int pos) const { return (pos - 1) >= 0 ? (pos - 1) : int(m_szSize - 1); }
inline int offPos(int pos1, int pos2) const { return (pos2 >= pos1) ? (pos2 - pos1) : int(m_szSize + pos2 - pos1); }
private:
void countBytes(int pkts, int bytes);
void updateNonreadPos();
void releaseUnitInPos(int pos);
/// @brief Drop a unit from the buffer.
/// @param pos position in the m_entries of the unit to drop.
/// @return false if nothing to drop, true if the unit was dropped successfully.
bool dropUnitInPos(int pos);
/// Release entries following the current buffer position if they were already
/// read out of order (EntryState_Read) or dropped (EntryState_Drop).
void releaseNextFillerEntries();
bool hasReadableInorderPkts() const { return (m_iFirstNonreadPos != m_iStartPos); }
/// Find position of the last packet of the message.
int findLastMessagePkt();
/// Scan for availability of out of order packets.
void onInsertNotInOrderPacket(int insertpos);
// Check if m_iFirstReadableOutOfOrder is still readable.
bool checkFirstReadableOutOfOrder();
void updateFirstReadableOutOfOrder();
int scanNotInOrderMessageRight(int startPos, int msgNo) const;
int scanNotInOrderMessageLeft(int startPos, int msgNo) const;
typedef bool copy_to_dst_f(char* data, int len, int dst_offset, void* arg);
/// Read acknowledged data directly into file.
/// @param [in] ofs C++ file stream.
/// @param [in] len expected length of data to write into the file.
/// @return size of data read.
int readBufferTo(int len, copy_to_dst_f funcCopyToDst, void* arg);
/// @brief Estimate timespan of the stored packets (acknowledged and unacknowledged).
/// @return timespan in milliseconds
int getTimespan_ms() const;
private:
// TODO: Call makeUnitGood upon assignment, and makeUnitFree upon clearing.
// TODO: CUnitPtr is not in use at the moment, but may be a smart pointer.
// class CUnitPtr
// {
// public:
// void operator=(CUnit* pUnit)
// {
// if (m_pUnit != NULL)
// {
// // m_pUnitQueue->makeUnitFree(m_entries[i].pUnit);
// }
// m_pUnit = pUnit;
// }
// private:
// CUnit* m_pUnit;
// };
enum EntryStatus
{
EntryState_Empty, //< No CUnit record.
EntryState_Avail, //< Entry is available for reading.
EntryState_Read, //< Entry has already been read (out of order).
EntryState_Drop //< Entry has been dropped.
};
struct Entry
{
Entry()
: pUnit(NULL)
, status(EntryState_Empty)
{}
CUnit* pUnit;
EntryStatus status;
};
//static Entry emptyEntry() { return Entry { NULL, EntryState_Empty }; }
FixedArray<Entry> m_entries;
const size_t m_szSize; // size of the array of units (buffer)
CUnitQueue* m_pUnitQueue; // the shared unit queue
int m_iStartSeqNo;
int m_iStartPos; // the head position for I/O (inclusive)
int m_iFirstNonreadPos; // First position that can't be read (<= m_iLastAckPos)
int m_iMaxPosInc; // the furthest data position
int m_iNotch; // the starting read point of the first unit
size_t m_numOutOfOrderPackets; // The number of stored packets with "inorder" flag set to false
int m_iFirstReadableOutOfOrder; // In case of out ouf order packet, points to a position of the first such packet to
// read
bool m_bPeerRexmitFlag; // Needed to read message number correctly
const bool m_bMessageAPI; // Operation mode flag: message or stream.
public: // TSBPD public functions
/// Set TimeStamp-Based Packet Delivery Rx Mode
/// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay
/// @param [in] wrap Is in wrapping period
/// @param [in] delay aggreed TsbPD delay
///
/// @return 0
void setTsbPdMode(const time_point& timebase, bool wrap, duration delay);
void setPeerRexmitFlag(bool flag) { m_bPeerRexmitFlag = flag; }
void applyGroupTime(const time_point& timebase, bool wrp, uint32_t delay, const duration& udrift);
void applyGroupDrift(const time_point& timebase, bool wrp, const duration& udrift);
bool addRcvTsbPdDriftSample(uint32_t usTimestamp, const time_point& tsPktArrival, int usRTTSample);
time_point getPktTsbPdTime(uint32_t usPktTimestamp) const;
time_point getTsbPdTimeBase(uint32_t usPktTimestamp) const;
void updateTsbPdTimeBase(uint32_t usPktTimestamp);
/// Form a string of the current buffer fullness state.
/// number of packets acknowledged, TSBPD readiness, etc.
std::string strFullnessState(int iFirstUnackSeqNo, const time_point& tsNow) const;
private:
CTsbpdTime m_tsbpd;
private: // Statistics
AvgBufSize m_mavg;
// TODO: m_BytesCountLock is probably not needed as the buffer has to be protected from simultaneous access.
mutable sync::Mutex m_BytesCountLock; // used to protect counters operations
int m_iBytesCount; // Number of payload bytes in the buffer
int m_iPktsCount; // Number of payload bytes in the buffer
unsigned m_uAvgPayloadSz; // Average payload size for dropped bytes estimation
};
} // namespace srt
#endif // ENABLE_NEW_RCVBUFFER
#endif // INC_SRT_BUFFER_RCV_H

View file

@ -38,10 +38,8 @@ written by
Yunhong Gu, last updated 05/05/2009
*****************************************************************************/
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#include "platform_sys.h"
#include <cstring>
#include "cache.h"
@ -49,22 +47,22 @@ written by
using namespace std;
CInfoBlock& CInfoBlock::operator=(const CInfoBlock& obj)
srt::CInfoBlock& srt::CInfoBlock::copyFrom(const CInfoBlock& obj)
{
std::copy(obj.m_piIP, obj.m_piIP + 4, m_piIP);
m_iIPversion = obj.m_iIPversion;
m_ullTimeStamp = obj.m_ullTimeStamp;
m_iRTT = obj.m_iRTT;
m_iBandwidth = obj.m_iBandwidth;
m_iLossRate = obj.m_iLossRate;
m_iIPversion = obj.m_iIPversion;
m_ullTimeStamp = obj.m_ullTimeStamp;
m_iSRTT = obj.m_iSRTT;
m_iBandwidth = obj.m_iBandwidth;
m_iLossRate = obj.m_iLossRate;
m_iReorderDistance = obj.m_iReorderDistance;
m_dInterval = obj.m_dInterval;
m_dCWnd = obj.m_dCWnd;
m_dInterval = obj.m_dInterval;
m_dCWnd = obj.m_dCWnd;
return *this;
}
bool CInfoBlock::operator==(const CInfoBlock& obj)
bool srt::CInfoBlock::operator==(const CInfoBlock& obj)
{
if (m_iIPversion != obj.m_iIPversion)
return false;
@ -81,24 +79,24 @@ bool CInfoBlock::operator==(const CInfoBlock& obj)
return true;
}
CInfoBlock* CInfoBlock::clone()
srt::CInfoBlock* srt::CInfoBlock::clone()
{
CInfoBlock* obj = new CInfoBlock;
std::copy(m_piIP, m_piIP + 4, obj->m_piIP);
obj->m_iIPversion = m_iIPversion;
obj->m_ullTimeStamp = m_ullTimeStamp;
obj->m_iRTT = m_iRTT;
obj->m_iBandwidth = m_iBandwidth;
obj->m_iLossRate = m_iLossRate;
obj->m_iIPversion = m_iIPversion;
obj->m_ullTimeStamp = m_ullTimeStamp;
obj->m_iSRTT = m_iSRTT;
obj->m_iBandwidth = m_iBandwidth;
obj->m_iLossRate = m_iLossRate;
obj->m_iReorderDistance = m_iReorderDistance;
obj->m_dInterval = m_dInterval;
obj->m_dCWnd = m_dCWnd;
obj->m_dInterval = m_dInterval;
obj->m_dCWnd = m_dCWnd;
return obj;
}
int CInfoBlock::getKey()
int srt::CInfoBlock::getKey()
{
if (m_iIPversion == AF_INET)
return m_piIP[0];
@ -106,15 +104,15 @@ int CInfoBlock::getKey()
return m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3];
}
void CInfoBlock::convert(const sockaddr* addr, int ver, uint32_t ip[])
void srt::CInfoBlock::convert(const sockaddr_any& addr, uint32_t aw_ip[4])
{
if (ver == AF_INET)
if (addr.family() == AF_INET)
{
ip[0] = ((sockaddr_in*)addr)->sin_addr.s_addr;
ip[1] = ip[2] = ip[3] = 0;
aw_ip[0] = addr.sin.sin_addr.s_addr;
aw_ip[1] = aw_ip[2] = aw_ip[3] = 0;
}
else
{
memcpy((char*)ip, (char*)((sockaddr_in6*)addr)->sin6_addr.s6_addr, 16);
memcpy((aw_ip), addr.sin6.sin6_addr.s6_addr, sizeof addr.sin6.sin6_addr.s6_addr);
}
}

View file

@ -38,15 +38,19 @@ written by
Yunhong Gu, last updated 01/27/2011
*****************************************************************************/
#ifndef __UDT_CACHE_H__
#define __UDT_CACHE_H__
#ifndef INC_SRT_CACHE_H
#define INC_SRT_CACHE_H
#include <list>
#include <vector>
#include "common.h"
#include "sync.h"
#include "netinet_any.h"
#include "udt.h"
namespace srt
{
class CCacheItem
{
public:
@ -82,13 +86,13 @@ public:
m_iCurrSize(0)
{
m_vHashPtr.resize(m_iHashSize);
CGuard::createMutex(m_Lock);
// Exception: -> CUDTUnited ctor
srt::sync::setupMutex(m_Lock, "Cache");
}
~CCache()
{
clear();
CGuard::releaseMutex(m_Lock);
}
public:
@ -98,7 +102,7 @@ public:
int lookup(T* data)
{
CGuard cacheguard(m_Lock);
srt::sync::ScopedLock cacheguard(m_Lock);
int key = data->getKey();
if (key < 0)
@ -126,7 +130,7 @@ public:
int update(T* data)
{
CGuard cacheguard(m_Lock);
srt::sync::ScopedLock cacheguard(m_Lock);
int key = data->getKey();
if (key < 0)
@ -223,7 +227,7 @@ private:
int m_iHashSize;
int m_iCurrSize;
pthread_mutex_t m_Lock;
srt::sync::Mutex m_Lock;
private:
CCache(const CCache&);
@ -234,23 +238,25 @@ private:
class CInfoBlock
{
public:
uint32_t m_piIP[4]; // IP address, machine read only, not human readable format
int m_iIPversion; // IP version
uint64_t m_ullTimeStamp; // last update time
int m_iRTT; // RTT
int m_iBandwidth; // estimated bandwidth
int m_iLossRate; // average loss rate
int m_iReorderDistance; // packet reordering distance
double m_dInterval; // inter-packet time, congestion control
double m_dCWnd; // congestion window size, congestion control
uint32_t m_piIP[4]; // IP address, machine read only, not human readable format.
int m_iIPversion; // Address family: AF_INET or AF_INET6.
uint64_t m_ullTimeStamp; // Last update time.
int m_iSRTT; // Smoothed RTT.
int m_iBandwidth; // Estimated link bandwidth.
int m_iLossRate; // Average loss rate.
int m_iReorderDistance; // Packet reordering distance.
double m_dInterval; // Inter-packet time (Congestion Control).
double m_dCWnd; // Congestion window size (Congestion Control).
public:
virtual ~CInfoBlock() {}
virtual CInfoBlock& operator=(const CInfoBlock& obj);
virtual bool operator==(const CInfoBlock& obj);
virtual CInfoBlock* clone();
virtual int getKey();
virtual void release() {}
CInfoBlock() {} // NOTE: leaves uninitialized
CInfoBlock& copyFrom(const CInfoBlock& obj);
CInfoBlock(const CInfoBlock& src) { copyFrom(src); }
CInfoBlock& operator=(const CInfoBlock& src) { return copyFrom(src); }
bool operator==(const CInfoBlock& obj);
CInfoBlock* clone();
int getKey();
void release() {}
public:
@ -259,8 +265,9 @@ public:
/// @param [in] ver IP version
/// @param [out] ip the result machine readable IP address in integer array
static void convert(const sockaddr* addr, int ver, uint32_t ip[]);
static void convert(const sockaddr_any& addr, uint32_t ip[4]);
};
} // namespace srt
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 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/.
*
*
*/
/*****************************************************************************
@ -50,138 +50,118 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_CHANNEL_H__
#define __UDT_CHANNEL_H__
#ifndef INC_SRT_CHANNEL_H
#define INC_SRT_CHANNEL_H
#include "platform_sys.h"
#include "udt.h"
#include "packet.h"
#include "socketconfig.h"
#include "netinet_any.h"
namespace srt
{
class CChannel
{
void createSocket(int family);
public:
// XXX There's currently no way to access the socket ID set for
// whatever the channel is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
// XXX There's currently no way to access the socket ID set for
// whatever the channel is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
CChannel();
~CChannel();
CChannel();
CChannel(int version);
~CChannel();
/// Open a UDP channel.
/// @param [in] addr The local address that UDP will use.
/// Open a UDP channel.
/// @param [in] addr The local address that UDP will use.
void open(const sockaddr_any& addr);
void open(const sockaddr* addr = NULL);
void open(int family);
/// Open a UDP channel based on an existing UDP socket.
/// @param [in] udpsock UDP socket descriptor.
/// Open a UDP channel based on an existing UDP socket.
/// @param [in] udpsock UDP socket descriptor.
void attach(UDPSOCKET udpsock);
void attach(UDPSOCKET udpsock, const sockaddr_any& adr);
/// Disconnect and close the UDP entity.
/// Disconnect and close the UDP entity.
void close() const;
void close() const;
/// Get the UDP sending buffer size.
/// @return Current UDP sending buffer size.
/// Get the UDP sending buffer size.
/// @return Current UDP sending buffer size.
int getSndBufSize();
int getSndBufSize();
/// Get the UDP receiving buffer size.
/// @return Current UDP receiving buffer size.
/// Get the UDP receiving buffer size.
/// @return Current UDP receiving buffer size.
int getRcvBufSize();
int getRcvBufSize();
/// Set the UDP sending buffer size.
/// @param [in] size expected UDP sending buffer size.
/// Query the socket address that the channel is using.
/// @param [out] addr pointer to store the returned socket address.
void setSndBufSize(int size);
void getSockAddr(sockaddr_any& addr) const;
/// Set the UDP receiving buffer size.
/// @param [in] size expected UDP receiving buffer size.
/// Query the peer side socket address that the channel is connect to.
/// @param [out] addr pointer to store the returned socket address.
void setRcvBufSize(int size);
void getPeerAddr(sockaddr_any& addr) const;
/// Set the IPV6ONLY option.
/// @param [in] IPV6ONLY value.
/// Send a packet to the given address.
/// @param [in] addr pointer to the destination address.
/// @param [in] packet reference to a CPacket entity.
/// @return Actual size of data sent.
void setIpV6Only(int ipV6Only);
int sendto(const sockaddr_any& addr, srt::CPacket& packet) const;
/// Query the socket address that the channel is using.
/// @param [out] addr pointer to store the returned socket address.
/// Receive a packet from the channel and record the source address.
/// @param [in] addr pointer to the source address.
/// @param [in] packet reference to a CPacket entity.
/// @return Actual size of data received.
void getSockAddr(sockaddr* addr) const;
EReadStatus recvfrom(sockaddr_any& addr, srt::CPacket& packet) const;
/// Query the peer side socket address that the channel is connect to.
/// @param [out] addr pointer to store the returned socket address.
void setConfig(const CSrtMuxerConfig& config);
void getPeerAddr(sockaddr* addr) const;
/// Get the IP TTL.
/// @param [in] ttl IP Time To Live.
/// @return TTL.
/// Send a packet to the given address.
/// @param [in] addr pointer to the destination address.
/// @param [in] packet reference to a CPacket entity.
/// @return Actual size of data sent.
int getIpTTL() const;
int sendto(const sockaddr* addr, CPacket& packet) const;
/// Get the IP Type of Service.
/// @return ToS.
/// Receive a packet from the channel and record the source address.
/// @param [in] addr pointer to the source address.
/// @param [in] packet reference to a CPacket entity.
/// @return Actual size of data received.
int getIpToS() const;
EReadStatus recvfrom(sockaddr* addr, CPacket& packet) const;
#ifdef SRT_ENABLE_IPOPTS
/// Set the IP TTL.
/// @param [in] ttl IP Time To Live.
/// @return none.
void setIpTTL(int ttl);
/// Set the IP Type of Service.
/// @param [in] tos IP Type of Service.
void setIpToS(int tos);
/// Get the IP TTL.
/// @param [in] ttl IP Time To Live.
/// @return TTL.
int getIpTTL() const;
/// Get the IP Type of Service.
/// @return ToS.
int getIpToS() const;
#ifdef SRT_ENABLE_BINDTODEVICE
bool getBind(char* dst, size_t len);
#endif
int ioctlQuery(int type) const;
int sockoptQuery(int level, int option) const;
int ioctlQuery(int type) const;
int sockoptQuery(int level, int option) const;
const sockaddr* bindAddress() { return &m_BindAddr; }
const sockaddr_any& bindAddressAny() { return m_BindAddr; }
const sockaddr* bindAddress() { return m_BindAddr.get(); }
const sockaddr_any& bindAddressAny() { return m_BindAddr; }
private:
void setUDPSockOpt();
void setUDPSockOpt();
private:
const int m_iIPversion; // IP version
int m_iSockAddrSize; // socket address structure size (pre-defined to avoid run-time test)
UDPSOCKET m_iSocket; // socket descriptor
UDPSOCKET m_iSocket; // socket descriptor
#ifdef SRT_ENABLE_IPOPTS
int m_iIpTTL;
int m_iIpToS;
#endif
int m_iSndBufSize; // UDP sending buffer size
int m_iRcvBufSize; // UDP receiving buffer size
int m_iIpV6Only; // IPV6_V6ONLY option (-1 if not set)
sockaddr_any m_BindAddr;
// Mutable because when querying original settings
// this comprises the cache for extracted values,
// although the object itself isn't considered modified.
mutable CSrtMuxerConfig m_mcfg; // Note: ReuseAddr is unused and ineffective.
sockaddr_any m_BindAddr;
};
} // namespace srt
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,7 @@
// This is a controversial thing, so temporarily blocking
//#define SRT_ENABLE_SYSTEMBUFFER_TRACE
#include "platform_sys.h"
#ifdef SRT_ENABLE_SYSTEMBUFFER_TRACE
@ -34,8 +34,11 @@
#include "logging.h"
using namespace std;
using namespace srt::sync;
using namespace srt_logging;
namespace srt {
SrtCongestionControlBase::SrtCongestionControlBase(CUDT* parent)
{
m_parent = parent;
@ -58,7 +61,7 @@ void SrtCongestion::Check()
class LiveCC: public SrtCongestionControlBase
{
int64_t m_llSndMaxBW; //Max bandwidth (bytes/sec)
size_t m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit
srt::sync::atomic<size_t> m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit
size_t m_zMaxPayloadSize;
// NAKREPORT stuff.
@ -74,12 +77,12 @@ public:
{
m_llSndMaxBW = BW_INFINITE; // 1 Gbbps in Bytes/sec BW_INFINITE
m_zMaxPayloadSize = parent->OPT_PayloadSize();
if ( m_zMaxPayloadSize == 0 )
if (m_zMaxPayloadSize == 0)
m_zMaxPayloadSize = parent->maxPayloadSize();
m_zSndAvgPayloadSize = m_zMaxPayloadSize;
m_iMinNakInterval_us = 20000; //Minimum NAK Report Period (usec)
m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator
m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator (send periodic NAK every RTT/2)
HLOGC(cclog.Debug, log << "Creating LiveCC: bw=" << m_llSndMaxBW << " avgplsize=" << m_zSndAvgPayloadSize);
@ -90,11 +93,11 @@ public:
// from receiving thread.
parent->ConnectSignal(TEV_SEND, SSLOT(updatePayloadSize));
/*
* Readjust the max SndPeriod onACK (and onTimeout)
*/
parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(updatePktSndPeriod_onTimer));
parent->ConnectSignal(TEV_ACK, SSLOT(updatePktSndPeriod_onAck));
//
// Adjust the max SndPeriod onACK and onTimeout.
//
parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(onRTO));
parent->ConnectSignal(TEV_ACK, SSLOT(onAck));
}
bool checkTransArgs(SrtCongestion::TransAPI api, SrtCongestion::TransDir dir, const char* , size_t size, int , bool ) ATR_OVERRIDE
@ -151,24 +154,30 @@ private:
HLOGC(cclog.Debug, log << "LiveCC: avg payload size updated: " << m_zSndAvgPayloadSize);
}
void updatePktSndPeriod_onTimer(ETransmissionEvent , EventVariant var)
/// @brief On RTO event update an inter-packet send interval.
/// @param arg EventVariant::STAGE to distinguish between INIT and actual RTO.
void onRTO(ETransmissionEvent , EventVariant var)
{
if ( var.get<EventVariant::STAGE>() != TEV_CHT_INIT )
if (var.get<EventVariant::STAGE>() != TEV_CHT_INIT )
updatePktSndPeriod();
}
void updatePktSndPeriod_onAck(ETransmissionEvent , EventVariant )
/// @brief Handle an incoming ACK event.
/// Mainly updates a send interval between packets relying on the maximum BW limit.
void onAck(ETransmissionEvent, EventVariant )
{
updatePktSndPeriod();
}
/// @brief Updates a send interval between packets relying on the maximum BW limit.
void updatePktSndPeriod()
{
// packet = payload + header
const double pktsize = (double) m_zSndAvgPayloadSize + CPacket::SRT_DATA_HDR_SIZE;
const double pktsize = (double) m_zSndAvgPayloadSize.load() + CPacket::SRT_DATA_HDR_SIZE;
m_dPktSndPeriod = 1000 * 1000.0 * (pktsize / m_llSndMaxBW);
HLOGC(cclog.Debug, log << "LiveCC: sending period updated: " << m_dPktSndPeriod
<< " (pktsize=" << pktsize << ", bw=" << m_llSndMaxBW);
<< " by avg pktsize=" << m_zSndAvgPayloadSize
<< ", bw=" << m_llSndMaxBW);
}
void setMaxBW(int64_t maxbw)
@ -176,7 +185,6 @@ private:
m_llSndMaxBW = maxbw > 0 ? maxbw : BW_INFINITE;
updatePktSndPeriod();
#ifdef SRT_ENABLE_NOCWND
/*
* UDT default flow control should not trigger under normal SRT operation
* UDT stops sending if the number of packets in transit (not acknowledged)
@ -186,9 +194,6 @@ private:
*/
// XXX Consider making this a socket option.
m_dCWndSize = m_dMaxCWndSize;
#else
m_dCWndSize = 1000;
#endif
}
void updateBandwidth(int64_t maxbw, int64_t bw) ATR_OVERRIDE
@ -215,7 +220,7 @@ private:
return SrtCongestion::SRM_FASTREXMIT;
}
uint64_t updateNAKInterval(uint64_t nakint_tk, int /*rcv_speed*/, size_t /*loss_length*/) ATR_OVERRIDE
int64_t updateNAKInterval(int64_t nakint_us, int /*rcv_speed*/, size_t /*loss_length*/) ATR_OVERRIDE
{
/*
* duB:
@ -233,12 +238,12 @@ private:
// Note: this value will still be reshaped to defined minimum,
// as per minNAKInterval.
return nakint_tk / m_iNakReportAccel;
return nakint_us / m_iNakReportAccel;
}
uint64_t minNAKInterval() ATR_OVERRIDE
int64_t minNAKInterval() ATR_OVERRIDE
{
return m_iMinNakInterval_us * CTimer::getCPUFrequency();
return m_iMinNakInterval_us;
}
};
@ -250,7 +255,7 @@ class FileCC : public SrtCongestionControlBase
// Fields from CUDTCC
int m_iRCInterval; // UDT Rate control interval
uint64_t m_LastRCTime; // last rate increase time
steady_clock::time_point m_LastRCTime; // last rate increase time
bool m_bSlowStart; // if in slow start phase
int32_t m_iLastAck; // last ACKed seq no
bool m_bLoss; // if loss happened since last rate increase
@ -268,7 +273,7 @@ public:
FileCC(CUDT* parent)
: SrtCongestionControlBase(parent)
, m_iRCInterval(CUDT::COMM_SYN_INTERVAL_US)
, m_LastRCTime(CTimer::getTime())
, m_LastRCTime(steady_clock::now())
, m_bSlowStart(true)
, m_iLastAck(parent->sndSeqNo())
, m_bLoss(false)
@ -290,9 +295,9 @@ public:
m_dCWndSize = 16;
m_dPktSndPeriod = 1;
parent->ConnectSignal(TEV_ACK, SSLOT(updateSndPeriod));
parent->ConnectSignal(TEV_LOSSREPORT, SSLOT(slowdownSndPeriod));
parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(speedupToWindowSize));
parent->ConnectSignal(TEV_ACK, SSLOT(onACK));
parent->ConnectSignal(TEV_LOSSREPORT, SSLOT(onLossReport));
parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(onRTO));
HLOGC(cclog.Debug, log << "Creating FileCC");
}
@ -306,10 +311,11 @@ public:
return true;
}
/// Tells if an early ACK is needed (before the next Full ACK happening every 10ms).
/// In FileCC, treat non-full-payload as an end-of-message (stream)
/// and request ACK to be sent immediately.
bool needsQuickACK(const CPacket& pkt) ATR_OVERRIDE
{
// For FileCC, treat non-full-buffer situation as an end-of-message situation;
// request ACK to be sent immediately.
if (pkt.getLength() < m_parent->maxPayloadSize())
{
// This is not a regular fixed size packet...
@ -330,14 +336,15 @@ public:
}
private:
// SLOTS
void updateSndPeriod(ETransmissionEvent, EventVariant arg)
/// Handle icoming ACK event.
/// In slow start stage increase CWND. Leave slow start once maximum CWND is reached.
/// In congestion avoidance stage adjust inter packet send interval value to achieve maximum rate.
void onACK(ETransmissionEvent, EventVariant arg)
{
const int ack = arg.get<EventVariant::ACK>();
const uint64_t currtime = CTimer::getTime();
if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval)
const steady_clock::time_point currtime = steady_clock::now();
if (count_microseconds(currtime - m_LastRCTime) < m_iRCInterval)
return;
m_LastRCTime = currtime;
@ -360,11 +367,11 @@ private:
}
else
{
m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval);
m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval);
HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:ENDED) wndsize="
<< m_dCWndSize << "/" << m_dMaxCWndSize
<< " sndperiod=" << m_dPktSndPeriod << "us = wndsize/(RTT+RCIV) RTT="
<< m_parent->RTT() << " RCIV=" << m_iRCInterval);
<< m_parent->SRTT() << " RCIV=" << m_iRCInterval);
}
}
else
@ -376,9 +383,9 @@ private:
}
else
{
m_dCWndSize = m_parent->deliveryRate() / 1000000.0 * (m_parent->RTT() + m_iRCInterval) + 16;
m_dCWndSize = m_parent->deliveryRate() / 1000000.0 * (m_parent->SRTT() + m_iRCInterval) + 16;
HLOGC(cclog.Debug, log << "FileCC: UPD (speed mode) wndsize="
<< m_dCWndSize << "/" << m_dMaxCWndSize << " RTT = " << m_parent->RTT()
<< m_dCWndSize << "/" << m_dMaxCWndSize << " RTT = " << m_parent->SRTT()
<< " sndperiod=" << m_dPktSndPeriod << "us. deliverRate = "
<< m_parent->deliveryRate() << " pkts/s)");
}
@ -393,7 +400,7 @@ private:
else
{
double inc = 0;
const int loss_bw = 2 * (1000000 / m_dLastDecPeriod); // 2 times last loss point
const int loss_bw = static_cast<int>(2 * (1000000 / m_dLastDecPeriod)); // 2 times last loss point
const int bw_pktps = min(loss_bw, m_parent->bandwidth());
int64_t B = (int64_t)(bw_pktps - 1000000.0 / m_dPktSndPeriod);
@ -456,9 +463,10 @@ private:
}
// When a lossreport has been received, it might be due to having
// reached the available bandwidth limit. Slowdown to avoid further losses.
void slowdownSndPeriod(ETransmissionEvent, EventVariant arg)
/// When a lossreport has been received, it might be due to having
/// reached the available bandwidth limit. Slowdown to avoid further losses.
/// Leave the slow start stage if it was active.
void onLossReport(ETransmissionEvent, EventVariant arg)
{
const int32_t* losslist = arg.get_ptr();
size_t losslist_size = arg.get_len();
@ -483,9 +491,9 @@ private:
}
else
{
m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval);
m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval);
HLOGC(cclog.Debug, log << "FileCC: LOSS, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (RTT="
<< m_parent->RTT() << " RCIV=" << m_iRCInterval << ")");
<< m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")");
}
}
@ -493,14 +501,14 @@ private:
m_bLoss = true;
// TODO: const int pktsInFlight = CSeqNo::seqoff(m_iLastAck, m_parent->sndSeqNo());
const int pktsInFlight = m_parent->RTT() / m_dPktSndPeriod;
const int pktsInFlight = static_cast<int>(m_parent->SRTT() / m_dPktSndPeriod);
const int numPktsLost = m_parent->sndLossLength();
const int lost_pcent_x10 = pktsInFlight > 0 ? (numPktsLost * 1000) / pktsInFlight : 0;
HLOGC(cclog.Debug, log << "FileCC: LOSS: "
<< "sent=" << CSeqNo::seqlen(m_iLastAck, m_parent->sndSeqNo()) << ", inFlight=" << pktsInFlight
<< ", lost=" << numPktsLost << " ("
<< lost_pcent_x10 / 10 << "." << lost_pcent_x10 % 10 << "\%)");
<< lost_pcent_x10 / 10 << "." << lost_pcent_x10 % 10 << "%)");
if (lost_pcent_x10 < 20) // 2.0%
{
HLOGC(cclog.Debug, log << "FileCC: LOSS: m_dLastDecPeriod=" << m_dLastDecPeriod << "->" << m_dPktSndPeriod);
@ -527,11 +535,8 @@ private:
m_iLastDecSeq = m_parent->sndSeqNo();
// remove global synchronization using randomization
srand(m_iLastDecSeq);
m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX));
if (m_iDecRandom < 1)
m_iDecRandom = 1;
m_iDecRandom = m_iAvgNAKNum > 1 ? genRandomInt(1, m_iAvgNAKNum) : 1;
SRT_ASSERT(m_iDecRandom >= 1);
HLOGC(cclog.Debug, log << "FileCC: LOSS:NEW lseqno=" << lossbegin
<< ", lastsentseqno=" << m_iLastDecSeq
<< ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin)
@ -562,7 +567,9 @@ private:
}
}
void speedupToWindowSize(ETransmissionEvent, EventVariant arg)
/// @brief On retransmission timeout leave slow start stage if it was active.
/// @param arg EventVariant::STAGE to distinguish between INIT and actual RTO.
void onRTO(ETransmissionEvent, EventVariant arg)
{
ECheckTimerStage stg = arg.get<EventVariant::STAGE>();
@ -583,9 +590,9 @@ private:
}
else
{
m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval);
m_dPktSndPeriod = m_dCWndSize / (m_parent->SRTT() + m_iRCInterval);
HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (wndsize="
<< setprecision(6) << m_dCWndSize << " RTT=" << m_parent->RTT() << " RCIV=" << m_iRCInterval << ")");
<< setprecision(6) << m_dCWndSize << " RTT=" << m_parent->SRTT() << " RCIV=" << m_iRCInterval << ")");
}
}
else
@ -636,8 +643,18 @@ bool SrtCongestion::configure(CUDT* parent)
return !!congctl;
}
void SrtCongestion::dispose()
{
if (congctl)
{
delete congctl;
congctl = 0;
}
}
SrtCongestion::~SrtCongestion()
{
delete congctl;
congctl = 0;
dispose();
}
} // namespace srt

View file

@ -8,17 +8,19 @@
*
*/
#ifndef INC__CONGCTL_H
#define INC__CONGCTL_H
#ifndef INC_SRT_CONGCTL_H
#define INC_SRT_CONGCTL_H
#include <algorithm>
#include <map>
#include <string>
#include <utility>
namespace srt {
class CUDT;
class SrtCongestionControlBase;
typedef SrtCongestionControlBase* srtcc_create_t(CUDT* parent);
typedef SrtCongestionControlBase* srtcc_create_t(srt::CUDT* parent);
class SrtCongestion
{
@ -55,13 +57,24 @@ public:
bool operator()(NamePtr np) { return n == np.first; }
};
static NamePtr* find(const std::string& name)
{
NamePtr* end = congctls+N_CONTROLLERS;
NamePtr* try_selector = std::find_if(congctls, end, IsName(name));
return try_selector != end ? try_selector : NULL;
}
static bool exists(const std::string& name)
{
return find(name);
}
// You can call select() multiple times, until finally
// the 'configure' method is called.
bool select(const std::string& name)
{
NamePtr* end = congctls+N_CONTROLLERS;
NamePtr* try_selector = std::find_if(congctls, end, IsName(name));
if (try_selector == end)
NamePtr* try_selector = find(name);
if (!try_selector)
return false;
selector = try_selector - congctls;
return true;
@ -79,12 +92,18 @@ public:
// 1. The congctl is individual, so don't copy it. Set NULL.
// 2. The selected name is copied so that it's configured correctly.
SrtCongestion(const SrtCongestion& source): congctl(), selector(source.selector) {}
void operator=(const SrtCongestion& source) { congctl = 0; selector = source.selector; }
// This function will be called by the parent CUDT
// in appropriate time. It should select appropriate
// congctl basing on the value in selector, then
// pin oneself in into CUDT for receiving event signals.
bool configure(CUDT* parent);
bool configure(srt::CUDT* parent);
// This function will intentionally delete the contained object.
// This makes future calls to ready() return false. Calling
// configure on it again will create it again.
void dispose();
// Will delete the pinned in congctl object.
// This must be defined in *.cpp file due to virtual
@ -111,12 +130,13 @@ public:
};
};
class CPacket;
class SrtCongestionControlBase
{
protected:
// Here can be some common fields
CUDT* m_parent;
srt::CUDT* m_parent;
double m_dPktSndPeriod;
double m_dCWndSize;
@ -127,11 +147,11 @@ protected:
//int m_iMSS; // NOT REQUIRED. Use m_parent->MSS() instead.
//int32_t m_iSndCurrSeqNo; // NOT REQUIRED. Use m_parent->sndSeqNo().
//int m_iRcvRate; // NOT REQUIRED. Use m_parent->deliveryRate() instead.
//int m_RTT; // NOT REQUIRED. Use m_parent->RTT() instead.
//int m_RTT; // NOT REQUIRED. Use m_parent->SRTT() instead.
//char* m_pcParam; // Used to access m_llMaxBw. Use m_parent->maxBandwidth() instead.
// Constructor in protected section so that this class is semi-abstract.
SrtCongestionControlBase(CUDT* parent);
SrtCongestionControlBase(srt::CUDT* parent);
public:
// This could be also made abstract, but this causes a linkage
@ -169,11 +189,11 @@ public:
virtual int ACKTimeout_us() const { return 0; }
// Called when the settings concerning m_llMaxBW were changed.
// Arg 1: value of CUDT::m_llMaxBW
// Arg 2: value calculated out of CUDT::m_llInputBW and CUDT::m_iOverheadBW.
// Arg 1: value of CUDT's m_config.m_llMaxBW
// Arg 2: value calculated out of CUDT's m_config.llInputBW and m_config.iOverheadBW.
virtual void updateBandwidth(int64_t, int64_t) {}
virtual bool needsQuickACK(const CPacket&)
virtual bool needsQuickACK(const srt::CPacket&)
{
return false;
}
@ -186,21 +206,21 @@ public:
virtual SrtCongestion::RexmitMethod rexmitMethod() = 0; // Implementation enforced.
virtual uint64_t updateNAKInterval(uint64_t nakint_tk, int rcv_speed, size_t loss_length)
virtual int64_t updateNAKInterval(int64_t nakint_us, int rcv_speed, size_t loss_length)
{
if (rcv_speed > 0)
nakint_tk += (loss_length * uint64_t(1000000) / rcv_speed) * CTimer::getCPUFrequency();
nakint_us += (loss_length * int64_t(1000000) / rcv_speed);
return nakint_tk;
return nakint_us;
}
virtual uint64_t minNAKInterval()
virtual int64_t minNAKInterval()
{
return 0; // Leave default
}
};
} // namespace srt
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,8 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#include "platform_sys.h"
#include <cstring>
#include <string>
#include <sstream>
@ -24,6 +26,7 @@ written by
#include "crypto.h"
#include "logging.h"
#include "core.h"
#include "api.h"
using namespace srt_logging;
@ -40,9 +43,10 @@ using namespace srt_logging;
*/
// 10* HAICRYPT_DEF_KM_PRE_ANNOUNCE
const int SRT_CRYPT_KM_PRE_ANNOUNCE = 0x10000;
const int SRT_CRYPT_KM_PRE_ANNOUNCE SRT_ATR_UNUSED = 0x10000;
#if ENABLE_LOGGING
namespace srt_logging
{
std::string KmStateStr(SRT_KM_STATE state)
{
switch (state)
@ -62,9 +66,21 @@ std::string KmStateStr(SRT_KM_STATE state)
}
}
}
} // namespace
using srt_logging::KmStateStr;
std::string CCryptoControl::FormatKmMessage(std::string hdr, int cmd, size_t srtlen)
void srt::CCryptoControl::globalInit()
{
#ifdef SRT_ENABLE_ENCRYPTION
// We need to force the Cryspr to be initialized during startup to avoid the
// possibility of multiple threads initialzing the same static data later on.
HaiCryptCryspr_Get_Instance();
#endif
}
#if ENABLE_LOGGING
std::string srt::CCryptoControl::FormatKmMessage(std::string hdr, int cmd, size_t srtlen)
{
std::ostringstream os;
os << hdr << ": cmd=" << cmd << "(" << (cmd == SRT_CMD_KMREQ ? "KMREQ":"KMRSP") <<") len="
@ -75,7 +91,7 @@ std::string CCryptoControl::FormatKmMessage(std::string hdr, int cmd, size_t srt
}
#endif
void CCryptoControl::updateKmState(int cmd, size_t srtlen SRT_ATR_UNUSED)
void srt::CCryptoControl::updateKmState(int cmd, size_t srtlen SRT_ATR_UNUSED)
{
if (cmd == SRT_CMD_KMREQ)
{
@ -83,43 +99,41 @@ void CCryptoControl::updateKmState(int cmd, size_t srtlen SRT_ATR_UNUSED)
{
m_SndKmState = SRT_KM_S_SECURING;
}
LOGP(mglog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen));
LOGP(cnlog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen));
}
else
{
LOGP(mglog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen));
LOGP(cnlog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen));
}
}
void CCryptoControl::createFakeSndContext()
void srt::CCryptoControl::createFakeSndContext()
{
if (!m_iSndKmKeyLen)
m_iSndKmKeyLen = 16;
if (!createCryptoCtx(Ref(m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX))
if (!createCryptoCtx(m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX, (m_hSndCrypto)))
{
HLOGC(mglog.Debug, log << "Error: Can't create fake crypto context for sending - sending will return ERROR!");
HLOGC(cnlog.Debug, log << "Error: Can't create fake crypto context for sending - sending will return ERROR!");
m_hSndCrypto = 0;
}
}
int CCryptoControl::processSrtMsg_KMREQ(
int srt::CCryptoControl::processSrtMsg_KMREQ(
const uint32_t* srtdata SRT_ATR_UNUSED,
size_t bytelen SRT_ATR_UNUSED,
uint32_t* srtdata_out, ref_t<size_t> r_srtlen, int hsv SRT_ATR_UNUSED)
int hsv SRT_ATR_UNUSED,
uint32_t pw_srtdata_out[], size_t& w_srtlen)
{
size_t& srtlen = *r_srtlen;
//Receiver
/* All 32-bit msg fields swapped on reception
* But HaiCrypt expect network order message
* Re-swap to cancel it.
*/
#ifdef SRT_ENABLE_ENCRYPTION
srtlen = bytelen/sizeof(srtdata[SRT_KMR_KMSTATE]);
HtoNLA(srtdata_out, srtdata, srtlen);
unsigned char* kmdata = reinterpret_cast<unsigned char*>(srtdata_out);
std::vector<unsigned char> kmcopy(kmdata, kmdata + bytelen);
w_srtlen = bytelen/sizeof(srtdata[SRT_KMR_KMSTATE]);
HtoNLA((pw_srtdata_out), srtdata, w_srtlen);
unsigned char* kmdata = reinterpret_cast<unsigned char*>(pw_srtdata_out);
// The side that has received KMREQ is always an HSD_RESPONDER, regardless of
// what has called this function. The HSv5 handshake only enforces bidirectional
@ -131,11 +145,11 @@ int CCryptoControl::processSrtMsg_KMREQ(
// CHANGED. The first version made HSv5 reject the connection.
// This isn't well handled by applications, so the connection is
// still established, but unable to handle any transport.
//#define KMREQ_RESULT_REJECTION() if (bidirectional) { return SRT_CMD_NONE; } else { srtlen = 1; goto HSv4_ErrorReport; }
#define KMREQ_RESULT_REJECTION() { srtlen = 1; goto HSv4_ErrorReport; }
//#define KMREQ_RESULT_REJECTION() if (bidirectional) { return SRT_CMD_NONE; } else { w_srtlen = 1; goto HSv4_ErrorReport; }
#define KMREQ_RESULT_REJECTION() { w_srtlen = 1; goto HSv4_ErrorReport; }
int rc = HAICRYPT_OK; // needed before 'goto' run from KMREQ_RESULT_REJECTION macro
bool SRT_ATR_UNUSED wasb4 = false;
bool wasb4 SRT_ATR_UNUSED = false;
size_t sek_len = 0;
// What we have to do:
@ -147,16 +161,16 @@ int CCryptoControl::processSrtMsg_KMREQ(
// function normally return SRT_CMD_KMRSP.
if ( bytelen <= HCRYPT_MSG_KM_OFS_SALT ) //Sanity on message
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: size of the KM (" << bytelen << ") is too small, must be >" << HCRYPT_MSG_KM_OFS_SALT);
LOGC(cnlog.Error, log << "processSrtMsg_KMREQ: size of the KM (" << bytelen << ") is too small, must be >" << HCRYPT_MSG_KM_OFS_SALT);
m_RcvKmState = SRT_KM_S_BADSECRET;
KMREQ_RESULT_REJECTION();
}
HLOGC(mglog.Debug, log << "KMREQ: getting SEK and creating receiver crypto");
HLOGC(cnlog.Debug, log << "KMREQ: getting SEK and creating receiver crypto");
sek_len = hcryptMsg_KM_GetSekLen(kmdata);
if ( sek_len == 0 )
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Received SEK is empty - REJECTING!");
LOGC(cnlog.Error, log << "processSrtMsg_KMREQ: Received SEK is empty - REJECTING!");
m_RcvKmState = SRT_KM_S_BADSECRET;
KMREQ_RESULT_REJECTION();
}
@ -168,7 +182,7 @@ int CCryptoControl::processSrtMsg_KMREQ(
#if ENABLE_HEAVY_LOGGING
if (m_iSndKmKeyLen != m_iRcvKmKeyLen)
{
LOGC(mglog.Debug, log << "processSrtMsg_KMREQ: Agent's PBKEYLEN=" << m_iSndKmKeyLen
LOGC(cnlog.Debug, log << "processSrtMsg_KMREQ: Agent's PBKEYLEN=" << m_iSndKmKeyLen
<< " overwritten by Peer's PBKEYLEN=" << m_iRcvKmKeyLen);
}
#endif
@ -179,22 +193,22 @@ int CCryptoControl::processSrtMsg_KMREQ(
// a wrong password.
if (m_KmSecret.len == 0) //We have a shared secret <==> encryption is on
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Agent does not declare encryption - won't decrypt incoming packets!");
LOGC(cnlog.Warn, log << "processSrtMsg_KMREQ: Agent does not declare encryption - won't decrypt incoming packets!");
m_RcvKmState = SRT_KM_S_NOSECRET;
KMREQ_RESULT_REJECTION();
}
wasb4 = m_hRcvCrypto;
if (!createCryptoCtx(Ref(m_hRcvCrypto), m_iRcvKmKeyLen, HAICRYPT_CRYPTO_DIR_RX))
if (!createCryptoCtx(m_iRcvKmKeyLen, HAICRYPT_CRYPTO_DIR_RX, (m_hRcvCrypto)))
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Can't create RCV CRYPTO CTX - must reject...");
LOGC(cnlog.Error, log << "processSrtMsg_KMREQ: Can't create RCV CRYPTO CTX - must reject...");
m_RcvKmState = SRT_KM_S_NOSECRET;
KMREQ_RESULT_REJECTION();
}
if (!wasb4)
{
HLOGC(mglog.Debug, log << "processSrtMsg_KMREQ: created RX ENC with KeyLen=" << m_iRcvKmKeyLen);
HLOGC(cnlog.Debug, log << "processSrtMsg_KMREQ: created RX ENC with KeyLen=" << m_iRcvKmKeyLen);
}
// We have both sides set with password, so both are pending for security
m_RcvKmState = SRT_KM_S_SECURING;
@ -207,36 +221,36 @@ int CCryptoControl::processSrtMsg_KMREQ(
{
case HAICRYPT_OK:
m_RcvKmState = SRT_KM_S_SECURED;
HLOGC(mglog.Debug, log << "KMREQ/rcv: (snd) Rx process successful - SECURED.");
HLOGC(cnlog.Debug, log << "KMREQ/rcv: (snd) Rx process successful - SECURED.");
//Send back the whole message to confirm
break;
case HAICRYPT_ERROR_WRONG_SECRET: //Unmatched shared secret to decrypt wrapped key
m_RcvKmState = m_SndKmState = SRT_KM_S_BADSECRET;
//Send status KMRSP message to tel error
srtlen = 1;
LOGC(mglog.Error, log << "KMREQ/rcv: (snd) Rx process failure - BADSECRET");
w_srtlen = 1;
LOGC(cnlog.Warn, log << "KMREQ/rcv: (snd) Rx process failure - BADSECRET");
break;
case HAICRYPT_ERROR: //Other errors
default:
m_RcvKmState = m_SndKmState = SRT_KM_S_NOSECRET;
srtlen = 1;
LOGC(mglog.Error, log << "KMREQ/rcv: (snd) Rx process failure (IPE) - NOSECRET");
w_srtlen = 1;
LOGC(cnlog.Warn, log << "KMREQ/rcv: (snd) Rx process failure (IPE) - NOSECRET");
break;
}
LOGP(mglog.Note, FormatKmMessage("processSrtMsg_KMREQ", SRT_CMD_KMREQ, bytelen));
LOGP(cnlog.Note, FormatKmMessage("processSrtMsg_KMREQ", SRT_CMD_KMREQ, bytelen));
// Since now, when CCryptoControl::decrypt() encounters an error, it will print it, ONCE,
// until the next KMREQ is received as a key regeneration.
m_bErrorReported = false;
if (srtlen == 1)
if (w_srtlen == 1)
goto HSv4_ErrorReport;
// Configure the sender context also, if it succeeded to configure the
// receiver context and we are using bidirectional mode.
if ( bidirectional )
if (bidirectional)
{
// Note: 'bidirectional' means that we want a bidirectional key update,
// which happens only and exclusively with HSv5 handshake - not when the
@ -249,7 +263,7 @@ int CCryptoControl::processSrtMsg_KMREQ(
m_iSndKmKeyLen = m_iRcvKmKeyLen;
if (HaiCrypt_Clone(m_hRcvCrypto, HAICRYPT_CRYPTO_DIR_TX, &m_hSndCrypto) != HAICRYPT_OK)
{
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Can't create SND CRYPTO CTX - WILL NOT SEND-ENCRYPT correctly!");
LOGC(cnlog.Error, log << "processSrtMsg_KMREQ: Can't create SND CRYPTO CTX - WILL NOT SEND-ENCRYPT correctly!");
if (hasPassphrase())
m_SndKmState = SRT_KM_S_BADSECRET;
else
@ -260,30 +274,30 @@ int CCryptoControl::processSrtMsg_KMREQ(
m_SndKmState = SRT_KM_S_SECURED;
}
LOGC(mglog.Note, log << FormatKmMessage("processSrtMsg_KMREQ", SRT_CMD_KMREQ, bytelen)
LOGC(cnlog.Note, log << FormatKmMessage("processSrtMsg_KMREQ", SRT_CMD_KMREQ, bytelen)
<< " SndKeyLen=" << m_iSndKmKeyLen
<< " TX CRYPTO CTX CLONED FROM RX"
);
// Write the KM message into the field from which it will be next sent.
memcpy(m_SndKmMsg[0].Msg, kmdata, bytelen);
memcpy((m_SndKmMsg[0].Msg), kmdata, bytelen);
m_SndKmMsg[0].MsgLen = bytelen;
m_SndKmMsg[0].iPeerRetry = 0; // Don't start sending them upon connection :)
}
else
{
HLOGC(mglog.Debug, log << "processSrtMsg_KMREQ: NOT cloning RX to TX crypto: already in "
HLOGC(cnlog.Debug, log << "processSrtMsg_KMREQ: NOT cloning RX to TX crypto: already in "
<< KmStateStr(m_SndKmState) << " state");
}
}
else
{
HLOGP(mglog.Debug, "processSrtMsg_KMREQ: NOT SECURED - not replaying failed security association to TX CRYPTO CTX");
HLOGP(cnlog.Debug, "processSrtMsg_KMREQ: NOT SECURED - not replaying failed security association to TX CRYPTO CTX");
}
}
else
{
HLOGC(mglog.Debug, log << "processSrtMsg_KMREQ: NOT REPLAYING the key update to TX CRYPTO CTX.");
HLOGC(cnlog.Debug, log << "processSrtMsg_KMREQ: NOT REPLAYING the key update to TX CRYPTO CTX.");
}
return SRT_CMD_KMRSP;
@ -304,17 +318,17 @@ HSv4_ErrorReport:
// It's ok that this is reported as error because this happens in a scenario,
// when non-encryption-enabled SRT application is contacted by encryption-enabled SRT
// application which tries to make a security association.
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Encryption not enabled at compile time - must reject...");
LOGC(cnlog.Warn, log << "processSrtMsg_KMREQ: Encryption not enabled at compile time - must reject...");
m_RcvKmState = SRT_KM_S_NOSECRET;
#endif
srtlen = 1;
w_srtlen = 1;
srtdata_out[SRT_KMR_KMSTATE] = m_RcvKmState;
pw_srtdata_out[SRT_KMR_KMSTATE] = m_RcvKmState;
return SRT_CMD_KMRSP;
}
int CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int /* XXX unused? hsv*/)
int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int /* XXX unused? hsv*/)
{
/* All 32-bit msg fields (if present) swapped on reception
* But HaiCrypt expect network order message
@ -367,7 +381,7 @@ int CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int
break;
default:
LOGC(mglog.Fatal, log << "processSrtMsg_KMRSP: IPE: unknown peer error state: "
LOGC(cnlog.Fatal, log << "processSrtMsg_KMRSP: IPE: unknown peer error state: "
<< KmStateStr(peerstate) << " (" << int(peerstate) << ")");
m_RcvKmState = SRT_KM_S_NOSECRET;
m_SndKmState = SRT_KM_S_NOSECRET;
@ -375,11 +389,11 @@ int CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int
break;
}
LOGC(mglog.Error, log << "processSrtMsg_KMRSP: received failure report. STATE: " << KmStateStr(m_RcvKmState));
LOGC(cnlog.Warn, log << "processSrtMsg_KMRSP: received failure report. STATE: " << KmStateStr(m_RcvKmState));
}
else
{
HLOGC(mglog.Debug, log << "processSrtMsg_KMRSP: received key response len=" << len);
HLOGC(cnlog.Debug, log << "processSrtMsg_KMRSP: received key response len=" << len);
// XXX INSECURE << ": [" << FormatBinaryString((uint8_t*)srtd, len) << "]";
bool key1 = getKmMsg_acceptResponse(0, srtd, len);
bool key2 = true;
@ -389,40 +403,40 @@ int CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int
if (key1 || key2)
{
m_SndKmState = m_RcvKmState = SRT_KM_S_SECURED;
HLOGC(mglog.Debug, log << "processSrtMsg_KMRSP: KM response matches " << (key1 ? "EVEN" : "ODD") << " key");
HLOGC(cnlog.Debug, log << "processSrtMsg_KMRSP: KM response matches " << (key1 ? "EVEN" : "ODD") << " key");
retstatus = 1;
}
else
{
retstatus = -1;
LOGC(mglog.Error, log << "processSrtMsg_KMRSP: IPE??? KM response key matches no key");
LOGC(cnlog.Error, log << "processSrtMsg_KMRSP: IPE??? KM response key matches no key");
/* XXX INSECURE
LOGC(mglog.Error, log << "processSrtMsg_KMRSP: KM response: [" << FormatBinaryString((uint8_t*)srtd, len)
LOGC(cnlog.Error, log << "processSrtMsg_KMRSP: KM response: [" << FormatBinaryString((uint8_t*)srtd, len)
<< "] matches no key 0=[" << FormatBinaryString((uint8_t*)m_SndKmMsg[0].Msg, m_SndKmMsg[0].MsgLen)
<< "] 1=[" << FormatBinaryString((uint8_t*)m_SndKmMsg[1].Msg, m_SndKmMsg[1].MsgLen) << "]");
*/
m_SndKmState = m_RcvKmState = SRT_KM_S_BADSECRET;
}
HLOGC(mglog.Debug, log << "processSrtMsg_KMRSP: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry
HLOGC(cnlog.Debug, log << "processSrtMsg_KMRSP: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry
<< "; key[1]: len=" << m_SndKmMsg[1].MsgLen << " retry=" << m_SndKmMsg[1].iPeerRetry);
}
LOGP(mglog.Note, FormatKmMessage("processSrtMsg_KMRSP", SRT_CMD_KMRSP, len));
LOGP(cnlog.Note, FormatKmMessage("processSrtMsg_KMRSP", SRT_CMD_KMRSP, len));
return retstatus;
}
void CCryptoControl::sendKeysToPeer(Whether2RegenKm regen SRT_ATR_UNUSED)
void srt::CCryptoControl::sendKeysToPeer(CUDT* sock SRT_ATR_UNUSED, int iSRTT SRT_ATR_UNUSED, Whether2RegenKm regen SRT_ATR_UNUSED)
{
if ( !m_hSndCrypto || m_SndKmState == SRT_KM_S_UNSECURED)
if (!m_hSndCrypto || m_SndKmState == SRT_KM_S_UNSECURED)
{
HLOGC(mglog.Debug, log << "sendKeysToPeer: NOT sending/regenerating keys: "
HLOGC(cnlog.Debug, log << "sendKeysToPeer: NOT sending/regenerating keys: "
<< (m_hSndCrypto ? "CONNECTION UNSECURED" : "NO TX CRYPTO CTX created"));
return;
}
#ifdef SRT_ENABLE_ENCRYPTION
uint64_t now = 0;
srt::sync::steady_clock::time_point now = srt::sync::steady_clock::now();
/*
* Crypto Key Distribution to peer:
* If...
@ -432,38 +446,35 @@ void CCryptoControl::sendKeysToPeer(Whether2RegenKm regen SRT_ATR_UNUSED)
* - last sent Keying Material req should have been replied (RTT*1.5 elapsed);
* then (re-)send handshake request.
*/
if ( ((m_SndKmMsg[0].iPeerRetry > 0) || (m_SndKmMsg[1].iPeerRetry > 0))
&& ((m_SndKmLastTime + ((m_parent->RTT() * 3)/2)) <= (now = CTimer::getTime())))
if (((m_SndKmMsg[0].iPeerRetry > 0) || (m_SndKmMsg[1].iPeerRetry > 0))
&& ((m_SndKmLastTime + srt::sync::microseconds_from((iSRTT * 3)/2)) <= now))
{
for (int ki = 0; ki < 2; ki++)
{
if (m_SndKmMsg[ki].iPeerRetry > 0 && m_SndKmMsg[ki].MsgLen > 0)
{
m_SndKmMsg[ki].iPeerRetry--;
HLOGC(mglog.Debug, log << "sendKeysToPeer: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen
HLOGC(cnlog.Debug, log << "sendKeysToPeer: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen
<< " retry(updated)=" << m_SndKmMsg[ki].iPeerRetry);
m_SndKmLastTime = now;
m_parent->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen/sizeof(uint32_t));
sock->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen / sizeof(uint32_t));
}
}
}
if (now == 0)
{
HLOGC(mglog.Debug, log << "sendKeysToPeer: NO KEYS RESENT, will " <<
(regen ? "" : "NOT ") << "regenerate.");
}
if (regen)
{
regenCryptoKm(
true, // send UMSG_EXT + SRT_CMD_KMREQ to the peer, if regenerated the key
false // Do not apply the regenerated key to the to the receiver context
); // regenerate and send
sock, // send UMSG_EXT + SRT_CMD_KMREQ to the peer using this socket
false // Do not apply the regenerated key to the to the receiver context
); // regenerate and send
}
#endif
}
#ifdef SRT_ENABLE_ENCRYPTION
void CCryptoControl::regenCryptoKm(bool sendit, bool bidirectional)
void srt::CCryptoControl::regenCryptoKm(CUDT* sock, bool bidirectional)
{
if (!m_hSndCrypto)
return;
@ -473,8 +484,8 @@ void CCryptoControl::regenCryptoKm(bool sendit, bool bidirectional)
int nbo = HaiCrypt_Tx_ManageKeys(m_hSndCrypto, out_p, out_len_p, 2);
int sent = 0;
HLOGC(mglog.Debug, log << "regenCryptoKm: regenerating crypto keys nbo=" << nbo <<
" THEN=" << (sendit ? "SEND" : "KEEP") << " DIR=" << (bidirectional ? "BOTH" : "SENDER"));
HLOGC(cnlog.Debug, log << "regenCryptoKm: regenerating crypto keys nbo=" << nbo <<
" THEN=" << (sock ? "SEND" : "KEEP") << " DIR=" << (bidirectional ? "BOTH" : "SENDER"));
for (int i = 0; i < nbo && i < 2; i++)
{
@ -491,71 +502,69 @@ void CCryptoControl::regenCryptoKm(bool sendit, bool bidirectional)
{
uint8_t* oldkey SRT_ATR_UNUSED = m_SndKmMsg[ki].Msg;
HLOGC(mglog.Debug, log << "new key[" << ki << "] index=" << kix
HLOGC(cnlog.Debug, log << "new key[" << ki << "] index=" << kix
<< " OLD=[" << m_SndKmMsg[ki].MsgLen << "]"
<< FormatBinaryString(m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen)
<< " NEW=[" << out_len_p[i] << "]"
<< FormatBinaryString((const uint8_t*)out_p[i], out_len_p[i]));
/* New Keying material, send to peer */
memcpy(m_SndKmMsg[ki].Msg, out_p[i], out_len_p[i]);
memcpy((m_SndKmMsg[ki].Msg), out_p[i], out_len_p[i]);
m_SndKmMsg[ki].MsgLen = out_len_p[i];
m_SndKmMsg[ki].iPeerRetry = SRT_MAX_KMRETRY;
if (bidirectional && !sendit)
if (bidirectional && !sock)
{
// "Send" this key also to myself, just to be applied to the receiver crypto,
// exactly the same way how this key is interpreted on the peer side into its receiver crypto
int rc = HaiCrypt_Rx_Process(m_hRcvCrypto, m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen, NULL, NULL, 0);
if ( rc < 0 )
{
LOGC(mglog.Fatal, log << "regenCryptoKm: IPE: applying key generated in snd crypto into rcv crypto: failed code=" << rc);
LOGC(cnlog.Fatal, log << "regenCryptoKm: IPE: applying key generated in snd crypto into rcv crypto: failed code=" << rc);
// The party won't be able to decrypt incoming data!
// Not sure if anything has to be reported.
}
}
if (sendit)
if (sock)
{
HLOGC(mglog.Debug, log << "regenCryptoKm: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen
HLOGC(cnlog.Debug, log << "regenCryptoKm: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen
<< " retry(updated)=" << m_SndKmMsg[ki].iPeerRetry);
m_parent->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen/sizeof(uint32_t));
sock->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen / sizeof(uint32_t));
sent++;
}
}
else if (out_len_p[i] == 0)
{
HLOGC(mglog.Debug, log << "no key[" << ki << "] index=" << kix << ": not generated");
HLOGC(cnlog.Debug, log << "no key[" << ki << "] index=" << kix << ": not generated");
}
else
{
HLOGC(mglog.Debug, log << "no key[" << ki << "] index=" << kix << ": key unchanged");
HLOGC(cnlog.Debug, log << "no key[" << ki << "] index=" << kix << ": key unchanged");
}
}
HLOGC(mglog.Debug, log << "regenCryptoKm: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry
HLOGC(cnlog.Debug, log << "regenCryptoKm: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry
<< "; key[1]: len=" << m_SndKmMsg[1].MsgLen << " retry=" << m_SndKmMsg[1].iPeerRetry);
if (sent)
m_SndKmLastTime = CTimer::getTime();
m_SndKmLastTime = srt::sync::steady_clock::now();
}
#endif
CCryptoControl::CCryptoControl(CUDT* parent, SRTSOCKET id):
m_parent(parent), // should be initialized in createCC()
m_SocketID(id),
m_iSndKmKeyLen(0),
m_iRcvKmKeyLen(0),
m_SndKmState(SRT_KM_S_UNSECURED),
m_RcvKmState(SRT_KM_S_UNSECURED),
m_KmRefreshRatePkt(0),
m_KmPreAnnouncePkt(0),
m_bErrorReported(false)
srt::CCryptoControl::CCryptoControl(SRTSOCKET id)
: m_SocketID(id)
, m_iSndKmKeyLen(0)
, m_iRcvKmKeyLen(0)
, m_SndKmState(SRT_KM_S_UNSECURED)
, m_RcvKmState(SRT_KM_S_UNSECURED)
, m_KmRefreshRatePkt(0)
, m_KmPreAnnouncePkt(0)
, m_bErrorReported(false)
{
m_KmSecret.len = 0;
//send
m_SndKmLastTime = 0;
m_SndKmMsg[0].MsgLen = 0;
m_SndKmMsg[0].iPeerRetry = 0;
m_SndKmMsg[1].MsgLen = 0;
@ -565,14 +574,14 @@ m_bErrorReported(false)
m_hRcvCrypto = NULL;
}
bool CCryptoControl::init(HandshakeSide side, bool bidirectional SRT_ATR_UNUSED)
bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool bidirectional SRT_ATR_UNUSED)
{
// NOTE: initiator creates m_hSndCrypto. When bidirectional,
// it creates also m_hRcvCrypto with the same key length.
// Acceptor creates nothing - it will create appropriate
// contexts when receiving KMREQ from the initiator.
HLOGC(mglog.Debug, log << "CCryptoControl::init: HS SIDE:"
HLOGC(cnlog.Debug, log << "CCryptoControl::init: HS SIDE:"
<< (side == HSD_INITIATOR ? "INITIATOR" : "RESPONDER")
<< " DIRECTION:" << (bidirectional ? "BOTH" : (side == HSD_INITIATOR) ? "SENDER" : "RECEIVER"));
@ -582,8 +591,8 @@ bool CCryptoControl::init(HandshakeSide side, bool bidirectional SRT_ATR_UNUSED)
// Set security-pending state, if a password was set.
m_SndKmState = hasPassphrase() ? SRT_KM_S_SECURING : SRT_KM_S_UNSECURED;
m_KmPreAnnouncePkt = m_parent->m_uKmPreAnnouncePkt;
m_KmRefreshRatePkt = m_parent->m_uKmRefreshRatePkt;
m_KmPreAnnouncePkt = cfg.uKmPreAnnouncePkt;
m_KmRefreshRatePkt = cfg.uKmRefreshRatePkt;
if ( side == HSD_INITIATOR )
{
@ -592,18 +601,18 @@ bool CCryptoControl::init(HandshakeSide side, bool bidirectional SRT_ATR_UNUSED)
#ifdef SRT_ENABLE_ENCRYPTION
if (m_iSndKmKeyLen == 0)
{
HLOGC(mglog.Debug, log << "CCryptoControl::init: PBKEYLEN still 0, setting default 16");
HLOGC(cnlog.Debug, log << "CCryptoControl::init: PBKEYLEN still 0, setting default 16");
m_iSndKmKeyLen = 16;
}
bool ok = createCryptoCtx(Ref(m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX);
HLOGC(mglog.Debug, log << "CCryptoControl::init: creating SND crypto context: " << ok);
bool ok = createCryptoCtx(m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX, (m_hSndCrypto));
HLOGC(cnlog.Debug, log << "CCryptoControl::init: creating SND crypto context: " << ok);
if (ok && bidirectional)
{
m_iRcvKmKeyLen = m_iSndKmKeyLen;
int st = HaiCrypt_Clone(m_hSndCrypto, HAICRYPT_CRYPTO_DIR_RX, &m_hRcvCrypto);
HLOGC(mglog.Debug, log << "CCryptoControl::init: creating CLONED RCV crypto context: status=" << st);
HLOGC(cnlog.Debug, log << "CCryptoControl::init: creating CLONED RCV crypto context: status=" << st);
ok = st == 0;
}
@ -618,45 +627,51 @@ bool CCryptoControl::init(HandshakeSide side, bool bidirectional SRT_ATR_UNUSED)
}
regenCryptoKm(
false, // Do not send the key (will be attached it to the HSv5 handshake)
bidirectional // replicate the key to the receiver context, if bidirectional
);
NULL, // Do not send the key (the KM msg will be attached to the HSv5 handshake)
bidirectional // replicate the key to the receiver context, if bidirectional
);
#else
LOGC(mglog.Error, log << "CCryptoControl::init: encryption not supported");
// This error would be a consequence of setting the passphrase, while encryption
// is turned off at compile time. Setting the password itself should be not allowed
// so this could only happen as a consequence of an IPE.
LOGC(cnlog.Error, log << "CCryptoControl::init: IPE: encryption not supported");
return true;
#endif
}
else
{
HLOGC(mglog.Debug, log << "CCryptoControl::init: CAN'T CREATE crypto: key length for SND = " << m_iSndKmKeyLen);
HLOGC(cnlog.Debug, log << "CCryptoControl::init: CAN'T CREATE crypto: key length for SND = " << m_iSndKmKeyLen);
}
}
else
{
HLOGC(mglog.Debug, log << "CCryptoControl::init: NOT creating crypto contexts - will be created upon reception of KMREQ");
HLOGC(cnlog.Debug, log << "CCryptoControl::init: NOT creating crypto contexts - will be created upon reception of KMREQ");
}
return true;
}
void CCryptoControl::close()
void srt::CCryptoControl::close()
{
/* Wipeout secrets */
memset(&m_KmSecret, 0, sizeof(m_KmSecret));
}
std::string CCryptoControl::CONID() const
std::string srt::CCryptoControl::CONID() const
{
if ( m_SocketID == 0 )
if (m_SocketID == 0)
return "";
std::ostringstream os;
os << "%" << m_SocketID << ":";
os << "@" << m_SocketID << ":";
return os.str();
}
#ifdef SRT_ENABLE_ENCRYPTION
#if ENABLE_HEAVY_LOGGING
namespace srt {
static std::string CryptoFlags(int flg)
{
using namespace std;
@ -673,13 +688,13 @@ static std::string CryptoFlags(int flg)
copy(f.begin(), f.end(), ostream_iterator<string>(os, "|"));
return os.str();
}
#endif
} // namespace srt
#endif // ENABLE_HEAVY_LOGGING
#ifdef SRT_ENABLE_ENCRYPTION
bool CCryptoControl::createCryptoCtx(ref_t<HaiCrypt_Handle> hCrypto, size_t keylen, HaiCrypt_CryptoDir cdir)
bool srt::CCryptoControl::createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir cdir, HaiCrypt_Handle& w_hCrypto)
{
if (*hCrypto)
if (w_hCrypto)
{
// XXX You can check here if the existing handle represents
// a correctly defined crypto. But this doesn't seem to be
@ -690,7 +705,7 @@ bool CCryptoControl::createCryptoCtx(ref_t<HaiCrypt_Handle> hCrypto, size_t keyl
if ((m_KmSecret.len <= 0) || (keylen <= 0))
{
LOGC(mglog.Error, log << CONID() << "cryptoCtx: missing secret (" << m_KmSecret.len << ") or key length (" << keylen << ")");
LOGC(cnlog.Error, log << CONID() << "cryptoCtx: IPE missing secret (" << m_KmSecret.len << ") or key length (" << keylen << ")");
return false;
}
@ -711,36 +726,35 @@ bool CCryptoControl::createCryptoCtx(ref_t<HaiCrypt_Handle> hCrypto, size_t keyl
crypto_cfg.secret = m_KmSecret;
//memcpy(&crypto_cfg.secret, &m_KmSecret, sizeof(crypto_cfg.secret));
HLOGC(mglog.Debug, log << "CRYPTO CFG: flags=" << CryptoFlags(crypto_cfg.flags) << " xport=" << crypto_cfg.xport << " cryspr=" << crypto_cfg.cryspr
HLOGC(cnlog.Debug, log << "CRYPTO CFG: flags=" << CryptoFlags(crypto_cfg.flags) << " xport=" << crypto_cfg.xport << " cryspr=" << crypto_cfg.cryspr
<< " keylen=" << crypto_cfg.key_len << " passphrase_length=" << crypto_cfg.secret.len);
if (HaiCrypt_Create(&crypto_cfg, &hCrypto.get()) != HAICRYPT_OK)
if (HaiCrypt_Create(&crypto_cfg, (&w_hCrypto)) != HAICRYPT_OK)
{
LOGC(mglog.Error, log << CONID() << "cryptoCtx: could not create " << (cdir == HAICRYPT_CRYPTO_DIR_TX ? "tx" : "rx") << " crypto ctx");
LOGC(cnlog.Error, log << CONID() << "cryptoCtx: could not create " << (cdir == HAICRYPT_CRYPTO_DIR_TX ? "tx" : "rx") << " crypto ctx");
return false;
}
HLOGC(mglog.Debug, log << CONID() << "cryptoCtx: CREATED crypto for dir=" << (cdir == HAICRYPT_CRYPTO_DIR_TX ? "tx" : "rx") << " keylen=" << keylen);
HLOGC(cnlog.Debug, log << CONID() << "cryptoCtx: CREATED crypto for dir=" << (cdir == HAICRYPT_CRYPTO_DIR_TX ? "tx" : "rx") << " keylen=" << keylen);
return true;
}
#else
bool CCryptoControl::createCryptoCtx(ref_t<HaiCrypt_Handle>, size_t, HaiCrypt_CryptoDir)
bool srt::CCryptoControl::createCryptoCtx(size_t, HaiCrypt_CryptoDir, HaiCrypt_Handle&)
{
return false;
}
#endif
#endif // SRT_ENABLE_ENCRYPTION
EncryptionStatus CCryptoControl::encrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
srt::EncryptionStatus srt::CCryptoControl::encrypt(CPacket& w_packet SRT_ATR_UNUSED)
{
#ifdef SRT_ENABLE_ENCRYPTION
// Encryption not enabled - do nothing.
if ( getSndCryptoFlags() == EK_NOENC )
return ENCS_CLEAR;
CPacket& packet = *r_packet;
int rc = HaiCrypt_Tx_Data(m_hSndCrypto, (uint8_t*)packet.getHeader(), (uint8_t*)packet.m_pcData, packet.getLength());
int rc = HaiCrypt_Tx_Data(m_hSndCrypto, ((uint8_t*)w_packet.getHeader()), ((uint8_t*)w_packet.m_pcData), w_packet.getLength());
if (rc < 0)
{
return ENCS_FAILED;
@ -749,7 +763,7 @@ EncryptionStatus CCryptoControl::encrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
{
// XXX what happens if the encryption is said to be "succeeded",
// but the length is 0? Shouldn't this be treated as unwanted?
packet.setLength(rc);
w_packet.setLength(rc);
}
return ENCS_CLEAR;
@ -758,14 +772,12 @@ EncryptionStatus CCryptoControl::encrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
#endif
}
EncryptionStatus CCryptoControl::decrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
srt::EncryptionStatus srt::CCryptoControl::decrypt(CPacket& w_packet SRT_ATR_UNUSED)
{
#ifdef SRT_ENABLE_ENCRYPTION
CPacket& packet = *r_packet;
if (packet.getMsgCryptoFlags() == EK_NOENC)
if (w_packet.getMsgCryptoFlags() == EK_NOENC)
{
HLOGC(mglog.Debug, log << "CPacket::decrypt: packet not encrypted");
HLOGC(cnlog.Debug, log << "CPacket::decrypt: packet not encrypted");
return ENCS_CLEAR; // not encrypted, no need do decrypt, no flags to be modified
}
@ -776,8 +788,8 @@ EncryptionStatus CCryptoControl::decrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
// We were unaware that the peer has set password,
// but now here we are.
m_RcvKmState = SRT_KM_S_SECURING;
LOGC(mglog.Note, log << "SECURITY UPDATE: Peer has surprised Agent with encryption, but KMX is pending - current packet size="
<< packet.getLength() << " dropped");
LOGC(cnlog.Note, log << "SECURITY UPDATE: Peer has surprised Agent with encryption, but KMX is pending - current packet size="
<< w_packet.getLength() << " dropped");
return ENCS_FAILED;
}
else
@ -786,7 +798,7 @@ EncryptionStatus CCryptoControl::decrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
// which means that it will be unable to decrypt
// sent payloads anyway.
m_RcvKmState = SRT_KM_S_NOSECRET;
LOGP(mglog.Error, "SECURITY FAILURE: Agent has no PW, but Peer sender has declared one, can't decrypt");
LOGP(cnlog.Warn, "SECURITY FAILURE: Agent has no PW, but Peer sender has declared one, can't decrypt");
// This only informs about the state change; it will be also caught by the condition below
}
}
@ -807,28 +819,28 @@ EncryptionStatus CCryptoControl::decrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
if (!m_bErrorReported)
{
m_bErrorReported = true;
LOGC(mglog.Error, log << "SECURITY STATUS: " << KmStateStr(m_RcvKmState) << " - can't decrypt packet.");
LOGC(cnlog.Error, log << "SECURITY STATUS: " << KmStateStr(m_RcvKmState) << " - can't decrypt w_packet.");
}
HLOGC(mglog.Debug, log << "Packet still not decrypted, status=" << KmStateStr(m_RcvKmState)
<< " - dropping size=" << packet.getLength());
HLOGC(cnlog.Debug, log << "Packet still not decrypted, status=" << KmStateStr(m_RcvKmState)
<< " - dropping size=" << w_packet.getLength());
return ENCS_FAILED;
}
int rc = HaiCrypt_Rx_Data(m_hRcvCrypto, (uint8_t *)packet.getHeader(), (uint8_t *)packet.m_pcData, packet.getLength());
int rc = HaiCrypt_Rx_Data(m_hRcvCrypto, ((uint8_t *)w_packet.getHeader()), ((uint8_t *)w_packet.m_pcData), w_packet.getLength());
if ( rc <= 0 )
{
LOGC(mglog.Error, log << "decrypt ERROR (IPE): HaiCrypt_Rx_Data failure=" << rc << " - returning failed decryption");
LOGC(cnlog.Error, log << "decrypt ERROR (IPE): HaiCrypt_Rx_Data failure=" << rc << " - returning failed decryption");
// -1: decryption failure
// 0: key not received yet
return ENCS_FAILED;
}
// Otherwise: rc == decrypted text length.
packet.setLength(rc); /* In case clr txt size is different from cipher txt */
w_packet.setLength(rc); /* In case clr txt size is different from cipher txt */
// Decryption succeeded. Update flags.
packet.setMsgCryptoFlags(EK_NOENC);
w_packet.setMsgCryptoFlags(EK_NOENC);
HLOGC(mglog.Debug, log << "decrypt: successfully decrypted, resulting length=" << rc);
HLOGC(cnlog.Debug, log << "decrypt: successfully decrypted, resulting length=" << rc);
return ENCS_CLEAR;
#else
return ENCS_NOTSUP;
@ -836,9 +848,10 @@ EncryptionStatus CCryptoControl::decrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
}
CCryptoControl::~CCryptoControl()
srt::CCryptoControl::~CCryptoControl()
{
#ifdef SRT_ENABLE_ENCRYPTION
close();
if (m_hSndCrypto)
{
HaiCrypt_Close(m_hSndCrypto);
@ -850,38 +863,3 @@ CCryptoControl::~CCryptoControl()
}
#endif
}
std::string SrtFlagString(int32_t flags)
{
#define LEN(arr) (sizeof (arr)/(sizeof ((arr)[0])))
std::string output;
static std::string namera[] = { "TSBPD-snd", "TSBPD-rcv", "haicrypt", "TLPktDrop", "NAKReport", "ReXmitFlag", "StreamAPI" };
size_t i = 0;
for ( ; i < LEN(namera); ++i )
{
if ( (flags & 1) == 1 )
{
output += "+" + namera[i] + " ";
}
else
{
output += "-" + namera[i] + " ";
}
flags >>= 1;
//if ( flags == 0 )
// break;
}
#undef LEN
if ( flags != 0 )
{
output += "+unknown";
}
return output;
}

View file

@ -13,8 +13,8 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__CRYPTO_H
#define INC__CRYPTO_H
#ifndef INC_SRT_CRYPTO_H
#define INC_SRT_CRYPTO_H
#include <cstring>
#include <string>
@ -28,33 +28,36 @@ written by
#include <haicrypt.h>
#include <hcrypt_msg.h>
#if ENABLE_LOGGING
std::string KmStateStr(SRT_KM_STATE state);
namespace srt_logging
{
extern Logger mglog;
std::string KmStateStr(SRT_KM_STATE state);
#if ENABLE_LOGGING
extern Logger cnlog;
#endif
}
#endif
namespace srt
{
class CUDT;
struct CSrtConfig;
// For KMREQ/KMRSP. Only one field is used.
const size_t SRT_KMR_KMSTATE = 0;
#define SRT_CMD_MAXSZ HCRYPT_MSG_KM_MAX_SZ /* Maximum SRT custom messages payload size (bytes) */
const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ/sizeof(int32_t);
const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ/sizeof(uint32_t);
enum Whether2RegenKm {DONT_REGEN_KM = 0, REGEN_KM = 1};
class CCryptoControl
{
//public:
class CUDT* m_parent;
SRTSOCKET m_SocketID;
SRTSOCKET m_SocketID;
size_t m_iSndKmKeyLen; //Key length
size_t m_iRcvKmKeyLen; //Key length from rx KM
size_t m_iSndKmKeyLen; //Key length
size_t m_iRcvKmKeyLen; //Key length from rx KM
// Temporarily allow these to be accessed.
public:
@ -69,7 +72,7 @@ private:
HaiCrypt_Secret m_KmSecret; //Key material shared secret
// Sender
uint64_t m_SndKmLastTime;
sync::steady_clock::time_point m_SndKmLastTime;
struct {
unsigned char Msg[HCRYPT_MSG_KM_MAX_SZ];
size_t MsgLen;
@ -82,6 +85,7 @@ private:
bool m_bErrorReported;
public:
static void globalInit();
bool sendingAllowed()
{
@ -106,9 +110,11 @@ public:
}
private:
#ifdef SRT_ENABLE_ENCRYPTION
void regenCryptoKm(bool sendit, bool bidirectional);
/// Regenerate cryptographic key material.
/// @param[in] sock If not null, the socket will be used to send the KM message to the peer (e.g. KM refresh).
/// @param[in] bidirectional If true, the key material will be regenerated for both directions (receiver and sender).
void regenCryptoKm(CUDT* sock, bool bidirectional);
#endif
public:
@ -119,7 +125,8 @@ public:
void updateKmState(int cmd, size_t srtlen);
// Detailed processing
int processSrtMsg_KMREQ(const uint32_t* srtdata, size_t len, uint32_t* srtdata_out, ref_t<size_t> r_srtlen, int hsv);
int processSrtMsg_KMREQ(const uint32_t* srtdata, size_t len, int hsv,
uint32_t srtdata_out[], size_t&);
// This returns:
// 1 - the given payload is the same as the currently used key
@ -158,18 +165,18 @@ public:
void getKmMsg_markSent(size_t ki, bool runtime)
{
#if ENABLE_LOGGING
using srt_logging::mglog;
using srt_logging::cnlog;
#endif
m_SndKmLastTime = CTimer::getTime();
m_SndKmLastTime = sync::steady_clock::now();
if (runtime)
{
m_SndKmMsg[ki].iPeerRetry--;
HLOGC(mglog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " retry=" << m_SndKmMsg[ki].iPeerRetry);
HLOGC(cnlog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " retry=" << m_SndKmMsg[ki].iPeerRetry);
}
else
{
HLOGC(mglog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " STILL IN USE.");
HLOGC(cnlog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " STILL IN USE.");
}
}
@ -191,25 +198,24 @@ public:
return false;
}
CCryptoControl(CUDT* parent, SRTSOCKET id);
CCryptoControl(SRTSOCKET id);
// DEBUG PURPOSES:
std::string CONID() const;
std::string FormatKmMessage(std::string hdr, int cmd, size_t srtlen);
bool init(HandshakeSide, bool);
bool init(HandshakeSide, const CSrtConfig&, bool);
void close();
// This function is used in:
// - HSv4 (initial key material exchange - in HSv5 it's attached to handshake)
// - case of key regeneration, which should be then exchanged again
void sendKeysToPeer(Whether2RegenKm regen);
/// @return True if the handshake is in progress.
/// This function is used in:
/// - HSv4 (initial key material exchange - in HSv5 it's attached to handshake)
/// - case of key regeneration, which should be then exchanged again.
void sendKeysToPeer(CUDT* sock, int iSRTT, Whether2RegenKm regen);
void setCryptoSecret(const HaiCrypt_Secret& secret)
{
m_KmSecret = secret;
//memcpy(&m_KmSecret, &secret, sizeof(m_KmSecret));
}
void setCryptoKeylen(size_t keylen)
@ -218,7 +224,7 @@ public:
m_iRcvKmKeyLen = keylen;
}
bool createCryptoCtx(ref_t<HaiCrypt_Handle> rh, size_t keylen, HaiCrypt_CryptoDir tx);
bool createCryptoCtx(size_t keylen, HaiCrypt_CryptoDir tx, HaiCrypt_Handle& rh);
int getSndCryptoFlags() const
{
@ -253,16 +259,18 @@ public:
/// the encryption will fail.
/// XXX Encryption flags in the PH_MSGNO
/// field in the header must be correctly set before calling.
EncryptionStatus encrypt(ref_t<CPacket> r_packet);
EncryptionStatus encrypt(CPacket& w_packet);
/// Decrypts the packet. If the packet has ENCKEYSPEC part
/// in PH_MSGNO set to EK_NOENC, it does nothing. It decrypts
/// only if the encryption correctly configured, otherwise it
/// fails. After successful decryption, the ENCKEYSPEC part
// in PH_MSGNO is set to EK_NOENC.
EncryptionStatus decrypt(ref_t<CPacket> r_packet);
EncryptionStatus decrypt(CPacket& w_packet);
~CCryptoControl();
};
} // namespace srt
#endif // SRT_CONGESTION_CONTROL_H

View file

@ -50,37 +50,35 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifdef LINUX
#include <sys/epoll.h>
#include <unistd.h>
#endif
#if __APPLE__
#include "TargetConditionals.h"
#endif
#if defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#endif
#if defined(__ANDROID__) || defined(ANDROID)
#include <sys/select.h>
#endif
#define SRT_IMPORT_EVENT
#include "platform_sys.h"
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <iterator>
#if defined(__FreeBSD_kernel__)
#include <sys/event.h>
#endif
#include "common.h"
#include "epoll.h"
#include "logging.h"
#include "udt.h"
using namespace std;
using namespace srt::sync;
#if ENABLE_HEAVY_LOGGING
namespace srt {
static ostream& PrintEpollEvent(ostream& os, int events, int et_events = 0);
}
#endif
namespace srt_logging
{
extern Logger mglog;
extern Logger eilog, ealog;
}
using namespace srt_logging;
@ -89,20 +87,21 @@ using namespace srt_logging;
#define IF_DIRNAME(tested, flag, name) (tested & flag ? name : "")
#endif
CEPoll::CEPoll():
srt::CEPoll::CEPoll():
m_iIDSeed(0)
{
CGuard::createMutex(m_EPollLock);
// Exception -> CUDTUnited ctor.
setupMutex(m_EPollLock, "EPoll");
}
CEPoll::~CEPoll()
srt::CEPoll::~CEPoll()
{
CGuard::releaseMutex(m_EPollLock);
releaseMutex(m_EPollLock);
}
int CEPoll::create()
int srt::CEPoll::create(CEPollDesc** pout)
{
CGuard pg(m_EPollLock);
ScopedLock pg(m_EPollLock);
if (++ m_iIDSeed >= 0x7FFFFFFF)
m_iIDSeed = 0;
@ -114,7 +113,31 @@ int CEPoll::create()
int localid = 0;
#ifdef LINUX
localid = epoll_create(1024);
// NOTE: epoll_create1() and EPOLL_CLOEXEC were introduced in GLIBC-2.9.
// So earlier versions of GLIBC, must use epoll_create() and set
// FD_CLOEXEC on the file descriptor returned by it after the fact.
#if defined(EPOLL_CLOEXEC)
int flags = 0;
#if ENABLE_SOCK_CLOEXEC
flags |= EPOLL_CLOEXEC;
#endif
localid = epoll_create1(flags);
#else
localid = epoll_create(1);
#if ENABLE_SOCK_CLOEXEC
if (localid != -1)
{
int fdFlags = fcntl(localid, F_GETFD);
if (fdFlags != -1)
{
fdFlags |= FD_CLOEXEC;
fcntl(localid, F_SETFD, fdFlags);
}
}
#endif
#endif
/* Possible reasons of -1 error:
EMFILE: The per-user limit on the number of epoll instances imposed by /proc/sys/fs/epoll/max_user_instances was encountered.
ENFILE: The system limit on the total number of open files has been reached.
@ -122,25 +145,82 @@ ENOMEM: There was insufficient memory to create the kernel object.
*/
if (localid < 0)
throw CUDTException(MJ_SETUP, MN_NONE, errno);
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#elif defined(BSD) || TARGET_OS_MAC
localid = kqueue();
if (localid < 0)
throw CUDTException(MJ_SETUP, MN_NONE, errno);
#else
// on Solaris, use /dev/poll
// TODO: Solaris, use port_getn()
// https://docs.oracle.com/cd/E86824_01/html/E54766/port-get-3c.html
// on Windows, select
#endif
pair<map<int, CEPollDesc>::iterator, bool> res = m_mPolls.insert(make_pair(m_iIDSeed, CEPollDesc(m_iIDSeed, localid)));
if (!res.second) // Insertion failed (no memory?)
throw CUDTException(MJ_SETUP, MN_NONE);
if (pout)
*pout = &res.first->second;
return m_iIDSeed;
}
int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
int srt::CEPoll::clear_usocks(int eid)
{
CGuard pg(m_EPollLock);
// This should remove all SRT sockets from given eid.
ScopedLock pg (m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
CEPollDesc& d = p->second;
d.clearAll();
return 0;
}
void srt::CEPoll::clear_ready_usocks(CEPollDesc& d, int direction)
{
if ((direction & ~SRT_EPOLL_EVENTTYPES) != 0)
{
// This is internal function, so simply report an IPE on incorrect usage.
LOGC(eilog.Error, log << "CEPoll::clear_ready_usocks: IPE, event flags exceed event types: " << direction);
return;
}
ScopedLock pg (m_EPollLock);
vector<SRTSOCKET> cleared;
CEPollDesc::enotice_t::iterator i = d.enotice_begin();
while (i != d.enotice_end())
{
IF_HEAVY_LOGGING(SRTSOCKET subsock = i->fd);
SRTSOCKET rs = d.clearEventSub(i++, direction);
// This function returns:
// - a valid socket - if there are no other subscription after 'direction' was cleared
// - SRT_INVALID_SOCK otherwise
// Valid sockets should be collected as sockets that no longer
// have a subscribed event should be deleted from subscriptions.
if (rs != SRT_INVALID_SOCK)
{
HLOGC(eilog.Debug, log << "CEPoll::clear_ready_usocks: @" << rs << " got all subscription cleared");
cleared.push_back(rs);
}
else
{
HLOGC(eilog.Debug, log << "CEPoll::clear_ready_usocks: @" << subsock << " is still subscribed");
}
}
for (size_t i = 0; i < cleared.size(); ++i)
d.removeSubscription(cleared[i]);
}
int srt::CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
{
ScopedLock pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
@ -155,18 +235,18 @@ int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
else
{
ev.events = 0;
if (*events & UDT_EPOLL_IN)
if (*events & SRT_EPOLL_IN)
ev.events |= EPOLLIN;
if (*events & UDT_EPOLL_OUT)
if (*events & SRT_EPOLL_OUT)
ev.events |= EPOLLOUT;
if (*events & UDT_EPOLL_ERR)
if (*events & SRT_EPOLL_ERR)
ev.events |= EPOLLERR;
}
ev.data.fd = s;
if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_ADD, s, &ev) < 0)
throw CUDTException();
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#elif defined(BSD) || TARGET_OS_MAC
struct kevent ke[2];
int num = 0;
@ -177,11 +257,11 @@ int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
}
else
{
if (*events & UDT_EPOLL_IN)
if (*events & SRT_EPOLL_IN)
{
EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL);
}
if (*events & UDT_EPOLL_OUT)
if (*events & SRT_EPOLL_OUT)
{
EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
}
@ -190,6 +270,10 @@ int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
throw CUDTException();
#else
// fake use 'events' to prevent warning. Remove when implemented.
(void)events;
(void)s;
#ifdef _MSC_VER
// Microsoft Visual Studio doesn't support the #warning directive - nonstandard anyway.
// Use #pragma message with the same text.
@ -206,9 +290,9 @@ int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
return 0;
}
int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s)
int srt::CEPoll::remove_ssock(const int eid, const SYSSOCKET& s)
{
CGuard pg(m_EPollLock);
ScopedLock pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
@ -218,7 +302,7 @@ int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s)
epoll_event ev; // ev is ignored, for compatibility with old Linux kernel only.
if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_DEL, s, &ev) < 0)
throw CUDTException();
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#elif defined(BSD) || TARGET_OS_MAC
struct kevent ke;
//
@ -237,9 +321,10 @@ int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s)
}
// Need this to atomically modify polled events (ex: remove write/keep read)
int CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
int srt::CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
{
CGuard pg(m_EPollLock);
ScopedLock pg(m_EPollLock);
IF_HEAVY_LOGGING(ostringstream evd);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
@ -250,9 +335,12 @@ int CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
int32_t evts = events ? *events : uint32_t(SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR);
bool edgeTriggered = evts & SRT_EPOLL_ET;
evts &= ~SRT_EPOLL_ET;
// et_evts = all events, if SRT_EPOLL_ET, or only those that are always ET otherwise.
int32_t et_evts = edgeTriggered ? evts : evts & SRT_EPOLL_ETONLY;
if (evts)
{
pair<CEPollDesc::ewatch_t::iterator, bool> iter_new = d.addWatch(u, evts, edgeTriggered);
pair<CEPollDesc::ewatch_t::iterator, bool> iter_new = d.addWatch(u, evts, et_evts);
CEPollDesc::Wait& wait = iter_new.first->second;
if (!iter_new.second)
{
@ -260,6 +348,7 @@ int CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
// parameter, but others are probably unchanged. Change them
// forcefully and take out notices that are no longer valid.
const int removable = wait.watch & ~evts;
IF_HEAVY_LOGGING(PrintEpollEvent(evd, evts & (~wait.watch)));
// Check if there are any events that would be removed.
// If there are no removed events watched (for example, when
@ -272,11 +361,16 @@ int CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
// Update the watch configuration, including edge
wait.watch = evts;
if (edgeTriggered)
wait.edge = evts;
wait.edge = et_evts;
// Now it should look exactly like newly added
// and the state is also updated
HLOGC(ealog.Debug, log << "srt_epoll_update_usock: UPDATED E" << eid << " for @" << u << " +" << evd.str());
}
else
{
IF_HEAVY_LOGGING(PrintEpollEvent(evd, evts));
HLOGC(ealog.Debug, log << "srt_epoll_update_usock: ADDED E" << eid << " for @" << u << " " << evd.str());
}
const int newstate = wait.watch & wait.state;
@ -287,20 +381,21 @@ int CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
}
else if (edgeTriggered)
{
// Specified only SRT_EPOLL_ET flag, but no event flag. Error.
LOGC(ealog.Error, log << "srt_epoll_update_usock: Specified only SRT_EPOLL_ET flag, but no event flag. Error.");
throw CUDTException(MJ_NOTSUP, MN_INVAL);
}
else
{
// Update with no events means to remove subscription
HLOGC(ealog.Debug, log << "srt_epoll_update_usock: REMOVED E" << eid << " socket @" << u);
d.removeSubscription(u);
}
return 0;
}
int CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events)
int srt::CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events)
{
CGuard pg(m_EPollLock);
ScopedLock pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
@ -315,18 +410,18 @@ int CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events)
else
{
ev.events = 0;
if (*events & UDT_EPOLL_IN)
if (*events & SRT_EPOLL_IN)
ev.events |= EPOLLIN;
if (*events & UDT_EPOLL_OUT)
if (*events & SRT_EPOLL_OUT)
ev.events |= EPOLLOUT;
if (*events & UDT_EPOLL_ERR)
if (*events & SRT_EPOLL_ERR)
ev.events |= EPOLLERR;
}
ev.data.fd = s;
if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_MOD, s, &ev) < 0)
throw CUDTException();
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#elif defined(BSD) || TARGET_OS_MAC
struct kevent ke[2];
int num = 0;
@ -345,17 +440,23 @@ int CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events)
}
else
{
if (*events & UDT_EPOLL_IN)
if (*events & SRT_EPOLL_IN)
{
EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL);
}
if (*events & UDT_EPOLL_OUT)
if (*events & SRT_EPOLL_OUT)
{
EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
}
}
if (kevent(p->second.m_iLocalID, ke, num, NULL, 0, NULL) < 0)
throw CUDTException();
#else
// fake use 'events' to prevent warning. Remove when implemented.
(void)events;
(void)s;
#endif
// Assuming add is used if not inserted
// p->second.m_sLocals.insert(s);
@ -363,9 +464,9 @@ int CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events)
return 0;
}
int CEPoll::setflags(const int eid, int32_t flags)
int srt::CEPoll::setflags(const int eid, int32_t flags)
{
CGuard pg(m_EPollLock);
ScopedLock pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
@ -388,7 +489,7 @@ int CEPoll::setflags(const int eid, int32_t flags)
return oflags;
}
int CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut)
int srt::CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut)
{
// It is allowed to call this function witn fdsSize == 0
// and therefore also NULL fdsSet. This will then only report
@ -396,12 +497,12 @@ int CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t m
if (fdsSize < 0 || (fdsSize > 0 && !fdsSet))
throw CUDTException(MJ_NOTSUP, MN_INVAL);
int64_t entertime = CTimer::getTime();
steady_clock::time_point entertime = steady_clock::now();
while (true)
{
{
CGuard pg(m_EPollLock);
ScopedLock pg(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
@ -410,7 +511,7 @@ int CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t m
if (!ed.flags(SRT_EPOLL_ENABLE_EMPTY) && ed.watch_empty())
{
// Empty EID is not allowed, report error.
throw CUDTException(MJ_NOTSUP, MN_INVAL);
throw CUDTException(MJ_NOTSUP, MN_EEMPTY);
}
if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK) && (fdsSet == NULL || fdsSize == 0))
@ -444,16 +545,16 @@ int CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t m
return total;
}
if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * int64_t(1000)))
if ((msTimeOut >= 0) && (count_microseconds(srt::sync::steady_clock::now() - entertime) >= msTimeOut * int64_t(1000)))
break; // official wait does: throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
CTimer::waitForEvent();
CGlobEvent::waitForEvent();
}
return 0;
}
int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefds, int64_t msTimeOut, set<SYSSOCKET>* lrfds, set<SYSSOCKET>* lwfds)
int srt::CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefds, int64_t msTimeOut, set<SYSSOCKET>* lrfds, set<SYSSOCKET>* lwfds)
{
// if all fields is NULL and waiting time is infinite, then this would be a deadlock
if (!readfds && !writefds && !lrfds && !lwfds && (msTimeOut < 0))
@ -467,18 +568,16 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
int total = 0;
int64_t entertime = CTimer::getTime();
HLOGC(mglog.Debug, log << "CEPoll::wait: START for eid=" << eid);
srt::sync::steady_clock::time_point entertime = srt::sync::steady_clock::now();
while (true)
{
{
CGuard epollock(m_EPollLock);
ScopedLock epollock(m_EPollLock);
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
if (p == m_mPolls.end())
{
LOGC(ealog.Error, log << "EID:" << eid << " INVALID.");
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
}
@ -487,7 +586,9 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
if (!ed.flags(SRT_EPOLL_ENABLE_EMPTY) && ed.watch_empty() && ed.m_sLocals.empty())
{
// Empty EID is not allowed, report error.
throw CUDTException(MJ_NOTSUP, MN_INVAL);
//throw CUDTException(MJ_NOTSUP, MN_INVAL);
LOGC(ealog.Error, log << "EID:" << eid << " no sockets to check, this would deadlock");
throw CUDTException(MJ_NOTSUP, MN_EEMPTY, 0);
}
if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK))
@ -507,13 +608,13 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
{
++it_next;
IF_HEAVY_LOGGING(++total_noticed);
if (readfds && ((it->events & UDT_EPOLL_IN) || (it->events & UDT_EPOLL_ERR)))
if (readfds && ((it->events & SRT_EPOLL_IN) || (it->events & SRT_EPOLL_ERR)))
{
if (readfds->insert(it->fd).second)
++total;
}
if (writefds && ((it->events & UDT_EPOLL_OUT) || (it->events & UDT_EPOLL_ERR)))
if (writefds && ((it->events & SRT_EPOLL_OUT) || (it->events & SRT_EPOLL_ERR)))
{
if (writefds->insert(it->fd).second)
++total;
@ -530,13 +631,14 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
}
}
HLOGC(mglog.Debug, log << "CEPoll::wait: REPORTED " << total << "/" << total_noticed
HLOGC(ealog.Debug, log << "CEPoll::wait: REPORTED " << total << "/" << total_noticed
<< debug_sockets.str());
if (lrfds || lwfds)
if ((lrfds || lwfds) && !ed.m_sLocals.empty())
{
#ifdef LINUX
const int max_events = ed.m_sLocals.size();
SRT_ASSERT(max_events > 0);
epoll_event ev[max_events];
int nfds = ::epoll_wait(ed.m_iLocalID, ev, max_events, 0);
@ -554,11 +656,12 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
++ total;
}
}
HLOGC(mglog.Debug, log << "CEPoll::wait: LINUX: picking up " << (total - prev_total) << " ready fds.");
HLOGC(ealog.Debug, log << "CEPoll::wait: LINUX: picking up " << (total - prev_total) << " ready fds.");
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#elif defined(BSD) || TARGET_OS_MAC
struct timespec tmout = {0, 0};
const int max_events = ed.m_sLocals.size();
SRT_ASSERT(max_events > 0);
struct kevent ke[max_events];
int nfds = kevent(ed.m_iLocalID, NULL, 0, ke, max_events, &tmout);
@ -578,7 +681,7 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
}
}
HLOGC(mglog.Debug, log << "CEPoll::wait: Darwin/BSD: picking up " << (total - prev_total) << " ready fds.");
HLOGC(ealog.Debug, log << "CEPoll::wait: Darwin/BSD: picking up " << (total - prev_total) << " ready fds.");
#else
//currently "select" is used for all non-Linux platforms.
@ -623,34 +726,127 @@ int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefd
}
}
HLOGC(mglog.Debug, log << "CEPoll::wait: select(otherSYS): picking up " << (total - prev_total) << " ready fds.");
HLOGC(ealog.Debug, log << "CEPoll::wait: select(otherSYS): picking up " << (total - prev_total) << " ready fds.");
#endif
}
} // END-LOCK: m_EPollLock
HLOGC(mglog.Debug, log << "CEPoll::wait: Total of " << total << " READY SOCKETS");
HLOGC(ealog.Debug, log << "CEPoll::wait: Total of " << total << " READY SOCKETS");
if (total > 0)
return total;
if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * int64_t(1000)))
if ((msTimeOut >= 0) && (count_microseconds(srt::sync::steady_clock::now() - entertime) >= msTimeOut * int64_t(1000)))
{
HLOGP(mglog.Debug, "... not waiting longer - timeout");
HLOGC(ealog.Debug, log << "EID:" << eid << ": TIMEOUT.");
throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
}
CTimer::EWait wt ATR_UNUSED = CTimer::waitForEvent();
HLOGC(mglog.Debug, log << "CEPoll::wait: EVENT WAITING: "
<< (wt == CTimer::WT_TIMEOUT ? "CHECKPOINT" : wt == CTimer::WT_EVENT ? "TRIGGERED" : "ERROR"));
const bool wait_signaled SRT_ATR_UNUSED = CGlobEvent::waitForEvent();
HLOGC(ealog.Debug, log << "CEPoll::wait: EVENT WAITING: "
<< (wait_signaled ? "TRIGGERED" : "CHECKPOINT"));
}
return 0;
}
int CEPoll::release(const int eid)
int srt::CEPoll::swait(CEPollDesc& d, map<SRTSOCKET, int>& st, int64_t msTimeOut, bool report_by_exception)
{
CGuard pg(m_EPollLock);
{
ScopedLock lg (m_EPollLock);
if (!d.flags(SRT_EPOLL_ENABLE_EMPTY) && d.watch_empty() && msTimeOut < 0)
{
// no socket is being monitored, this may be a deadlock
LOGC(ealog.Error, log << "EID:" << d.m_iID << " no sockets to check, this would deadlock");
if (report_by_exception)
throw CUDTException(MJ_NOTSUP, MN_EEMPTY, 0);
return -1;
}
}
st.clear();
steady_clock::time_point entertime = steady_clock::now();
while (true)
{
{
// Not extracting separately because this function is
// for internal use only and we state that the eid could
// not be deleted or changed the target CEPollDesc in the
// meantime.
// Here we only prevent the pollset be updated simultaneously
// with unstable reading.
ScopedLock lg (m_EPollLock);
if (!d.flags(SRT_EPOLL_ENABLE_EMPTY) && d.watch_empty())
{
// Empty EID is not allowed, report error.
throw CUDTException(MJ_NOTSUP, MN_EEMPTY);
}
if (!d.m_sLocals.empty())
{
// XXX Add error log
// uwait should not be used with EIDs subscribed to system sockets
throw CUDTException(MJ_NOTSUP, MN_INVAL);
}
bool empty = d.enotice_empty();
if (!empty || msTimeOut == 0)
{
IF_HEAVY_LOGGING(ostringstream singles);
// If msTimeOut == 0, it means that we need the information
// immediately, we don't want to wait. Therefore in this case
// report also when none is ready.
int total = 0; // This is a list, so count it during iteration
CEPollDesc::enotice_t::iterator i = d.enotice_begin();
while (i != d.enotice_end())
{
++total;
st[i->fd] = i->events;
IF_HEAVY_LOGGING(singles << "@" << i->fd << ":");
IF_HEAVY_LOGGING(PrintEpollEvent(singles, i->events, i->parent->edgeOnly()));
const bool edged SRT_ATR_UNUSED = d.checkEdge(i++); // NOTE: potentially deletes `i`
IF_HEAVY_LOGGING(singles << (edged ? "<^> " : " "));
}
// Logging into 'singles' because it notifies as to whether
// the edge-triggered event has been cleared
HLOGC(ealog.Debug, log << "E" << d.m_iID << " rdy=" << total << ": "
<< singles.str()
<< " TRACKED: " << d.DisplayEpollWatch());
return total;
}
// Don't report any updates because this check happens
// extremely often.
}
if ((msTimeOut >= 0) && ((steady_clock::now() - entertime) >= microseconds_from(msTimeOut * int64_t(1000))))
{
HLOGC(ealog.Debug, log << "EID:" << d.m_iID << ": TIMEOUT.");
if (report_by_exception)
throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
return 0; // meaning "none is ready"
}
CGlobEvent::waitForEvent();
}
return 0;
}
bool srt::CEPoll::empty(const CEPollDesc& d) const
{
ScopedLock lg (m_EPollLock);
return d.watch_empty();
}
int srt::CEPoll::release(const int eid)
{
ScopedLock pg(m_EPollLock);
map<int, CEPollDesc>::iterator i = m_mPolls.find(eid);
if (i == m_mPolls.end())
@ -659,7 +855,7 @@ int CEPoll::release(const int eid)
#ifdef LINUX
// release local/system epoll descriptor
::close(i->second.m_iLocalID);
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
#elif defined(BSD) || TARGET_OS_MAC
::close(i->second.m_iLocalID);
#endif
@ -669,16 +865,29 @@ int CEPoll::release(const int eid)
}
int CEPoll::update_events(const SRTSOCKET& uid, std::set<int>& eids, const int events, const bool enable)
int srt::CEPoll::update_events(const SRTSOCKET& uid, std::set<int>& eids, const int events, const bool enable)
{
// As event flags no longer contain only event types, check now.
if ((events & ~SRT_EPOLL_EVENTTYPES) != 0)
{
LOGC(eilog.Fatal, log << "epoll/update: IPE: 'events' parameter shall not contain special flags!");
return -1; // still, ignored.
}
int nupdated = 0;
vector<int> lost;
CGuard pg(m_EPollLock);
IF_HEAVY_LOGGING(ostringstream debug);
IF_HEAVY_LOGGING(debug << "epoll/update: @" << uid << " " << (enable ? "+" : "-"));
IF_HEAVY_LOGGING(PrintEpollEvent(debug, events));
ScopedLock pg (m_EPollLock);
for (set<int>::iterator i = eids.begin(); i != eids.end(); ++ i)
{
map<int, CEPollDesc>::iterator p = m_mPolls.find(*i);
if (p == m_mPolls.end())
{
HLOGC(eilog.Note, log << "epoll/update: E" << *i << " was deleted in the meantime");
// EID invalid, though still present in the socket's subscriber list
// (dangling in the socket). Postpone to fix the subscruption and continue.
lost.push_back(*i);
@ -692,9 +901,12 @@ int CEPoll::update_events(const SRTSOCKET& uid, std::set<int>& eids, const int e
if (!pwait)
{
// As this is mapped in the socket's data, it should be impossible.
LOGC(eilog.Error, log << "epoll/update: IPE: update struck E"
<< (*i) << " which is NOT SUBSCRIBED to @" << uid);
continue;
}
IF_HEAVY_LOGGING(string tracking = " TRACKING: " + ed.DisplayEpollWatch());
// compute new states
// New state to be set into the permanent state
@ -704,13 +916,21 @@ int CEPoll::update_events(const SRTSOCKET& uid, std::set<int>& eids, const int e
// compute states changes!
int changes = pwait->state ^ newstate; // oldState XOR newState
if (!changes)
{
HLOGC(eilog.Debug, log << debug.str() << ": E" << (*i)
<< tracking << " NOT updated: no changes");
continue; // no changes!
}
// assign new state
pwait->state = newstate;
// filter change relating what is watching
changes &= pwait->watch;
if (!changes)
{
HLOGC(eilog.Debug, log << debug.str() << ": E" << (*i)
<< tracking << " NOT updated: not subscribed");
continue; // no change watching
}
// set events changes!
// This function will update the notice object associated with
@ -718,10 +938,75 @@ int CEPoll::update_events(const SRTSOCKET& uid, std::set<int>& eids, const int e
// - if enable, it will set event flags, possibly in a new notice object
// - if !enable, it will clear event flags, possibly remove notice if resulted in 0
ed.updateEventNotice(*pwait, uid, events, enable);
++nupdated;
HLOGC(eilog.Debug, log << debug.str() << ": E" << (*i)
<< " TRACKING: " << ed.DisplayEpollWatch());
}
for (vector<int>::iterator i = lost.begin(); i != lost.end(); ++ i)
eids.erase(*i);
return 0;
return nupdated;
}
// Debug use only.
#if ENABLE_HEAVY_LOGGING
namespace srt
{
static ostream& PrintEpollEvent(ostream& os, int events, int et_events)
{
static pair<int, const char*> const namemap [] = {
make_pair(SRT_EPOLL_IN, "R"),
make_pair(SRT_EPOLL_OUT, "W"),
make_pair(SRT_EPOLL_ERR, "E"),
make_pair(SRT_EPOLL_UPDATE, "U")
};
int N = Size(namemap);
for (int i = 0; i < N; ++i)
{
if (events & namemap[i].first)
{
os << "[";
if (et_events & namemap[i].first)
os << "^";
os << namemap[i].second << "]";
}
}
return os;
}
string DisplayEpollResults(const std::map<SRTSOCKET, int>& sockset)
{
typedef map<SRTSOCKET, int> fmap_t;
ostringstream os;
for (fmap_t::const_iterator i = sockset.begin(); i != sockset.end(); ++i)
{
os << "@" << i->first << ":";
PrintEpollEvent(os, i->second);
os << " ";
}
return os.str();
}
string CEPollDesc::DisplayEpollWatch()
{
ostringstream os;
for (ewatch_t::const_iterator i = m_USockWatchState.begin(); i != m_USockWatchState.end(); ++i)
{
os << "@" << i->first << ":";
PrintEpollEvent(os, i->second.watch, i->second.edge);
os << " ";
}
return os.str();
}
} // namespace srt
#endif

269
trunk/3rdparty/srt-1-fit/srtcore/epoll.h vendored Executable file → Normal file
View file

@ -50,8 +50,8 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_EPOLL_H__
#define __UDT_EPOLL_H__
#ifndef INC_SRT_EPOLL_H
#define INC_SRT_EPOLL_H
#include <map>
@ -59,8 +59,15 @@ modified by
#include <list>
#include "udt.h"
namespace srt
{
struct CEPollDesc
class CUDT;
class CRendezvousQueue;
class CUDTGroup;
class CEPollDesc
{
const int m_iID; // epoll ID
@ -86,40 +93,62 @@ struct CEPollDesc
{
/// Events the subscriber is interested with. Only those will be
/// regarded when updating event flags.
int watch;
int32_t watch;
/// Which events should be edge-triggered. When the event isn't
/// mentioned in `watch`, this bit flag is disregarded. Otherwise
/// it means that the event is to be waited for persistent state
/// if this flag is not present here, and for edge trigger, if
/// the flag is present here.
int edge;
int32_t edge;
/// The current persistent state. This is usually duplicated in
/// a dedicated state object in `m_USockEventNotice`, however the state
/// here will stay forever as is, regardless of the edge/persistent
/// subscription mode for the event.
int state;
int32_t state;
/// The iterator to `m_USockEventNotice` container that contains the
/// event notice object for this subscription, or the value from
/// `nullNotice()` if there is no such object.
enotice_t::iterator notit;
Wait(int sub, bool etr, enotice_t::iterator i)
Wait(explicit_t<int32_t> sub, explicit_t<int32_t> etr, enotice_t::iterator i)
:watch(sub)
,edge(etr ? sub : 0)
,edge(etr)
,state(0)
,notit(i)
{
}
int edgeOnly() { return edge & watch; }
/// Clear all flags for given direction from the notices
/// and subscriptions, and checks if this made the event list
/// for this watch completely empty.
/// @param direction event type that has to be cleared
/// @return true, if this cleared the last event (the caller
/// want to remove the subscription for this socket)
bool clear(int32_t direction)
{
if (watch & direction)
{
watch &= ~direction;
edge &= ~direction;
state &= ~direction;
return watch == 0;
}
return false;
}
};
typedef std::map<SRTSOCKET, Wait> ewatch_t;
private:
#if ENABLE_HEAVY_LOGGING
std::string DisplayEpollWatch();
#endif
/// Sockets that are subscribed for events in this eid.
ewatch_t m_USockWatchState;
@ -135,7 +164,10 @@ private:
enotice_t::iterator nullNotice() { return m_USockEventNotice.end(); }
public:
// Only CEPoll class should have access to it.
// Guarding private access to the class is not necessary
// within the epoll module.
friend class CEPoll;
CEPollDesc(int id, int localID)
: m_iID(id)
@ -147,13 +179,13 @@ public:
static const int32_t EF_NOCHECK_EMPTY = 1 << 0;
static const int32_t EF_CHECK_REP = 1 << 1;
int32_t flags() { return m_Flags; }
bool flags(int32_t f) { return (m_Flags & f) != 0; }
int32_t flags() const { return m_Flags; }
bool flags(int32_t f) const { return (m_Flags & f) != 0; }
void set_flags(int32_t flg) { m_Flags |= flg; }
void clr_flags(int32_t flg) { m_Flags &= ~flg; }
// Container accessors for ewatch_t.
bool watch_empty() { return m_USockWatchState.empty(); }
bool watch_empty() const { return m_USockWatchState.empty(); }
Wait* watch_find(SRTSOCKET sock)
{
ewatch_t::iterator i = m_USockWatchState.find(sock);
@ -165,13 +197,16 @@ public:
// Container accessors for enotice_t.
enotice_t::iterator enotice_begin() { return m_USockEventNotice.begin(); }
enotice_t::iterator enotice_end() { return m_USockEventNotice.end(); }
enotice_t::const_iterator enotice_begin() const { return m_USockEventNotice.begin(); }
enotice_t::const_iterator enotice_end() const { return m_USockEventNotice.end(); }
bool enotice_empty() const { return m_USockEventNotice.empty(); }
const int m_iLocalID; // local system epoll ID
std::set<SYSSOCKET> m_sLocals; // set of local (non-UDT) descriptors
std::pair<ewatch_t::iterator, bool> addWatch(SRTSOCKET sock, int32_t events, bool edgeTrg)
std::pair<ewatch_t::iterator, bool> addWatch(SRTSOCKET sock, explicit_t<int32_t> events, explicit_t<int32_t> et_events)
{
return m_USockWatchState.insert(std::make_pair(sock, Wait(events, edgeTrg, nullNotice())));
return m_USockWatchState.insert(std::make_pair(sock, Wait(events, et_events, nullNotice())));
}
void addEventNotice(Wait& wait, SRTSOCKET sock, int events)
@ -224,6 +259,12 @@ public:
m_USockWatchState.erase(i);
}
void clearAll()
{
m_USockEventNotice.clear();
m_USockWatchState.clear();
}
void removeExistingNotices(Wait& wait)
{
m_USockEventNotice.erase(wait.notit);
@ -281,12 +322,44 @@ public:
}
return false;
}
/// This should work in a loop around the notice container of
/// the given eid container and clear out the notice for
/// particular event type. If this has cleared effectively the
/// last existing event, it should return the socket id
/// so that the caller knows to remove it also from subscribers.
///
/// @param i iterator in the notice container
/// @param event event type to be cleared
/// @retval (socket) Socket to be removed from subscriptions
/// @retval SRT_INVALID_SOCK Nothing to be done (associated socket
/// still has other subscriptions)
SRTSOCKET clearEventSub(enotice_t::iterator i, int event)
{
// We need to remove the notice and subscription
// for this event. The 'i' iterator is safe to
// delete, even indirectly.
// This works merely like checkEdge, just on request to clear the
// identified event, if found.
if (i->events & event)
{
// The notice has a readiness flag on this event.
// This means that there exists also a subscription.
Wait* w = i->parent;
if (w->clear(event))
return i->fd;
}
return SRT_INVALID_SOCK;
}
};
class CEPoll
{
friend class CUDT;
friend class CRendezvousQueue;
friend class srt::CUDT;
friend class srt::CUDTGroup;
friend class srt::CRendezvousQueue;
public:
CEPoll();
@ -294,90 +367,122 @@ public:
public: // for CUDTUnited API
/// create a new EPoll.
/// @return new EPoll ID if success, otherwise an error number.
/// create a new EPoll.
/// @return new EPoll ID if success, otherwise an error number.
int create(CEPollDesc** ppd = 0);
int create();
/// add a UDT socket to an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT Socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
/// delete all user sockets (SRT sockets) from an EPoll
/// @param [in] eid EPoll ID.
/// @return 0
int clear_usocks(int eid);
int add_usock(const int eid, const SRTSOCKET& u, const int* events = NULL) { return update_usock(eid, u, events); }
/// add a system socket to an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] s system Socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
/// add a system socket to an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] s system Socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
int add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL);
/// remove a UDT socket event from an EPoll; socket will be removed if no events to watch.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @return 0 if success, otherwise an error number.
int remove_usock(const int eid, const SRTSOCKET& u) { static const int Null(0); return update_usock(eid, u, &Null);}
/// remove a system socket event from an EPoll; socket will be removed if no events to watch.
/// @param [in] eid EPoll ID.
/// @param [in] s system socket ID.
/// @return 0 if success, otherwise an error number.
/// remove a system socket event from an EPoll; socket will be removed if no events to watch.
/// @param [in] eid EPoll ID.
/// @param [in] s system socket ID.
/// @return 0 if success, otherwise an error number.
int remove_ssock(const int eid, const SYSSOCKET& s);
/// update a UDT socket events from an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
/// update a UDT socket events from an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
int update_usock(const int eid, const SRTSOCKET& u, const int* events);
/// update a system socket events from an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
/// update a system socket events from an EPoll.
/// @param [in] eid EPoll ID.
/// @param [in] u UDT socket ID.
/// @param [in] events events to watch.
/// @return 0 if success, otherwise an error number.
int update_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL);
/// wait for EPoll events or timeout.
/// @param [in] eid EPoll ID.
/// @param [out] readfds UDT sockets available for reading.
/// @param [out] writefds UDT sockets available for writing.
/// @param [in] msTimeOut timeout threshold, in milliseconds.
/// @param [out] lrfds system file descriptors for reading.
/// @param [out] lwfds system file descriptors for writing.
/// @return number of sockets available for IO.
/// wait for EPoll events or timeout.
/// @param [in] eid EPoll ID.
/// @param [out] readfds UDT sockets available for reading.
/// @param [out] writefds UDT sockets available for writing.
/// @param [in] msTimeOut timeout threshold, in milliseconds.
/// @param [out] lrfds system file descriptors for reading.
/// @param [out] lwfds system file descriptors for writing.
/// @return number of sockets available for IO.
int wait(const int eid, std::set<SRTSOCKET>* readfds, std::set<SRTSOCKET>* writefds, int64_t msTimeOut, std::set<SYSSOCKET>* lrfds, std::set<SYSSOCKET>* lwfds);
/// wait for EPoll events or timeout optimized with explicit EPOLL_ERR event and the edge mode option.
/// @param [in] eid EPoll ID.
/// @param [out] fdsSet array of user socket events (SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR).
/// @param [int] fdsSize of fds array
/// @param [in] msTimeOut timeout threshold, in milliseconds.
/// @return total of available events in the epoll system (can be greater than fdsSize)
typedef std::map<SRTSOCKET, int> fmap_t;
/// Lightweit and more internal-reaching version of `uwait` for internal use only.
/// This function wait for sockets to be ready and reports them in `st` map.
///
/// @param d the internal structure of the epoll container
/// @param st output container for the results: { socket_type, event }
/// @param msTimeOut timeout after which return with empty output is allowed
/// @param report_by_exception if true, errors will result in exception intead of returning -1
/// @retval -1 error occurred
/// @retval >=0 number of ready sockets (actually size of `st`)
int swait(CEPollDesc& d, fmap_t& st, int64_t msTimeOut, bool report_by_exception = true);
/// Empty subscription check - for internal use only.
bool empty(const CEPollDesc& d) const;
/// Reports which events are ready on the given socket.
/// @param mp socket event map retirned by `swait`
/// @param sock which socket to ask
/// @return event flags for given socket, or 0 if none
static int ready(const fmap_t& mp, SRTSOCKET sock)
{
fmap_t::const_iterator y = mp.find(sock);
if (y == mp.end())
return 0;
return y->second;
}
/// Reports whether socket is ready for given event.
/// @param mp socket event map retirned by `swait`
/// @param sock which socket to ask
/// @param event which events it should be ready for
/// @return true if the given socket is ready for given event
static bool isready(const fmap_t& mp, SRTSOCKET sock, SRT_EPOLL_OPT event)
{
return (ready(mp, sock) & event) != 0;
}
// Could be a template directly, but it's now hidden in the imp file.
void clear_ready_usocks(CEPollDesc& d, int direction);
/// wait for EPoll events or timeout optimized with explicit EPOLL_ERR event and the edge mode option.
/// @param [in] eid EPoll ID.
/// @param [out] fdsSet array of user socket events (SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR).
/// @param [int] fdsSize of fds array
/// @param [in] msTimeOut timeout threshold, in milliseconds.
/// @return total of available events in the epoll system (can be greater than fdsSize)
int uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
/// close and release an EPoll.
/// @param [in] eid EPoll ID.
/// @return 0 if success, otherwise an error number.
/// close and release an EPoll.
/// @param [in] eid EPoll ID.
/// @return 0 if success, otherwise an error number.
int release(const int eid);
public: // for CUDT to acknowledge IO status
/// Update events available for a UDT socket.
/// @param [in] uid UDT socket ID.
/// @param [in] eids EPoll IDs to be set
/// @param [in] events Combination of events to update
/// @param [in] enable true -> enable, otherwise disable
/// @return 0 if success, otherwise an error number
/// Update events available for a UDT socket. At the end this function
/// counts the number of updated EIDs with given events.
/// @param [in] uid UDT socket ID.
/// @param [in] eids EPoll IDs to be set
/// @param [in] events Combination of events to update
/// @param [in] enable true -> enable, otherwise disable
/// @return -1 if invalid events, otherwise the number of changes
int update_events(const SRTSOCKET& uid, std::set<int>& eids, int events, bool enable);
@ -385,11 +490,17 @@ public: // for CUDT to acknowledge IO status
private:
int m_iIDSeed; // seed to generate a new ID
pthread_mutex_t m_SeedLock;
srt::sync::Mutex m_SeedLock;
std::map<int, CEPollDesc> m_mPolls; // all epolls
pthread_mutex_t m_EPollLock;
mutable srt::sync::Mutex m_EPollLock;
};
#if ENABLE_HEAVY_LOGGING
std::string DisplayEpollResults(const std::map<SRTSOCKET, int>& sockset);
#endif
} // namespace srt
#endif

File diff suppressed because it is too large Load diff

View file

@ -9,8 +9,8 @@
*/
#ifndef INC__SRT_FEC_H
#define INC__SRT_FEC_H
#ifndef INC_SRT_FEC_H
#define INC_SRT_FEC_H
#include <string>
#include <map>
@ -19,6 +19,8 @@
#include "packetfilter_api.h"
namespace srt {
class FECFilterBuiltin: public SrtPacketFilterBase
{
SrtFilterConfig cfg;
@ -68,6 +70,12 @@ public:
SINGLE // Horizontal-only with no recursion
};
static Type FlipType(Type t)
{
SRT_ASSERT(t != SINGLE);
return (t == HORIZ) ? VERT : HORIZ;
}
};
struct RcvGroup: Group
@ -183,21 +191,43 @@ private:
// Receiving
void CheckLargeDrop(int32_t seqno);
int ExtendRows(int rowx);
int ExtendColumns(int colgx);
void MarkCellReceived(int32_t seq);
bool HangHorizontal(const CPacket& pkt, bool fec_ctl, loss_seqs_t& irrecover);
bool HangVertical(const CPacket& pkt, signed char fec_colx, loss_seqs_t& irrecover);
size_t ExtendRows(size_t rowx);
size_t ExtendColumns(size_t colgx);
enum ECellReceived
{
CELL_RECEIVED, //< mark cell for a received packet (no matter current value)
CELL_EXTEND, //< just make sure there's a place for a packet, set false if not
CELL_REMOVE //< even if a packet was marked true, remove the cell existence confirmation
};
void MarkCellReceived(int32_t seq, ECellReceived recv = CELL_RECEIVED);
enum EHangStatus
{
HANG_NOTDONE,
HANG_SUCCESS,
HANG_PAST,
HANG_CRAZY
};
friend bool operator <(FECFilterBuiltin::EHangStatus a, FECFilterBuiltin::EHangStatus b)
{
return int(a) < int(b);
}
EHangStatus HangHorizontal(const CPacket& pkt, bool fec_ctl, loss_seqs_t& irrecover);
EHangStatus HangVertical(const CPacket& pkt, signed char fec_colx, loss_seqs_t& irrecover);
void ClipControlPacket(Group& g, const CPacket& pkt);
void ClipRebuiltPacket(Group& g, Receive::PrivPacket& pkt);
void RcvRebuild(Group& g, int32_t seqno, Group::Type tp);
int32_t RcvGetLossSeqHoriz(Group& g);
int32_t RcvGetLossSeqVert(Group& g);
void EmergencyShrink(size_t n_series);
static void TranslateLossRecords(const std::set<int32_t>& loss, loss_seqs_t& irrecover);
void RcvCheckDismissColumn(int32_t seqno, int colgx, loss_seqs_t& irrecover);
int RcvGetRowGroupIndex(int32_t seq);
int RcvGetColumnGroupIndex(int32_t seq);
int RcvGetRowGroupIndex(int32_t seq, EHangStatus& w_status);
int RcvGetColumnGroupIndex(int32_t seqno, EHangStatus& w_status);
void CollectIrrecoverRow(RcvGroup& g, loss_seqs_t& irrecover) const;
bool IsLost(int32_t seq) const;
@ -243,6 +273,11 @@ public:
static const size_t EXTRA_SIZE = 4;
virtual SRT_ARQLevel arqLevel() ATR_OVERRIDE { return m_fallback_level; }
static const char defaultConfig [];
static bool verifyConfig(const SrtFilterConfig& config, std::string& w_errormsg);
};
} // namespace srt
#endif

View file

@ -3,6 +3,7 @@
SOURCES
api.cpp
buffer.cpp
buffer_rcv.cpp
cache.cpp
channel.cpp
common.cpp
@ -12,27 +13,48 @@ epoll.cpp
fec.cpp
handshake.cpp
list.cpp
logger_default.cpp
logger_defs.cpp
md5.cpp
packet.cpp
packetfilter.cpp
queue.cpp
congctl.cpp
socketconfig.cpp
srt_c_api.cpp
window.cpp
srt_compat.c
strerror_defs.cpp
sync.cpp
tsbpd_time.cpp
window.cpp
SOURCES - ENABLE_BONDING
group.cpp
group_backup.cpp
group_common.cpp
SOURCES - !ENABLE_STDCXX_SYNC
sync_posix.cpp
SOURCES - ENABLE_STDCXX_SYNC
sync_cxx11.cpp
SOURCES - EXTRA_WIN32_SHARED
srt_shared.rc
PUBLIC HEADERS
srt.h
logging_api.h
access_control.h
PROTECTED HEADERS
platform_sys.h
udt.h
srt4udt.h
PRIVATE HEADERS
api.h
buffer.h
buffer_rcv.h
cache.h
channel.h
common.h
@ -45,13 +67,18 @@ logging.h
md5.h
netinet_any.h
packet.h
sync.h
queue.h
congctl.h
srt4udt.h
socketconfig.h
srt_compat.h
stats.h
threadname.h
tsbpd_time.h
utilities.h
window.h
SOURCES WIN32 SHARED
srt_shared.rc
PRIVATE HEADERS - ENABLE_BONDING
group.h
group_backup.h
group_common.h

4717
trunk/3rdparty/srt-1-fit/srtcore/group.cpp vendored Normal file

File diff suppressed because it is too large Load diff

822
trunk/3rdparty/srt-1-fit/srtcore/group.h vendored Normal file
View file

@ -0,0 +1,822 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2020 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/.
*
*/
/*****************************************************************************
Written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_SRT_GROUP_H
#define INC_SRT_GROUP_H
#include "srt.h"
#include "common.h"
#include "packet.h"
#include "group_common.h"
#include "group_backup.h"
namespace srt
{
#if ENABLE_HEAVY_LOGGING
const char* const srt_log_grp_state[] = {"PENDING", "IDLE", "RUNNING", "BROKEN"};
#endif
class CUDTGroup
{
friend class CUDTUnited;
typedef sync::steady_clock::time_point time_point;
typedef sync::steady_clock::duration duration;
typedef sync::steady_clock steady_clock;
typedef groups::SocketData SocketData;
typedef groups::SendBackupCtx SendBackupCtx;
typedef groups::BackupMemberState BackupMemberState;
public:
typedef SRT_MEMBERSTATUS GroupState;
// Note that the use of states may differ in particular group types:
//
// Broadcast: links that are freshly connected become PENDING and then IDLE only
// for a short moment to be activated immediately at the nearest sending operation.
//
// Balancing: like with broadcast, just that the link activation gets its shared percentage
// of traffic balancing
//
// Multicast: The link is never idle. The data are always sent over the UDP multicast link
// and the receiver simply gets subscribed and reads packets once it's ready.
//
// Backup: The link stays idle until it's activated, and the activation can only happen
// at the moment when the currently active link is "suspected of being likely broken"
// (the current active link fails to receive ACK in a time when two ACKs should already
// be received). After a while when the current active link is confirmed broken, it turns
// into broken state.
static const char* StateStr(GroupState);
static int32_t s_tokenGen;
static int32_t genToken() { ++s_tokenGen; if (s_tokenGen < 0) s_tokenGen = 0; return s_tokenGen;}
struct ConfigItem
{
SRT_SOCKOPT so;
std::vector<unsigned char> value;
template <class T>
bool get(T& refr)
{
if (sizeof(T) > value.size())
return false;
refr = *(T*)&value[0];
return true;
}
ConfigItem(SRT_SOCKOPT o, const void* val, int size)
: so(o)
{
value.resize(size);
unsigned char* begin = (unsigned char*)val;
std::copy(begin, begin + size, value.begin());
}
struct OfType
{
SRT_SOCKOPT so;
OfType(SRT_SOCKOPT soso)
: so(soso)
{
}
bool operator()(ConfigItem& ci) { return ci.so == so; }
};
};
typedef std::list<SocketData> group_t;
typedef group_t::iterator gli_t;
typedef std::vector< std::pair<SRTSOCKET, srt::CUDTSocket*> > sendable_t;
struct Sendstate
{
SRTSOCKET id;
SocketData* mb;
int stat;
int code;
};
CUDTGroup(SRT_GROUP_TYPE);
~CUDTGroup();
SocketData* add(SocketData data);
struct HaveID
{
SRTSOCKET id;
HaveID(SRTSOCKET sid)
: id(sid)
{
}
bool operator()(const SocketData& s) { return s.id == id; }
};
bool contains(SRTSOCKET id, SocketData*& w_f)
{
srt::sync::ScopedLock g(m_GroupLock);
gli_t f = std::find_if(m_Group.begin(), m_Group.end(), HaveID(id));
if (f == m_Group.end())
{
w_f = NULL;
return false;
}
w_f = &*f;
return true;
}
// NEED LOCKING
gli_t begin() { return m_Group.begin(); }
gli_t end() { return m_Group.end(); }
/// Remove the socket from the group container.
/// REMEMBER: the group spec should be taken from the socket
/// (set m_GroupOf and m_GroupMemberData to NULL
/// PRIOR TO calling this function.
/// @param id Socket ID to look for in the container to remove
/// @return true if the container still contains any sockets after the operation
bool remove(SRTSOCKET id)
{
using srt_logging::gmlog;
srt::sync::ScopedLock g(m_GroupLock);
bool empty = false;
HLOGC(gmlog.Debug, log << "group/remove: going to remove @" << id << " from $" << m_GroupID);
gli_t f = std::find_if(m_Group.begin(), m_Group.end(), HaveID(id));
if (f != m_Group.end())
{
m_Group.erase(f);
// Reset sequence numbers on a dead group so that they are
// initialized anew with the new alive connection within
// the group.
// XXX The problem is that this should be done after the
// socket is considered DISCONNECTED, not when it's being
// closed. After being disconnected, the sequence numbers
// are no longer valid, and will be reinitialized when the
// socket is connected again. This may stay as is for now
// as in SRT it's not predicted to do anything with the socket
// that was disconnected other than immediately closing it.
if (m_Group.empty())
{
// When the group is empty, there's no danger that this
// number will collide with any ISN provided by a socket.
// Also since now every socket will derive this ISN.
m_iLastSchedSeqNo = generateISN();
resetInitialRxSequence();
empty = true;
}
}
else
{
HLOGC(gmlog.Debug, log << "group/remove: IPE: id @" << id << " NOT FOUND");
empty = true; // not exactly true, but this is to cause error on group in the APP
}
if (m_Group.empty())
{
m_bOpened = false;
m_bConnected = false;
}
// XXX BUGFIX
m_Positions.erase(id);
return !empty;
}
bool groupEmpty()
{
srt::sync::ScopedLock g(m_GroupLock);
return m_Group.empty();
}
void setGroupConnected();
int send(const char* buf, int len, SRT_MSGCTRL& w_mc);
int sendBroadcast(const char* buf, int len, SRT_MSGCTRL& w_mc);
int sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc);
static int32_t generateISN();
private:
// For Backup, sending all previous packet
int sendBackupRexmit(srt::CUDT& core, SRT_MSGCTRL& w_mc);
// Support functions for sendBackup and sendBroadcast
/// Check if group member is idle.
/// @param d group member
/// @param[in,out] w_wipeme array of sockets to remove from group
/// @param[in,out] w_pendingLinks array of sockets pending for connection
/// @returns true if d is idle (standby), false otherwise
bool send_CheckIdle(const gli_t d, std::vector<SRTSOCKET>& w_wipeme, std::vector<SRTSOCKET>& w_pendingLinks);
/// This function checks if the member has just become idle (check if sender buffer is empty) to send a KEEPALIVE immidiatelly.
/// @todo Check it is some abandoned logic.
void sendBackup_CheckIdleTime(gli_t w_d);
/// Qualify states of member links.
/// [[using locked(this->m_GroupLock, m_pGlobal->m_GlobControlLock)]]
/// @param[out] w_sendBackupCtx the context will be updated with state qualifications
/// @param[in] currtime current timestamp
void sendBackup_QualifyMemberStates(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime);
void sendBackup_AssignBackupState(srt::CUDT& socket, BackupMemberState state, const steady_clock::time_point& currtime);
/// Qualify the state of the active link: fresh, stable, unstable, wary.
/// @retval active backup member state: fresh, stable, unstable, wary.
BackupMemberState sendBackup_QualifyActiveState(const gli_t d, const time_point currtime);
BackupMemberState sendBackup_QualifyIfStandBy(const gli_t d);
/// Sends the same payload over all active members.
/// @param[in] buf payload
/// @param[in] len payload length in bytes
/// @param[in,out] w_mc message control
/// @param[in] currtime current time
/// @param[in] currseq current packet sequence number
/// @param[out] w_nsuccessful number of members with successfull sending.
/// @param[in,out] maxActiveWeight
/// @param[in,out] sendBackupCtx context
/// @param[in,out] w_cx error
/// @return group send result: -1 if sending over all members has failed; number of bytes sent overwise.
int sendBackup_SendOverActive(const char* buf, int len, SRT_MSGCTRL& w_mc, const steady_clock::time_point& currtime, int32_t& w_curseq,
size_t& w_nsuccessful, uint16_t& w_maxActiveWeight, SendBackupCtx& w_sendBackupCtx, CUDTException& w_cx);
/// Check link sending status
/// @param[in] currtime Current time (logging only)
/// @param[in] send_status Result of sending over the socket
/// @param[in] lastseq Last sent sequence number before the current sending operation
/// @param[in] pktseq Packet sequence number currently tried to be sent
/// @param[out] w_u CUDT unit of the current member (to allow calling overrideSndSeqNo)
/// @param[out] w_curseq Group's current sequence number (either -1 or the value used already for other links)
/// @param[out] w_final_stat w_final_stat = send_status if sending succeded.
///
/// @returns true if the sending operation result (submitted in stat) is a success, false otherwise.
bool sendBackup_CheckSendStatus(const time_point& currtime,
const int send_status,
const int32_t lastseq,
const int32_t pktseq,
CUDT& w_u,
int32_t& w_curseq,
int& w_final_stat);
void sendBackup_Buffering(const char* buf, const int len, int32_t& curseq, SRT_MSGCTRL& w_mc);
size_t sendBackup_TryActivateStandbyIfNeeded(
const char* buf,
const int len,
bool& w_none_succeeded,
SRT_MSGCTRL& w_mc,
int32_t& w_curseq,
int32_t& w_final_stat,
SendBackupCtx& w_sendBackupCtx,
CUDTException& w_cx,
const steady_clock::time_point& currtime);
/// Check if pending sockets are to be qualified as broken.
/// This qualification later results in removing the socket from a group and closing it.
/// @param[in,out] a context with a list of member sockets, some pending might qualified broken
void sendBackup_CheckPendingSockets(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime);
/// Check if unstable sockets are to be qualified as broken.
/// The main reason for such qualification is if a socket is unstable for too long.
/// This qualification later results in removing the socket from a group and closing it.
/// @param[in,out] a context with a list of member sockets, some pending might qualified broken
void sendBackup_CheckUnstableSockets(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime);
/// @brief Marks broken sockets as closed. Used in broadcast sending.
/// @param w_wipeme a list of sockets to close
void send_CloseBrokenSockets(std::vector<SRTSOCKET>& w_wipeme);
/// @brief Marks broken sockets as closed. Used in backup sending.
/// @param w_sendBackupCtx the context with a list of broken sockets
void sendBackup_CloseBrokenSockets(SendBackupCtx& w_sendBackupCtx);
void sendBackup_RetryWaitBlocked(SendBackupCtx& w_sendBackupCtx,
int& w_final_stat,
bool& w_none_succeeded,
SRT_MSGCTRL& w_mc,
CUDTException& w_cx);
void sendBackup_SilenceRedundantLinks(SendBackupCtx& w_sendBackupCtx, const steady_clock::time_point& currtime);
void send_CheckValidSockets();
public:
int recv(char* buf, int len, SRT_MSGCTRL& w_mc);
void close();
void setOpt(SRT_SOCKOPT optname, const void* optval, int optlen);
void getOpt(SRT_SOCKOPT optName, void* optval, int& w_optlen);
void deriveSettings(srt::CUDT* source);
bool applyFlags(uint32_t flags, HandshakeSide);
SRT_SOCKSTATUS getStatus();
void debugMasterData(SRTSOCKET slave);
bool isGroupReceiver()
{
// XXX add here also other group types, which
// predict group receiving.
return m_type == SRT_GTYPE_BROADCAST;
}
sync::Mutex* exp_groupLock() { return &m_GroupLock; }
void addEPoll(int eid);
void removeEPollEvents(const int eid);
void removeEPollID(const int eid);
/// @brief Update read-ready state.
/// @param sock member socket ID (unused)
/// @param sequence the latest packet sequence number available for reading.
void updateReadState(SRTSOCKET sock, int32_t sequence);
void updateWriteState();
void updateFailedLink();
void activateUpdateEvent(bool still_have_items);
int32_t getRcvBaseSeqNo();
/// Update the in-group array of packet providers per sequence number.
/// Also basing on the information already provided by possibly other sockets,
/// report the real status of packet loss, including packets maybe lost
/// by the caller provider, but already received from elsewhere. Note that
/// these packets are not ready for extraction until ACK-ed.
///
/// @param exp_sequence The previously received sequence at this socket
/// @param sequence The sequence of this packet
/// @param provider The core of the socket for which the packet was dispatched
/// @param time TSBPD time of this packet
/// @return The bitmap that marks by 'false' packets lost since next to exp_sequence
std::vector<bool> providePacket(int32_t exp_sequence, int32_t sequence, srt::CUDT* provider, uint64_t time);
/// This is called from the ACK action by particular socket, which
/// actually signs off the packet for extraction.
///
/// @param core The socket core for which the ACK was sent
/// @param ack The past-the-last-received ACK sequence number
void readyPackets(srt::CUDT* core, int32_t ack);
void syncWithSocket(const srt::CUDT& core, const HandshakeSide side);
int getGroupData(SRT_SOCKGROUPDATA* pdata, size_t* psize);
int getGroupData_LOCKED(SRT_SOCKGROUPDATA* pdata, size_t* psize);
/// Predicted to be called from the reading function to fill
/// the group data array as requested.
void fillGroupData(SRT_MSGCTRL& w_out, //< MSGCTRL to be written
const SRT_MSGCTRL& in //< MSGCTRL read from the data-providing socket
);
void copyGroupData(const CUDTGroup::SocketData& source, SRT_SOCKGROUPDATA& w_target);
#if ENABLE_HEAVY_LOGGING
void debugGroup();
#else
void debugGroup() {}
#endif
void ackMessage(int32_t msgno);
void processKeepalive(SocketData*);
void internalKeepalive(SocketData*);
private:
// Check if there's at least one connected socket.
// If so, grab the status of all member sockets.
void getGroupCount(size_t& w_size, bool& w_still_alive);
srt::CUDTUnited& m_Global;
srt::sync::Mutex m_GroupLock;
SRTSOCKET m_GroupID;
SRTSOCKET m_PeerGroupID;
struct GroupContainer
{
std::list<SocketData> m_List;
/// This field is used only by some types of groups that need
/// to keep track as to which link was lately used. Note that
/// by removal of a node from the m_List container, this link
/// must be appropriately reset.
gli_t m_LastActiveLink;
GroupContainer()
: m_LastActiveLink(m_List.end())
{
}
// Property<gli_t> active = { m_LastActiveLink; }
SRTU_PROPERTY_RW(gli_t, active, m_LastActiveLink);
gli_t begin() { return m_List.begin(); }
gli_t end() { return m_List.end(); }
bool empty() { return m_List.empty(); }
void push_back(const SocketData& data) { m_List.push_back(data); }
void clear()
{
m_LastActiveLink = end();
m_List.clear();
}
size_t size() { return m_List.size(); }
void erase(gli_t it);
};
GroupContainer m_Group;
const bool m_bSyncOnMsgNo; // It goes into a dedicated HS field. Could be true for balancing groups (not implemented).
SRT_GROUP_TYPE m_type;
CUDTSocket* m_listener; // A "group" can only have one listener.
srt::sync::atomic<int> m_iBusy;
CallbackHolder<srt_connect_callback_fn> m_cbConnectHook;
void installConnectHook(srt_connect_callback_fn* hook, void* opaq)
{
m_cbConnectHook.set(opaq, hook);
}
public:
void apiAcquire() { ++m_iBusy; }
void apiRelease() { --m_iBusy; }
// A normal cycle of the send/recv functions is the following:
// - [Initial API call for a group]
// - GroupKeeper - ctor
// - LOCK: GlobControlLock
// - Find the group ID in the group container (break if not found)
// - LOCK: GroupLock of that group
// - Set BUSY flag
// - UNLOCK GroupLock
// - UNLOCK GlobControlLock
// - [Call the sending function (sendBroadcast/sendBackup)]
// - LOCK GroupLock
// - Preparation activities
// - Loop over group members
// - Send over a single socket
// - Check send status and conditions
// - Exit, if nothing else to be done
// - Check links to send extra
// - UNLOCK GroupLock
// - Wait for first ready link
// - LOCK GroupLock
// - Check status and find sendable link
// - Send over a single socket
// - Check status and update data
// - UNLOCK GroupLock, Exit
// - GroupKeeper - dtor
// - LOCK GroupLock
// - Clear BUSY flag
// - UNLOCK GroupLock
// END.
//
// The possibility for isStillBusy to go on is only the following:
// 1. Before calling the API function. As GlobControlLock is locked,
// the nearest lock on GlobControlLock by GroupKeeper can happen:
// - before the group is moved to ClosedGroups (this allows it to be found)
// - after the group is moved to ClosedGroups (this makes the group not found)
// - NOT after the group was deleted, as it could not be found and occupied.
//
// 2. Before release of GlobControlLock (acquired by GC), but before the
// API function locks GroupLock:
// - the GC call to isStillBusy locks GroupLock, but BUSY flag is already set
// - GC then avoids deletion of the group
//
// 3. In any further place up to the exit of the API implementation function,
// the BUSY flag is still set.
//
// 4. After exit of GroupKeeper destructor and unlock of GroupLock
// - the group is no longer being accessed and can be freely deleted.
// - the group also can no longer be found by ID.
bool isStillBusy()
{
sync::ScopedLock glk(m_GroupLock);
return m_iBusy || !m_Group.empty();
}
struct BufferedMessageStorage
{
size_t blocksize;
size_t maxstorage;
std::vector<char*> storage;
BufferedMessageStorage(size_t blk, size_t max = 0)
: blocksize(blk)
, maxstorage(max)
, storage()
{
}
char* get()
{
if (storage.empty())
return new char[blocksize];
// Get the element from the end
char* block = storage.back();
storage.pop_back();
return block;
}
void put(char* block)
{
if (storage.size() >= maxstorage)
{
// Simply delete
delete[] block;
return;
}
// Put the block into the spare buffer
storage.push_back(block);
}
~BufferedMessageStorage()
{
for (size_t i = 0; i < storage.size(); ++i)
delete[] storage[i];
}
};
struct BufferedMessage
{
static BufferedMessageStorage storage;
SRT_MSGCTRL mc;
mutable char* data;
size_t size;
BufferedMessage()
: data()
, size()
{
}
~BufferedMessage()
{
if (data)
storage.put(data);
}
// NOTE: size 's' must be checked against SRT_LIVE_MAX_PLSIZE
// before calling
void copy(const char* buf, size_t s)
{
size = s;
data = storage.get();
memcpy(data, buf, s);
}
BufferedMessage(const BufferedMessage& foreign)
: mc(foreign.mc)
, data(foreign.data)
, size(foreign.size)
{
foreign.data = 0;
}
BufferedMessage& operator=(const BufferedMessage& foreign)
{
data = foreign.data;
size = foreign.size;
mc = foreign.mc;
foreign.data = 0;
return *this;
}
private:
void swap_with(BufferedMessage& b)
{
std::swap(this->mc, b.mc);
std::swap(this->data, b.data);
std::swap(this->size, b.size);
}
};
typedef std::deque<BufferedMessage> senderBuffer_t;
// typedef StaticBuffer<BufferedMessage, 1000> senderBuffer_t;
private:
// Fields required for SRT_GTYPE_BACKUP groups.
senderBuffer_t m_SenderBuffer;
int32_t m_iSndOldestMsgNo; // oldest position in the sender buffer
sync::atomic<int32_t> m_iSndAckedMsgNo;
uint32_t m_uOPT_MinStabilityTimeout_us;
// THIS function must be called only in a function for a group type
// that does use sender buffer.
int32_t addMessageToBuffer(const char* buf, size_t len, SRT_MSGCTRL& w_mc);
std::set<int> m_sPollID; // set of epoll ID to trigger
int m_iMaxPayloadSize;
int m_iAvgPayloadSize;
bool m_bSynRecving;
bool m_bSynSending;
bool m_bTsbPd;
bool m_bTLPktDrop;
int64_t m_iTsbPdDelay_us;
int m_RcvEID;
class CEPollDesc* m_RcvEpolld;
int m_SndEID;
class CEPollDesc* m_SndEpolld;
int m_iSndTimeOut; // sending timeout in milliseconds
int m_iRcvTimeOut; // receiving timeout in milliseconds
// Start times for TsbPd. These times shall be synchronized
// between all sockets in the group. The first connected one
// defines it, others shall derive it. The value 0 decides if
// this has been already set.
time_point m_tsStartTime;
time_point m_tsRcvPeerStartTime;
struct ReadPos
{
std::vector<char> packet;
SRT_MSGCTRL mctrl;
ReadPos(int32_t s)
: mctrl(srt_msgctrl_default)
{
mctrl.pktseq = s;
}
};
std::map<SRTSOCKET, ReadPos> m_Positions;
ReadPos* checkPacketAhead();
void recv_CollectAliveAndBroken(std::vector<srt::CUDTSocket*>& w_alive, std::set<srt::CUDTSocket*>& w_broken);
/// The function polls alive member sockets and retrieves a list of read-ready.
/// [acquires lock for CUDT::uglobal()->m_GlobControlLock]
/// [[using locked(m_GroupLock)]] temporally unlocks-locks internally
///
/// @returns list of read-ready sockets
/// @throws CUDTException(MJ_CONNECTION, MN_NOCONN, 0)
/// @throws CUDTException(MJ_AGAIN, MN_RDAVAIL, 0)
std::vector<srt::CUDTSocket*> recv_WaitForReadReady(const std::vector<srt::CUDTSocket*>& aliveMembers, std::set<srt::CUDTSocket*>& w_broken);
// This is the sequence number of a packet that has been previously
// delivered. Initially it should be set to SRT_SEQNO_NONE so that the sequence read
// from the first delivering socket will be taken as a good deal.
sync::atomic<int32_t> m_RcvBaseSeqNo;
bool m_bOpened; // Set to true when at least one link is at least pending
bool m_bConnected; // Set to true on first link confirmed connected
bool m_bClosing;
// There's no simple way of transforming config
// items that are predicted to be used on socket.
// Use some options for yourself, store the others
// for setting later on a socket.
std::vector<ConfigItem> m_config;
// Signal for the blocking user thread that the packet
// is ready to deliver.
sync::Condition m_RcvDataCond;
sync::Mutex m_RcvDataLock;
sync::atomic<int32_t> m_iLastSchedSeqNo; // represetnts the value of CUDT::m_iSndNextSeqNo for each running socket
sync::atomic<int32_t> m_iLastSchedMsgNo;
// Statistics
struct Stats
{
// Stats state
time_point tsActivateTime; // Time when this group sent or received the first data packet
time_point tsLastSampleTime; // Time reset when clearing stats
stats::Metric<stats::BytesPackets> sent; // number of packets sent from the application
stats::Metric<stats::BytesPackets> recv; // number of packets delivered from the group to the application
stats::Metric<stats::BytesPackets> recvDrop; // number of packets dropped by the group receiver (not received from any member)
stats::Metric<stats::BytesPackets> recvDiscard; // number of packets discarded as already delivered
void init()
{
tsActivateTime = srt::sync::steady_clock::time_point();
tsLastSampleTime = srt::sync::steady_clock::now();
sent.reset();
recv.reset();
recvDrop.reset();
recvDiscard.reset();
}
void reset()
{
tsLastSampleTime = srt::sync::steady_clock::now();
sent.resetTrace();
recv.resetTrace();
recvDrop.resetTrace();
recvDiscard.resetTrace();
}
} m_stats;
void updateAvgPayloadSize(int size)
{
if (m_iAvgPayloadSize == -1)
m_iAvgPayloadSize = size;
else
m_iAvgPayloadSize = avg_iir<4>(m_iAvgPayloadSize, size);
}
int avgRcvPacketSize()
{
// In case when no packet has been received yet, but already notified
// a dropped packet, its size will be SRT_LIVE_DEF_PLSIZE. It will be
// the value most matching in the typical uses, although no matter what
// value would be used here, each one would be wrong from some points
// of view. This one is simply the best choice for typical uses of groups
// provided that they are to be ued only for live mode.
return m_iAvgPayloadSize == -1 ? SRT_LIVE_DEF_PLSIZE : m_iAvgPayloadSize;
}
public:
void bstatsSocket(CBytePerfMon* perf, bool clear);
// Required after the call on newGroup on the listener side.
// On the listener side the group is lazily created just before
// accepting a new socket and therefore always open.
void setOpen() { m_bOpened = true; }
std::string CONID() const
{
#if ENABLE_LOGGING
std::ostringstream os;
os << "@" << m_GroupID << ":";
return os.str();
#else
return "";
#endif
}
void resetInitialRxSequence()
{
// The app-reader doesn't care about the real sequence number.
// The first provided one will be taken as a good deal; even if
// this is going to be past the ISN, at worst it will be caused
// by TLPKTDROP.
m_RcvBaseSeqNo = SRT_SEQNO_NONE;
}
bool applyGroupTime(time_point& w_start_time, time_point& w_peer_start_time)
{
using srt::sync::is_zero;
using srt_logging::gmlog;
if (is_zero(m_tsStartTime))
{
// The first socket, defines the group time for the whole group.
m_tsStartTime = w_start_time;
m_tsRcvPeerStartTime = w_peer_start_time;
return true;
}
// Sanity check. This should never happen, fix the bug if found!
if (is_zero(m_tsRcvPeerStartTime))
{
LOGC(gmlog.Error, log << "IPE: only StartTime is set, RcvPeerStartTime still 0!");
// Kinda fallback, but that's not too safe.
m_tsRcvPeerStartTime = w_peer_start_time;
}
// The redundant connection, derive the times
w_start_time = m_tsStartTime;
w_peer_start_time = m_tsRcvPeerStartTime;
return false;
}
// Live state synchronization
bool getBufferTimeBase(srt::CUDT* forthesakeof, time_point& w_tb, bool& w_wp, duration& w_dr);
bool applyGroupSequences(SRTSOCKET, int32_t& w_snd_isn, int32_t& w_rcv_isn);
/// @brief Synchronize TSBPD base time and clock drift among members using the @a srcMember as a reference.
/// @param srcMember a reference for synchronization.
void synchronizeDrift(const srt::CUDT* srcMember);
void updateLatestRcv(srt::CUDTSocket*);
// Property accessors
SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRTSOCKET, id, m_GroupID);
SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRTSOCKET, peerid, m_PeerGroupID);
SRTU_PROPERTY_RW_CHAIN(CUDTGroup, SRT_GROUP_TYPE, type, m_type);
SRTU_PROPERTY_RW_CHAIN(CUDTGroup, int32_t, currentSchedSequence, m_iLastSchedSeqNo);
SRTU_PROPERTY_RRW(std::set<int>&, epollset, m_sPollID);
SRTU_PROPERTY_RW_CHAIN(CUDTGroup, int64_t, latency, m_iTsbPdDelay_us);
SRTU_PROPERTY_RO(bool, synconmsgno, m_bSyncOnMsgNo);
SRTU_PROPERTY_RO(bool, closing, m_bClosing);
};
} // namespace srt
#endif // INC_SRT_GROUP_H

View file

@ -0,0 +1,159 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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/.
*
*/
/*****************************************************************************
Written by
Haivision Systems Inc.
*****************************************************************************/
#include "platform_sys.h"
#include <algorithm>
#include <sstream>
#include "group_backup.h"
namespace srt
{
namespace groups
{
using namespace std;
using namespace srt_logging;
const char* stateToStr(BackupMemberState state)
{
switch (state)
{
case srt::groups::BKUPST_UNKNOWN:
return "UNKNOWN";
case srt::groups::BKUPST_PENDING:
return "PENDING";
case srt::groups::BKUPST_STANDBY:
return "STANDBY";
case srt::groups::BKUPST_ACTIVE_FRESH:
return "ACTIVE_FRESH";
case srt::groups::BKUPST_ACTIVE_STABLE:
return "ACTIVE_STABLE";
case srt::groups::BKUPST_ACTIVE_UNSTABLE:
return "ACTIVE_UNSTABLE";
case srt::groups::BKUPST_ACTIVE_UNSTABLE_WARY:
return "ACTIVE_UNSTABLE_WARY";
case srt::groups::BKUPST_BROKEN:
return "BROKEN";
default:
break;
}
return "WRONG_STATE";
}
/// @brief Compares group members by their weight (higher weight comes first), then state.
/// Higher weight comes first, same weight: stable, then fresh active.
struct FCompareByWeight
{
/// @returns true if the first argument is less than (i.e. is ordered before) the second.
bool operator()(const BackupMemberStateEntry& a, const BackupMemberStateEntry& b)
{
if (a.pSocketData != NULL && b.pSocketData != NULL
&& (a.pSocketData->weight != b.pSocketData->weight))
return a.pSocketData->weight > b.pSocketData->weight;
if (a.state != b.state)
{
SRT_STATIC_ASSERT(BKUPST_ACTIVE_STABLE > BKUPST_ACTIVE_FRESH, "Wrong ordering");
return a.state > b.state;
}
// the order does not matter, but comparator must return a different value for not equal a and b
return a.socketID < b.socketID;
}
};
void SendBackupCtx::recordMemberState(SocketData* pSockData, BackupMemberState st)
{
m_memberStates.push_back(BackupMemberStateEntry(pSockData, st));
++m_stateCounter[st];
if (st == BKUPST_STANDBY)
{
m_standbyMaxWeight = max(m_standbyMaxWeight, pSockData->weight);
}
else if (isStateActive(st))
{
m_activeMaxWeight = max(m_activeMaxWeight, pSockData->weight);
}
}
void SendBackupCtx::updateMemberState(const SocketData* pSockData, BackupMemberState st)
{
typedef vector<BackupMemberStateEntry>::iterator iter_t;
for (iter_t i = m_memberStates.begin(); i != m_memberStates.end(); ++i)
{
if (i->pSocketData == NULL)
continue;
if (i->pSocketData != pSockData)
continue;
if (i->state == st)
return;
--m_stateCounter[i->state];
++m_stateCounter[st];
i->state = st;
return;
}
LOGC(gslog.Error,
log << "IPE: SendBackupCtx::updateMemberState failed to locate member");
}
void SendBackupCtx::sortByWeightAndState()
{
sort(m_memberStates.begin(), m_memberStates.end(), FCompareByWeight());
}
BackupMemberState SendBackupCtx::getMemberState(const SocketData* pSockData) const
{
typedef vector<BackupMemberStateEntry>::const_iterator const_iter_t;
for (const_iter_t i = m_memberStates.begin(); i != m_memberStates.end(); ++i)
{
if (i->pSocketData != pSockData)
continue;
return i->state;
}
// The entry was not found
// TODO: Maybe throw an exception here?
return BKUPST_UNKNOWN;
}
unsigned SendBackupCtx::countMembersByState(BackupMemberState st) const
{
return m_stateCounter[st];
}
std::string SendBackupCtx::printMembers() const
{
stringstream ss;
typedef vector<BackupMemberStateEntry>::const_iterator const_iter_t;
for (const_iter_t i = m_memberStates.begin(); i != m_memberStates.end(); ++i)
{
ss << "@" << i->socketID << " w " << i->pSocketData->weight << " state " << stateToStr(i->state) << ", ";
}
return ss.str();
}
} // namespace groups
} // namespace srt

View file

@ -0,0 +1,128 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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/.
*
*/
/*****************************************************************************
Written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_SRT_GROUP_BACKUP_H
#define INC_SRT_GROUP_BACKUP_H
#include "srt.h"
#include "common.h"
#include "group_common.h"
#include <list>
namespace srt
{
namespace groups
{
enum BackupMemberState
{
BKUPST_UNKNOWN = -1,
BKUPST_PENDING = 0,
BKUPST_STANDBY = 1,
BKUPST_BROKEN = 2,
BKUPST_ACTIVE_UNSTABLE = 3,
BKUPST_ACTIVE_UNSTABLE_WARY = 4,
BKUPST_ACTIVE_FRESH = 5,
BKUPST_ACTIVE_STABLE = 6,
BKUPST_E_SIZE = 7
};
const char* stateToStr(BackupMemberState state);
inline bool isStateActive(BackupMemberState state)
{
if (state == BKUPST_ACTIVE_FRESH
|| state == BKUPST_ACTIVE_STABLE
|| state == BKUPST_ACTIVE_UNSTABLE
|| state == BKUPST_ACTIVE_UNSTABLE_WARY)
{
return true;
}
return false;
}
struct BackupMemberStateEntry
{
BackupMemberStateEntry(SocketData* psock, BackupMemberState st)
: pSocketData(psock)
, socketID(psock->id)
, state(st)
{}
SocketData* pSocketData; // accessing pSocketDataIt requires m_GroupLock
SRTSOCKET socketID; // therefore socketID is saved separately (needed to close broken sockets)
BackupMemberState state;
};
/// @brief A context needed for main/backup sending function.
/// @todo Using gli_t here does not allow to safely store the context outside of the sendBackup calls.
class SendBackupCtx
{
public:
SendBackupCtx()
: m_stateCounter() // default init with zeros
, m_activeMaxWeight()
, m_standbyMaxWeight()
{
}
/// @brief Adds or updates a record of the member socket state.
/// @param pSocketDataIt Iterator to a socket
/// @param st State of the memmber socket
/// @todo Implement updating member state
void recordMemberState(SocketData* pSocketDataIt, BackupMemberState st);
/// @brief Updates a record of the member socket state.
/// @param pSocketDataIt Iterator to a socket
/// @param st State of the memmber socket
/// @todo To be replaced by recordMemberState
/// @todo Update max weights?
void updateMemberState(const SocketData* pSocketDataIt, BackupMemberState st);
/// @brief sorts members in order
/// Higher weight comes first, same weight: stable first, then fresh active.
void sortByWeightAndState();
BackupMemberState getMemberState(const SocketData* pSocketDataIt) const;
unsigned countMembersByState(BackupMemberState st) const;
const std::vector<BackupMemberStateEntry>& memberStates() const { return m_memberStates; }
uint16_t maxStandbyWeight() const { return m_standbyMaxWeight; }
uint16_t maxActiveWeight() const { return m_activeMaxWeight; }
std::string printMembers() const;
void setRateEstimate(const CRateEstimator& rate) { m_rateEstimate = rate; }
const CRateEstimator& getRateEstimate() const { return m_rateEstimate; }
private:
std::vector<BackupMemberStateEntry> m_memberStates; // TODO: consider std::map here?
unsigned m_stateCounter[BKUPST_E_SIZE];
uint16_t m_activeMaxWeight;
uint16_t m_standbyMaxWeight;
CRateEstimator m_rateEstimate; // The rate estimator state of the active link to copy to a backup on activation.
};
} // namespace groups
} // namespace srt
#endif // INC_SRT_GROUP_BACKUP_H

View file

@ -0,0 +1,63 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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/.
*
*/
/*****************************************************************************
Written by
Haivision Systems Inc.
*****************************************************************************/
#include "platform_sys.h"
#include "group_common.h"
#include "api.h"
namespace srt
{
namespace groups
{
SocketData prepareSocketData(CUDTSocket* s)
{
// This uses default SRT_GST_BROKEN because when the group operation is done,
// then the SRT_GST_IDLE state automatically turns into SRT_GST_RUNNING. This is
// recognized as an initial state of the fresh added socket to the group,
// so some "initial configuration" must be done on it, after which it's
// turned into SRT_GST_RUNNING, that is, it's treated as all others. When
// set to SRT_GST_BROKEN, this socket is disregarded. This socket isn't cleaned
// up, however, unless the status is simultaneously SRTS_BROKEN.
// The order of operations is then:
// - add the socket to the group in this "broken" initial state
// - connect the socket (or get it extracted from accept)
// - update the socket state (should be SRTS_CONNECTED)
// - once the connection is established (may take time with connect), set SRT_GST_IDLE
// - the next operation of send/recv will automatically turn it into SRT_GST_RUNNING
SocketData sd = {
s->m_SocketID,
s,
-1,
SRTS_INIT,
SRT_GST_BROKEN,
SRT_GST_BROKEN,
-1,
-1,
sockaddr_any(),
sockaddr_any(),
false,
false,
false,
0, // weight
0 // pktSndDropTotal
};
return sd;
}
} // namespace groups
} // namespace srt

View file

@ -0,0 +1,62 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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/.
*
*/
/*****************************************************************************
Written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_SRT_GROUP_COMMON_H
#define INC_SRT_GROUP_COMMON_H
#include "srt.h"
#include "common.h"
#include "core.h"
#include <list>
namespace srt
{
namespace groups
{
typedef SRT_MEMBERSTATUS GroupState;
struct SocketData
{
SRTSOCKET id; // same as ps->m_SocketID
CUDTSocket* ps;
int token;
SRT_SOCKSTATUS laststatus;
GroupState sndstate;
GroupState rcvstate;
int sndresult;
int rcvresult;
sockaddr_any agent;
sockaddr_any peer;
bool ready_read;
bool ready_write;
bool ready_error;
// Configuration
uint16_t weight;
// Stats
int64_t pktSndDropTotal;
};
SocketData prepareSocketData(CUDTSocket* s);
typedef std::list<SocketData> group_t;
typedef group_t::iterator gli_t;
} // namespace groups
} // namespace srt
#endif // INC_SRT_GROUP_COMMON_H

View file

@ -43,6 +43,8 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include "platform_sys.h"
#include <cstring>
#include <string>
#include <sstream>
@ -50,32 +52,33 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include "udt.h"
#include "api.h"
#include "core.h"
#include "handshake.h"
#include "utilities.h"
using namespace std;
using namespace srt;
CHandShake::CHandShake():
m_iVersion(0),
m_iType(0), // Universal: UDT_UNDEFINED or no flags
m_iISN(0),
m_iMSS(0),
m_iFlightFlagSize(0),
m_iReqType(URQ_WAVEAHAND),
m_iID(0),
m_iCookie(0),
m_extension(false)
srt::CHandShake::CHandShake()
: m_iVersion(0)
, m_iType(0) // Universal: UDT_UNDEFINED or no flags
, m_iISN(0)
, m_iMSS(0)
, m_iFlightFlagSize(0)
, m_iReqType(URQ_WAVEAHAND)
, m_iID(0)
, m_iCookie(0)
, m_extension(false)
{
for (int i = 0; i < 4; ++ i)
m_piPeerIP[i] = 0;
}
int CHandShake::store_to(char* buf, ref_t<size_t> r_size)
int srt::CHandShake::store_to(char* buf, size_t& w_size)
{
size_t& size = *r_size;
if (size < m_iContentSize)
if (w_size < m_iContentSize)
return -1;
int32_t* p = reinterpret_cast<int32_t*>(buf);
@ -90,12 +93,12 @@ int CHandShake::store_to(char* buf, ref_t<size_t> r_size)
for (int i = 0; i < 4; ++ i)
*p++ = m_piPeerIP[i];
size = m_iContentSize;
w_size = m_iContentSize;
return 0;
}
int CHandShake::load_from(const char* buf, size_t size)
int srt::CHandShake::load_from(const char* buf, size_t size)
{
if (size < m_iContentSize)
return -1;
@ -118,6 +121,8 @@ int CHandShake::load_from(const char* buf, size_t size)
#ifdef ENABLE_LOGGING
namespace srt
{
const char* srt_rejectreason_name [] = {
"UNKNOWN",
"SYSTEM",
@ -134,15 +139,31 @@ const char* srt_rejectreason_name [] = {
"MESSAGEAPI",
"CONGESTION",
"FILTER",
"GROUP",
"TIMEOUT"
};
}
std::string RequestTypeStr(UDTRequestType rq)
std::string srt::RequestTypeStr(UDTRequestType rq)
{
if (rq >= URQ_FAILURE_TYPES)
{
SRT_REJECT_REASON rej = RejectReasonForURQ(rq);
int id = rej;
return std::string("ERROR:") + srt_rejectreason_name[id];
std::ostringstream rt;
rt << "ERROR:";
int id = RejectReasonForURQ(rq);
if (id < (int) Size(srt_rejectreason_name))
rt << srt_rejectreason_name[id];
else if (id < SRT_REJC_USERDEFINED)
{
if (id < SRT_REJC_PREDEFINED)
rt << "UNKNOWN:" << id;
else
rt << "PREDEFINED:" << (id - SRT_REJC_PREDEFINED);
}
else
rt << "USERDEFINED:" << (id - SRT_REJC_USERDEFINED);
return rt.str();
}
switch ( rq )
@ -156,7 +177,7 @@ std::string RequestTypeStr(UDTRequestType rq)
}
}
string CHandShake::RdvStateStr(CHandShake::RendezvousState s)
string srt::CHandShake::RdvStateStr(CHandShake::RendezvousState s)
{
switch (s)
{
@ -172,11 +193,22 @@ string CHandShake::RdvStateStr(CHandShake::RendezvousState s)
}
#endif
string CHandShake::show()
bool srt::CHandShake::valid()
{
if (m_iVersion < CUDT::HS_VERSION_UDT4
|| m_iISN < 0 || m_iISN >= CSeqNo::m_iMaxSeqNo
|| m_iMSS < 32
|| m_iFlightFlagSize < 2)
return false;
return true;
}
string srt::CHandShake::show()
{
ostringstream so;
so << "version=" << m_iVersion << " type=" << hex << m_iType << dec
so << "version=" << m_iVersion << " type=0x" << hex << m_iType << dec
<< " ISN=" << m_iISN << " MSS=" << m_iMSS << " FLW=" << m_iFlightFlagSize
<< " reqtype=" << RequestTypeStr(m_iReqType) << " srcID=" << m_iID
<< " cookie=" << hex << m_iCookie << dec
@ -191,9 +223,12 @@ string CHandShake::show()
// CHandShake, not CUDT.
if ( m_iVersion > CUDT::HS_VERSION_UDT4 )
{
so << "EXT: ";
if (m_iType == 0) // no flags at all
so << "none";
const int flags = SrtHSRequest::SRT_HSTYPE_HSFLAGS::unwrap(m_iType);
so << "FLAGS: ";
if (flags == SrtHSRequest::SRT_MAGIC_CODE)
so << "MAGIC";
else if (m_iType == 0)
so << "NONE"; // no flags and no advertised pbkeylen
else
so << ExtensionFlagStr(m_iType);
}
@ -201,7 +236,7 @@ string CHandShake::show()
return so.str();
}
string CHandShake::ExtensionFlagStr(int32_t fl)
string srt::CHandShake::ExtensionFlagStr(int32_t fl)
{
std::ostringstream out;
if ( fl & HS_EXT_HSREQ )
@ -211,7 +246,7 @@ string CHandShake::ExtensionFlagStr(int32_t fl)
if ( fl & HS_EXT_CONFIG )
out << " config";
int kl = SrtHSRequest::SRT_HSTYPE_ENCFLAGS::unwrap(fl) << 6;
const int kl = SrtHSRequest::SRT_HSTYPE_ENCFLAGS::unwrap(fl) << 6;
if (kl != 0)
{
out << " AES-" << kl;
@ -228,7 +263,7 @@ string CHandShake::ExtensionFlagStr(int32_t fl)
// XXX This code isn't currently used. Left here because it can
// be used in future, should any refactoring for the "manual word placement"
// code be done.
bool SrtHSRequest::serialize(char* buf, size_t size) const
bool srt::SrtHSRequest::serialize(char* buf, size_t size) const
{
if (size < SRT_HS_SIZE)
return false;
@ -243,7 +278,7 @@ bool SrtHSRequest::serialize(char* buf, size_t size) const
}
bool SrtHSRequest::deserialize(const char* buf, size_t size)
bool srt::SrtHSRequest::deserialize(const char* buf, size_t size)
{
m_iSrtVersion = 0; // just to let users recognize if it succeeded or not.
@ -258,3 +293,35 @@ bool SrtHSRequest::deserialize(const char* buf, size_t size)
m_iSrtReserved = (*p++);
return true;
}
std::string srt::SrtFlagString(int32_t flags)
{
#define LEN(arr) (sizeof (arr)/(sizeof ((arr)[0])))
std::string output;
static std::string namera[] = { "TSBPD-snd", "TSBPD-rcv", "haicrypt", "TLPktDrop", "NAKReport", "ReXmitFlag", "StreamAPI" };
size_t i = 0;
for (; i < LEN(namera); ++i)
{
if ((flags & 1) == 1)
{
output += "+" + namera[i] + " ";
}
else
{
output += "-" + namera[i] + " ";
}
flags >>= 1;
}
#undef LEN
if (flags != 0)
{
output += "+unknown";
}
return output;
}

View file

@ -43,12 +43,17 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef INC__HANDSHAKE_H
#define INC__HANDSHAKE_H
#ifndef INC_SRT_HANDSHAKE_H
#define INC_SRT_HANDSHAKE_H
#include <vector>
#include "crypto.h"
#include "utilities.h"
namespace srt
{
typedef Bits<31, 16> HS_CMDSPEC_CMD;
typedef Bits<15, 0> HS_CMDSPEC_SIZE;
@ -93,6 +98,7 @@ const int SRT_CMD_REJECT = 0, // REJECT is only a symbol for return type
SRT_CMD_SID = 5,
SRT_CMD_CONGESTION = 6,
SRT_CMD_FILTER = 7,
SRT_CMD_GROUP = 8,
SRT_CMD_NONE = -1; // for cases when {no pong for ping is required} | {no extension block found}
enum SrtDataStruct
@ -102,7 +108,7 @@ enum SrtDataStruct
SRT_HS_LATENCY,
// Keep it always last
SRT_HS__SIZE
SRT_HS_E_SIZE
};
// For HSv5 the lo and hi part is used for particular side's latency
@ -112,30 +118,21 @@ typedef Bits<15, 0> SRT_HS_LATENCY_SND;
typedef Bits<15, 0> SRT_HS_LATENCY_LEG;
// XXX These structures are currently unused. The code can be changed
// so that these are used instead of manual tailoring of the messages.
struct SrtHandshakeExtension
{
protected:
uint32_t m_SrtCommand; // Used only in extension
public:
SrtHandshakeExtension(int cmd)
{
m_SrtCommand = cmd;
}
void setCommand(int cmd)
{
m_SrtCommand = cmd;
}
int16_t type;
std::vector<uint32_t> contents;
SrtHandshakeExtension(int16_t cmd): type(cmd) {}
};
// Implemented in core.cpp, so far
void SrtExtractHandshakeExtensions(const char* bufbegin, size_t size,
std::vector<SrtHandshakeExtension>& w_output);
struct SrtHSRequest: public SrtHandshakeExtension
{
typedef Bits<31, 16> SRT_HSTYPE_ENCFLAGS;
typedef Bits<15, 0> SRT_HSTYPE_HSFLAGS;
@ -154,6 +151,19 @@ struct SrtHSRequest: public SrtHandshakeExtension
return base | SRT_HSTYPE_ENCFLAGS::wrap( SRT_PBKEYLEN_BITS::unwrap(crypto_keylen) );
}
// Group handshake extension layout
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Group ID |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Group Type | Group's Flags | Group's Weight |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
typedef Bits<31, 24> HS_GROUP_TYPE;
typedef Bits<23, 16> HS_GROUP_FLAGS;
typedef Bits<15, 0> HS_GROUP_WEIGHT;
private:
friend class CHandShake;
@ -229,25 +239,37 @@ enum UDTRequestType
// --> CONCLUSION (with response extensions, if RESPONDER)
// <-- AGREEMENT (sent exclusively by INITIATOR upon reception of CONCLUSIOn with response extensions)
// Errors reported by the peer, also used as useless error codes
// in handshake processing functions.
URQ_FAILURE_TYPES = 1000
// This marks the beginning of values that are error codes.
URQ_FAILURE_TYPES = 1000,
// NOTE: codes above 1000 are reserved for failure codes for
// rejection reason, as per `SRT_REJECT_REASON` enum. DO NOT
// add any new values here.
// rejection reason, as per `SRT_REJECT_REASON` enum. The
// actual rejection code is the value of the request type
// minus URQ_FAILURE_TYPES.
// This is in order to return standard error codes for server
// data retrieval failures.
URQ_SERVER_FAILURE_TYPES = URQ_FAILURE_TYPES + SRT_REJC_PREDEFINED,
// This is for a completely user-defined reject reasons.
URQ_USER_FAILURE_TYPES = URQ_FAILURE_TYPES + SRT_REJC_USERDEFINED
};
inline UDTRequestType URQFailure(SRT_REJECT_REASON reason)
inline UDTRequestType URQFailure(int reason)
{
return UDTRequestType(URQ_FAILURE_TYPES + int(reason));
}
inline SRT_REJECT_REASON RejectReasonForURQ(UDTRequestType req)
inline int RejectReasonForURQ(UDTRequestType req)
{
if (req < URQ_FAILURE_TYPES || req - URQ_FAILURE_TYPES >= SRT_REJ__SIZE)
if (req < URQ_FAILURE_TYPES)
return SRT_REJ_UNKNOWN;
return SRT_REJECT_REASON(req - URQ_FAILURE_TYPES);
int reason = req - URQ_FAILURE_TYPES;
if (reason < SRT_REJC_PREDEFINED && reason >= SRT_REJ_E_SIZE)
return SRT_REJ_UNKNOWN;
return reason;
}
// DEPRECATED values. Use URQFailure(SRT_REJECT_REASON).
@ -265,20 +287,20 @@ inline std::string RequestTypeStr(UDTRequestType) { return ""; }
class CHandShake
{
public:
CHandShake();
CHandShake();
int store_to(char* buf, ref_t<size_t> size);
int load_from(const char* buf, size_t size);
int store_to(char* buf, size_t& size);
int load_from(const char* buf, size_t size);
public:
// This is the size of SERIALIZED handshake.
// Might be defined as simply sizeof(CHandShake), but the
// enum values would have to be forced as int32_t, which is only
// available in C++11. Theoretically they are all 32-bit, but
// such a statement is not reliable and not portable.
static const size_t m_iContentSize = 48; // Size of hand shake data
// This is the size of SERIALIZED handshake.
// Might be defined as simply sizeof(CHandShake), but the
// enum values would have to be forced as int32_t, which is only
// available in C++11. Theoretically they are all 32-bit, but
// such a statement is not reliable and not portable.
static const size_t m_iContentSize = 48; // Size of hand shake data
// Extension flags
// Extension flags
static const int32_t HS_EXT_HSREQ = BIT(0);
static const int32_t HS_EXT_KMREQ = BIT(1);
@ -290,53 +312,55 @@ public:
int32_t flags() { return m_iType; }
public:
int32_t m_iVersion; // UDT version (HS_VERSION_* symbols)
int32_t m_iType; // UDT4: socket type (only UDT_DGRAM is valid); SRT1: extension flags
int32_t m_iISN; // random initial sequence number
int32_t m_iMSS; // maximum segment size
int32_t m_iFlightFlagSize; // flow control window size
UDTRequestType m_iReqType; // handshake stage
int32_t m_iID; // socket ID
int32_t m_iCookie; // cookie
uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to
int32_t m_iVersion; // UDT version (HS_VERSION_* symbols)
int32_t m_iType; // UDT4: socket type (only UDT_DGRAM is valid); SRT1: extension flags
int32_t m_iISN; // random initial sequence number
int32_t m_iMSS; // maximum segment size
int32_t m_iFlightFlagSize; // flow control window size
UDTRequestType m_iReqType; // handshake stage
int32_t m_iID; // SRT socket ID of HS sender
int32_t m_iCookie; // cookie
uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to
bool m_extension;
bool m_extension;
std::string show();
bool valid();
std::string show();
// The rendezvous state machine used in HSv5 only (in HSv4 everything is happening the old way).
//
// The WAVING state is the very initial state of the rendezvous connection and restored after the
// connection is closed.
// The ATTENTION and FINE are two alternative states that are transited to from WAVING. The possible
// situations are:
// - "serial arrangement": one party transits to ATTENTION and the other party transits to FINE
// - "parallel arrangement" both parties transit to ATTENTION
//
// Parallel arrangement is a "virtually impossible" case, in which both parties must send the first
// URQ_WAVEAHAND message in a perfect time synchronization, when they are started at exactly the same
// time, on machines with exactly the same performance and all things preceding the message sending
// have taken perfectly identical amount of time. This isn't anyhow possible otherwise because if
// the clients have started at different times, the one who started first sends a message and the
// system of the receiver buffers this message even before the client binds the port for enough long
// time so that it outlasts also the possible second, repeated waveahand.
enum RendezvousState
{
RDV_INVALID, //< This socket wasn't prepared for rendezvous process. Reject any events.
RDV_WAVING, //< Initial state for rendezvous. No contact seen from the peer.
RDV_ATTENTION, //< When received URQ_WAVEAHAND. [WAVING]:URQ_WAVEAHAND --> [ATTENTION].
RDV_FINE, //< When received URQ_CONCLUSION. [WAVING]:URQ_CONCLUSION --> [FINE].
RDV_INITIATED, //< When received URQ_CONCLUSION+HSREQ extension in ATTENTION state.
RDV_CONNECTED //< Final connected state. [ATTENTION]:URQ_CONCLUSION --> [CONNECTED] <-- [FINE]:URQ_AGREEMENT.
};
// The rendezvous state machine used in HSv5 only (in HSv4 everything is happening the old way).
//
// The WAVING state is the very initial state of the rendezvous connection and restored after the
// connection is closed.
// The ATTENTION and FINE are two alternative states that are transited to from WAVING. The possible
// situations are:
// - "serial arrangement": one party transits to ATTENTION and the other party transits to FINE
// - "parallel arrangement" both parties transit to ATTENTION
//
// Parallel arrangement is a "virtually impossible" case, in which both parties must send the first
// URQ_WAVEAHAND message in a perfect time synchronization, when they are started at exactly the same
// time, on machines with exactly the same performance and all things preceding the message sending
// have taken perfectly identical amount of time. This isn't anyhow possible otherwise because if
// the clients have started at different times, the one who started first sends a message and the
// system of the receiver buffers this message even before the client binds the port for enough long
// time so that it outlasts also the possible second, repeated waveahand.
enum RendezvousState
{
RDV_INVALID, //< This socket wasn't prepared for rendezvous process. Reject any events.
RDV_WAVING, //< Initial state for rendezvous. No contact seen from the peer.
RDV_ATTENTION, //< When received URQ_WAVEAHAND. [WAVING]:URQ_WAVEAHAND --> [ATTENTION].
RDV_FINE, //< When received URQ_CONCLUSION. [WAVING]:URQ_CONCLUSION --> [FINE].
RDV_INITIATED, //< When received URQ_CONCLUSION+HSREQ extension in ATTENTION state.
RDV_CONNECTED //< Final connected state. [ATTENTION]:URQ_CONCLUSION --> [CONNECTED] <-- [FINE]:URQ_AGREEMENT.
};
#if ENABLE_LOGGING
static std::string RdvStateStr(RendezvousState s);
static std::string RdvStateStr(RendezvousState s);
#else
static std::string RdvStateStr(RendezvousState) { return ""; }
static std::string RdvStateStr(RendezvousState) { return ""; }
#endif
};
} // namespace srt
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 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/.
*
*
*/
/*****************************************************************************
@ -50,60 +50,77 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_LIST_H__
#define __UDT_LIST_H__
#ifndef INC_SRT_LIST_H
#define INC_SRT_LIST_H
#include "udt.h"
#include "common.h"
namespace srt {
class CSndLossList
{
public:
CSndLossList(int size = 1024);
~CSndLossList();
CSndLossList(int size = 1024);
~CSndLossList();
/// Insert a seq. no. into the sender loss list.
/// @param [in] seqno1 sequence number starts.
/// @param [in] seqno2 sequence number ends.
/// @return number of packets that are not in the list previously.
/// Insert a seq. no. into the sender loss list.
/// @param [in] seqno1 sequence number starts.
/// @param [in] seqno2 sequence number ends.
/// @return number of packets that are not in the list previously.
int insert(int32_t seqno1, int32_t seqno2);
int insert(int32_t seqno1, int32_t seqno2);
/// Remove the given sequence number and all numbers that precede it.
/// @param [in] seqno sequence number.
void removeUpTo(int32_t seqno);
/// Remove ALL the seq. no. that are not greater than the parameter.
/// @param [in] seqno sequence number.
/// Read the loss length.∏
/// @return The length of the list.
int getLossLength() const;
void remove(int32_t seqno);
/// Read the first (smallest) loss seq. no. in the list and remove it.
/// @return The seq. no. or -1 if the list is empty.
int32_t popLostSeq();
/// Read the loss length.
/// @return The length of the list.
int getLossLength() const;
/// Read the first (smallest) loss seq. no. in the list and remove it.
/// @return The seq. no. or -1 if the list is empty.
int32_t popLostSeq();
void traceState() const;
private:
struct Seq
{
int32_t data1; // sequence number starts
int32_t data2; // seqnence number ends
int next; // next node in the list
}* m_caSeq;
struct Seq
{
int32_t seqstart; // sequence number starts
int32_t seqend; // sequence number ends
int inext; // index of the next node in the list
} * m_caSeq;
int m_iHead; // first node
int m_iLength; // loss length
int m_iSize; // size of the static array
int m_iLastInsertPos; // position of last insert node
int m_iHead; // first node
int m_iLength; // loss length
const int m_iSize; // size of the static array
int m_iLastInsertPos; // position of last insert node
mutable pthread_mutex_t m_ListLock; // used to synchronize list operation
mutable srt::sync::Mutex m_ListLock; // used to synchronize list operation
private:
CSndLossList(const CSndLossList&);
CSndLossList& operator=(const CSndLossList&);
/// Inserts an element to the beginning and updates head pointer.
/// No lock.
void insertHead(int pos, int32_t seqno1, int32_t seqno2);
/// Inserts an element after previous element.
/// No lock.
void insertAfter(int pos, int pos_after, int32_t seqno1, int32_t seqno2);
/// Check if it is possible to coalesce element at loc with further elements.
/// @param loc - last changed location
void coalesce(int loc);
/// Update existing element with the new range (increase only)
/// @param pos position of the element being updated
/// @param seqno1 first sequence number in range
/// @param seqno2 last sequence number in range (SRT_SEQNO_NONE if no range)
bool updateElement(int pos, int32_t seqno1, int32_t seqno2);
private:
CSndLossList(const CSndLossList&);
CSndLossList& operator=(const CSndLossList&);
};
////////////////////////////////////////////////////////////////////////////////
@ -111,123 +128,124 @@ private:
class CRcvLossList
{
public:
CRcvLossList(int size = 1024);
~CRcvLossList();
CRcvLossList(int size = 1024);
~CRcvLossList();
/// Insert a series of loss seq. no. between "seqno1" and "seqno2" into the receiver's loss list.
/// @param [in] seqno1 sequence number starts.
/// @param [in] seqno2 seqeunce number ends.
/// Insert a series of loss seq. no. between "seqno1" and "seqno2" into the receiver's loss list.
/// @param [in] seqno1 sequence number starts.
/// @param [in] seqno2 seqeunce number ends.
void insert(int32_t seqno1, int32_t seqno2);
void insert(int32_t seqno1, int32_t seqno2);
/// Remove a loss seq. no. from the receiver's loss list.
/// @param [in] seqno sequence number.
/// @return if the packet is removed (true) or no such lost packet is found (false).
/// Remove a loss seq. no. from the receiver's loss list.
/// @param [in] seqno sequence number.
/// @return if the packet is removed (true) or no such lost packet is found (false).
bool remove(int32_t seqno);
bool remove(int32_t seqno);
/// Remove all packets between seqno1 and seqno2.
/// @param [in] seqno1 start sequence number.
/// @param [in] seqno2 end sequence number.
/// @return if the packet is removed (true) or no such lost packet is found (false).
/// Remove all packets between seqno1 and seqno2.
/// @param [in] seqno1 start sequence number.
/// @param [in] seqno2 end sequence number.
/// @return if the packet is removed (true) or no such lost packet is found (false).
bool remove(int32_t seqno1, int32_t seqno2);
bool remove(int32_t seqno1, int32_t seqno2);
/// Find if there is any lost packets whose sequence number falling seqno1 and seqno2.
/// @param [in] seqno1 start sequence number.
/// @param [in] seqno2 end sequence number.
/// @return True if found; otherwise false.
/// Find if there is any lost packets whose sequence number falling seqno1 and seqno2.
/// @param [in] seqno1 start sequence number.
/// @param [in] seqno2 end sequence number.
/// @return True if found; otherwise false.
bool find(int32_t seqno1, int32_t seqno2) const;
bool find(int32_t seqno1, int32_t seqno2) const;
/// Read the loss length.
/// @return the length of the list.
/// Read the loss length.
/// @return the length of the list.
int getLossLength() const;
int getLossLength() const;
/// Read the first (smallest) seq. no. in the list.
/// @return the sequence number or -1 if the list is empty.
/// Read the first (smallest) seq. no. in the list.
/// @return the sequence number or -1 if the list is empty.
int getFirstLostSeq() const;
int32_t getFirstLostSeq() const;
/// Get a encoded loss array for NAK report.
/// @param [out] array the result list of seq. no. to be included in NAK.
/// @param [out] len physical length of the result array.
/// @param [in] limit maximum length of the array.
/// Get a encoded loss array for NAK report.
/// @param [out] array the result list of seq. no. to be included in NAK.
/// @param [out] len physical length of the result array.
/// @param [in] limit maximum length of the array.
void getLossArray(int32_t* array, int& len, int limit);
void getLossArray(int32_t* array, int& len, int limit);
private:
struct Seq
{
int32_t data1; // sequence number starts
int32_t data2; // sequence number ends
int next; // next node in the list
int prior; // prior node in the list;
}* m_caSeq;
struct Seq
{
int32_t seqstart; // sequence number starts
int32_t seqend; // sequence number ends
int inext; // index of the next node in the list
int iprior; // index of the previous node in the list
} * m_caSeq;
int m_iHead; // first node in the list
int m_iTail; // last node in the list;
int m_iLength; // loss length
int m_iSize; // size of the static array
int m_iHead; // first node in the list
int m_iTail; // last node in the list;
int m_iLength; // loss length
int m_iSize; // size of the static array
int m_iLargestSeq; // largest seq ever seen
private:
CRcvLossList(const CRcvLossList&);
CRcvLossList& operator=(const CRcvLossList&);
CRcvLossList(const CRcvLossList&);
CRcvLossList& operator=(const CRcvLossList&);
public:
struct iterator
{
int32_t head;
Seq* seq;
struct iterator
{
int32_t head;
Seq* seq;
iterator(Seq* str, int32_t v)
: head(v)
, seq(str)
{
}
iterator(Seq* str, int32_t v): head(v), seq(str) {}
iterator next() const
{
if (head == -1)
return *this; // should report error, but we can only throw exception, so simply ignore it.
iterator next() const
{
if ( head == -1 )
return *this; // should report error, but we can only throw exception, so simply ignore it.
return iterator(seq, seq[head].inext);
}
return iterator(seq, seq[head].next);
}
iterator& operator++()
{
*this = next();
return *this;
}
iterator& operator++()
{
*this = next();
return *this;
}
iterator operator++(int)
{
iterator old(seq, head);
*this = next();
return old;
}
iterator operator++(int)
{
iterator old (seq, head);
*this = next();
return old;
}
bool operator==(const iterator& second) const
{
// Ignore seq - should be the same and this is only a sanity check.
return head == second.head;
}
bool operator==(const iterator& second) const
{
// Ignore seq - should be the same and this is only a sanity check.
return head == second.head;
}
bool operator!=(const iterator& second) const { return !(*this == second); }
bool operator!=(const iterator& second) const { return !(*this == second); }
std::pair<int32_t, int32_t> operator*()
{
return std::make_pair(seq[head].data1, seq[head].data2);
}
};
iterator begin() { return iterator(m_caSeq, m_iHead); }
iterator end() { return iterator(m_caSeq, -1); }
std::pair<int32_t, int32_t> operator*() { return std::make_pair(seq[head].seqstart, seq[head].seqend); }
};
iterator begin() { return iterator(m_caSeq, m_iHead); }
iterator end() { return iterator(m_caSeq, -1); }
};
struct CRcvFreshLoss
{
int32_t seq[2];
int ttl;
uint64_t timestamp;
int32_t seq[2];
int ttl;
srt::sync::steady_clock::time_point timestamp;
CRcvFreshLoss(int32_t seqlo, int32_t seqhi, int initial_ttl);
@ -236,15 +254,18 @@ struct CRcvFreshLoss
#ifdef DELETE
#undef DELETE
#endif
enum Emod {
NONE, //< the given sequence was not found in this range
enum Emod
{
NONE, //< the given sequence was not found in this range
STRIPPED, //< it was equal to first or last, already taken care of
SPLIT, //< found in the middle, you have to split this range into two
DELETE //< This was a range of one element exactly equal to sequence. Simply delete it.
SPLIT, //< found in the middle, you have to split this range into two
DELETE //< This was a range of one element exactly equal to sequence. Simply delete it.
};
Emod revoke(int32_t sequence);
Emod revoke(int32_t lo, int32_t hi);
};
} // namespace srt
#endif

View file

@ -0,0 +1,53 @@
/*
WARNING: Generated from ../scripts/generate-logging-defs.tcl
DO NOT MODIFY.
Copyright applies as per the generator script.
*/
#include "srt.h"
#include "logging.h"
#include "logger_defs.h"
namespace srt_logging
{
AllFaOn::AllFaOn()
{
allfa.set(SRT_LOGFA_GENERAL, true);
allfa.set(SRT_LOGFA_SOCKMGMT, true);
allfa.set(SRT_LOGFA_CONN, true);
allfa.set(SRT_LOGFA_XTIMER, true);
allfa.set(SRT_LOGFA_TSBPD, true);
allfa.set(SRT_LOGFA_RSRC, true);
allfa.set(SRT_LOGFA_CONGEST, true);
allfa.set(SRT_LOGFA_PFILTER, true);
allfa.set(SRT_LOGFA_API_CTRL, true);
allfa.set(SRT_LOGFA_QUE_CTRL, true);
allfa.set(SRT_LOGFA_EPOLL_UPD, true);
allfa.set(SRT_LOGFA_API_RECV, true);
allfa.set(SRT_LOGFA_BUF_RECV, true);
allfa.set(SRT_LOGFA_QUE_RECV, true);
allfa.set(SRT_LOGFA_CHN_RECV, true);
allfa.set(SRT_LOGFA_GRP_RECV, true);
allfa.set(SRT_LOGFA_API_SEND, true);
allfa.set(SRT_LOGFA_BUF_SEND, true);
allfa.set(SRT_LOGFA_QUE_SEND, true);
allfa.set(SRT_LOGFA_CHN_SEND, true);
allfa.set(SRT_LOGFA_GRP_SEND, true);
allfa.set(SRT_LOGFA_INTERNAL, true);
allfa.set(SRT_LOGFA_QUE_MGMT, true);
allfa.set(SRT_LOGFA_CHN_MGMT, true);
allfa.set(SRT_LOGFA_GRP_MGMT, true);
allfa.set(SRT_LOGFA_EPOLL_API, true);
}
} // namespace srt_logging

View file

@ -0,0 +1,55 @@
/*
WARNING: Generated from ../scripts/generate-logging-defs.tcl
DO NOT MODIFY.
Copyright applies as per the generator script.
*/
#include "srt.h"
#include "logging.h"
#include "logger_defs.h"
namespace srt_logging { AllFaOn logger_fa_all; }
// We need it outside the namespace to preserve the global name.
// It's a part of "hidden API" (used by applications)
SRT_API srt_logging::LogConfig srt_logger_config(srt_logging::logger_fa_all.allfa);
namespace srt_logging
{
Logger gglog(SRT_LOGFA_GENERAL, srt_logger_config, "SRT.gg");
Logger smlog(SRT_LOGFA_SOCKMGMT, srt_logger_config, "SRT.sm");
Logger cnlog(SRT_LOGFA_CONN, srt_logger_config, "SRT.cn");
Logger xtlog(SRT_LOGFA_XTIMER, srt_logger_config, "SRT.xt");
Logger tslog(SRT_LOGFA_TSBPD, srt_logger_config, "SRT.ts");
Logger rslog(SRT_LOGFA_RSRC, srt_logger_config, "SRT.rs");
Logger cclog(SRT_LOGFA_CONGEST, srt_logger_config, "SRT.cc");
Logger pflog(SRT_LOGFA_PFILTER, srt_logger_config, "SRT.pf");
Logger aclog(SRT_LOGFA_API_CTRL, srt_logger_config, "SRT.ac");
Logger qclog(SRT_LOGFA_QUE_CTRL, srt_logger_config, "SRT.qc");
Logger eilog(SRT_LOGFA_EPOLL_UPD, srt_logger_config, "SRT.ei");
Logger arlog(SRT_LOGFA_API_RECV, srt_logger_config, "SRT.ar");
Logger brlog(SRT_LOGFA_BUF_RECV, srt_logger_config, "SRT.br");
Logger qrlog(SRT_LOGFA_QUE_RECV, srt_logger_config, "SRT.qr");
Logger krlog(SRT_LOGFA_CHN_RECV, srt_logger_config, "SRT.kr");
Logger grlog(SRT_LOGFA_GRP_RECV, srt_logger_config, "SRT.gr");
Logger aslog(SRT_LOGFA_API_SEND, srt_logger_config, "SRT.as");
Logger bslog(SRT_LOGFA_BUF_SEND, srt_logger_config, "SRT.bs");
Logger qslog(SRT_LOGFA_QUE_SEND, srt_logger_config, "SRT.qs");
Logger kslog(SRT_LOGFA_CHN_SEND, srt_logger_config, "SRT.ks");
Logger gslog(SRT_LOGFA_GRP_SEND, srt_logger_config, "SRT.gs");
Logger inlog(SRT_LOGFA_INTERNAL, srt_logger_config, "SRT.in");
Logger qmlog(SRT_LOGFA_QUE_MGMT, srt_logger_config, "SRT.qm");
Logger kmlog(SRT_LOGFA_CHN_MGMT, srt_logger_config, "SRT.km");
Logger gmlog(SRT_LOGFA_GRP_MGMT, srt_logger_config, "SRT.gm");
Logger ealog(SRT_LOGFA_EPOLL_API, srt_logger_config, "SRT.ea");
} // namespace srt_logging

View file

@ -0,0 +1,61 @@
/*
WARNING: Generated from ../scripts/generate-logging-defs.tcl
DO NOT MODIFY.
Copyright applies as per the generator script.
*/
#ifndef INC_SRT_LOGGER_DEFS_H
#define INC_SRT_LOGGER_DEFS_H
#include "srt.h"
#include "logging.h"
namespace srt_logging
{
struct AllFaOn
{
LogConfig::fa_bitset_t allfa;
AllFaOn();
};
extern Logger gglog;
extern Logger smlog;
extern Logger cnlog;
extern Logger xtlog;
extern Logger tslog;
extern Logger rslog;
extern Logger cclog;
extern Logger pflog;
extern Logger aclog;
extern Logger qclog;
extern Logger eilog;
extern Logger arlog;
extern Logger brlog;
extern Logger qrlog;
extern Logger krlog;
extern Logger grlog;
extern Logger aslog;
extern Logger bslog;
extern Logger qslog;
extern Logger kslog;
extern Logger gslog;
extern Logger inlog;
extern Logger qmlog;
extern Logger kmlog;
extern Logger gmlog;
extern Logger ealog;
} // namespace srt_logging
#endif

View file

@ -13,8 +13,8 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__SRT_LOGGING_H
#define INC__SRT_LOGGING_H
#ifndef INC_SRT_LOGGING_H
#define INC_SRT_LOGGING_H
#include <iostream>
@ -28,16 +28,13 @@ written by
#else
#include <sys/time.h>
#endif
#include <pthread.h>
#if HAVE_CXX11
#include <mutex>
#endif
#include "srt.h"
#include "utilities.h"
#include "threadname.h"
#include "logging_api.h"
#include "srt_compat.h"
#include "sync.h"
#ifdef __GNUC__
#define PRINTF_LIKE __attribute__((format(printf,2,3)))
@ -53,17 +50,24 @@ written by
// LOGC uses an iostream-like syntax, using the special 'log' symbol.
// This symbol isn't visible outside the log macro parameters.
// Usage: LOGC(mglog.Debug, log << param1 << param2 << param3);
#define LOGC(logdes, args) if (logdes.CheckEnabled()) { srt_logging::LogDispatcher::Proxy log(logdes); log.setloc(__FILE__, __LINE__, __FUNCTION__); args; }
// Usage: LOGC(gglog.Debug, log << param1 << param2 << param3);
#define LOGC(logdes, args) if (logdes.CheckEnabled()) \
{ \
srt_logging::LogDispatcher::Proxy log(logdes); \
log.setloc(__FILE__, __LINE__, __FUNCTION__); \
const srt_logging::LogDispatcher::Proxy& log_prox SRT_ATR_UNUSED = args; \
}
// LOGF uses printf-like style formatting.
// Usage: LOGF(mglog.Debug, "%s: %d", param1.c_str(), int(param2));
// Usage: LOGF(gglog.Debug, "%s: %d", param1.c_str(), int(param2));
#define LOGF(logdes, ...) if (logdes.CheckEnabled()) logdes().setloc(__FILE__, __LINE__, __FUNCTION__).form(__VA_ARGS__)
// LOGP is C++11 only OR with only one string argument.
// Usage: LOGP(mglog.Debug, param1, param2, param3);
// Usage: LOGP(gglog.Debug, param1, param2, param3);
#define LOGP(logdes, ...) if (logdes.CheckEnabled()) logdes.printloc(__FILE__, __LINE__, __FUNCTION__,##__VA_ARGS__)
#define IF_LOGGING(instr) instr
#if ENABLE_HEAVY_LOGGING
#define HLOGC LOGC
@ -93,6 +97,7 @@ written by
#define HLOGP(...)
#define IF_HEAVY_LOGGING(instr) (void)0
#define IF_LOGGING(instr) (void)0
#endif
@ -107,29 +112,30 @@ struct LogConfig
std::ostream* log_stream;
SRT_LOG_HANDLER_FN* loghandler_fn;
void* loghandler_opaque;
pthread_mutex_t mutex;
srt::sync::Mutex mutex;
int flags;
LogConfig(const fa_bitset_t& initial_fa):
enabled_fa(initial_fa),
max_level(LogLevel::warning),
log_stream(&std::cerr)
LogConfig(const fa_bitset_t& efa,
LogLevel::type l = LogLevel::warning,
std::ostream* ls = &std::cerr)
: enabled_fa(efa)
, max_level(l)
, log_stream(ls)
, loghandler_fn()
, loghandler_opaque()
, flags()
{
pthread_mutex_init(&mutex, 0);
}
LogConfig(const fa_bitset_t& efa, LogLevel::type l, std::ostream* ls):
enabled_fa(efa), max_level(l), log_stream(ls)
{
pthread_mutex_init(&mutex, 0);
}
~LogConfig()
{
pthread_mutex_destroy(&mutex);
}
void lock() { pthread_mutex_lock(&mutex); }
void unlock() { pthread_mutex_unlock(&mutex); }
SRT_ATTR_ACQUIRE(mutex)
void lock() { mutex.lock(); }
SRT_ATTR_RELEASE(mutex)
void unlock() { mutex.unlock(); }
};
// The LogDispatcher class represents the object that is responsible for
@ -142,7 +148,6 @@ private:
static const size_t MAX_PREFIX_SIZE = 32;
char prefix[MAX_PREFIX_SIZE+1];
LogConfig* src_config;
pthread_mutex_t mutex;
bool isset(int flg) { return (src_config->flags & flg) != 0; }
@ -169,12 +174,10 @@ public:
strcat(prefix, ":");
strcat(prefix, logger_pfx);
}
pthread_mutex_init(&mutex, 0);
}
~LogDispatcher()
{
pthread_mutex_destroy(&mutex);
}
bool CheckEnabled();
@ -244,6 +247,11 @@ public:
return *this;
}
DummyProxy& vform(const char*, va_list)
{
return *this;
}
DummyProxy& setloc(const char* , int , std::string)
{
return *this;
@ -292,7 +300,7 @@ struct LogDispatcher::Proxy
// or better __func__.
std::string ExtractName(std::string pretty_function);
Proxy(LogDispatcher& guy);
Proxy(LogDispatcher& guy);
// Copy constructor is needed due to noncopyable ostringstream.
// This is used only in creation of the default object, so just
@ -407,7 +415,6 @@ inline bool LogDispatcher::CheckEnabled()
return configured_enabled_fa && level <= configured_maxlevel;
}
SRT_API std::string FormatTime(uint64_t time);
#if HAVE_CXX11
@ -423,7 +430,7 @@ inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args)
}
template <class... Args>
inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, Args&&... args ATR_UNUSED)
inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, Args&&... args SRT_ATR_UNUSED)
{
#ifdef ENABLE_LOGGING
std::ostringstream serr;
@ -441,7 +448,7 @@ inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line AT
#else
template <class Arg>
inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, const Arg& arg ATR_UNUSED)
inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, const Arg& arg SRT_ATR_UNUSED)
{
#ifdef ENABLE_LOGGING
std::ostringstream serr;

View file

@ -13,8 +13,8 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__SRT_LOGGING_API_H
#define INC__SRT_LOGGING_API_H
#ifndef INC_SRT_LOGGING_API_H
#define INC_SRT_LOGGING_API_H
// These are required for access functions:
// - adding FA (requires set)
@ -24,7 +24,6 @@ written by
#include <iostream>
#endif
#include <pthread.h>
#ifdef _WIN32
#include "win/syslog_defs.h"
#else

View file

@ -54,6 +54,12 @@
#include "md5.h"
#include <string.h>
/*
* All symbols have been put under the srt namespace
* to avoid potential linkage conflicts.
*/
namespace srt {
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
#ifdef ARCH_IS_BIG_ENDIAN
# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
@ -166,7 +172,7 @@ md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
X = (const md5_word_t *)data;
} else {
/* not aligned */
memcpy(xbuf, data, 64);
memcpy((xbuf), data, 64);
X = xbuf;
}
}
@ -340,7 +346,7 @@ md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
if (offset) {
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
memcpy(pms->buf + offset, p, copy);
memcpy((pms->buf + offset), p, copy);
if (offset + copy < 64)
return;
p += copy;
@ -354,7 +360,7 @@ md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
/* Process a final partial block. */
if (left)
memcpy(pms->buf, p, left);
memcpy((pms->buf), p, left);
}
void
@ -379,3 +385,5 @@ md5_finish(md5_state_t *pms, md5_byte_t digest[16])
for (i = 0; i < 16; ++i)
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
}
} // namespace srt

View file

@ -50,6 +50,12 @@
#ifndef md5_INCLUDED
# define md5_INCLUDED
/*
* All symbols have been put under the srt namespace
* to avoid potential linkage conflicts.
*/
namespace srt {
/*
* This package supports both compile-time and run-time determination of CPU
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
@ -70,11 +76,6 @@ typedef struct md5_state_s {
md5_byte_t buf[64]; /* accumulate block */
} md5_state_t;
#ifdef __cplusplus
extern "C"
{
#endif
/* Initialize the algorithm. */
void md5_init(md5_state_t *pms);
@ -84,8 +85,6 @@ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
/* Finish the message and return the digest. */
void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
#ifdef __cplusplus
} /* end extern "C" */
#endif
} // namespace srt
#endif /* md5_INCLUDED */

View file

@ -13,10 +13,12 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__NETINET_ANY_H
#define INC__NETINET_ANY_H
#ifndef INC_SRT_NETINET_ANY_H
#define INC_SRT_NETINET_ANY_H
#include <cstring>
#include <cstring> // memcmp
#include <string>
#include <sstream>
#include "platform_sys.h"
// This structure should replace every use of sockaddr and its currently
@ -25,6 +27,9 @@ written by
// You can use the instances of sockaddr_any in every place where sockaddr is
// required.
namespace srt
{
struct sockaddr_any
{
union
@ -33,27 +38,234 @@ struct sockaddr_any
sockaddr_in6 sin6;
sockaddr sa;
};
socklen_t len;
sockaddr_any(int domain = AF_INET)
// The type is intended to be the same as the length
// parameter in ::accept, ::bind and ::connect functions.
// This is the type used by SRT.
typedef int len_t;
// This is the type used by system functions
#ifdef _WIN32
typedef int syslen_t;
#else
typedef socklen_t syslen_t;
#endif
// Note: by having `len_t` type here the usage in
// API functions is here limited to SRT. For system
// functions you can pass the address here as (socklen_t*)&sa.len,
// but just do it on your own risk, as there's no guarantee
// that sizes of `int` and `socklen_t` do not differ. The safest
// way seems to be using an intermediate proxy to be written
// back here from the value of `syslen_t`.
len_t len;
struct SysLenWrapper
{
memset(this, 0, sizeof *this);
sa.sa_family = domain;
len = size();
syslen_t syslen;
len_t& backwriter;
syslen_t* operator&() { return &syslen; }
SysLenWrapper(len_t& source): syslen(source), backwriter(source)
{
}
~SysLenWrapper()
{
backwriter = syslen;
}
};
// Usage:
// ::accept(lsn_sock, sa.get(), &sa.syslen());
SysLenWrapper syslen()
{
return SysLenWrapper(len);
}
socklen_t size() const
static size_t storage_size()
{
switch (sa.sa_family)
typedef union
{
case AF_INET: return socklen_t(sizeof sin);
case AF_INET6: return socklen_t(sizeof sin6);
sockaddr_in sin;
sockaddr_in6 sin6;
sockaddr sa;
} ucopy;
return sizeof (ucopy);
}
default: return 0; // fallback, impossible
void reset()
{
// sin6 is the largest field
memset((&sin6), 0, sizeof sin6);
len = 0;
}
// Default domain is unspecified, and
// in this case the size is 0.
// Note that AF_* (and alias PF_*) types have
// many various values, of which only
// AF_INET and AF_INET6 are handled here.
// Others make the same effect as unspecified.
explicit sockaddr_any(int domain = AF_UNSPEC)
{
// Default domain is "unspecified", 0
reset();
// Overriding family as required in the parameters
// and the size then accordingly.
sa.sa_family = domain == AF_INET || domain == AF_INET6 ? domain : AF_UNSPEC;
switch (domain)
{
case AF_INET:
len = len_t(sizeof (sockaddr_in));
break;
// Use size of sin6 as the default size
// len must be properly set so that the
// family-less sockaddr is passed to bind/accept
default:
len = len_t(sizeof (sockaddr_in6));
break;
}
}
sockaddr_any(const sockaddr_storage& stor)
{
// Here the length isn't passed, so just rely on family.
set((const sockaddr*)&stor);
}
sockaddr_any(const sockaddr* source, len_t namelen = 0)
{
if (namelen == 0)
set(source);
else
set(source, namelen);
}
void set(const sockaddr* source)
{
// Less safe version, simply trust the caller that the
// memory at 'source' is also large enough to contain
// all data required for particular family.
if (source->sa_family == AF_INET)
{
memcpy((&sin), source, sizeof sin);
len = sizeof sin;
}
else if (source->sa_family == AF_INET6)
{
memcpy((&sin6), source, sizeof sin6);
len = sizeof sin6;
}
else
{
// Error fallback: no other families than IP are regarded.
// Note: socket set up this way isn't intended to be used
// for bind/accept.
sa.sa_family = AF_UNSPEC;
len = 0;
}
}
void set(const sockaddr* source, syslen_t namelen)
{
// It's not safe to copy it directly, so check.
if (source->sa_family == AF_INET && namelen >= syslen_t(sizeof sin))
{
memcpy((&sin), source, sizeof sin);
len = sizeof sin;
}
else if (source->sa_family == AF_INET6 && namelen >= syslen_t(sizeof sin6))
{
// Note: this isn't too safe, may crash for stupid values
// of source->sa_family or any other data
// in the source structure, so make sure it's correct first.
memcpy((&sin6), source, sizeof sin6);
len = sizeof sin6;
}
else
{
reset();
}
}
void set(const sockaddr_in& in4)
{
memcpy((&sin), &in4, sizeof in4);
len = sizeof in4;
}
void set(const sockaddr_in6& in6)
{
memcpy((&sin6), &in6, sizeof in6);
len = sizeof in6;
}
sockaddr_any(const in_addr& i4_adr, uint16_t port)
{
// Some cases require separately IPv4 address passed as in_addr,
// so port is given separately.
sa.sa_family = AF_INET;
sin.sin_addr = i4_adr;
sin.sin_port = htons(port);
len = sizeof sin;
}
sockaddr_any(const in6_addr& i6_adr, uint16_t port)
{
sa.sa_family = AF_INET6;
sin6.sin6_addr = i6_adr;
sin6.sin6_port = htons(port);
len = sizeof sin6;
}
static len_t size(int family)
{
switch (family)
{
case AF_INET:
return len_t(sizeof (sockaddr_in));
case AF_INET6:
return len_t(sizeof (sockaddr_in6));
default:
return 0; // fallback
}
}
bool empty() const
{
bool isempty = true; // unspec-family address is always empty
if (sa.sa_family == AF_INET)
{
isempty = (sin.sin_port == 0
&& sin.sin_addr.s_addr == 0);
}
else if (sa.sa_family == AF_INET6)
{
isempty = (sin6.sin6_port == 0
&& memcmp(&sin6.sin6_addr, &in6addr_any, sizeof in6addr_any) == 0);
}
// otherwise isempty stays with default false
return isempty;
}
len_t size() const
{
return size(sa.sa_family);
}
int family() const { return sa.sa_family; }
void family(int val)
{
sa.sa_family = val;
len = size();
}
// port is in exactly the same location in both sin and sin6
// and has the same size. This is actually yet another common
@ -71,10 +283,26 @@ struct sockaddr_any
}
sockaddr* get() { return &sa; }
sockaddr* operator&() { return &sa; }
const sockaddr* get() const { return &sa; }
const sockaddr* operator&() const { return &sa; }
// Sometimes you need to get the address
// the way suitable for e.g. inet_ntop.
const void* get_addr() const
{
if (sa.sa_family == AF_INET)
return &sin.sin_addr.s_addr;
if (sa.sa_family == AF_INET6)
return &sin6.sin6_addr;
return NULL;
}
void* get_addr()
{
const sockaddr_any* that = this;
return (void*)that->get_addr();
}
template <int> struct TypeMap;
@ -85,7 +313,26 @@ struct sockaddr_any
{
bool operator()(const sockaddr_any& c1, const sockaddr_any& c2)
{
return memcmp(&c1, &c2, sizeof(c1)) == 0;
if (c1.family() != c2.family())
return false;
// Cannot use memcmp due to having in some systems
// another field like sockaddr_in::sin_len. This exists
// in some BSD-derived systems, but is not required by POSIX.
// Therefore sockaddr_any class cannot operate with it,
// as in this situation it would be safest to state that
// particular implementations may have additional fields
// of different purpose beside those required by POSIX.
//
// The only reliable way to compare two underlying sockaddr
// object is then to compare the port value and the address
// value.
//
// Fortunately the port is 16-bit and located at the same
// offset in both sockaddr_in and sockaddr_in6.
return c1.sin.sin_port == c2.sin.sin_port
&& c1.equal_address(c2);
}
};
@ -120,6 +367,50 @@ struct sockaddr_any
return memcmp(&c1, &c2, sizeof(c1)) < 0;
}
};
// Tests if the current address is the "any" wildcard.
bool isany() const
{
if (sa.sa_family == AF_INET)
return sin.sin_addr.s_addr == INADDR_ANY;
if (sa.sa_family == AF_INET6)
return memcmp(&sin6.sin6_addr, &in6addr_any, sizeof in6addr_any) == 0;
return false;
}
// Debug support
std::string str() const
{
if (family() != AF_INET && family() != AF_INET6)
return "unknown:0";
std::ostringstream output;
char hostbuf[1024];
int flags;
#if ENABLE_GETNAMEINFO
flags = NI_NAMEREQD;
#else
flags = NI_NUMERICHOST | NI_NUMERICSERV;
#endif
if (!getnameinfo(get(), size(), hostbuf, 1024, NULL, 0, flags))
{
output << hostbuf;
}
output << ":" << hport();
return output.str();
}
bool operator==(const sockaddr_any& other) const
{
return Equal()(*this, other);
}
bool operator!=(const sockaddr_any& other) const { return !(*this == other); }
};
template<> struct sockaddr_any::TypeMap<AF_INET> { typedef sockaddr_in type; };
@ -130,4 +421,6 @@ inline sockaddr_any::TypeMap<AF_INET>::type& sockaddr_any::get<AF_INET>() { retu
template <>
inline sockaddr_any::TypeMap<AF_INET6>::type& sockaddr_any::get<AF_INET6>() { return sin6; }
} // namespace srt
#endif

View file

@ -1,11 +1,11 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 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/.
*
*
*/
/*****************************************************************************
@ -50,7 +50,6 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@ -137,9 +136,9 @@ modified by
// Add. Info: Error code
// Control Info: None
// 0x7FFF: Explained by bits 16 - 31 (UMSG_EXT)
//
//
// bit 16 - 31:
// This space is used for future expansion or user defined control packets.
// This space is used for future expansion or user defined control packets.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@ -159,26 +158,29 @@ modified by
// For any single loss or consectutive loss less than 2 packets, use
// the original sequence numbers in the field.
#include "platform_sys.h"
#include <cstring>
#include "packet.h"
#include "handshake.h"
#include "logging.h"
#include "handshake.h"
namespace srt_logging
{
extern Logger mglog;
extern Logger inlog;
}
using namespace srt_logging;
// Set up the aliases in the constructure
CPacket::CPacket():
__pad(),
m_data_owned(false),
m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])),
m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])),
m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])),
m_iID((int32_t&)(m_nHeader[SRT_PH_ID])),
m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef()))
srt::CPacket::CPacket()
: m_extra_pad()
, m_data_owned(false)
, m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO]))
, m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO]))
, m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP]))
, m_iID((int32_t&)(m_nHeader[SRT_PH_ID]))
, m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef()))
{
m_nHeader.clear();
@ -192,166 +194,236 @@ m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef()))
m_PacketVector[PV_DATA].set(NULL, 0);
}
void CPacket::allocate(size_t alloc_buffer_size)
char* srt::CPacket::getData()
{
return (char*)m_PacketVector[PV_DATA].dataRef();
}
void srt::CPacket::allocate(size_t alloc_buffer_size)
{
if (m_data_owned)
{
if (getLength() == alloc_buffer_size)
return; // already allocated
// Would be nice to reallocate; for now just allocate again.
delete[] m_pcData;
}
m_PacketVector[PV_DATA].set(new char[alloc_buffer_size], alloc_buffer_size);
m_data_owned = true;
}
void CPacket::deallocate()
void srt::CPacket::deallocate()
{
if (m_data_owned)
delete [] (char*)m_PacketVector[PV_DATA].data();
delete[](char*) m_PacketVector[PV_DATA].data();
m_PacketVector[PV_DATA].set(NULL, 0);
}
CPacket::~CPacket()
char* srt::CPacket::release()
{
// When not owned, release returns NULL.
char* buffer = NULL;
if (m_data_owned)
{
buffer = getData();
m_data_owned = false;
}
deallocate(); // won't delete because m_data_owned == false
return buffer;
}
srt::CPacket::~CPacket()
{
// PV_HEADER is always owned, PV_DATA may use a "borrowed" buffer.
// Delete the internal buffer only if it was declared as owned.
if (m_data_owned)
delete[](char*)m_PacketVector[PV_DATA].data();
delete[](char*) m_PacketVector[PV_DATA].data();
}
size_t CPacket::getLength() const
size_t srt::CPacket::getLength() const
{
return m_PacketVector[PV_DATA].size();
return m_PacketVector[PV_DATA].size();
}
void CPacket::setLength(size_t len)
void srt::CPacket::setLength(size_t len)
{
m_PacketVector[PV_DATA].setLength(len);
m_PacketVector[PV_DATA].setLength(len);
}
void CPacket::pack(UDTMessageType pkttype, const void* lparam, void* rparam, int size)
void srt::CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size)
{
// Set (bit-0 = 1) and (bit-1~15 = type)
setControl(pkttype);
HLOGC(inlog.Debug,
log << "pack: type=" << MessageTypeStr(pkttype) << " ARG=" << (lparam ? Sprint(*lparam) : std::string("NULL"))
<< " [ " << (rparam ? Sprint(*(int32_t*)rparam) : std::string()) << " ]");
// Set additional information and control information field
switch (pkttype)
{
case UMSG_ACK: //0010 - Acknowledgement (ACK)
// ACK packet seq. no.
if (NULL != lparam)
m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam;
// Set additional information and control information field
switch (pkttype)
{
case UMSG_ACK: // 0010 - Acknowledgement (ACK)
// ACK packet seq. no.
if (NULL != lparam)
m_nHeader[SRT_PH_MSGNO] = *lparam;
// data ACK seq. no.
// optional: RTT (microsends), RTT variance (microseconds) advertised flow window size (packets), and estimated link capacity (packets per second)
m_PacketVector[PV_DATA].set(rparam, size);
// data ACK seq. no.
// optional: RTT (microsends), RTT variance (microseconds) advertised flow window size (packets), and estimated
// link capacity (packets per second)
m_PacketVector[PV_DATA].set(rparam, size);
break;
break;
case UMSG_ACKACK: //0110 - Acknowledgement of Acknowledgement (ACK-2)
// ACK packet seq. no.
m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam;
case UMSG_ACKACK: // 0110 - Acknowledgement of Acknowledgement (ACK-2)
// ACK packet seq. no.
m_nHeader[SRT_PH_MSGNO] = *lparam;
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void*)&m_extra_pad, 4);
break;
break;
case UMSG_LOSSREPORT: //0011 - Loss Report (NAK)
// loss list
m_PacketVector[PV_DATA].set(rparam, size);
case UMSG_LOSSREPORT: // 0011 - Loss Report (NAK)
// loss list
m_PacketVector[PV_DATA].set(rparam, size);
break;
break;
case UMSG_CGWARNING: //0100 - Congestion Warning
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
break;
case UMSG_CGWARNING: // 0100 - Congestion Warning
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void*)&m_extra_pad, 4);
case UMSG_KEEPALIVE: //0001 - Keep-alive
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
break;
break;
case UMSG_KEEPALIVE: // 0001 - Keep-alive
if (lparam)
{
// XXX EXPERIMENTAL. Pass the 32-bit integer here.
m_nHeader[SRT_PH_MSGNO] = *lparam;
}
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void*)&m_extra_pad, 4);
case UMSG_HANDSHAKE: //0000 - Handshake
// control info filed is handshake info
m_PacketVector[PV_DATA].set(rparam, size);
break;
break;
case UMSG_HANDSHAKE: // 0000 - Handshake
// control info filed is handshake info
m_PacketVector[PV_DATA].set(rparam, size);
case UMSG_SHUTDOWN: //0101 - Shutdown
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
break;
break;
case UMSG_SHUTDOWN: // 0101 - Shutdown
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void*)&m_extra_pad, 4);
case UMSG_DROPREQ: //0111 - Message Drop Request
// msg id
m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam;
break;
//first seq no, last seq no
m_PacketVector[PV_DATA].set(rparam, size);
case UMSG_DROPREQ: // 0111 - Message Drop Request
// msg id
m_nHeader[SRT_PH_MSGNO] = *lparam;
break;
// first seq no, last seq no
m_PacketVector[PV_DATA].set(rparam, size);
case UMSG_PEERERROR: //1000 - Error Signal from the Peer Side
// Error type
m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam;
break;
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
case UMSG_PEERERROR: // 1000 - Error Signal from the Peer Side
// Error type
m_nHeader[SRT_PH_MSGNO] = *lparam;
break;
// control info field should be none
// but "writev" does not allow this
m_PacketVector[PV_DATA].set((void*)&m_extra_pad, 4);
case UMSG_EXT: //0x7FFF - Reserved for user defined control packets
// for extended control packet
// "lparam" contains the extended type information for bit 16 - 31
// "rparam" is the control information
m_nHeader[SRT_PH_SEQNO] |= *(int32_t *)lparam;
break;
if (NULL != rparam)
{
m_PacketVector[PV_DATA].set(rparam, size);
}
else
{
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
}
case UMSG_EXT: // 0x7FFF - Reserved for user defined control packets
// for extended control packet
// "lparam" contains the extended type information for bit 16 - 31
// "rparam" is the control information
m_nHeader[SRT_PH_SEQNO] |= *lparam;
break;
if (NULL != rparam)
{
m_PacketVector[PV_DATA].set(rparam, size);
}
else
{
m_PacketVector[PV_DATA].set((void*)&m_extra_pad, 4);
}
default:
break;
}
break;
default:
break;
}
}
IOVector* CPacket::getPacketVector()
void srt::CPacket::toNL()
{
return m_PacketVector;
// XXX USE HtoNLA!
if (isControl())
{
for (ptrdiff_t i = 0, n = getLength() / 4; i < n; ++i)
*((uint32_t*)m_pcData + i) = htonl(*((uint32_t*)m_pcData + i));
}
// convert packet header into network order
uint32_t* p = m_nHeader;
for (int j = 0; j < 4; ++j)
{
*p = htonl(*p);
++p;
}
}
UDTMessageType CPacket::getType() const
void srt::CPacket::toHL()
{
// convert back into local host order
uint32_t* p = m_nHeader;
for (int k = 0; k < 4; ++k)
{
*p = ntohl(*p);
++p;
}
if (isControl())
{
for (ptrdiff_t l = 0, n = getLength() / 4; l < n; ++l)
*((uint32_t*)m_pcData + l) = ntohl(*((uint32_t*)m_pcData + l));
}
}
srt::IOVector* srt::CPacket::getPacketVector()
{
return m_PacketVector;
}
srt::UDTMessageType srt::CPacket::getType() const
{
return UDTMessageType(SEQNO_MSGTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]));
}
int CPacket::getExtendedType() const
int srt::CPacket::getExtendedType() const
{
return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]);
}
int32_t CPacket::getAckSeqNo() const
int32_t srt::CPacket::getAckSeqNo() const
{
// read additional information field
// This field is used only in UMSG_ACK and UMSG_ACKACK,
// so 'getAckSeqNo' symbolically defines the only use of it
// in case of CONTROL PACKET.
return m_nHeader[SRT_PH_MSGNO];
// read additional information field
// This field is used only in UMSG_ACK and UMSG_ACKACK,
// so 'getAckSeqNo' symbolically defines the only use of it
// in case of CONTROL PACKET.
return m_nHeader[SRT_PH_MSGNO];
}
uint16_t CPacket::getControlFlags() const
uint16_t srt::CPacket::getControlFlags() const
{
// This returns exactly the "extended type" value,
// which is not used at all in case when the standard
@ -360,19 +432,19 @@ uint16_t CPacket::getControlFlags() const
return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]);
}
PacketBoundary CPacket::getMsgBoundary() const
srt::PacketBoundary srt::CPacket::getMsgBoundary() const
{
return PacketBoundary(MSGNO_PACKET_BOUNDARY::unwrap(m_nHeader[SRT_PH_MSGNO]));
}
bool CPacket::getMsgOrderFlag() const
bool srt::CPacket::getMsgOrderFlag() const
{
return 0!= MSGNO_PACKET_INORDER::unwrap(m_nHeader[SRT_PH_MSGNO]);
return 0 != MSGNO_PACKET_INORDER::unwrap(m_nHeader[SRT_PH_MSGNO]);
}
int32_t CPacket::getMsgSeq(bool has_rexmit) const
int32_t srt::CPacket::getMsgSeq(bool has_rexmit) const
{
if ( has_rexmit )
if (has_rexmit)
{
return MSGNO_SEQ::unwrap(m_nHeader[SRT_PH_MSGNO]);
}
@ -382,13 +454,13 @@ int32_t CPacket::getMsgSeq(bool has_rexmit) const
}
}
bool CPacket::getRexmitFlag() const
bool srt::CPacket::getRexmitFlag() const
{
// return false; //
return 0 != MSGNO_REXMIT::unwrap(m_nHeader[SRT_PH_MSGNO]);
return 0 != MSGNO_REXMIT::unwrap(m_nHeader[SRT_PH_MSGNO]);
}
EncryptionKeySpec CPacket::getMsgCryptoFlags() const
srt::EncryptionKeySpec srt::CPacket::getMsgCryptoFlags() const
{
return EncryptionKeySpec(MSGNO_ENCKEYSPEC::unwrap(m_nHeader[SRT_PH_MSGNO]));
}
@ -396,82 +468,31 @@ EncryptionKeySpec CPacket::getMsgCryptoFlags() const
// This is required as the encryption/decryption happens in place.
// This is required to clear off the flags after decryption or set
// crypto flags after encrypting a packet.
void CPacket::setMsgCryptoFlags(EncryptionKeySpec spec)
void srt::CPacket::setMsgCryptoFlags(EncryptionKeySpec spec)
{
int32_t clr_msgno = m_nHeader[SRT_PH_MSGNO] & ~MSGNO_ENCKEYSPEC::mask;
int32_t clr_msgno = m_nHeader[SRT_PH_MSGNO] & ~MSGNO_ENCKEYSPEC::mask;
m_nHeader[SRT_PH_MSGNO] = clr_msgno | EncryptionKeyBits(spec);
}
/*
Leaving old code for historical reasons. This is moved to CSRTCC.
EncryptionStatus CPacket::encrypt(HaiCrypt_Handle hcrypto)
uint32_t srt::CPacket::getMsgTimeStamp() const
{
if ( !hcrypto )
{
LOGC(mglog.Error, log << "IPE: NULL crypto passed to CPacket::encrypt!");
return ENCS_FAILED;
}
int rc = HaiCrypt_Tx_Data(hcrypto, (uint8_t *)m_nHeader.raw(), (uint8_t *)m_pcData, m_PacketVector[PV_DATA].iov_len);
if ( rc < 0 )
{
// -1: encryption failure
// 0: key not received yet
return ENCS_FAILED;
} else if (rc > 0) {
m_PacketVector[PV_DATA].iov_len = rc;
}
return ENCS_CLEAR;
// SRT_DEBUG_TSBPD_WRAP may enable smaller timestamp for faster wraparoud handling tests
return (uint32_t)m_nHeader[SRT_PH_TIMESTAMP] & TIMESTAMP_MASK;
}
EncryptionStatus CPacket::decrypt(HaiCrypt_Handle hcrypto)
srt::CPacket* srt::CPacket::clone() const
{
if (getMsgCryptoFlags() == EK_NOENC)
{
//HLOGC(mglog.Debug, log << "CPacket::decrypt: packet not encrypted");
return ENCS_CLEAR; // not encrypted, no need do decrypt, no flags to be modified
}
CPacket* pkt = new CPacket;
memcpy((pkt->m_nHeader), m_nHeader, HDR_SIZE);
pkt->m_pcData = new char[m_PacketVector[PV_DATA].size()];
memcpy((pkt->m_pcData), m_pcData, m_PacketVector[PV_DATA].size());
pkt->m_PacketVector[PV_DATA].setLength(m_PacketVector[PV_DATA].size());
if (!hcrypto)
{
LOGC(mglog.Error, log << "IPE: NULL crypto passed to CPacket::decrypt!");
return ENCS_FAILED; // "invalid argument" (leave encryption flags untouched)
}
int rc = HaiCrypt_Rx_Data(hcrypto, (uint8_t *)m_nHeader.raw(), (uint8_t *)m_pcData, m_PacketVector[PV_DATA].iov_len);
if ( rc <= 0 )
{
// -1: decryption failure
// 0: key not received yet
return ENCS_FAILED;
}
// Otherwise: rc == decrypted text length.
m_PacketVector[PV_DATA].iov_len = rc; // In case clr txt size is different from cipher txt
// Decryption succeeded. Update flags.
m_nHeader[SRT_PH_MSGNO] &= ~MSGNO_ENCKEYSPEC::mask; // sets EK_NOENC to ENCKEYSPEC bits.
return ENCS_CLEAR;
return pkt;
}
*/
uint32_t CPacket::getMsgTimeStamp() const
namespace srt
{
// SRT_DEBUG_TSBPD_WRAP may enable smaller timestamp for faster wraparoud handling tests
return (uint32_t)m_nHeader[SRT_PH_TIMESTAMP] & TIMESTAMP_MASK;
}
CPacket* CPacket::clone() const
{
CPacket* pkt = new CPacket;
memcpy(pkt->m_nHeader, m_nHeader, HDR_SIZE);
pkt->m_pcData = new char[m_PacketVector[PV_DATA].size()];
memcpy(pkt->m_pcData, m_pcData, m_PacketVector[PV_DATA].size());
pkt->m_PacketVector[PV_DATA].setLength(m_PacketVector[PV_DATA].size());
return pkt;
}
// Useful for debugging
std::string PacketMessageFlagStr(uint32_t msgno_field)
@ -480,10 +501,10 @@ std::string PacketMessageFlagStr(uint32_t msgno_field)
stringstream out;
static const char* const boundary [] = { "PB_SUBSEQUENT", "PB_LAST", "PB_FIRST", "PB_SOLO" };
static const char* const order [] = { "ORD_RELAXED", "ORD_REQUIRED" };
static const char* const crypto [] = { "EK_NOENC", "EK_EVEN", "EK_ODD", "EK*ERROR" };
static const char* const rexmit [] = { "SN_ORIGINAL", "SN_REXMIT" };
static const char* const boundary[] = {"PB_SUBSEQUENT", "PB_LAST", "PB_FIRST", "PB_SOLO"};
static const char* const order[] = {"ORD_RELAXED", "ORD_REQUIRED"};
static const char* const crypto[] = {"EK_NOENC", "EK_EVEN", "EK_ODD", "EK*ERROR"};
static const char* const rexmit[] = {"SN_ORIGINAL", "SN_REXMIT"};
out << boundary[MSGNO_PACKET_BOUNDARY::unwrap(msgno_field)] << " ";
out << order[MSGNO_PACKET_INORDER::unwrap(msgno_field)] << " ";
@ -492,3 +513,70 @@ std::string PacketMessageFlagStr(uint32_t msgno_field)
return out.str();
}
inline void SprintSpecialWord(std::ostream& os, int32_t val)
{
if (val & LOSSDATA_SEQNO_RANGE_FIRST)
os << "<" << (val & (~LOSSDATA_SEQNO_RANGE_FIRST)) << ">";
else
os << val;
}
} // namespace srt
#if ENABLE_LOGGING
std::string srt::CPacket::Info()
{
std::ostringstream os;
os << "TARGET=@" << m_iID << " ";
if (isControl())
{
os << "CONTROL: size=" << getLength() << " type=" << MessageTypeStr(getType(), getExtendedType());
if (getType() == UMSG_HANDSHAKE)
{
os << " HS: ";
// For handshake we already have a parsing method
CHandShake hs;
hs.load_from(m_pcData, getLength());
os << hs.show();
}
else
{
// This is a value that some messages use for some purposes.
// The "ack seq no" is one of the purposes, used by UMSG_ACK and UMSG_ACKACK.
// This is simply the SRT_PH_MSGNO field used as a message number in data packets.
os << " ARG: 0x";
os << std::hex << getAckSeqNo() << " ";
os << std::dec << getAckSeqNo();
// It would be nice to see the extended packet data, but this
// requires strictly a message-dependent interpreter. So let's simply
// display all numbers in the array with the following restrictions:
// - all data contained in the buffer are considered 32-bit integer
// - sign flag will be cleared before displaying, with additional mark
size_t wordlen = getLength() / 4; // drop any remainder if present
int32_t* array = (int32_t*)m_pcData;
os << " [ ";
for (size_t i = 0; i < wordlen; ++i)
{
SprintSpecialWord(os, array[i]);
os << " ";
}
os << "]";
}
}
else
{
// It's hard to extract the information about peer's supported rexmit flag.
// This is only a log, nothing crucial, so we can risk displaying incorrect message number.
// Declaring that the peer supports rexmit flag cuts off the highest bit from
// the displayed number.
os << "DATA: size=" << getLength() << " " << BufferStamp(m_pcData, getLength()) << " #" << getMsgSeq(true)
<< " %" << getSeqNo() << " " << MessageFlagStr();
}
return os.str();
}
#endif

View file

@ -1,11 +1,11 @@
/*
* SRT - Secure, Reliable, Transport
* SRT - Secure Reliable Transport
* Copyright (c) 2018 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/.
*
*
*/
/*****************************************************************************
@ -50,86 +50,85 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_PACKET_H__
#define __UDT_PACKET_H__
#ifndef INC_SRT_PACKET_H
#define INC_SRT_PACKET_H
#include "udt.h"
#include "common.h"
#include "utilities.h"
#include "netinet_any.h"
#include "packetfilter_api.h"
namespace srt
{
//////////////////////////////////////////////////////////////////////////////
// The purpose of the IOVector class is to proide a platform-independet interface
// to the WSABUF on Windows and iovec on Linux, that can be easilly converted
// to the native structure for use in WSARecvFrom() and recvmsg(...) functions
// to the native structure for use in WSARecvFrom() and recvmsg(...) functions
class IOVector
#ifdef _WIN32
: public WSABUF
: public WSABUF
#else
: public iovec
: public iovec
#endif
{
public:
inline void set(void *buffer, size_t length)
{
inline void set(void* buffer, size_t length)
{
#ifdef _WIN32
len = (ULONG)length;
buf = (CHAR*)buffer;
len = (ULONG)length;
buf = (CHAR*)buffer;
#else
iov_base = (void*)buffer;
iov_len = length;
iov_base = (void*)buffer;
iov_len = length;
#endif
}
}
inline char*& dataRef()
{
inline char*& dataRef()
{
#ifdef _WIN32
return buf;
return buf;
#else
return (char*&) iov_base;
return (char*&)iov_base;
#endif
}
}
inline char* data()
{
inline char* data()
{
#ifdef _WIN32
return buf;
return buf;
#else
return (char*)iov_base;
return (char*)iov_base;
#endif
}
}
inline size_t size() const
{
inline size_t size() const
{
#ifdef _WIN32
return (size_t) len;
return (size_t)len;
#else
return iov_len;
return iov_len;
#endif
}
}
inline void setLength(size_t length)
{
inline void setLength(size_t length)
{
#ifdef _WIN32
len = length;
len = (ULONG)length;
#else
iov_len = length;
iov_len = length;
#endif
}
}
};
/// To define packets in order in the buffer. This is public due to being used in buffer.
enum PacketBoundary
{
PB_SUBSEQUENT = 0, // 00
/// 01: last packet of a message
PB_LAST = 1, // 01
/// 10: first packet of a message
PB_FIRST = 2, // 10
/// 11: solo message packet
PB_SOLO = 3, // 11
PB_SUBSEQUENT = 0, // 00: a packet in the middle of a message, neither the first, not the last.
PB_LAST = 1, // 01: last packet of a message
PB_FIRST = 2, // 10: first packet of a message
PB_SOLO = 3, // 11: solo message packet
};
// Breakdown of the PM_SEQNO field in the header:
@ -137,7 +136,7 @@ enum PacketBoundary
typedef Bits<31> SEQNO_CONTROL;
// 1|T T T T T T T T T T T T T T T|E E...E
typedef Bits<30, 16> SEQNO_MSGTYPE;
typedef Bits<15, 0> SEQNO_EXTTYPE;
typedef Bits<15, 0> SEQNO_EXTTYPE;
// 0|S S ... S
typedef Bits<30, 0> SEQNO_VALUE;
@ -161,11 +160,11 @@ inline int32_t CreateControlExtSeqNo(int exttype)
// MSGNO breakdown: B B|O|K K|R|M M M M M M M M M M...M
typedef Bits<31, 30> MSGNO_PACKET_BOUNDARY;
typedef Bits<29> MSGNO_PACKET_INORDER;
typedef Bits<29> MSGNO_PACKET_INORDER;
typedef Bits<28, 27> MSGNO_ENCKEYSPEC;
#if 1 // can block rexmit flag
// New bit breakdown - rexmit flag supported.
typedef Bits<26> MSGNO_REXMIT;
typedef Bits<26> MSGNO_REXMIT;
typedef Bits<25, 0> MSGNO_SEQ;
// Old bit breakdown - no rexmit flag
typedef Bits<26, 0> MSGNO_SEQ_OLD;
@ -173,32 +172,36 @@ typedef Bits<26, 0> MSGNO_SEQ_OLD;
// The message should be extracted as PMASK_MSGNO_SEQ, if REXMIT is supported, and PMASK_MSGNO_SEQ_OLD otherwise.
const uint32_t PACKET_SND_NORMAL = 0, PACKET_SND_REXMIT = MSGNO_REXMIT::mask;
const int MSGNO_SEQ_MAX = MSGNO_SEQ::mask;
#else
// Old bit breakdown - no rexmit flag
typedef Bits<26, 0> MSGNO_SEQ;
#endif
typedef RollNumber<MSGNO_SEQ::size - 1, 1> MsgNo;
// constexpr in C++11 !
inline int32_t PacketBoundaryBits(PacketBoundary o) { return MSGNO_PACKET_BOUNDARY::wrap(int32_t(o)); }
inline int32_t PacketBoundaryBits(PacketBoundary o)
{
return MSGNO_PACKET_BOUNDARY::wrap(int32_t(o));
}
enum EncryptionKeySpec
{
EK_NOENC = 0,
EK_EVEN = 1,
EK_ODD = 2
EK_EVEN = 1,
EK_ODD = 2
};
enum EncryptionStatus
{
ENCS_CLEAR = 0,
ENCS_CLEAR = 0,
ENCS_FAILED = -1,
ENCS_NOTSUP = -2
};
const int32_t PMASK_MSGNO_ENCKEYSPEC = MSGNO_ENCKEYSPEC::mask;
const int32_t PMASK_MSGNO_ENCKEYSPEC = MSGNO_ENCKEYSPEC::mask;
inline int32_t EncryptionKeyBits(EncryptionKeySpec f)
{
return MSGNO_ENCKEYSPEC::wrap(int32_t(f));
@ -212,210 +215,170 @@ const int32_t PUMASK_SEQNO_PROBE = 0xF;
std::string PacketMessageFlagStr(uint32_t msgno_field);
class CChannel;
class CPacket
{
friend class CChannel;
friend class CSndQueue;
friend class CRcvQueue;
friend class CChannel;
friend class CSndQueue;
friend class CRcvQueue;
public:
CPacket();
~CPacket();
CPacket();
~CPacket();
void allocate(size_t size);
void deallocate();
void allocate(size_t size);
void deallocate();
/// Get the payload or the control information field length.
/// @return the payload or the control information field length.
/// Get the payload or the control information field length.
/// @return the payload or the control information field length.
size_t getLength() const;
size_t getLength() const;
/// Set the payload or the control information field length.
/// @param len [in] the payload or the control information field length.
void setLength(size_t len);
/// Set the payload or the control information field length.
/// @param len [in] the payload or the control information field length.
/// Pack a Control packet.
/// @param pkttype [in] packet type filed.
/// @param lparam [in] pointer to the first data structure, explained by the packet type.
/// @param rparam [in] pointer to the second data structure, explained by the packet type.
/// @param size [in] size of rparam, in number of bytes;
void pack(UDTMessageType pkttype, const int32_t* lparam = NULL, void* rparam = NULL, size_t size = 0);
void setLength(size_t len);
/// Read the packet vector.
/// @return Pointer to the packet vector.
IOVector* getPacketVector();
/// Pack a Control packet.
/// @param pkttype [in] packet type filed.
/// @param lparam [in] pointer to the first data structure, explained by the packet type.
/// @param rparam [in] pointer to the second data structure, explained by the packet type.
/// @param size [in] size of rparam, in number of bytes;
uint32_t* getHeader() { return m_nHeader; }
void pack(UDTMessageType pkttype, const void* lparam = NULL, void* rparam = NULL, int size = 0);
/// Read the packet type.
/// @return packet type filed (000 ~ 111).
UDTMessageType getType() const;
/// Read the packet vector.
/// @return Pointer to the packet vector.
bool isControl(UDTMessageType type) const { return isControl() && type == getType(); }
IOVector* getPacketVector();
bool isControl() const { return 0 != SEQNO_CONTROL::unwrap(m_nHeader[SRT_PH_SEQNO]); }
uint32_t* getHeader() { return m_nHeader; }
void setControl(UDTMessageType type) { m_nHeader[SRT_PH_SEQNO] = SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(type); }
/// Read the packet flag.
/// @return packet flag (0 or 1).
/// Read the extended packet type.
/// @return extended packet type filed (0x000 ~ 0xFFF).
int getExtendedType() const;
// XXX DEPRECATED. Use isControl() instead
ATR_DEPRECATED
int getFlag() const
{
return isControl() ? 1 : 0;
}
/// Read the ACK-2 seq. no.
/// @return packet header field (bit 16~31).
int32_t getAckSeqNo() const;
/// Read the packet type.
/// @return packet type filed (000 ~ 111).
uint16_t getControlFlags() const;
UDTMessageType getType() const;
// Note: this will return a "singular" value, if the packet
// contains the control message
int32_t getSeqNo() const { return m_nHeader[SRT_PH_SEQNO]; }
bool isControl(UDTMessageType type) const
{
return isControl() && type == getType();
}
/// Read the message boundary flag bit.
/// @return packet header field [1] (bit 0~1).
PacketBoundary getMsgBoundary() const;
bool isControl() const
{
// read bit 0
return 0!= SEQNO_CONTROL::unwrap(m_nHeader[SRT_PH_SEQNO]);
}
/// Read the message inorder delivery flag bit.
/// @return packet header field [1] (bit 2).
bool getMsgOrderFlag() const;
void setControl(UDTMessageType type)
{
m_nHeader[SRT_PH_SEQNO] = SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(type);
}
/// Read the rexmit flag (true if the packet was sent due to retransmission).
/// If the peer does not support retransmission flag, the current agent cannot use it as well
/// (because the peer will understand this bit as a part of MSGNO field).
bool getRexmitFlag() const;
/// Read the extended packet type.
/// @return extended packet type filed (0x000 ~ 0xFFF).
/// Read the message sequence number.
/// @return packet header field [1]
int32_t getMsgSeq(bool has_rexmit = true) const;
int getExtendedType() const;
/// Read the message crypto key bits.
/// @return packet header field [1] (bit 3~4).
EncryptionKeySpec getMsgCryptoFlags() const;
/// Read the ACK-2 seq. no.
/// @return packet header field (bit 16~31).
void setMsgCryptoFlags(EncryptionKeySpec spec);
int32_t getAckSeqNo() const;
uint16_t getControlFlags() const;
/// Read the message time stamp.
/// @return packet header field [2] (bit 0~31, bit 0-26 if SRT_DEBUG_TSBPD_WRAP).
uint32_t getMsgTimeStamp() const;
// Note: this will return a "singular" value, if the packet
// contains the control message
int32_t getSeqNo() const
{
return m_nHeader[SRT_PH_SEQNO];
}
/// Read the message boundary flag bit.
/// @return packet header field [1] (bit 0~1).
PacketBoundary getMsgBoundary() const;
/// Read the message inorder delivery flag bit.
/// @return packet header field [1] (bit 2).
bool getMsgOrderFlag() const;
/// Read the rexmit flag (true if the packet was sent due to retransmission).
/// If the peer does not support retransmission flag, the current agent cannot use it as well
/// (because the peer will understand this bit as a part of MSGNO field).
bool getRexmitFlag() const;
/// Read the message sequence number.
/// @return packet header field [1]
int32_t getMsgSeq(bool has_rexmit = true) const;
/// Read the message crypto key bits.
/// @return packet header field [1] (bit 3~4).
EncryptionKeySpec getMsgCryptoFlags() const;
void setMsgCryptoFlags(EncryptionKeySpec spec);
/// Read the message time stamp.
/// @return packet header field [2] (bit 0~31, bit 0-26 if SRT_DEBUG_TSBPD_WRAP).
uint32_t getMsgTimeStamp() const;
#ifdef SRT_DEBUG_TSBPD_WRAP //Receiver
static const uint32_t MAX_TIMESTAMP = 0x07FFFFFF; //27 bit fast wraparound for tests (~2m15s)
#ifdef SRT_DEBUG_TSBPD_WRAP // Receiver
static const uint32_t MAX_TIMESTAMP = 0x07FFFFFF; // 27 bit fast wraparound for tests (~2m15s)
#else
static const uint32_t MAX_TIMESTAMP = 0xFFFFFFFF; //Full 32 bit (01h11m35s)
static const uint32_t MAX_TIMESTAMP = 0xFFFFFFFF; // Full 32 bit (01h11m35s)
#endif
protected:
static const uint32_t TIMESTAMP_MASK = MAX_TIMESTAMP; // this value to be also used as a mask
static const uint32_t TIMESTAMP_MASK = MAX_TIMESTAMP; // this value to be also used as a mask
public:
/// Clone this packet.
/// @return Pointer to the new packet.
CPacket* clone() const;
/// Clone this packet.
/// @return Pointer to the new packet.
enum PacketVectorFields
{
PV_HEADER = 0,
PV_DATA = 1,
CPacket* clone() const;
enum PacketVectorFields
{
PV_HEADER = 0,
PV_DATA = 1,
PV_SIZE = 2
};
protected:
// Length in bytes
// DynamicStruct is the same as array of given type and size, just it
// enforces that you index it using a symbol from symbolic enum type, not by a bare integer.
typedef DynamicStruct<uint32_t, SRT_PH__SIZE, SrtPktHeaderFields> HEADER_TYPE;
HEADER_TYPE m_nHeader; //< The 128-bit header field
// XXX NOTE: iovec here is not portable. On Windows there's a different
// (although similar) structure defined, which means that this way the
// Windows function that is an equivalent of `recvmsg` cannot be used.
// For example, something like that:
// class IoVector: public iovec { public: size_t size() { return iov_len; } char* data() { return iov_base; } };
// class IoVector: public WSAMSG { public: size_t size() { return len; } char* data() { return buf; } };
IOVector m_PacketVector[PV_SIZE]; //< The 2-demension vector of UDT packet [header, data]
int32_t __pad;
bool m_data_owned;
protected:
CPacket& operator=(const CPacket&);
CPacket (const CPacket&);
PV_SIZE = 2
};
public:
void toNL();
void toHL();
int32_t& m_iSeqNo; // alias: sequence number
int32_t& m_iMsgNo; // alias: message number
int32_t& m_iTimeStamp; // alias: timestamp
int32_t& m_iID; // alias: socket ID
char*& m_pcData; // alias: data/control information
protected:
// DynamicStruct is the same as array of given type and size, just it
// enforces that you index it using a symbol from symbolic enum type, not by a bare integer.
typedef DynamicStruct<uint32_t, SRT_PH_E_SIZE, SrtPktHeaderFields> HEADER_TYPE;
HEADER_TYPE m_nHeader; //< The 128-bit header field
//static const int m_iPktHdrSize; // packet header size
static const size_t HDR_SIZE = sizeof(HEADER_TYPE); // packet header size = SRT_PH__SIZE * sizeof(uint32_t)
IOVector m_PacketVector[PV_SIZE]; //< The two-dimensional vector of an SRT packet [header, data]
// Used in many computations
// Actually this can be also calculated as: sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct udphdr).
static const size_t UDP_HDR_SIZE = 28; // 20 bytes IPv4 + 8 bytes of UDP { u16 sport, dport, len, csum }.
int32_t m_extra_pad;
bool m_data_owned;
static const size_t SRT_DATA_HDR_SIZE = UDP_HDR_SIZE + HDR_SIZE;
protected:
CPacket& operator=(const CPacket&);
CPacket(const CPacket&);
// Some well known data
static const size_t ETH_MAX_MTU_SIZE = 1500;
public:
int32_t& m_iSeqNo; // alias: sequence number
int32_t& m_iMsgNo; // alias: message number
int32_t& m_iTimeStamp; // alias: timestamp
int32_t& m_iID; // alias: destination SRT socket ID
char*& m_pcData; // alias: payload (data packet) / control information fields (control packet)
// And derived
static const size_t SRT_MAX_PAYLOAD_SIZE = ETH_MAX_MTU_SIZE - SRT_DATA_HDR_SIZE;
// Experimental: sometimes these references don't work!
char* getData();
char* release();
// Packet interface
char* data() { return m_pcData; }
const char* data() const { return m_pcData; }
size_t size() const { return getLength(); }
uint32_t header(SrtPktHeaderFields field) const { return m_nHeader[field]; }
static const size_t HDR_SIZE = sizeof(HEADER_TYPE); // packet header size = SRT_PH_E_SIZE * sizeof(uint32_t)
// Can also be calculated as: sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct udphdr).
static const size_t UDP_HDR_SIZE = 28; // 20 bytes IPv4 + 8 bytes of UDP { u16 sport, dport, len, csum }.
static const size_t SRT_DATA_HDR_SIZE = UDP_HDR_SIZE + HDR_SIZE;
// Maximum transmission unit size. 1500 in case of Ethernet II (RFC 1191).
static const size_t ETH_MAX_MTU_SIZE = 1500;
// Maximum payload size of an SRT packet.
static const size_t SRT_MAX_PAYLOAD_SIZE = ETH_MAX_MTU_SIZE - SRT_DATA_HDR_SIZE;
// Packet interface
char* data() { return m_pcData; }
const char* data() const { return m_pcData; }
size_t size() const { return getLength(); }
uint32_t header(SrtPktHeaderFields field) const { return m_nHeader[field]; }
std::string MessageFlagStr()
#if ENABLE_LOGGING
{ return PacketMessageFlagStr(m_nHeader[SRT_PH_MSGNO]); }
std::string MessageFlagStr() { return PacketMessageFlagStr(m_nHeader[SRT_PH_MSGNO]); }
std::string Info();
#else
{ return ""; }
std::string MessageFlagStr() { return std::string(); }
std::string Info() { return std::string(); }
#endif
};
} // namespace srt
#endif

View file

@ -8,6 +8,7 @@
*
*/
#include "platform_sys.h"
#include <string>
#include <map>
@ -23,79 +24,138 @@
using namespace std;
using namespace srt_logging;
using namespace srt::sync;
bool ParseFilterConfig(std::string s, SrtFilterConfig& out)
bool srt::ParseFilterConfig(string s, SrtFilterConfig& w_config, PacketFilter::Factory** ppf)
{
vector<string> parts;
Split(s, ',', back_inserter(parts));
if (!SrtParseConfig(s, (w_config)))
return false;
out.type = parts[0];
PacketFilter::Factory* fac = PacketFilter::find(out.type);
PacketFilter::Factory* fac = PacketFilter::find(w_config.type);
if (!fac)
return false;
for (vector<string>::iterator i = parts.begin()+1; i != parts.end(); ++i)
{
vector<string> keyval;
Split(*i, ':', back_inserter(keyval));
if (keyval.size() != 2)
return false;
out.parameters[keyval[0]] = keyval[1];
}
if (ppf)
*ppf = fac;
// Extract characteristic data
out.extra_size = fac->ExtraSize();
w_config.extra_size = fac->ExtraSize();
return true;
}
struct SortBySequence
bool srt::ParseFilterConfig(string s, SrtFilterConfig& w_config)
{
bool operator()(const CUnit* u1, const CUnit* u2)
return ParseFilterConfig(s, (w_config), NULL);
}
// Parameters are passed by value because they need to be potentially modicied inside.
bool srt::CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer)
{
PacketFilter::Factory* fac = PacketFilter::find(w_agent.type);
if (!fac)
return false;
SrtFilterConfig defaults;
if (!ParseFilterConfig(fac->defaultConfig(), (defaults)))
{
int32_t s1 = u1->m_Packet.getSeqNo();
int32_t s2 = u2->m_Packet.getSeqNo();
return CSeqNo::seqcmp(s1, s2) < 0;
return false;
}
};
void PacketFilter::receive(CUnit* unit, ref_t< std::vector<CUnit*> > r_incoming, ref_t<loss_seqs_t> r_loss_seqs)
set<string> keys;
// Extract all keys to identify also unspecified parameters on both sides
// Note that theoretically for FEC it could simply check for the "cols" parameter
// that is the only mandatory one, but this is a procedure for packet filters in
// general and every filter may define its own set of parameters and mandatory rules.
for (map<string, string>::iterator x = w_agent.parameters.begin(); x != w_agent.parameters.end(); ++x)
{
keys.insert(x->first);
if (peer.parameters.count(x->first) == 0)
peer.parameters[x->first] = x->second;
}
for (map<string, string>::iterator x = peer.parameters.begin(); x != peer.parameters.end(); ++x)
{
keys.insert(x->first);
if (w_agent.parameters.count(x->first) == 0)
w_agent.parameters[x->first] = x->second;
}
HLOGC(cnlog.Debug, log << "CheckFilterCompat: re-filled: AGENT:" << Printable(w_agent.parameters)
<< " PEER:" << Printable(peer.parameters));
// Complete nonexistent keys with default values
for (map<string, string>::iterator x = defaults.parameters.begin(); x != defaults.parameters.end(); ++x)
{
if (!w_agent.parameters.count(x->first))
w_agent.parameters[x->first] = x->second;
if (!peer.parameters.count(x->first))
peer.parameters[x->first] = x->second;
}
for (set<string>::iterator x = keys.begin(); x != keys.end(); ++x)
{
// Note: operator[] will insert an element with default value
// if it doesn't exist. This will inject the empty string as value,
// which is acceptable.
if (w_agent.parameters[*x] != peer.parameters[*x])
{
LOGC(cnlog.Error, log << "Packet Filter (" << defaults.type << "): collision on '" << (*x)
<< "' parameter (agent:" << w_agent.parameters[*x] << " peer:" << (peer.parameters[*x]) << ")");
return false;
}
}
// Mandatory parameters will be checked when trying to create the filter object.
return true;
}
namespace srt {
struct SortBySequence
{
bool operator()(const CUnit* u1, const CUnit* u2)
{
int32_t s1 = u1->m_Packet.getSeqNo();
int32_t s2 = u2->m_Packet.getSeqNo();
return CSeqNo::seqcmp(s1, s2) < 0;
}
};
} // namespace srt
void srt::PacketFilter::receive(CUnit* unit, std::vector<CUnit*>& w_incoming, loss_seqs_t& w_loss_seqs)
{
const CPacket& rpkt = unit->m_Packet;
if (m_filter->receive(rpkt, *r_loss_seqs))
if (m_filter->receive(rpkt, w_loss_seqs))
{
// For the sake of rebuilding MARK THIS UNIT GOOD, otherwise the
// unit factory will supply it from getNextAvailUnit() as if it were not in use.
unit->m_iFlag = CUnit::GOOD;
HLOGC(mglog.Debug, log << "FILTER: PASSTHRU current packet %" << unit->m_Packet.getSeqNo());
r_incoming.get().push_back(unit);
HLOGC(pflog.Debug, log << "FILTER: PASSTHRU current packet %" << unit->m_Packet.getSeqNo());
w_incoming.push_back(unit);
}
else
{
// Packet not to be passthru, update stats
CGuard lg(m_parent->m_StatsLock);
++m_parent->m_stats.rcvFilterExtra;
++m_parent->m_stats.rcvFilterExtraTotal;
ScopedLock lg(m_parent->m_StatsLock);
m_parent->m_stats.rcvr.recvdFilterExtra.count(1);
}
// r_loss_seqs enters empty into this function and can be only filled here.
for (loss_seqs_t::iterator i = r_loss_seqs.get().begin();
i != r_loss_seqs.get().end(); ++i)
// w_loss_seqs enters empty into this function and can be only filled here. XXX ASSERT?
for (loss_seqs_t::iterator i = w_loss_seqs.begin();
i != w_loss_seqs.end(); ++i)
{
// Sequences here are low-high, if there happens any negative distance
// here, simply skip and report IPE.
int dist = CSeqNo::seqoff(i->first, i->second) + 1;
if (dist > 0)
{
CGuard lg(m_parent->m_StatsLock);
m_parent->m_stats.rcvFilterLoss += dist;
m_parent->m_stats.rcvFilterLossTotal += dist;
ScopedLock lg(m_parent->m_StatsLock);
m_parent->m_stats.rcvr.lossFilter.count(dist);
}
else
{
LOGC(mglog.Error, log << "FILTER: IPE: loss record: invalid loss: %"
LOGC(pflog.Error, log << "FILTER: IPE: loss record: invalid loss: %"
<< i->first << " - %" << i->second);
}
}
@ -103,14 +163,13 @@ void PacketFilter::receive(CUnit* unit, ref_t< std::vector<CUnit*> > r_incoming,
// Pack first recovered packets, if any.
if (!m_provided.empty())
{
HLOGC(mglog.Debug, log << "FILTER: inserting REBUILT packets (" << m_provided.size() << "):");
HLOGC(pflog.Debug, log << "FILTER: inserting REBUILT packets (" << m_provided.size() << "):");
size_t nsupply = m_provided.size();
InsertRebuilt(*r_incoming, m_unitq);
InsertRebuilt(w_incoming, m_unitq);
CGuard lg(m_parent->m_StatsLock);
m_parent->m_stats.rcvFilterSupply += nsupply;
m_parent->m_stats.rcvFilterSupplyTotal += nsupply;
ScopedLock lg(m_parent->m_StatsLock);
m_parent->m_stats.rcvr.suppliedByFilter.count(nsupply);
}
// Now that all units have been filled as they should be,
@ -120,8 +179,7 @@ void PacketFilter::receive(CUnit* unit, ref_t< std::vector<CUnit*> > r_incoming,
// with FREE and therefore will be returned at the next
// call to getNextAvailUnit().
unit->m_iFlag = CUnit::FREE;
vector<CUnit*>& inco = *r_incoming;
for (vector<CUnit*>::iterator i = inco.begin(); i != inco.end(); ++i)
for (vector<CUnit*>::iterator i = w_incoming.begin(); i != w_incoming.end(); ++i)
{
CUnit* u = *i;
u->m_iFlag = CUnit::FREE;
@ -129,7 +187,7 @@ void PacketFilter::receive(CUnit* unit, ref_t< std::vector<CUnit*> > r_incoming,
// Packets must be sorted by sequence number, ascending, in order
// not to challenge the SRT's contiguity checker.
sort(inco.begin(), inco.end(), SortBySequence());
sort(w_incoming.begin(), w_incoming.end(), SortBySequence());
// For now, report immediately the irrecoverable packets
// from the row.
@ -147,7 +205,7 @@ void PacketFilter::receive(CUnit* unit, ref_t< std::vector<CUnit*> > r_incoming,
}
bool PacketFilter::packControlPacket(ref_t<CPacket> r_packet, int32_t seq, int kflg)
bool srt::PacketFilter::packControlPacket(int32_t seq, int kflg, CPacket& w_packet)
{
bool have = m_filter->packControlPacket(m_sndctlpkt, seq);
if (!have)
@ -155,12 +213,12 @@ bool PacketFilter::packControlPacket(ref_t<CPacket> r_packet, int32_t seq, int k
// Now this should be repacked back to CPacket.
// The header must be copied, it's always part of CPacket.
uint32_t* hdr = r_packet.get().getHeader();
memcpy(hdr, m_sndctlpkt.hdr, SRT_PH__SIZE * sizeof(*hdr));
uint32_t* hdr = w_packet.getHeader();
memcpy((hdr), m_sndctlpkt.hdr, SRT_PH_E_SIZE * sizeof(*hdr));
// The buffer can be assigned.
r_packet.get().m_pcData = m_sndctlpkt.buffer;
r_packet.get().setLength(m_sndctlpkt.length);
w_packet.m_pcData = m_sndctlpkt.buffer;
w_packet.setLength(m_sndctlpkt.length);
// This sets only the Packet Boundary flags, while all other things:
// - Order
@ -168,10 +226,10 @@ bool PacketFilter::packControlPacket(ref_t<CPacket> r_packet, int32_t seq, int k
// - Crypto
// - Message Number
// will be set to 0/false
r_packet.get().m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO);
w_packet.m_iMsgNo = SRT_MSGNO_CONTROL | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO);
// ... and then fix only the Crypto flags
r_packet.get().setMsgCryptoFlags(EncryptionKeySpec(kflg));
w_packet.setMsgCryptoFlags(EncryptionKeySpec(kflg));
// Don't set the ID, it will be later set for any kind of packet.
// Write the timestamp clip into the timestamp field.
@ -179,7 +237,7 @@ bool PacketFilter::packControlPacket(ref_t<CPacket> r_packet, int32_t seq, int k
}
void PacketFilter::InsertRebuilt(vector<CUnit*>& incoming, CUnitQueue* uq)
void srt::PacketFilter::InsertRebuilt(vector<CUnit*>& incoming, CUnitQueue* uq)
{
if (m_provided.empty())
return;
@ -189,7 +247,7 @@ void PacketFilter::InsertRebuilt(vector<CUnit*>& incoming, CUnitQueue* uq)
CUnit* u = uq->getNextAvailUnit();
if (!u)
{
LOGC(mglog.Error, log << "FILTER: LOCAL STORAGE DEPLETED. Can't return rebuilt packets.");
LOGC(pflog.Error, log << "FILTER: LOCAL STORAGE DEPLETED. Can't return rebuilt packets.");
break;
}
@ -202,11 +260,11 @@ void PacketFilter::InsertRebuilt(vector<CUnit*>& incoming, CUnitQueue* uq)
CPacket& packet = u->m_Packet;
memcpy(packet.getHeader(), i->hdr, CPacket::HDR_SIZE);
memcpy(packet.m_pcData, i->buffer, i->length);
memcpy((packet.getHeader()), i->hdr, CPacket::HDR_SIZE);
memcpy((packet.m_pcData), i->buffer, i->length);
packet.setLength(i->length);
HLOGC(mglog.Debug, log << "FILTER: PROVIDING rebuilt packet %" << packet.getSeqNo());
HLOGC(pflog.Debug, log << "FILTER: PROVIDING rebuilt packet %" << packet.getSeqNo());
incoming.push_back(u);
}
@ -214,19 +272,21 @@ void PacketFilter::InsertRebuilt(vector<CUnit*>& incoming, CUnitQueue* uq)
m_provided.clear();
}
bool PacketFilter::IsBuiltin(const string& s)
bool srt::PacketFilter::IsBuiltin(const string& s)
{
return builtin_filters.count(s);
}
namespace srt {
std::set<std::string> PacketFilter::builtin_filters;
PacketFilter::filters_map_t PacketFilter::filters;
}
PacketFilter::Factory::~Factory()
srt::PacketFilter::Factory::~Factory()
{
}
void PacketFilter::globalInit()
void srt::PacketFilter::globalInit()
{
// Add here builtin packet filters and mark them
// as builtin. This will disallow users to register
@ -236,12 +296,12 @@ void PacketFilter::globalInit()
builtin_filters.insert("fec");
}
bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& confstr)
bool srt::PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& confstr)
{
m_parent = parent;
SrtFilterConfig cfg;
if (!ParseFilterConfig(confstr, cfg))
if (!ParseFilterConfig(confstr, (cfg)))
return false;
// Extract the "type" key from parameters, or use
@ -255,7 +315,7 @@ bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& co
init.snd_isn = parent->sndSeqNo();
init.rcv_isn = parent->rcvSeqNo();
init.payload_size = parent->OPT_PayloadSize();
init.rcvbuf_size = parent->m_config.iRcvBufSize;
// Found a filter, so call the creation function
m_filter = selector->second->Create(init, m_provided, confstr);
@ -270,7 +330,7 @@ bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& co
return true;
}
bool PacketFilter::correctConfig(const SrtFilterConfig& conf)
bool srt::PacketFilter::correctConfig(const SrtFilterConfig& conf)
{
const string* pname = map_getp(conf.parameters, "type");
@ -287,7 +347,7 @@ bool PacketFilter::correctConfig(const SrtFilterConfig& conf)
return true;
}
PacketFilter::~PacketFilter()
srt::PacketFilter::~PacketFilter()
{
delete m_filter;
}

View file

@ -8,40 +8,50 @@
*
*/
#ifndef INC__PACKETFILTER_H
#define INC__PACKETFILTER_H
#ifndef INC_SRT_PACKETFILTER_H
#define INC_SRT_PACKETFILTER_H
#include <cstdlib>
#include <map>
#include <string>
#include "packet.h"
#include "queue.h"
#include "utilities.h"
#include "packetfilter_api.h"
namespace srt {
class CUnitQueue;
struct CUnit;
class CUDT;
class PacketFilter
{
friend class SrtPacketFilterBase;
public:
typedef std::vector< std::pair<int32_t, int32_t> > loss_seqs_t;
typedef SrtPacketFilterBase* filter_create_t(const SrtFilterInitializer& init, std::vector<SrtPacket>&, const std::string& config);
private:
friend bool ParseFilterConfig(std::string s, SrtFilterConfig& out);
class Factory
{
public:
virtual SrtPacketFilterBase* Create(const SrtFilterInitializer& init, std::vector<SrtPacket>& provided, const std::string& confstr) = 0;
// Characteristic data
virtual size_t ExtraSize() = 0;
virtual size_t ExtraSize() const = 0;
// Represent default parameters. This is for completing and comparing
// filter configurations from both parties. Possible values to return:
// - an empty string (all parameters are mandatory)
// - a form of: "<filter-name>,<param1>:<value1>,..."
virtual std::string defaultConfig() const = 0;
virtual bool verifyConfig(const SrtFilterConfig& config, std::string& w_errormsg) const = 0;
virtual ~Factory();
};
private:
friend bool ParseFilterConfig(std::string s, SrtFilterConfig& out, PacketFilter::Factory** ppf);
template <class Target>
class Creator: public Factory
@ -52,7 +62,12 @@ private:
{ return new Target(init, provided, confstr); }
// Import the extra size data
virtual size_t ExtraSize() ATR_OVERRIDE { return Target::EXTRA_SIZE; }
virtual size_t ExtraSize() const ATR_OVERRIDE { return Target::EXTRA_SIZE; }
virtual std::string defaultConfig() const ATR_OVERRIDE { return Target::defaultConfig; }
virtual bool verifyConfig(const SrtFilterConfig& config, std::string& w_errormsg) const ATR_OVERRIDE
{
return Target::verifyConfig(config, (w_errormsg));
}
public:
Creator() {}
@ -157,7 +172,7 @@ public:
// Things being done:
// 1. The filter is individual, so don't copy it. Set NULL.
// 2. This will be configued anyway basing on possibly a new rule set.
PacketFilter(const PacketFilter& source SRT_ATR_UNUSED): m_filter(), m_sndctlpkt(0), m_unitq() {}
PacketFilter(const PacketFilter& source SRT_ATR_UNUSED): m_filter(), m_parent(), m_sndctlpkt(0), m_unitq() {}
// This function will be called by the parent CUDT
// in appropriate time. It should select appropriate
@ -173,12 +188,13 @@ public:
~PacketFilter();
// Simple wrappers
void feedSource(ref_t<CPacket> r_packet);
void feedSource(CPacket& w_packet);
SRT_ARQLevel arqLevel();
bool packControlPacket(ref_t<CPacket> r_packet, int32_t seq, int kflg);
void receive(CUnit* unit, ref_t< std::vector<CUnit*> > r_incoming, ref_t<loss_seqs_t> r_loss_seqs);
bool packControlPacket(int32_t seq, int kflg, CPacket& w_packet);
void receive(CUnit* unit, std::vector<CUnit*>& w_incoming, loss_seqs_t& w_loss_seqs);
protected:
PacketFilter& operator=(const PacketFilter& p);
void InsertRebuilt(std::vector<CUnit*>& incoming, CUnitQueue* uq);
CUDT* m_parent;
@ -191,8 +207,13 @@ protected:
std::vector<SrtPacket> m_provided;
};
bool CheckFilterCompat(SrtFilterConfig& w_agent, SrtFilterConfig peer);
inline void PacketFilter::feedSource(ref_t<CPacket> r_packet) { SRT_ASSERT(m_filter); return m_filter->feedSource(*r_packet); }
inline void PacketFilter::feedSource(CPacket& w_packet) { SRT_ASSERT(m_filter); return m_filter->feedSource((w_packet)); }
inline SRT_ARQLevel PacketFilter::arqLevel() { SRT_ASSERT(m_filter); return m_filter->arqLevel(); }
bool ParseFilterConfig(std::string s, SrtFilterConfig& out, PacketFilter::Factory** ppf);
} // namespace srt
#endif

View file

@ -8,8 +8,20 @@
*
*/
#ifndef INC__PACKETFILTER_API_H
#define INC__PACKETFILTER_API_H
#ifndef INC_SRT_PACKETFILTER_API_H
#define INC_SRT_PACKETFILTER_API_H
#include "platform_sys.h"
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <utility>
namespace srt {
class CPacket;
enum SrtPktHeaderFields
{
@ -19,7 +31,7 @@ enum SrtPktHeaderFields
SRT_PH_ID = 3, //< socket ID
// Must be the last value - this is size of all, not a field id
SRT_PH__SIZE
SRT_PH_E_SIZE
};
@ -30,11 +42,15 @@ enum SRT_ARQLevel
SRT_ARQ_ALWAYS, //< always send LOSSREPORT immediately after detecting a loss
};
struct SrtFilterConfig
struct SrtConfig
{
std::string type;
std::map<std::string, std::string> parameters;
typedef std::map<std::string, std::string> par_t;
par_t parameters;
};
struct SrtFilterConfig: SrtConfig
{
size_t extra_size; // needed for filter option check against payload size
};
@ -44,11 +60,12 @@ struct SrtFilterInitializer
int32_t snd_isn;
int32_t rcv_isn;
size_t payload_size;
size_t rcvbuf_size;
};
struct SrtPacket
{
uint32_t hdr[SRT_PH__SIZE];
uint32_t hdr[SRT_PH_E_SIZE];
char buffer[SRT_LIVE_MAX_PLSIZE];
size_t length;
@ -64,7 +81,7 @@ struct SrtPacket
};
bool ParseFilterConfig(std::string s, SrtFilterConfig& out);
bool ParseFilterConfig(std::string s, SrtFilterConfig& w_config);
class SrtPacketFilterBase
@ -77,6 +94,7 @@ protected:
int32_t sndISN() const { return initParams.snd_isn; }
int32_t rcvISN() const { return initParams.rcv_isn; }
size_t payloadSize() const { return initParams.payload_size; }
size_t rcvBufferSize() const { return initParams.rcvbuf_size; }
friend class PacketFilter;
@ -135,6 +153,6 @@ protected:
}
};
} // namespace srt
#endif

View file

@ -9,8 +9,8 @@
*/
#ifndef INC__PACKETFILTER_BUILTIN_H
#define INC__PACKETFILTER_BUILTIN_H
#ifndef INC_SRT_PACKETFILTER_BUILTIN_H
#define INC_SRT_PACKETFILTER_BUILTIN_H
// Integration header
#include "fec.h"

View file

@ -7,28 +7,117 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
#ifndef INC__PLATFORM_SYS_H
#define INC__PLATFORM_SYS_H
#ifndef INC_SRT_PLATFORM_SYS_H
#define INC_SRT_PLATFORM_SYS_H
// INFORMATION
//
// This file collects all required platform-specific declarations
// required to provide everything that the SRT library needs from system.
//
// There's also semi-modular system implemented using SRT_IMPORT_* macros.
// To require a module to be imported, #define SRT_IMPORT_* where * is
// the module name. Currently handled module macros:
//
// SRT_IMPORT_TIME (mach time on Mac, portability gettimeofday on WIN32)
// SRT_IMPORT_EVENT (includes kevent on Mac)
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS 1 // silences windows complaints for sscanf
#include <winsock2.h>
#include <ws2tcpip.h>
#include <ws2ipdef.h>
#include <windows.h>
#ifndef __MINGW32__
#include <intrin.h>
#endif
#ifdef SRT_IMPORT_TIME
#include <win/wintime.h>
#endif
#include <stdint.h>
#include <inttypes.h>
#if defined(_MSC_VER)
#pragma warning(disable:4251)
#pragma warning(disable: 4251 26812)
#endif
#else
#if defined(__APPLE__) && __APPLE__
// Warning: please keep this test as it is, do not make it
// "#if __APPLE__" or "#ifdef __APPLE__". In applications with
// a strict "no warning policy", "#if __APPLE__" triggers an "undef"
// error. With GCC, an old & never fixed bug prevents muting this
// warning (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431).
// Before this fix, the solution was to "#define __APPLE__ 0" before
// including srt.h. So, don't use "#ifdef __APPLE__" either.
// XXX Check if this condition doesn't require checking of
// also other macros, like TARGET_OS_IOS etc.
#include "TargetConditionals.h"
#define __APPLE_USE_RFC_3542 /* IPV6_PKTINFO */
#ifdef SRT_IMPORT_TIME
#include <mach/mach_time.h>
#endif
#ifdef SRT_IMPORT_EVENT
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#endif
#endif
#ifdef BSD
#ifdef SRT_IMPORT_EVENT
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#endif
#endif
#ifdef LINUX
#ifdef SRT_IMPORT_EVENT
#include <sys/epoll.h>
#include <unistd.h>
#endif
#endif
#ifdef __ANDROID__
#ifdef SRT_IMPORT_EVENT
#include <sys/select.h>
#endif
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#ifdef __cplusplus
// Headers for errno, string and stdlib are
// included indirectly correct C++ way.
#else
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#endif
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 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/.
*
*
*/
/*****************************************************************************
@ -50,13 +50,12 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_SRT_QUEUE_H
#define INC_SRT_QUEUE_H
#ifndef __UDT_QUEUE_H__
#define __UDT_QUEUE_H__
#include "channel.h"
#include "common.h"
#include "packet.h"
#include "socketconfig.h"
#include "netinet_any.h"
#include "utilities.h"
#include <list>
@ -64,485 +63,548 @@ modified by
#include <queue>
#include <vector>
namespace srt
{
class CChannel;
class CUDT;
struct CUnit
{
CPacket m_Packet; // packet
enum Flag { FREE = 0, GOOD = 1, PASSACK = 2, DROPPED = 3 };
Flag m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped
CPacket m_Packet; // packet
enum Flag
{
FREE = 0,
GOOD = 1,
PASSACK = 2,
DROPPED = 3
};
// TODO: The new RcvBuffer allows to use atomic_bool here.
sync::atomic<Flag> m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped
};
class CUnitQueue
{
public:
/// @brief Construct a unit queue.
/// @param mss Initial number of units to allocate.
/// @param mss Maximum segment size meaning the size of each unit.
/// @throws CUDTException SRT_ENOBUF.
CUnitQueue(int initNumUnits, int mss);
~CUnitQueue();
public:
CUnitQueue();
~CUnitQueue();
public: // Storage size operations
/// Initialize the unit queue.
/// @param [in] size queue size
/// @param [in] mss maximum segment size
/// @param [in] version IP version
/// @return 0: success, -1: failure.
int init(int size, int mss, int version);
/// Increase (double) the unit queue size.
/// @return 0: success, -1: failure.
int increase();
/// Decrease (halve) the unit queue size.
/// @return 0: success, -1: failure.
int shrink();
public: // Operations on units
/// find an available unit for incoming packet.
/// @return Pointer to the available unit, NULL if not found.
CUnit* getNextAvailUnit();
void makeUnitFree(CUnit * unit);
void makeUnitGood(CUnit * unit);
int capacity() const { return m_iSize; }
int size() const { return m_iSize - m_iNumTaken; }
public:
/// @brief Find an available unit for incoming packet. Allocate new units if 90% or more are in use.
/// @note This function is not thread-safe. Currently only CRcvQueue::worker thread calls it, thus
/// it is not an issue. However, must be protected if used from several threads in the future.
/// @return Pointer to the available unit, NULL if not found.
CUnit* getNextAvailUnit();
inline int getIPversion() const { return m_iIPversion; }
void makeUnitFree(CUnit* unit);
void makeUnitGood(CUnit* unit);
private:
struct CQEntry
{
CUnit* m_pUnit; // unit queue
char* m_pBuffer; // data buffer
int m_iSize; // size of each queue
struct CQEntry
{
CUnit* m_pUnit; // unit queue
char* m_pBuffer; // data buffer
int m_iSize; // size of each queue
CQEntry* m_pNext;
}
*m_pQEntry, // pointer to the first unit queue
*m_pCurrQueue, // pointer to the current available queue
*m_pLastQueue; // pointer to the last unit queue
CQEntry* m_pNext;
};
CUnit* m_pAvailUnit; // recent available unit
/// Increase the unit queue size (by @a m_iBlockSize units).
/// Uses m_mtx to protect access and changes of the queue state.
/// @return 0: success, -1: failure.
int increase_();
int m_iSize; // total size of the unit queue, in number of packets
int m_iCount; // total number of valid packets in the queue
int m_iMSS; // unit buffer size
int m_iIPversion; // IP version
/// @brief Allocated a CQEntry of iNumUnits with each unit of mss bytes.
/// @param iNumUnits a number of units to allocate
/// @param mss the size of each unit in bytes.
/// @return a pointer to a newly allocated entry on success, NULL otherwise.
static CQEntry* allocateEntry(const int iNumUnits, const int mss);
private:
CUnitQueue(const CUnitQueue&);
CUnitQueue& operator=(const CUnitQueue&);
CQEntry* m_pQEntry; // pointer to the first unit queue
CQEntry* m_pCurrQueue; // pointer to the current available queue
CQEntry* m_pLastQueue; // pointer to the last unit queue
CUnit* m_pAvailUnit; // recent available unit
int m_iSize; // total size of the unit queue, in number of packets
sync::atomic<int> m_iNumTaken; // total number of valid (occupied) packets in the queue
const int m_iMSS; // unit buffer size
const int m_iBlockSize; // Number of units in each CQEntry.
private:
CUnitQueue(const CUnitQueue&);
CUnitQueue& operator=(const CUnitQueue&);
};
struct CSNode
{
CUDT* m_pUDT; // Pointer to the instance of CUDT socket
uint64_t m_llTimeStamp_tk; // Time Stamp
CUDT* m_pUDT; // Pointer to the instance of CUDT socket
sync::steady_clock::time_point m_tsTimeStamp;
int m_iHeapLoc; // location on the heap, -1 means not on the heap
sync::atomic<int> m_iHeapLoc; // location on the heap, -1 means not on the heap
};
class CSndUList
{
friend class CSndQueue;
public:
CSndUList(sync::CTimer* pTimer);
~CSndUList();
public:
CSndUList();
~CSndUList();
enum EReschedule
{
DONT_RESCHEDULE = 0,
DO_RESCHEDULE = 1
};
public:
static EReschedule rescheduleIf(bool cond) { return cond ? DO_RESCHEDULE : DONT_RESCHEDULE; }
enum EReschedule { DONT_RESCHEDULE = 0, DO_RESCHEDULE = 1 };
/// Update the timestamp of the UDT instance on the list.
/// @param [in] u pointer to the UDT instance
/// @param [in] reschedule if the timestamp should be rescheduled
/// @param [in] ts the next time to trigger sending logic on the CUDT
void update(const CUDT* u, EReschedule reschedule, sync::steady_clock::time_point ts = sync::steady_clock::now());
static EReschedule rescheduleIf(bool cond) { return cond ? DO_RESCHEDULE : DONT_RESCHEDULE; }
/// Retrieve the next (in time) socket from the heap to process its sending request.
/// @return a pointer to CUDT instance to process next.
CUDT* pop();
/// Update the timestamp of the UDT instance on the list.
/// @param [in] u pointer to the UDT instance
/// @param [in] reschedule if the timestamp should be rescheduled
/// Remove UDT instance from the list.
/// @param [in] u pointer to the UDT instance
void remove(const CUDT* u);// EXCLUDES(m_ListLock);
void update(const CUDT* u, EReschedule reschedule);
/// Retrieve the next scheduled processing time.
/// @return Scheduled processing time of the first UDT socket in the list.
sync::steady_clock::time_point getNextProcTime();
/// Retrieve the next packet and peer address from the first entry, and reschedule it in the queue.
/// @param [out] addr destination address of the next packet
/// @param [out] pkt the next packet to be sent
/// @return 1 if successfully retrieved, -1 if no packet found.
/// Wait for the list to become non empty.
void waitNonEmpty() const;
int pop(sockaddr*& addr, CPacket& pkt);
/// Remove UDT instance from the list.
/// @param [in] u pointer to the UDT instance
void remove(const CUDT* u);
/// Retrieve the next scheduled processing time.
/// @return Scheduled processing time of the first UDT socket in the list.
uint64_t getNextProcTime();
/// Signal to stop waiting in waitNonEmpty().
void signalInterrupt() const;
private:
/// Doubles the size of the list.
///
void realloc_();// REQUIRES(m_ListLock);
/// Doubles the size of the list.
///
void realloc_();
/// Insert a new UDT instance into the list with realloc if required.
///
/// @param [in] ts time stamp: next processing time
/// @param [in] u pointer to the UDT instance
void insert_(const sync::steady_clock::time_point& ts, const CUDT* u);
/// Insert a new UDT instance into the list with realloc if required.
///
/// @param [in] ts time stamp: next processing time
/// @param [in] u pointer to the UDT instance
void insert_(int64_t ts, const CUDT* u);
/// Insert a new UDT instance into the list without realloc.
/// Should be called if there is a gauranteed space for the element.
///
/// @param [in] ts time stamp: next processing time
/// @param [in] u pointer to the UDT instance
void insert_norealloc_(const sync::steady_clock::time_point& ts, const CUDT* u);// REQUIRES(m_ListLock);
/// Insert a new UDT instance into the list without realloc.
/// Should be called if there is a gauranteed space for the element.
///
/// @param [in] ts time stamp: next processing time
/// @param [in] u pointer to the UDT instance
void insert_norealloc_(int64_t ts, const CUDT* u);
void remove_(const CUDT* u);
/// Removes CUDT entry from the list.
/// If the last entry is removed, calls sync::CTimer::interrupt().
void remove_(const CUDT* u);
private:
CSNode** m_pHeap; // The heap array
int m_iArrayLength; // physical length of the array
int m_iLastEntry; // position of last entry on the heap array
CSNode** m_pHeap; // The heap array
int m_iArrayLength; // physical length of the array
int m_iLastEntry; // position of last entry on the heap array or -1 if empty.
pthread_mutex_t m_ListLock;
mutable sync::Mutex m_ListLock; // Protects the list (m_pHeap, m_iArrayLength, m_iLastEntry).
mutable sync::Condition m_ListCond;
pthread_mutex_t* m_pWindowLock;
pthread_cond_t* m_pWindowCond;
CTimer* m_pTimer;
sync::CTimer* const m_pTimer;
private:
CSndUList(const CSndUList&);
CSndUList& operator=(const CSndUList&);
CSndUList(const CSndUList&);
CSndUList& operator=(const CSndUList&);
};
struct CRNode
{
CUDT* m_pUDT; // Pointer to the instance of CUDT socket
uint64_t m_llTimeStamp_tk; // Time Stamp
CUDT* m_pUDT; // Pointer to the instance of CUDT socket
sync::steady_clock::time_point m_tsTimeStamp; // Time Stamp
CRNode* m_pPrev; // previous link
CRNode* m_pNext; // next link
CRNode* m_pPrev; // previous link
CRNode* m_pNext; // next link
bool m_bOnList; // if the node is already on the list
sync::atomic<bool> m_bOnList; // if the node is already on the list
};
class CRcvUList
{
public:
CRcvUList();
~CRcvUList();
CRcvUList();
~CRcvUList();
public:
/// Insert a new UDT instance to the list.
/// @param [in] u pointer to the UDT instance
/// Insert a new UDT instance to the list.
/// @param [in] u pointer to the UDT instance
void insert(const CUDT* u);
void insert(const CUDT* u);
/// Remove the UDT instance from the list.
/// @param [in] u pointer to the UDT instance
/// Remove the UDT instance from the list.
/// @param [in] u pointer to the UDT instance
void remove(const CUDT* u);
void remove(const CUDT* u);
/// Move the UDT instance to the end of the list, if it already exists; otherwise, do nothing.
/// @param [in] u pointer to the UDT instance
/// Move the UDT instance to the end of the list, if it already exists; otherwise, do nothing.
/// @param [in] u pointer to the UDT instance
void update(const CUDT* u);
void update(const CUDT* u);
public:
CRNode* m_pUList; // the head node
CRNode* m_pUList; // the head node
private:
CRNode* m_pLast; // the last node
CRNode* m_pLast; // the last node
private:
CRcvUList(const CRcvUList&);
CRcvUList& operator=(const CRcvUList&);
CRcvUList(const CRcvUList&);
CRcvUList& operator=(const CRcvUList&);
};
class CHash
{
public:
CHash();
~CHash();
CHash();
~CHash();
public:
/// Initialize the hash table.
/// @param [in] size hash table size
/// Initialize the hash table.
/// @param [in] size hash table size
void init(int size);
void init(int size);
/// Look for a UDT instance from the hash table.
/// @param [in] id socket ID
/// @return Pointer to a UDT instance, or NULL if not found.
/// Look for a UDT instance from the hash table.
/// @param [in] id socket ID
/// @return Pointer to a UDT instance, or NULL if not found.
CUDT* lookup(int32_t id);
CUDT* lookup(int32_t id);
/// Insert an entry to the hash table.
/// @param [in] id socket ID
/// @param [in] u pointer to the UDT instance
/// Insert an entry to the hash table.
/// @param [in] id socket ID
/// @param [in] u pointer to the UDT instance
void insert(int32_t id, CUDT* u);
void insert(int32_t id, CUDT* u);
/// Remove an entry from the hash table.
/// @param [in] id socket ID
/// Remove an entry from the hash table.
/// @param [in] id socket ID
void remove(int32_t id);
void remove(int32_t id);
private:
struct CBucket
{
int32_t m_iID; // Socket ID
CUDT* m_pUDT; // Socket instance
struct CBucket
{
int32_t m_iID; // Socket ID
CUDT* m_pUDT; // Socket instance
CBucket* m_pNext; // next bucket
} **m_pBucket; // list of buckets (the hash table)
CBucket* m_pNext; // next bucket
} * *m_pBucket; // list of buckets (the hash table)
int m_iHashSize; // size of hash table
int m_iHashSize; // size of hash table
private:
CHash(const CHash&);
CHash& operator=(const CHash&);
CHash(const CHash&);
CHash& operator=(const CHash&);
};
/// @brief A queue of sockets pending for connection.
/// It can be either a caller socket in a non-blocking mode
/// (the connection has to be handled in background),
/// or a socket in rendezvous connection mode.
class CRendezvousQueue
{
public:
CRendezvousQueue();
~CRendezvousQueue();
CRendezvousQueue();
~CRendezvousQueue();
public:
void insert(const SRTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl);
/// @brief Insert a new socket pending for connection (non-blocking caller or rendezvous).
/// @param id socket ID.
/// @param u pointer to a corresponding CUDT instance.
/// @param addr remote address to connect to.
/// @param ttl timepoint for connection attempt to expire.
void insert(const SRTSOCKET& id, CUDT* u, const sockaddr_any& addr, const srt::sync::steady_clock::time_point& ttl);
// The should_lock parameter is given here to state as to whether
// the lock should be applied here. If called from some internals
// and the lock IS ALREADY APPLIED, use false here to prevent
// double locking and deadlock in result.
void remove(const SRTSOCKET& id, bool should_lock);
CUDT* retrieve(const sockaddr* addr, ref_t<SRTSOCKET> id);
/// @brief Remove a socket from the connection pending list.
/// @param id socket ID.
void remove(const SRTSOCKET& id);
void updateConnStatus(EReadStatus rst, EConnectStatus, const CPacket& response);
/// @brief Locate a socket in the connection pending queue.
/// @param addr source address of the packet received over UDP (peer address).
/// @param id socket ID.
/// @return a pointer to CUDT instance retrieved, or NULL if nothing was found.
CUDT* retrieve(const sockaddr_any& addr, SRTSOCKET& id) const;
/// @brief Update status of connections in the pending queue.
/// Stop connecting if TTL expires. Resend handshake request every 250 ms if no response from the peer.
/// @param rst result of reading from a UDP socket: received packet / nothin read / read error.
/// @param cst target status for pending connection: reject or proceed.
/// @param pktIn packet received from the UDP socket.
void updateConnStatus(EReadStatus rst, EConnectStatus cst, CUnit* unit);
private:
struct CRL
{
SRTSOCKET m_iID; // UDT socket ID (self)
CUDT* m_pUDT; // UDT instance
int m_iIPversion; // IP version
sockaddr* m_pPeerAddr; // UDT sonnection peer address
uint64_t m_ullTTL; // the time that this request expires
};
std::list<CRL> m_lRendezvousID; // The sockets currently in rendezvous mode
struct LinkStatusInfo
{
CUDT* u;
SRTSOCKET id;
int errorcode;
sockaddr_any peeraddr;
int token;
pthread_mutex_t m_RIDVectorLock;
struct HasID
{
SRTSOCKET id;
HasID(SRTSOCKET p)
: id(p)
{
}
bool operator()(const LinkStatusInfo& i) { return i.id == id; }
};
};
/// @brief Qualify pending connections:
/// - Sockets with expired TTL go to the 'to_remove' list and removed from the queue straight away.
/// - If HS request is to be resent (resend 250 ms if no response from the peer) go to the 'to_process' list.
///
/// @param rst result of reading from a UDP socket: received packet / nothin read / read error.
/// @param cst target status for pending connection: reject or proceed.
/// @param iDstSockID destination socket ID of the received packet.
/// @param[in,out] toRemove stores sockets with expired TTL.
/// @param[in,out] toProcess stores sockets which should repeat (resend) HS connection request.
bool qualifyToHandle(EReadStatus rst,
EConnectStatus cst,
int iDstSockID,
std::vector<LinkStatusInfo>& toRemove,
std::vector<LinkStatusInfo>& toProcess);
private:
struct CRL
{
SRTSOCKET m_iID; // SRT socket ID (self)
CUDT* m_pUDT; // CUDT instance
sockaddr_any m_PeerAddr; // SRT sonnection peer address
sync::steady_clock::time_point m_tsTTL; // the time that this request expires
};
std::list<CRL> m_lRendezvousID; // The sockets currently in rendezvous mode
mutable sync::Mutex m_RIDListLock;
};
class CSndQueue
{
friend class CUDT;
friend class CUDTUnited;
friend class CUDT;
friend class CUDTUnited;
public:
CSndQueue();
~CSndQueue();
CSndQueue();
~CSndQueue();
public:
// XXX There's currently no way to access the socket ID set for
// whatever the queue is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
// XXX There's currently no way to access the socket ID set for
// whatever the queue is currently working for. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
/// Initialize the sending queue.
/// @param [in] c UDP channel to be associated to the queue
/// @param [in] t Timer
/// Initialize the sending queue.
/// @param [in] c UDP channel to be associated to the queue
/// @param [in] t Timer
void init(CChannel* c, sync::CTimer* t);
void init(CChannel* c, CTimer* t);
/// Send out a packet to a given address.
/// @param [in] addr destination address
/// @param [in] packet packet to be sent out
/// @return Size of data sent out.
/// Send out a packet to a given address.
/// @param [in] addr destination address
/// @param [in] packet packet to be sent out
/// @return Size of data sent out.
int sendto(const sockaddr_any& addr, CPacket& packet);
int sendto(const sockaddr* addr, CPacket& packet);
/// Get the IP TTL.
/// @param [in] ttl IP Time To Live.
/// @return TTL.
#ifdef SRT_ENABLE_IPOPTS
/// Get the IP TTL.
/// @param [in] ttl IP Time To Live.
/// @return TTL.
int getIpTTL() const;
int getIpTTL() const;
/// Get the IP Type of Service.
/// @return ToS.
/// Get the IP Type of Service.
/// @return ToS.
int getIpToS() const;
int getIpToS() const;
#ifdef SRT_ENABLE_BINDTODEVICE
bool getBind(char* dst, size_t len) const;
#endif
int ioctlQuery(int type) const { return m_pChannel->ioctlQuery(type); }
int sockoptQuery(int level, int type) const { return m_pChannel->sockoptQuery(level, type); }
int ioctlQuery(int type) const;
int sockoptQuery(int level, int type) const;
void setClosing()
{
m_bClosing = true;
}
void setClosing() { m_bClosing = true; }
private:
static void* worker(void* param);
pthread_t m_WorkerThread;
static void* worker(void* param);
sync::CThread m_WorkerThread;
private:
CSndUList* m_pSndUList; // List of UDT instances for data sending
CChannel* m_pChannel; // The UDP channel for data sending
CTimer* m_pTimer; // Timing facility
CSndUList* m_pSndUList; // List of UDT instances for data sending
CChannel* m_pChannel; // The UDP channel for data sending
sync::CTimer* m_pTimer; // Timing facility
pthread_mutex_t m_WindowLock;
pthread_cond_t m_WindowCond;
sync::atomic<bool> m_bClosing; // closing the worker
volatile bool m_bClosing; // closing the worker
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)//>>debug high freq worker
uint64_t m_ullDbgPeriod;
uint64_t m_ullDbgTime;
struct {
#if defined(SRT_DEBUG_SNDQ_HIGHRATE) //>>debug high freq worker
uint64_t m_ullDbgPeriod;
uint64_t m_ullDbgTime;
struct
{
unsigned long lIteration; //
unsigned long lSleepTo; //SleepTo
unsigned long lNotReadyPop; //Continue
unsigned long lSleepTo; // SleepTo
unsigned long lNotReadyPop; // Continue
unsigned long lSendTo;
unsigned long lNotReadyTs;
unsigned long lCondWait; //block on m_WindowCond
} m_WorkerStats;
unsigned long lNotReadyTs;
unsigned long lCondWait; // block on m_WindowCond
} m_WorkerStats;
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
#if ENABLE_LOGGING
static int m_counter;
#endif
private:
CSndQueue(const CSndQueue&);
CSndQueue& operator=(const CSndQueue&);
CSndQueue(const CSndQueue&);
CSndQueue& operator=(const CSndQueue&);
};
class CRcvQueue
{
friend class CUDT;
friend class CUDTUnited;
friend class CUDT;
friend class CUDTUnited;
public:
CRcvQueue();
~CRcvQueue();
CRcvQueue();
~CRcvQueue();
public:
// XXX There's currently no way to access the socket ID set for
// whatever the queue is currently working. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
// XXX There's currently no way to access the socket ID set for
// whatever the queue is currently working. Required to find
// some way to do this, possibly by having a "reverse pointer".
// Currently just "unimplemented".
std::string CONID() const { return ""; }
/// Initialize the receiving queue.
/// @param [in] size queue size
/// @param [in] mss maximum packet size
/// @param [in] version IP version
/// @param [in] hsize hash table size
/// @param [in] c UDP channel to be associated to the queue
/// @param [in] t timer
void init(int size, size_t payload, int version, int hsize, CChannel* c, sync::CTimer* t);
/// Initialize the receiving queue.
/// @param [in] size queue size
/// @param [in] mss maximum packet size
/// @param [in] version IP version
/// @param [in] hsize hash table size
/// @param [in] c UDP channel to be associated to the queue
/// @param [in] t timer
/// Read a packet for a specific UDT socket id.
/// @param [in] id Socket ID
/// @param [out] packet received packet
/// @return Data size of the packet
int recvfrom(int32_t id, CPacket& to_packet);
void init(int size, int payload, int version, int hsize, CChannel* c, CTimer* t);
void stopWorker();
/// Read a packet for a specific UDT socket id.
/// @param [in] id Socket ID
/// @param [out] packet received packet
/// @return Data size of the packet
void setClosing() { m_bClosing = true; }
int recvfrom(int32_t id, ref_t<CPacket> packet);
void setClosing()
{
m_bClosing = true;
}
int getIPversion() { return m_iIPversion; }
private:
static void* worker(void* param);
pthread_t m_WorkerThread;
// Subroutines of worker
EReadStatus worker_RetrieveUnit(ref_t<int32_t> id, ref_t<CUnit*> unit, sockaddr* sa);
EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr* sa);
EConnectStatus worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr* sa);
EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr* sa);
static void* worker(void* param);
sync::CThread m_WorkerThread;
// Subroutines of worker
EReadStatus worker_RetrieveUnit(int32_t& id, CUnit*& unit, sockaddr_any& sa);
EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr_any& sa);
EConnectStatus worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr_any& sa);
EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr_any& sa);
private:
CUnitQueue m_UnitQueue; // The received packet queue
CUnitQueue* m_pUnitQueue; // The received packet queue
CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue
CHash* m_pHash; // Hash table for UDT socket looking up
CChannel* m_pChannel; // UDP channel for receving packets
sync::CTimer* m_pTimer; // shared timer with the snd queue
CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue
CHash* m_pHash; // Hash table for UDT socket looking up
CChannel* m_pChannel; // UDP channel for receving packets
CTimer* m_pTimer; // shared timer with the snd queue
int m_iIPversion; // IP version
size_t m_szPayloadSize; // packet payload size
int m_iPayloadSize; // packet payload size
volatile bool m_bClosing; // closing the worker
sync::atomic<bool> m_bClosing; // closing the worker
#if ENABLE_LOGGING
static srt::sync::atomic<int> m_counter; // A static counter to log RcvQueue worker thread number.
#endif
private:
int setListener(CUDT* u);
void removeListener(const CUDT* u);
int setListener(CUDT* u);
void removeListener(const CUDT* u);
void registerConnector(const SRTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl);
void removeConnector(const SRTSOCKET& id, bool should_lock = true);
void registerConnector(const SRTSOCKET& id,
CUDT* u,
const sockaddr_any& addr,
const sync::steady_clock::time_point& ttl);
void removeConnector(const SRTSOCKET& id);
void setNewEntry(CUDT* u);
bool ifNewEntry();
CUDT* getNewEntry();
void setNewEntry(CUDT* u);
bool ifNewEntry();
CUDT* getNewEntry();
void storePkt(int32_t id, CPacket* pkt);
void storePkt(int32_t id, CPacket* pkt);
private:
pthread_mutex_t m_LSLock;
CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity
CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode
sync::Mutex m_LSLock;
CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity
CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode
std::vector<CUDT*> m_vNewEntry; // newly added entries, to be inserted
pthread_mutex_t m_IDLock;
std::vector<CUDT*> m_vNewEntry; // newly added entries, to be inserted
sync::Mutex m_IDLock;
std::map<int32_t, std::queue<CPacket*> > m_mBuffer; // temporary buffer for rendezvous connection request
pthread_mutex_t m_PassLock;
pthread_cond_t m_PassCond;
std::map<int32_t, std::queue<CPacket*> > m_mBuffer; // temporary buffer for rendezvous connection request
sync::Mutex m_BufferLock;
sync::Condition m_BufferCond;
private:
CRcvQueue(const CRcvQueue&);
CRcvQueue& operator=(const CRcvQueue&);
CRcvQueue(const CRcvQueue&);
CRcvQueue& operator=(const CRcvQueue&);
};
struct CMultiplexer
{
CSndQueue* m_pSndQueue; // The sending queue
CRcvQueue* m_pRcvQueue; // The receiving queue
CChannel* m_pChannel; // The UDP channel for sending and receiving
CTimer* m_pTimer; // The timer
CSndQueue* m_pSndQueue; // The sending queue
CRcvQueue* m_pRcvQueue; // The receiving queue
CChannel* m_pChannel; // The UDP channel for sending and receiving
sync::CTimer* m_pTimer; // The timer
int m_iPort; // The UDP port number of this multiplexer
int m_iIPversion; // IP version
#ifdef SRT_ENABLE_IPOPTS
int m_iIpTTL;
int m_iIpToS;
#endif
int m_iMSS; // Maximum Segment Size
int m_iRefCount; // number of UDT instances that are associated with this multiplexer
int m_iIpV6Only; // IPV6_V6ONLY option
bool m_bReusable; // if this one can be shared with others
int m_iPort; // The UDP port number of this multiplexer
int m_iIPversion; // Address family (AF_INET or AF_INET6)
int m_iRefCount; // number of UDT instances that are associated with this multiplexer
int m_iID; // multiplexer ID
CSrtMuxerConfig m_mcfg;
int m_iID; // multiplexer ID
// Constructor should reset all pointers to NULL
// to prevent dangling pointer when checking for memory alloc fails
CMultiplexer()
: m_pSndQueue(NULL)
, m_pRcvQueue(NULL)
, m_pChannel(NULL)
, m_pTimer(NULL)
{
}
void destroy();
};
} // namespace srt
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,377 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 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/.
*
*/
/*****************************************************************************
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the
above copyright notice, this list of conditions
and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the University of Illinois
nor the names of its contributors may be used to
endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/*****************************************************************************
written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC_SRT_SOCKETCONFIG_H
#define INC_SRT_SOCKETCONFIG_H
#include "platform_sys.h"
#ifdef SRT_ENABLE_BINDTODEVICE
#include <linux/if.h>
#endif
#include <string>
#include "haicrypt.h"
#include "congctl.h"
#include "packet.h"
#include "handshake.h"
#include "logger_defs.h"
#include "packetfilter.h"
// SRT Version constants
#define SRT_VERSION_UNK 0
#define SRT_VERSION_MAJ1 0x010000 /* Version 1 major */
#define SRT_VERSION_MAJ(v) (0xFF0000 & (v)) /* Major number ensuring backward compatibility */
#define SRT_VERSION_MIN(v) (0x00FF00 & (v))
#define SRT_VERSION_PCH(v) (0x0000FF & (v))
// NOTE: SRT_VERSION is primarily defined in the build file.
extern const int32_t SRT_DEF_VERSION;
namespace srt
{
struct CSrtMuxerConfig
{
static const int DEF_UDP_BUFFER_SIZE = 65536;
int iIpTTL;
int iIpToS;
int iIpV6Only; // IPV6_V6ONLY option (-1 if not set)
bool bReuseAddr; // reuse an exiting port or not, for UDP multiplexer
#ifdef SRT_ENABLE_BINDTODEVICE
std::string sBindToDevice;
#endif
int iUDPSndBufSize; // UDP sending buffer size
int iUDPRcvBufSize; // UDP receiving buffer size
bool operator==(const CSrtMuxerConfig& other) const
{
#define CEQUAL(field) (field == other.field)
return CEQUAL(iIpTTL)
&& CEQUAL(iIpToS)
&& CEQUAL(iIpV6Only)
&& CEQUAL(bReuseAddr)
#ifdef SRT_ENABLE_BINDTODEVICE
&& CEQUAL(sBindToDevice)
#endif
&& CEQUAL(iUDPSndBufSize)
&& CEQUAL(iUDPRcvBufSize);
#undef CEQUAL
}
CSrtMuxerConfig()
: iIpTTL(-1) /* IPv4 TTL or IPv6 HOPs [1..255] (-1:undefined) */
, iIpToS(-1) /* IPv4 Type of Service or IPv6 Traffic Class [0x00..0xff] (-1:undefined) */
, iIpV6Only(-1)
, bReuseAddr(true) // This is default in SRT
, iUDPSndBufSize(DEF_UDP_BUFFER_SIZE)
, iUDPRcvBufSize(DEF_UDP_BUFFER_SIZE)
{
}
};
struct CSrtConfig;
template <size_t SIZE>
class StringStorage
{
char stor[SIZE + 1];
uint16_t len;
// NOTE: default copying allowed.
public:
StringStorage()
: len(0)
{
memset(stor, 0, sizeof stor);
}
bool set(const char* s, size_t length)
{
if (length > SIZE)
return false;
memcpy(stor, s, length);
stor[length] = 0;
len = (int) length;
return true;
}
bool set(const std::string& s)
{
return set(s.c_str(), s.size());
}
std::string str() const
{
return len == 0 ? std::string() : std::string(stor);
}
const char* c_str() const
{
return stor;
}
size_t size() const { return size_t(len); }
bool empty() const { return len == 0; }
};
struct CSrtConfig: CSrtMuxerConfig
{
typedef srt::sync::steady_clock::time_point time_point;
typedef srt::sync::steady_clock::duration duration;
static const int
DEF_MSS = 1500,
DEF_FLIGHT_SIZE = 25600,
DEF_BUFFER_SIZE = 8192, //Rcv buffer MUST NOT be bigger than Flight Flag size
DEF_LINGER_S = 3*60, // 3 minutes
DEF_CONNTIMEO_S = 3; // 3 seconds
static const int COMM_RESPONSE_TIMEOUT_MS = 5 * 1000; // 5 seconds
static const uint32_t COMM_DEF_MIN_STABILITY_TIMEOUT_MS = 60; // 60 ms
// Mimimum recv flight flag size is 32 packets
static const int DEF_MIN_FLIGHT_PKT = 32;
static const size_t MAX_SID_LENGTH = 512;
static const size_t MAX_PFILTER_LENGTH = 64;
static const size_t MAX_CONG_LENGTH = 16;
int iMSS; // Maximum Segment Size, in bytes
size_t zExpPayloadSize; // Expected average payload size (user option)
// Options
bool bSynSending; // Sending syncronization mode
bool bSynRecving; // Receiving syncronization mode
int iFlightFlagSize; // Maximum number of packets in flight from the peer side
int iSndBufSize; // Maximum UDT sender buffer size
int iRcvBufSize; // Maximum UDT receiver buffer size
linger Linger; // Linger information on close
bool bRendezvous; // Rendezvous connection mode
duration tdConnTimeOut; // connect timeout in milliseconds
bool bDriftTracer;
int iSndTimeOut; // sending timeout in milliseconds
int iRcvTimeOut; // receiving timeout in milliseconds
int64_t llMaxBW; // maximum data transfer rate (threshold)
// These fields keep the options for encryption
// (SRTO_PASSPHRASE, SRTO_PBKEYLEN). Crypto object is
// created later and takes values from these.
HaiCrypt_Secret CryptoSecret;
int iSndCryptoKeyLen;
// XXX Consider removing. The bDataSender stays here
// in order to maintain the HS side selection in HSv4.
bool bDataSender;
bool bMessageAPI;
bool bTSBPD; // Whether AGENT will do TSBPD Rx (whether peer does, is not agent's problem)
int iRcvLatency; // Agent's Rx latency
int iPeerLatency; // Peer's Rx latency for the traffic made by Agent's Tx.
bool bTLPktDrop; // Whether Agent WILL DO TLPKTDROP on Rx.
int iSndDropDelay; // Extra delay when deciding to snd-drop for TLPKTDROP, -1 to off
bool bEnforcedEnc; // Off by default. When on, any connection other than nopw-nopw & pw1-pw1 is rejected.
int iGroupConnect; // 1 - allow group connections
int iPeerIdleTimeout_ms; // Timeout for hearing anything from the peer (ms).
uint32_t uMinStabilityTimeout_ms;
int iRetransmitAlgo;
int64_t llInputBW; // Input stream rate (bytes/sec). 0: use internally estimated input bandwidth
int64_t llMinInputBW; // Minimum input stream rate estimate (bytes/sec)
int iOverheadBW; // Percent above input stream rate (applies if llMaxBW == 0)
bool bRcvNakReport; // Enable Receiver Periodic NAK Reports
int iMaxReorderTolerance; //< Maximum allowed value for dynamic reorder tolerance
// For the use of CCryptoControl
// HaiCrypt configuration
unsigned int uKmRefreshRatePkt;
unsigned int uKmPreAnnouncePkt;
uint32_t uSrtVersion;
uint32_t uMinimumPeerSrtVersion;
StringStorage<MAX_CONG_LENGTH> sCongestion;
StringStorage<MAX_PFILTER_LENGTH> sPacketFilterConfig;
StringStorage<MAX_SID_LENGTH> sStreamName;
// Shortcuts and utilities
int32_t flightCapacity()
{
return std::min(iRcvBufSize, iFlightFlagSize);
}
CSrtConfig()
: iMSS(DEF_MSS)
, zExpPayloadSize(SRT_LIVE_DEF_PLSIZE)
, bSynSending(true)
, bSynRecving(true)
, iFlightFlagSize(DEF_FLIGHT_SIZE)
, iSndBufSize(DEF_BUFFER_SIZE)
, iRcvBufSize(DEF_BUFFER_SIZE)
, bRendezvous(false)
, tdConnTimeOut(srt::sync::seconds_from(DEF_CONNTIMEO_S))
, bDriftTracer(true)
, iSndTimeOut(-1)
, iRcvTimeOut(-1)
, llMaxBW(-1)
, bDataSender(false)
, bMessageAPI(true)
, bTSBPD(true)
, iRcvLatency(SRT_LIVE_DEF_LATENCY_MS)
, iPeerLatency(0)
, bTLPktDrop(true)
, iSndDropDelay(0)
, bEnforcedEnc(true)
, iGroupConnect(0)
, iPeerIdleTimeout_ms(COMM_RESPONSE_TIMEOUT_MS)
, uMinStabilityTimeout_ms(COMM_DEF_MIN_STABILITY_TIMEOUT_MS)
, iRetransmitAlgo(1)
, llInputBW(0)
, llMinInputBW(0)
, iOverheadBW(25)
, bRcvNakReport(true)
, iMaxReorderTolerance(0) // Sensible optimal value is 10, 0 preserves old behavior
, uKmRefreshRatePkt(0)
, uKmPreAnnouncePkt(0)
, uSrtVersion(SRT_DEF_VERSION)
, uMinimumPeerSrtVersion(SRT_VERSION_MAJ1)
{
// Default UDT configurations
iUDPRcvBufSize = iRcvBufSize * iMSS;
// Linger: LIVE mode defaults, please refer to `SRTO_TRANSTYPE` option
// for other modes.
Linger.l_onoff = 0;
Linger.l_linger = 0;
CryptoSecret.len = 0;
iSndCryptoKeyLen = 0;
// Default congestion is "live".
// Available builtin congestions: "file".
// Others can be registerred.
sCongestion.set("live", 4);
}
~CSrtConfig()
{
// Wipeout critical data
memset(&CryptoSecret, 0, sizeof(CryptoSecret));
}
int set(SRT_SOCKOPT optName, const void* val, int size);
};
template <typename T>
inline T cast_optval(const void* optval)
{
return *reinterpret_cast<const T*>(optval);
}
template <typename T>
inline T cast_optval(const void* optval, int optlen)
{
if (optlen > 0 && optlen != sizeof(T))
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
return cast_optval<T>(optval);
}
// This function is to make it possible for both C and C++
// API to accept both bool and int types for boolean options.
// (it's not that C couldn't use <stdbool.h>, it's that people
// often forget to use correct type).
template <>
inline bool cast_optval(const void* optval, int optlen)
{
if (optlen == sizeof(bool))
{
return *reinterpret_cast<const bool*>(optval);
}
if (optlen == sizeof(int))
{
// 0!= is a windows warning-killer int-to-bool conversion
return 0 != *reinterpret_cast<const int*>(optval);
}
return false;
}
} // namespace srt
struct SRT_SocketOptionObject
{
struct SingleOption
{
uint16_t option;
uint16_t length;
unsigned char storage[1]; // NOTE: Variable length object!
};
std::vector<SingleOption*> options;
SRT_SocketOptionObject() {}
~SRT_SocketOptionObject()
{
for (size_t i = 0; i < options.size(); ++i)
{
// Convert back
unsigned char* mem = reinterpret_cast<unsigned char*>(options[i]);
delete[] mem;
}
}
bool add(SRT_SOCKOPT optname, const void* optval, size_t optlen);
};
#endif

View file

@ -13,8 +13,8 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__SRTC_H
#define INC__SRTC_H
#ifndef INC_SRTC_H
#define INC_SRTC_H
#include "version.h"
@ -23,7 +23,6 @@ written by
#include <string.h>
#include <stdlib.h>
#include "srt4udt.h"
#include "logging_api.h"
////////////////////////////////////////////////////////////////////////////////
@ -36,7 +35,7 @@ written by
#ifdef _WIN32
#ifndef __MINGW__
#ifndef __MINGW32__
// Explicitly define 32-bit and 64-bit numbers
typedef __int32 int32_t;
typedef __int64 int64_t;
@ -47,17 +46,14 @@ written by
// VC 6.0 does not support unsigned __int64: may cause potential problems.
typedef __int64 uint64_t;
#endif
#ifdef SRT_DYNAMIC
#ifdef SRT_EXPORTS
#define SRT_API __declspec(dllexport)
#else
#define SRT_API __declspec(dllimport)
#endif
#endif
#ifdef SRT_DYNAMIC
#ifdef SRT_EXPORTS
#define SRT_API __declspec(dllexport)
#else
#define SRT_API
#define SRT_API __declspec(dllimport)
#endif
#else // __MINGW__
#else // !SRT_DYNAMIC
#define SRT_API
#endif
#else
@ -69,48 +65,99 @@ written by
// You can use these constants with SRTO_MINVERSION option.
#define SRT_VERSION_FEAT_HSv5 0x010300
#if defined(__cplusplus) && __cplusplus > 201406
#define SRT_HAVE_CXX17 1
#else
#define SRT_HAVE_CXX17 0
#endif
// Stadnard attributes
// When compiling in C++17 mode, use the standard C++17 attributes
// (out of these, only [[deprecated]] is supported in C++14, so
// for all lesser standard use compiler-specific attributes).
#if defined(SRT_NO_DEPRECATED)
#define SRT_ATR_UNUSED
#define SRT_ATR_DEPRECATED
#define SRT_ATR_NODISCARD
#elif defined(__cplusplus) && __cplusplus > 201406
#if SRT_HAVE_CXX17
// Unused: DO NOT issue a warning if this entity is unused.
#define SRT_ATR_UNUSED [[maybe_unused]]
#define SRT_ATR_DEPRECATED [[deprecated]]
// Nodiscard: issue a warning if the return value was discarded.
#define SRT_ATR_NODISCARD [[nodiscard]]
// GNUG is GNU C/C++; this syntax is also supported by Clang
#elif defined( __GNUC__)
#elif defined(__GNUC__)
#define SRT_ATR_UNUSED __attribute__((unused))
#define SRT_ATR_DEPRECATED __attribute__((deprecated))
#define SRT_ATR_NODISCARD __attribute__((warn_unused_result))
#elif defined(_MSC_VER)
#define SRT_ATR_UNUSED __pragma(warning(suppress: 4100 4101))
#define SRT_ATR_NODISCARD _Check_return_
#else
#define SRT_ATR_UNUSED
#define SRT_ATR_DEPRECATED
#define SRT_ATR_NODISCARD
#endif
// DEPRECATED attributes
// There's needed DEPRECATED and DEPRECATED_PX, as some compilers require them
// before the entity, others after the entity.
// The *_PX version is the prefix attribute, which applies only
// to functions (Microsoft compilers).
// When deprecating a function, mark it:
//
// SRT_ATR_DEPRECATED_PX retval function(arguments) SRT_ATR_DEPRECATED;
//
// When SRT_NO_DEPRECATED defined, do not issue any deprecation warnings.
// Regardless of the compiler type.
#if defined(SRT_NO_DEPRECATED)
#define SRT_ATR_DEPRECATED
#define SRT_ATR_DEPRECATED_PX
#elif SRT_HAVE_CXX17
#define SRT_ATR_DEPRECATED
#define SRT_ATR_DEPRECATED_PX [[deprecated]]
// GNUG is GNU C/C++; this syntax is also supported by Clang
#elif defined(__GNUC__)
#define SRT_ATR_DEPRECATED_PX
#define SRT_ATR_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define SRT_ATR_DEPRECATED_PX __declspec(deprecated)
#define SRT_ATR_DEPRECATED // no postfix-type modifier
#else
#define SRT_ATR_DEPRECATED_PX
#define SRT_ATR_DEPRECATED
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef int SRTSOCKET; // SRTSOCKET is a typedef to int anyway, and it's not even in UDT namespace :)
typedef int32_t SRTSOCKET;
// The most significant bit 31 (sign bit actually) is left unused,
// so that all people who check the value for < 0 instead of -1
// still get what they want. The bit 30 is reserved for marking
// the "socket group". Most of the API functions should work
// transparently with the socket descriptor designating a single
// socket or a socket group.
static const int32_t SRTGROUP_MASK = (1 << 30);
#ifdef _WIN32
#ifndef __MINGW__
typedef SOCKET SYSSOCKET;
#else
typedef int SYSSOCKET;
#endif
typedef SOCKET SYSSOCKET;
#else
typedef int SYSSOCKET;
#endif
#ifndef ENABLE_BONDING
#define ENABLE_BONDING 0
#endif
typedef SYSSOCKET UDPSOCKET;
@ -141,8 +188,7 @@ typedef enum SRT_SOCKOPT {
SRTO_LINGER = 7, // waiting for unsent data when closing
SRTO_UDP_SNDBUF = 8, // UDP sending buffer size
SRTO_UDP_RCVBUF = 9, // UDP receiving buffer size
// XXX Free space for 2 options
// after deprecated ones are removed
// (some space left)
SRTO_RENDEZVOUS = 12, // rendezvous connection mode
SRTO_SNDTIMEO = 13, // send() timeout
SRTO_RCVTIMEO = 14, // recv() timeout
@ -155,11 +201,10 @@ typedef enum SRT_SOCKOPT {
SRTO_SENDER = 21, // Sender mode (independent of conn mode), for encryption, tsbpd handshake.
SRTO_TSBPDMODE = 22, // Enable/Disable TsbPd. Enable -> Tx set origin timestamp, Rx deliver packet at origin time + delay
SRTO_LATENCY = 23, // NOT RECOMMENDED. SET: to both SRTO_RCVLATENCY and SRTO_PEERLATENCY. GET: same as SRTO_RCVLATENCY.
SRTO_TSBPDDELAY = 23, // DEPRECATED. ALIAS: SRTO_LATENCY
SRTO_INPUTBW = 24, // Estimated input stream rate.
SRTO_OHEADBW, // MaxBW ceiling based on % over input stream rate. Applies when UDT_MAXBW=0 (auto).
SRTO_PASSPHRASE = 26, // Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto
SRTO_PBKEYLEN, // Crypto key len in bytes {16,24,32} Default: 16 (128-bit)
SRTO_PASSPHRASE = 26, // Crypto PBKDF2 Passphrase (must be 10..79 characters, or empty to disable encryption)
SRTO_PBKEYLEN, // Crypto key len in bytes {16,24,32} Default: 16 (AES-128)
SRTO_KMSTATE, // Key Material exchange status (UDT_SRTKmState)
SRTO_IPTTL = 29, // IP Time To Live (passthru for system sockopt IPPROTO_IP/IP_TTL)
SRTO_IPTOS, // IP Type of Service (passthru for system sockopt IPPROTO_IP/IP_TOS)
@ -168,10 +213,10 @@ typedef enum SRT_SOCKOPT {
SRTO_NAKREPORT = 33, // Enable receiver to send periodic NAK reports
SRTO_VERSION = 34, // Local SRT Version
SRTO_PEERVERSION, // Peer SRT Version (from SRT Handshake)
SRTO_CONNTIMEO = 36, // Connect timeout in msec. Ccaller default: 3000, rendezvous (x 10)
// deprecated: SRTO_TWOWAYDATA, SRTO_SNDPBKEYLEN, SRTO_RCVPBKEYLEN (@c below)
_DEPRECATED_SRTO_SNDPBKEYLEN = 38, // (needed to use inside the code without generating -Wswitch)
//
SRTO_CONNTIMEO = 36, // Connect timeout in msec. Caller default: 3000, rendezvous (x 10)
SRTO_DRIFTTRACER = 37, // Enable or disable drift tracer
SRTO_MININPUTBW = 38, // Minimum estimate of input stream rate.
// (some space left)
SRTO_SNDKMSTATE = 40, // (GET) the current state of the encryption at the peer side
SRTO_RCVKMSTATE, // (GET) the current state of the encryption at the agent side
SRTO_LOSSMAXTTL, // Maximum possible packet reorder tolerance (number of packets to receive after loss to send lossreport)
@ -188,15 +233,34 @@ typedef enum SRT_SOCKOPT {
SRTO_ENFORCEDENCRYPTION, // Connection to be rejected or quickly broken when one side encryption set or bad password
SRTO_IPV6ONLY, // IPV6_V6ONLY mode
SRTO_PEERIDLETIMEO, // Peer-idle timeout (max time of silence heard from peer) in [ms]
// (some space left)
SRTO_PACKETFILTER = 60 // Add and configure a packet filter
SRTO_BINDTODEVICE, // Forward the SOL_SOCKET/SO_BINDTODEVICE option on socket (pass packets only from that device)
SRTO_GROUPCONNECT, // Set on a listener to allow group connection (ENABLE_BONDING)
SRTO_GROUPMINSTABLETIMEO, // Minimum Link Stability timeout (backup mode) in milliseconds (ENABLE_BONDING)
SRTO_GROUPTYPE, // Group type to which an accepted socket is about to be added, available in the handshake (ENABLE_BONDING)
SRTO_PACKETFILTER = 60, // Add and configure a packet filter
SRTO_RETRANSMITALGO = 61, // An option to select packet retransmission algorithm
SRTO_E_SIZE // Always last element, not a valid option.
} SRT_SOCKOPT;
#ifdef __cplusplus
typedef SRT_ATR_DEPRECATED SRT_SOCKOPT SRT_SOCKOPT_DEPRECATED;
#if __cplusplus > 199711L // C++11
// Newer compilers report error when [[deprecated]] is applied to types,
// and C++11 and higher uses this.
// Note that this doesn't exactly use the 'deprecated' attribute,
// as it's introduced in C++14. What is actually used here is the
// fact that unknown attributes are ignored, but still warned about.
// This should only catch an eye - and that's what it does.
#define SRT_DEPRECATED_OPTION(value) ((SRT_SOCKOPT [[deprecated]])value)
#else
// Older (pre-C++11) compilers use gcc deprecated applied to a typedef
typedef SRT_ATR_DEPRECATED_PX SRT_SOCKOPT SRT_SOCKOPT_DEPRECATED SRT_ATR_DEPRECATED;
#define SRT_DEPRECATED_OPTION(value) ((SRT_SOCKOPT_DEPRECATED)value)
#endif
#else
@ -213,46 +277,9 @@ enum SRT_ATR_DEPRECATED SRT_SOCKOPT_DEPRECATED
#define SRT_DEPRECATED_OPTION(value) ((enum SRT_SOCKOPT_DEPRECATED)value)
#endif
// DEPRECATED OPTIONS:
// SRTO_TWOWAYDATA: not to be used. SRT connection is always bidirectional if
// both clients support HSv5 - that is, since version 1.3.0. This flag was
// introducted around 1.2.0 version when full bidirectional support was added,
// but the bidirectional feature was decided no to be enabled due to huge
// differences between bidirectional support (especially concerning encryption)
// with HSv4 and HSv5 (that is, HSv4 was decided to remain unidirectional only,
// even though partial support is already provided in this version).
#define SRTO_TWOWAYDATA SRT_DEPRECATED_OPTION(37)
// This has been deprecated a long time ago, treat this as never implemented.
// The value is also already reused for another option.
#define SRTO_TSBPDMAXLAG SRT_DEPRECATED_OPTION(32)
// This option is a derivative from UDT; the mechanism that uses it is now
// settable by SRTO_CONGESTION, or more generally by SRTO_TRANSTYPE. The freed
// number has been reused for a read-only option SRTO_ISN. This option should
// have never been used anywhere, just for safety this is temporarily declared
// as deprecated.
#define SRTO_CC SRT_DEPRECATED_OPTION(3)
// These two flags were derived from UDT, but they were never used.
// Probably it didn't make sense anyway. The maximum size of the message
// in File/Message mode is defined by SRTO_SNDBUF, and the MSGTTL is
// a parameter used in `srt_sendmsg` and `srt_sendmsg2`.
#define SRTO_MAXMSG SRT_DEPRECATED_OPTION(10)
#define SRTO_MSGTTL SRT_DEPRECATED_OPTION(11)
// These flags come from an older experimental implementation of bidirectional
// encryption support, which were used two different SEKs, KEKs and passphrases
// per direction. The current implementation uses just one in both directions,
// so SRTO_PBKEYLEN should be used for both cases.
#define SRTO_SNDPBKEYLEN SRT_DEPRECATED_OPTION(38)
#define SRTO_RCVPBKEYLEN SRT_DEPRECATED_OPTION(39)
// Keeping old name for compatibility (deprecated)
#define SRTO_SMOOTHER SRT_DEPRECATED_OPTION(47)
#define SRTO_STRICTENC SRT_DEPRECATED_OPTION(53)
// Note that there are no deprecated options at the moment, but the mechanism
// stays so that it can be used in future. Example:
// #define SRTO_STRICTENC SRT_DEPRECATED_OPTION(53)
typedef enum SRT_TRANSTYPE
{
@ -295,9 +322,7 @@ struct CBytePerfMon
int pktRcvUndecryptTotal; // number of undecrypted packets
uint64_t byteSentTotal; // total number of sent data bytes, including retransmissions
uint64_t byteRecvTotal; // total number of received bytes
#ifdef SRT_ENABLE_LOSTBYTESCOUNT
uint64_t byteRcvLossTotal; // total number of lost bytes
#endif
uint64_t byteRetransTotal; // total number of retransmitted bytes
uint64_t byteSndDropTotal; // number of too-late-to-send dropped bytes
uint64_t byteRcvDropTotal; // number of too-late-to play missing bytes (estimate based on average packet size)
@ -327,9 +352,7 @@ struct CBytePerfMon
int pktRcvUndecrypt; // number of undecrypted packets
uint64_t byteSent; // number of sent data bytes, including retransmissions
uint64_t byteRecv; // number of received bytes
#ifdef SRT_ENABLE_LOSTBYTESCOUNT
uint64_t byteRcvLoss; // number of retransmitted bytes
#endif
uint64_t byteRetrans; // number of retransmitted bytes
uint64_t byteSndDrop; // number of too-late-to-send dropped bytes
uint64_t byteRcvDrop; // number of too-late-to play missing bytes (estimate based on average packet size)
@ -370,6 +393,20 @@ struct CBytePerfMon
int pktRcvFilterLoss; // number of packet loss not coverable by filter
int pktReorderTolerance; // packet reorder tolerance value
//<
// New stats in 1.5.0
// Total
int64_t pktSentUniqueTotal; // total number of data packets sent by the application
int64_t pktRecvUniqueTotal; // total number of packets to be received by the application
uint64_t byteSentUniqueTotal; // total number of data bytes, sent by the application
uint64_t byteRecvUniqueTotal; // total number of data bytes to be received by the application
// Local
int64_t pktSentUnique; // number of data packets sent by the application
int64_t pktRecvUnique; // number of packets to be received by the application
uint64_t byteSentUnique; // number of data bytes, sent by the application
uint64_t byteRecvUnique; // number of data bytes to be received by the application
};
////////////////////////////////////////////////////////////////////////////////
@ -399,12 +436,14 @@ enum CodeMinor
MN_REJECTED = 2,
MN_NORES = 3,
MN_SECURITY = 4,
MN_CLOSED = 5,
// MJ_CONNECTION
MN_CONNLOST = 1,
MN_NOCONN = 2,
// MJ_SYSTEMRES
MN_THREAD = 1,
MN_MEMORY = 2,
MN_OBJECT = 3,
// MJ_FILESYSTEM
MN_SEEKGFAIL = 1,
MN_READFAIL = 2,
@ -424,6 +463,8 @@ enum CodeMinor
MN_BUSY = 11,
MN_XSIZE = 12,
MN_EIDINVAL = 13,
MN_EEMPTY = 14,
MN_BUSYPORT = 15,
// MJ_AGAIN
MN_WRAVAIL = 1,
MN_RDAVAIL = 2,
@ -431,12 +472,10 @@ enum CodeMinor
MN_CONGESTION = 4
};
static const enum CodeMinor MN_ISSTREAM SRT_ATR_DEPRECATED = (enum CodeMinor)(9);
static const enum CodeMinor MN_ISDGRAM SRT_ATR_DEPRECATED = (enum CodeMinor)(10);
// Stupid, but effective. This will be #undefined, so don't worry.
#define MJ(major) (1000 * MJ_##major)
#define MN(major, minor) (1000 * MJ_##major + MN_##minor)
#define SRT_EMJ(major) (1000 * MJ_##major)
#define SRT_EMN(major, minor) (1000 * MJ_##major + MN_##minor)
// Some better way to define it, and better for C language.
typedef enum SRT_ERRNO
@ -444,55 +483,56 @@ typedef enum SRT_ERRNO
SRT_EUNKNOWN = -1,
SRT_SUCCESS = MJ_SUCCESS,
SRT_ECONNSETUP = MJ(SETUP),
SRT_ENOSERVER = MN(SETUP, TIMEOUT),
SRT_ECONNREJ = MN(SETUP, REJECTED),
SRT_ESOCKFAIL = MN(SETUP, NORES),
SRT_ESECFAIL = MN(SETUP, SECURITY),
SRT_ECONNSETUP = SRT_EMJ(SETUP),
SRT_ENOSERVER = SRT_EMN(SETUP, TIMEOUT),
SRT_ECONNREJ = SRT_EMN(SETUP, REJECTED),
SRT_ESOCKFAIL = SRT_EMN(SETUP, NORES),
SRT_ESECFAIL = SRT_EMN(SETUP, SECURITY),
SRT_ESCLOSED = SRT_EMN(SETUP, CLOSED),
SRT_ECONNFAIL = MJ(CONNECTION),
SRT_ECONNLOST = MN(CONNECTION, CONNLOST),
SRT_ENOCONN = MN(CONNECTION, NOCONN),
SRT_ECONNFAIL = SRT_EMJ(CONNECTION),
SRT_ECONNLOST = SRT_EMN(CONNECTION, CONNLOST),
SRT_ENOCONN = SRT_EMN(CONNECTION, NOCONN),
SRT_ERESOURCE = MJ(SYSTEMRES),
SRT_ETHREAD = MN(SYSTEMRES, THREAD),
SRT_ENOBUF = MN(SYSTEMRES, MEMORY),
SRT_ERESOURCE = SRT_EMJ(SYSTEMRES),
SRT_ETHREAD = SRT_EMN(SYSTEMRES, THREAD),
SRT_ENOBUF = SRT_EMN(SYSTEMRES, MEMORY),
SRT_ESYSOBJ = SRT_EMN(SYSTEMRES, OBJECT),
SRT_EFILE = MJ(FILESYSTEM),
SRT_EINVRDOFF = MN(FILESYSTEM, SEEKGFAIL),
SRT_ERDPERM = MN(FILESYSTEM, READFAIL),
SRT_EINVWROFF = MN(FILESYSTEM, SEEKPFAIL),
SRT_EWRPERM = MN(FILESYSTEM, WRITEFAIL),
SRT_EFILE = SRT_EMJ(FILESYSTEM),
SRT_EINVRDOFF = SRT_EMN(FILESYSTEM, SEEKGFAIL),
SRT_ERDPERM = SRT_EMN(FILESYSTEM, READFAIL),
SRT_EINVWROFF = SRT_EMN(FILESYSTEM, SEEKPFAIL),
SRT_EWRPERM = SRT_EMN(FILESYSTEM, WRITEFAIL),
SRT_EINVOP = MJ(NOTSUP),
SRT_EBOUNDSOCK = MN(NOTSUP, ISBOUND),
SRT_ECONNSOCK = MN(NOTSUP, ISCONNECTED),
SRT_EINVPARAM = MN(NOTSUP, INVAL),
SRT_EINVSOCK = MN(NOTSUP, SIDINVAL),
SRT_EUNBOUNDSOCK = MN(NOTSUP, ISUNBOUND),
SRT_ENOLISTEN = MN(NOTSUP, NOLISTEN),
SRT_ERDVNOSERV = MN(NOTSUP, ISRENDEZVOUS),
SRT_ERDVUNBOUND = MN(NOTSUP, ISRENDUNBOUND),
SRT_EINVALMSGAPI = MN(NOTSUP, INVALMSGAPI),
SRT_EINVALBUFFERAPI = MN(NOTSUP, INVALBUFFERAPI),
SRT_EDUPLISTEN = MN(NOTSUP, BUSY),
SRT_ELARGEMSG = MN(NOTSUP, XSIZE),
SRT_EINVPOLLID = MN(NOTSUP, EIDINVAL),
SRT_EINVOP = SRT_EMJ(NOTSUP),
SRT_EBOUNDSOCK = SRT_EMN(NOTSUP, ISBOUND),
SRT_ECONNSOCK = SRT_EMN(NOTSUP, ISCONNECTED),
SRT_EINVPARAM = SRT_EMN(NOTSUP, INVAL),
SRT_EINVSOCK = SRT_EMN(NOTSUP, SIDINVAL),
SRT_EUNBOUNDSOCK = SRT_EMN(NOTSUP, ISUNBOUND),
SRT_ENOLISTEN = SRT_EMN(NOTSUP, NOLISTEN),
SRT_ERDVNOSERV = SRT_EMN(NOTSUP, ISRENDEZVOUS),
SRT_ERDVUNBOUND = SRT_EMN(NOTSUP, ISRENDUNBOUND),
SRT_EINVALMSGAPI = SRT_EMN(NOTSUP, INVALMSGAPI),
SRT_EINVALBUFFERAPI = SRT_EMN(NOTSUP, INVALBUFFERAPI),
SRT_EDUPLISTEN = SRT_EMN(NOTSUP, BUSY),
SRT_ELARGEMSG = SRT_EMN(NOTSUP, XSIZE),
SRT_EINVPOLLID = SRT_EMN(NOTSUP, EIDINVAL),
SRT_EPOLLEMPTY = SRT_EMN(NOTSUP, EEMPTY),
SRT_EBINDCONFLICT = SRT_EMN(NOTSUP, BUSYPORT),
SRT_EASYNCFAIL = MJ(AGAIN),
SRT_EASYNCSND = MN(AGAIN, WRAVAIL),
SRT_EASYNCRCV = MN(AGAIN, RDAVAIL),
SRT_ETIMEOUT = MN(AGAIN, XMTIMEOUT),
SRT_ECONGEST = MN(AGAIN, CONGESTION),
SRT_EASYNCFAIL = SRT_EMJ(AGAIN),
SRT_EASYNCSND = SRT_EMN(AGAIN, WRAVAIL),
SRT_EASYNCRCV = SRT_EMN(AGAIN, RDAVAIL),
SRT_ETIMEOUT = SRT_EMN(AGAIN, XMTIMEOUT),
SRT_ECONGEST = SRT_EMN(AGAIN, CONGESTION),
SRT_EPEERERR = MJ(PEERERROR)
SRT_EPEERERR = SRT_EMJ(PEERERROR)
} SRT_ERRNO;
static const SRT_ERRNO SRT_EISSTREAM SRT_ATR_DEPRECATED = (SRT_ERRNO) MN(NOTSUP, INVALMSGAPI);
static const SRT_ERRNO SRT_EISDGRAM SRT_ATR_DEPRECATED = (SRT_ERRNO) MN(NOTSUP, INVALBUFFERAPI);
#undef MJ
#undef MN
#undef SRT_EMJ
#undef SRT_EMN
enum SRT_REJECT_REASON
{
@ -510,33 +550,87 @@ enum SRT_REJECT_REASON
SRT_REJ_UNSECURE, // password required or unexpected
SRT_REJ_MESSAGEAPI, // streamapi/messageapi collision
SRT_REJ_CONGESTION, // incompatible congestion-controller type
SRT_REJ_FILTER, // incompatible packet filter
SRT_REJ_FILTER, // incompatible packet filter
SRT_REJ_GROUP, // incompatible group
SRT_REJ_TIMEOUT, // connection timeout
SRT_REJ__SIZE,
SRT_REJ_E_SIZE,
};
// XXX This value remains for some time, but it's deprecated
// Planned deprecation removal: rel1.6.0.
#define SRT_REJ__SIZE SRT_REJ_E_SIZE
// Reject category codes:
#define SRT_REJC_VALUE(code) (1000 * (code/1000))
#define SRT_REJC_INTERNAL 0 // Codes from above SRT_REJECT_REASON enum
#define SRT_REJC_PREDEFINED 1000 // Standard server error codes
#define SRT_REJC_USERDEFINED 2000 // User defined error codes
// Logging API - specialization for SRT.
// Define logging functional areas for log selection.
// Use values greater than 0. Value 0 is reserved for LOGFA_GENERAL,
// which is considered always enabled.
// WARNING: This part is generated.
// Logger Functional Areas
// Note that 0 is "general".
// Made by #define so that it's available also for C API.
#define SRT_LOGFA_GENERAL 0
#define SRT_LOGFA_BSTATS 1
#define SRT_LOGFA_CONTROL 2
#define SRT_LOGFA_DATA 3
#define SRT_LOGFA_TSBPD 4
#define SRT_LOGFA_REXMIT 5
#define SRT_LOGFA_HAICRYPT 6
#define SRT_LOGFA_CONGEST 7
// Values 0* - general, unqualified
// Values 1* - control
// Values 2* - receiving
// Values 3* - sending
// Values 4* - management
// To make a typical int32_t size, although still use std::bitset.
// Made by #define so that it's available also for C API.
// Use ../scripts/generate-logging-defs.tcl to regenerate.
// SRT_LOGFA BEGIN GENERATED SECTION {
#define SRT_LOGFA_GENERAL 0 // gglog: General uncategorized log, for serious issues only
#define SRT_LOGFA_SOCKMGMT 1 // smlog: Socket create/open/close/configure activities
#define SRT_LOGFA_CONN 2 // cnlog: Connection establishment and handshake
#define SRT_LOGFA_XTIMER 3 // xtlog: The checkTimer and around activities
#define SRT_LOGFA_TSBPD 4 // tslog: The TsBPD thread
#define SRT_LOGFA_RSRC 5 // rslog: System resource allocation and management
#define SRT_LOGFA_CONGEST 7 // cclog: Congestion control module
#define SRT_LOGFA_PFILTER 8 // pflog: Packet filter module
#define SRT_LOGFA_API_CTRL 11 // aclog: API part for socket and library managmenet
#define SRT_LOGFA_QUE_CTRL 13 // qclog: Queue control activities
#define SRT_LOGFA_EPOLL_UPD 16 // eilog: EPoll, internal update activities
#define SRT_LOGFA_API_RECV 21 // arlog: API part for receiving
#define SRT_LOGFA_BUF_RECV 22 // brlog: Buffer, receiving side
#define SRT_LOGFA_QUE_RECV 23 // qrlog: Queue, receiving side
#define SRT_LOGFA_CHN_RECV 24 // krlog: CChannel, receiving side
#define SRT_LOGFA_GRP_RECV 25 // grlog: Group, receiving side
#define SRT_LOGFA_API_SEND 31 // aslog: API part for sending
#define SRT_LOGFA_BUF_SEND 32 // bslog: Buffer, sending side
#define SRT_LOGFA_QUE_SEND 33 // qslog: Queue, sending side
#define SRT_LOGFA_CHN_SEND 34 // kslog: CChannel, sending side
#define SRT_LOGFA_GRP_SEND 35 // gslog: Group, sending side
#define SRT_LOGFA_INTERNAL 41 // inlog: Internal activities not connected directly to a socket
#define SRT_LOGFA_QUE_MGMT 43 // qmlog: Queue, management part
#define SRT_LOGFA_CHN_MGMT 44 // kmlog: CChannel, management part
#define SRT_LOGFA_GRP_MGMT 45 // gmlog: Group, management part
#define SRT_LOGFA_EPOLL_API 46 // ealog: EPoll, API part
#define SRT_LOGFA_HAICRYPT 6 // hclog: Haicrypt module area
#define SRT_LOGFA_APPLOG 10 // aplog: Applications
// } SRT_LOGFA END GENERATED SECTION
// To make a typical int64_t size, although still use std::bitset.
// C API will carry it over.
#define SRT_LOGFA_LASTNONE 31
#define SRT_LOGFA_LASTNONE 63
enum SRT_KM_STATE
{
@ -550,16 +644,73 @@ enum SRT_KM_STATE
enum SRT_EPOLL_OPT
{
SRT_EPOLL_OPT_NONE = 0x0, // fallback
// this values are defined same as linux epoll.h
// Values intended to be the same as in `<sys/epoll.h>`.
// so that if system values are used by mistake, they should have the same effect
// This applies to: IN, OUT, ERR and ET.
/// Ready for 'recv' operation:
///
/// - For stream mode it means that at least 1 byte is available.
/// In this mode the buffer may extract only a part of the packet,
/// leaving next data possible for extraction later.
///
/// - For message mode it means that there is at least one packet
/// available (this may change in future, as it is desired that
/// one full message should only wake up, not single packet of a
/// not yet extractable message).
///
/// - For live mode it means that there's at least one packet
/// ready to play.
///
/// - For listener sockets, this means that there is a new connection
/// waiting for pickup through the `srt_accept()` call, that is,
/// the next call to `srt_accept()` will succeed without blocking
/// (see an alias SRT_EPOLL_ACCEPT below).
SRT_EPOLL_IN = 0x1,
/// Ready for 'send' operation.
///
/// - For stream mode it means that there's a free space in the
/// sender buffer for at least 1 byte of data. The next send
/// operation will only allow to send as much data as it is free
/// space in the buffer.
///
/// - For message mode it means that there's a free space for at
/// least one UDP packet. The edge-triggered mode can be used to
/// pick up updates as the free space in the sender buffer grows.
///
/// - For live mode it means that there's a free space for at least
/// one UDP packet. On the other hand, no readiness for OUT usually
/// means an extraordinary congestion on the link, meaning also that
/// you should immediately slow down the sending rate or you may get
/// a connection break soon.
///
/// - For non-blocking sockets used with `srt_connect*` operation,
/// this flag simply means that the connection was established.
SRT_EPOLL_OUT = 0x4,
/// The socket has encountered an error in the last operation
/// and the next operation on that socket will end up with error.
/// You can retry the operation, but getting the error from it
/// is certain, so you may as well close the socket.
SRT_EPOLL_ERR = 0x8,
// To avoid confusion in the internal code, the following
// duplicates are introduced to improve clarity.
SRT_EPOLL_CONNECT = SRT_EPOLL_OUT,
SRT_EPOLL_ACCEPT = SRT_EPOLL_IN,
SRT_EPOLL_UPDATE = 0x10,
SRT_EPOLL_ET = 1u << 31
};
// These are actually flags - use a bit container:
typedef int32_t SRT_EPOLL_T;
// Define which epoll flags determine events. All others are special flags.
#define SRT_EPOLL_EVENTTYPES (SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_UPDATE | SRT_EPOLL_ERR)
#define SRT_EPOLL_ETONLY (SRT_EPOLL_UPDATE)
enum SRT_EPOLL_FLAGS
{
/// This allows the EID container to be empty when calling the waiting
@ -582,17 +733,8 @@ inline SRT_EPOLL_OPT operator|(SRT_EPOLL_OPT a1, SRT_EPOLL_OPT a2)
return SRT_EPOLL_OPT( (int)a1 | (int)a2 );
}
inline bool operator&(int flags, SRT_EPOLL_OPT eflg)
{
// Using an enum prevents treating int automatically as enum,
// requires explicit enum to be passed here, and minimizes the
// risk that the right side value will contain multiple flags.
return (flags & int(eflg)) != 0;
}
#endif
typedef struct CBytePerfMon SRT_TRACEBSTATS;
static const SRTSOCKET SRT_INVALID_SOCK = -1;
@ -605,18 +747,32 @@ SRT_API int srt_cleanup(void);
//
// Socket operations
//
SRT_API SRTSOCKET srt_socket (int af, int type, int protocol);
SRT_API SRTSOCKET srt_create_socket();
// DEPRECATED: srt_socket with 3 arguments. All these arguments are ignored
// and socket creation doesn't need any arguments. Use srt_create_socket().
// Planned deprecation removal: rel1.6.0
SRT_ATR_DEPRECATED_PX SRT_API SRTSOCKET srt_socket(int, int, int) SRT_ATR_DEPRECATED;
SRT_API SRTSOCKET srt_create_socket(void);
SRT_API int srt_bind (SRTSOCKET u, const struct sockaddr* name, int namelen);
SRT_API int srt_bind_peerof (SRTSOCKET u, UDPSOCKET udpsock);
SRT_API int srt_bind_acquire (SRTSOCKET u, UDPSOCKET sys_udp_sock);
// Old name of srt_bind_acquire(), please don't use
// Planned deprecation removal: rel1.6.0
SRT_ATR_DEPRECATED_PX static inline int srt_bind_peerof(SRTSOCKET u, UDPSOCKET sys_udp_sock) SRT_ATR_DEPRECATED;
static inline int srt_bind_peerof (SRTSOCKET u, UDPSOCKET sys_udp_sock) { return srt_bind_acquire(u, sys_udp_sock); }
SRT_API int srt_listen (SRTSOCKET u, int backlog);
SRT_API SRTSOCKET srt_accept (SRTSOCKET u, struct sockaddr* addr, int* addrlen);
SRT_API SRTSOCKET srt_accept_bond (const SRTSOCKET listeners[], int lsize, int64_t msTimeOut);
typedef int srt_listen_callback_fn (void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid);
SRT_API int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque);
typedef void srt_connect_callback_fn (void* opaq, SRTSOCKET ns, int errorcode, const struct sockaddr* peeraddr, int token);
SRT_API int srt_connect_callback(SRTSOCKET clr, srt_connect_callback_fn* hook_fn, void* hook_opaque);
SRT_API int srt_connect (SRTSOCKET u, const struct sockaddr* name, int namelen);
SRT_API int srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn);
SRT_API int srt_connect_bind (SRTSOCKET u, const struct sockaddr* source,
const struct sockaddr* target, int len);
SRT_API int srt_rendezvous (SRTSOCKET u, const struct sockaddr* local_name, int local_namelen,
const struct sockaddr* remote_name, int remote_namelen);
SRT_API int srt_close (SRTSOCKET u);
SRT_API int srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen);
SRT_API int srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen);
@ -625,19 +781,34 @@ SRT_API int srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCK
SRT_API int srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen);
SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen);
typedef struct SRT_SocketGroupData_ SRT_SOCKGROUPDATA;
// XXX Note that the srctime functionality doesn't work yet and needs fixing.
typedef struct SRT_MsgCtrl_
{
int flags; // Left for future
int msgttl; // TTL for a message, default -1 (no TTL limitation)
int msgttl; // TTL for a message (millisec), default -1 (no TTL limitation)
int inorder; // Whether a message is allowed to supersede partially lost one. Unused in stream and live mode.
int boundary; // 0:mid pkt, 1(01b):end of frame, 2(11b):complete frame, 3(10b): start of frame
uint64_t srctime; // source timestamp (usec), 0: use internal time
int64_t srctime; // source time since epoch (usec), 0: use internal time (sender)
int32_t pktseq; // sequence number of the first packet in received message (unused for sending)
int32_t msgno; // message number (output value for both sending and receiving)
SRT_SOCKGROUPDATA* grpdata;
size_t grpdata_size;
} SRT_MSGCTRL;
// Trap representation for sequence and message numbers
// This value means that this is "unset", and it's never
// a result of an operation made on this number.
static const int32_t SRT_SEQNO_NONE = -1; // -1: no seq (0 is a valid seqno!)
static const int32_t SRT_MSGNO_NONE = -1; // -1: unset
static const int32_t SRT_MSGNO_CONTROL = 0; // 0: control (used by packet filter)
static const int SRT_MSGTTL_INF = -1; // unlimited TTL specification for message TTL
// XXX Might be useful also other special uses of -1:
// - -1 as infinity for srt_epoll_wait
// - -1 as a trap index value used in list.cpp
// You are free to use either of these two methods to set SRT_MSGCTRL object
// to default values: either call srt_msgctrl_init(&obj) or obj = srt_msgctrl_default.
SRT_API void srt_msgctrl_init(SRT_MSGCTRL* mctrl);
@ -657,11 +828,6 @@ SRT_API extern const SRT_MSGCTRL srt_msgctrl_default;
// parameters will be filled, as needed. NULL is acceptable, in which case
// the defaults are used.
// NOTE: srt_send and srt_recv have the last "..." left to allow ignore a
// deprecated and unused "flags" parameter. After confirming that all
// compat applications that pass useless 0 there are fixed, this will be
// removed.
//
// Sending functions
//
@ -692,16 +858,17 @@ SRT_API int srt_getlasterror(int* errno_loc);
SRT_API const char* srt_strerror(int code, int errnoval);
SRT_API void srt_clearlasterror(void);
// performance track
// perfmon with Byte counters for better bitrate estimation.
// Performance tracking
// Performance monitor with Byte counters for better bitrate estimation.
SRT_API int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear);
// permon with Byte counters and instantaneous stats instead of moving averages for Snd/Rcvbuffer sizes.
// Performance monitor with Byte counters and instantaneous stats instead of moving averages for Snd/Rcvbuffer sizes.
SRT_API int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous);
// Socket Status (for problem tracking)
SRT_API SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u);
SRT_API int srt_epoll_create(void);
SRT_API int srt_epoll_clear_usocks(int eid);
SRT_API int srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events);
SRT_API int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events);
SRT_API int srt_epoll_remove_usock(int eid, SRTSOCKET u);
@ -711,10 +878,14 @@ SRT_API int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events);
SRT_API int srt_epoll_wait(int eid, SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum, int64_t msTimeOut,
SYSSOCKET* lrfds, int* lrnum, SYSSOCKET* lwfds, int* lwnum);
typedef struct SRT_EPOLL_EVENT_
typedef struct SRT_EPOLL_EVENT_STR
{
SRTSOCKET fd;
int events; // SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR
#ifdef __cplusplus
SRT_EPOLL_EVENT_STR(SRTSOCKET s, int ev): fd(s), events(ev) {}
SRT_EPOLL_EVENT_STR(): fd(-1), events(0) {} // NOTE: allows singular values, no init.
#endif
} SRT_EPOLL_EVENT;
SRT_API int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
@ -736,9 +907,88 @@ SRT_API void srt_setlogflags(int flags);
SRT_API int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes);
SRT_API enum SRT_REJECT_REASON srt_getrejectreason(SRTSOCKET sock);
SRT_API extern const char* const srt_rejectreason_msg [];
const char* srt_rejectreason_str(enum SRT_REJECT_REASON id);
SRT_API int srt_getrejectreason(SRTSOCKET sock);
SRT_API int srt_setrejectreason(SRTSOCKET sock, int value);
// The srt_rejectreason_msg[] array is deprecated (as unsafe).
// Planned removal: v1.6.0.
SRT_API SRT_ATR_DEPRECATED extern const char* const srt_rejectreason_msg [];
SRT_API const char* srt_rejectreason_str(int id);
SRT_API uint32_t srt_getversion(void);
SRT_API int64_t srt_time_now(void);
SRT_API int64_t srt_connection_time(SRTSOCKET sock);
// Possible internal clock types
#define SRT_SYNC_CLOCK_STDCXX_STEADY 0 // C++11 std::chrono::steady_clock
#define SRT_SYNC_CLOCK_GETTIME_MONOTONIC 1 // clock_gettime with CLOCK_MONOTONIC
#define SRT_SYNC_CLOCK_WINQPC 2
#define SRT_SYNC_CLOCK_MACH_ABSTIME 3
#define SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY 4
#define SRT_SYNC_CLOCK_AMD64_RDTSC 5
#define SRT_SYNC_CLOCK_IA32_RDTSC 6
#define SRT_SYNC_CLOCK_IA64_ITC 7
SRT_API int srt_clock_type(void);
// SRT Socket Groups API (ENABLE_BONDING)
typedef enum SRT_GROUP_TYPE
{
SRT_GTYPE_UNDEFINED,
SRT_GTYPE_BROADCAST,
SRT_GTYPE_BACKUP,
// ...
SRT_GTYPE_E_END
} SRT_GROUP_TYPE;
// Free-form flags for groups
// Flags may be type-specific!
static const uint32_t SRT_GFLAG_SYNCONMSG = 1;
typedef enum SRT_MemberStatus
{
SRT_GST_PENDING, // The socket is created correctly, but not yet ready for getting data.
SRT_GST_IDLE, // The socket is ready to be activated
SRT_GST_RUNNING, // The socket was already activated and is in use
SRT_GST_BROKEN // The last operation broke the socket, it should be closed.
} SRT_MEMBERSTATUS;
struct SRT_SocketGroupData_
{
SRTSOCKET id;
struct sockaddr_storage peeraddr; // Don't want to expose sockaddr_any to public API
SRT_SOCKSTATUS sockstate;
uint16_t weight;
SRT_MEMBERSTATUS memberstate;
int result;
int token;
};
typedef struct SRT_SocketOptionObject SRT_SOCKOPT_CONFIG;
typedef struct SRT_GroupMemberConfig_
{
SRTSOCKET id;
struct sockaddr_storage srcaddr;
struct sockaddr_storage peeraddr; // Don't want to expose sockaddr_any to public API
uint16_t weight;
SRT_SOCKOPT_CONFIG* config;
int errorcode;
int token;
} SRT_SOCKGROUPCONFIG;
SRT_API SRTSOCKET srt_create_group(SRT_GROUP_TYPE);
SRT_API SRTSOCKET srt_groupof(SRTSOCKET socket);
SRT_API int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA* output, size_t* inoutlen);
SRT_API SRT_SOCKOPT_CONFIG* srt_create_config(void);
SRT_API void srt_delete_config(SRT_SOCKOPT_CONFIG* config /*nullable*/);
SRT_API int srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len);
SRT_API SRT_SOCKGROUPCONFIG srt_prepare_endpoint(const struct sockaddr* src /*nullable*/, const struct sockaddr* adr, int namelen);
SRT_API int srt_connect_group(SRTSOCKET group, SRT_SOCKGROUPCONFIG name[], int arraysize);
#ifdef __cplusplus
}

View file

@ -1,71 +0,0 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 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/.
*
*/
/*****************************************************************************
written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef SRT4UDT_H
#define SRT4UDT_H
#ifndef INC__SRTC_H
#error "This is protected header, used by udt.h. This shouldn't be included directly"
#endif
//undef SRT_ENABLE_ECN 1 /* Early Congestion Notification (for source bitrate control) */
//undef SRT_DEBUG_TSBPD_OUTJITTER 1 /* Packet Delivery histogram */
//undef SRT_DEBUG_TSBPD_DRIFT 1 /* Debug Encoder-Decoder Drift) */
//undef SRT_DEBUG_TSBPD_WRAP 1 /* Debug packet timestamp wraparound */
//undef SRT_DEBUG_TLPKTDROP_DROPSEQ 1
//undef SRT_DEBUG_SNDQ_HIGHRATE 1
/*
* SRT_ENABLE_CONNTIMEO
* Option UDT_CONNTIMEO added to the API to set/get the connection timeout.
* The UDT hard coded default of 3000 msec is too small for some large RTT (satellite) use cases.
* The SRT handshake (2 exchanges) needs 2 times the RTT to complete with no packet loss.
*/
#define SRT_ENABLE_CONNTIMEO 1
/*
* SRT_ENABLE_NOCWND
* Set the congestion window at its max (then disabling it) to prevent stopping transmission
* when too many packets are not acknowledged.
* The congestion windows is the maximum distance in pkts since the last acknowledged packets.
*/
#define SRT_ENABLE_NOCWND 1
/*
* SRT_ENABLE_NAKREPORT
* Send periodic NAK report for more efficient retransmission instead of relying on ACK timeout
* to retransmit all non-ACKed packets, very inefficient with real-time and no congestion window.
*/
#define SRT_ENABLE_NAKREPORT 1
#define SRT_ENABLE_RCVBUFSZ_MAVG 1 /* Recv buffer size moving average */
#define SRT_ENABLE_SNDBUFSZ_MAVG 1 /* Send buffer size moving average */
#define SRT_MAVG_SAMPLING_RATE 40 /* Max sampling rate */
#define SRT_ENABLE_LOSTBYTESCOUNT 1
/*
* SRT_ENABLE_IPOPTS
* Enable IP TTL and ToS setting
*/
#define SRT_ENABLE_IPOPTS 1
#define SRT_ENABLE_CLOSE_SYNCH 1
#endif /* SRT4UDT_H */

View file

@ -0,0 +1,191 @@
/*
* 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/.
*
*/
/*****************************************************************************
The file contains various planform and compiler dependent attribute definitions
used by SRT library internally.
*****************************************************************************/
#ifndef INC_SRT_ATTR_DEFS_H
#define INC_SRT_ATTR_DEFS_H
// ATTRIBUTES:
//
// SRT_ATR_UNUSED: declare an entity ALLOWED to be unused (prevents warnings)
// ATR_DEPRECATED: declare an entity deprecated (compiler should warn when used)
// ATR_NOEXCEPT: The true `noexcept` from C++11, or nothing if compiling in pre-C++11 mode
// ATR_NOTHROW: In C++11: `noexcept`. In pre-C++11: `throw()`. Required for GNU libstdc++.
// ATR_CONSTEXPR: In C++11: `constexpr`. Otherwise empty.
// ATR_OVERRIDE: In C++11: `override`. Otherwise empty.
// ATR_FINAL: In C++11: `final`. Otherwise empty.
#ifdef __GNUG__
#define ATR_DEPRECATED __attribute__((deprecated))
#else
#define ATR_DEPRECATED
#endif
#if defined(__cplusplus) && __cplusplus > 199711L
#define HAVE_CXX11 1
// For gcc 4.7, claim C++11 is supported, as long as experimental C++0x is on,
// however it's only the "most required C++11 support".
#if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 7 // 4.7 only!
#define ATR_NOEXCEPT
#define ATR_NOTHROW throw()
#define ATR_CONSTEXPR
#define ATR_OVERRIDE
#define ATR_FINAL
#else
#define HAVE_FULL_CXX11 1
#define ATR_NOEXCEPT noexcept
#define ATR_NOTHROW noexcept
#define ATR_CONSTEXPR constexpr
#define ATR_OVERRIDE override
#define ATR_FINAL final
#endif
#elif defined(_MSC_VER) && _MSC_VER >= 1800
// Microsoft Visual Studio supports C++11, but not fully,
// and still did not change the value of __cplusplus. Treat
// this special way.
// _MSC_VER == 1800 means Microsoft Visual Studio 2013.
#define HAVE_CXX11 1
#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026
#define HAVE_FULL_CXX11 1
#define ATR_NOEXCEPT noexcept
#define ATR_NOTHROW noexcept
#define ATR_CONSTEXPR constexpr
#define ATR_OVERRIDE override
#define ATR_FINAL final
#else
#define ATR_NOEXCEPT
#define ATR_NOTHROW throw()
#define ATR_CONSTEXPR
#define ATR_OVERRIDE
#define ATR_FINAL
#endif
#else
#define HAVE_CXX11 0
#define ATR_NOEXCEPT
#define ATR_NOTHROW throw()
#define ATR_CONSTEXPR
#define ATR_OVERRIDE
#define ATR_FINAL
#endif // __cplusplus
#if !HAVE_CXX11 && defined(REQUIRE_CXX11) && REQUIRE_CXX11 == 1
#error "The currently compiled application required C++11, but your compiler doesn't support it."
#endif
///////////////////////////////////////////////////////////////////////////////
// Attributes for thread safety analysis
// - Clang TSA (https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutexheader).
// - MSVC SAL (partially).
// - Other compilers: none.
///////////////////////////////////////////////////////////////////////////////
#if _MSC_VER >= 1920
// In case of MSVC these attributes have to preceed the attributed objects (variable, function).
// E.g. SRT_ATTR_GUARDED_BY(mtx) int object;
// It is tricky to annotate e.g. the following function, as clang complaints it does not know 'm'.
// SRT_ATTR_EXCLUDES(m) SRT_ATTR_ACQUIRE(m)
// inline void enterCS(Mutex& m) { m.lock(); }
#define SRT_ATTR_CAPABILITY(expr)
#define SRT_ATTR_SCOPED_CAPABILITY
#define SRT_ATTR_GUARDED_BY(expr) _Guarded_by_(expr)
#define SRT_ATTR_PT_GUARDED_BY(expr)
#define SRT_ATTR_ACQUIRED_BEFORE(...)
#define SRT_ATTR_ACQUIRED_AFTER(...)
#define SRT_ATTR_REQUIRES(expr) _Requires_lock_held_(expr)
#define SRT_ATTR_REQUIRES2(expr1, expr2) _Requires_lock_held_(expr1) _Requires_lock_held_(expr2)
#define SRT_ATTR_REQUIRES_SHARED(...)
#define SRT_ATTR_ACQUIRE(expr) _Acquires_nonreentrant_lock_(expr)
#define SRT_ATTR_ACQUIRE_SHARED(...)
#define SRT_ATTR_RELEASE(expr) _Releases_lock_(expr)
#define SRT_ATTR_RELEASE_SHARED(...)
#define SRT_ATTR_RELEASE_GENERIC(...)
#define SRT_ATTR_TRY_ACQUIRE(...) _Acquires_nonreentrant_lock_(expr)
#define SRT_ATTR_TRY_ACQUIRE_SHARED(...)
#define SRT_ATTR_EXCLUDES(...)
#define SRT_ATTR_ASSERT_CAPABILITY(expr)
#define SRT_ATTR_ASSERT_SHARED_CAPABILITY(x)
#define SRT_ATTR_RETURN_CAPABILITY(x)
#define SRT_ATTR_NO_THREAD_SAFETY_ANALYSIS
#else
#if defined(__clang__) && defined(__clang_major__) && (__clang_major__ > 5)
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
#endif
#define SRT_ATTR_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
#define SRT_ATTR_SCOPED_CAPABILITY \
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
#define SRT_ATTR_GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
#define SRT_ATTR_PT_GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
#define SRT_ATTR_ACQUIRED_BEFORE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
#define SRT_ATTR_ACQUIRED_AFTER(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
#define SRT_ATTR_REQUIRES(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
#define SRT_ATTR_REQUIRES2(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
#define SRT_ATTR_REQUIRES_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
#define SRT_ATTR_ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
#define SRT_ATTR_ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
#define SRT_ATTR_RELEASE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
#define SRT_ATTR_RELEASE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
#define SRT_ATTR_RELEASE_GENERIC(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__))
#define SRT_ATTR_TRY_ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
#define SRT_ATTR_TRY_ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
#define SRT_ATTR_EXCLUDES(...) \
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
#define SRT_ATTR_ASSERT_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
#define SRT_ATTR_ASSERT_SHARED_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
#define SRT_ATTR_RETURN_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
#define SRT_ATTR_NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
#endif // not _MSC_VER
#endif // INC_SRT_ATTR_DEFS_H

View file

@ -13,17 +13,18 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#include "platform_sys.h"
#include <iterator>
#include <fstream>
#if __APPLE__
#include "TargetConditionals.h"
#endif
#include "srt.h"
#include "common.h"
#include "packet.h"
#include "core.h"
#include "utilities.h"
using namespace std;
using namespace srt;
extern "C" {
@ -31,51 +32,111 @@ extern "C" {
int srt_startup() { return CUDT::startup(); }
int srt_cleanup() { return CUDT::cleanup(); }
SRTSOCKET srt_socket(int af, int type, int protocol) { return CUDT::socket(af, type, protocol); }
SRTSOCKET srt_create_socket()
// Socket creation.
SRTSOCKET srt_socket(int , int , int ) { return CUDT::socket(); }
SRTSOCKET srt_create_socket() { return CUDT::socket(); }
#if ENABLE_BONDING
// Group management.
SRTSOCKET srt_create_group(SRT_GROUP_TYPE gt) { return CUDT::createGroup(gt); }
SRTSOCKET srt_groupof(SRTSOCKET socket) { return CUDT::getGroupOfSocket(socket); }
int srt_group_data(SRTSOCKET socketgroup, SRT_SOCKGROUPDATA* output, size_t* inoutlen)
{
// XXX This must include rework around m_iIPVersion. This must be
// abandoned completely and all "IP VERSION" thing should rely on
// the exact specification in the 'sockaddr' objects passed to other functions,
// that is, the "current IP Version" remains undefined until any of
// srt_bind() or srt_connect() function is done. And when any of these
// functions are being called, the IP version is contained in the
// sockaddr object passed there.
// Until this rework is done, srt_create_socket() will set the
// default AF_INET family.
// Note that all arguments except the first one here are ignored.
return CUDT::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
return CUDT::getGroupData(socketgroup, output, inoutlen);
}
SRT_SOCKOPT_CONFIG* srt_create_config()
{
return new SRT_SocketOptionObject;
}
int srt_config_add(SRT_SOCKOPT_CONFIG* config, SRT_SOCKOPT option, const void* contents, int len)
{
if (!config)
return -1;
if (!config->add(option, contents, len))
return -1;
return 0;
}
int srt_connect_group(SRTSOCKET group,
SRT_SOCKGROUPCONFIG name[], int arraysize)
{
return CUDT::connectLinks(group, name, arraysize);
}
#else
SRTSOCKET srt_create_group(SRT_GROUP_TYPE) { return SRT_INVALID_SOCK; }
SRTSOCKET srt_groupof(SRTSOCKET) { return SRT_INVALID_SOCK; }
int srt_group_data(SRTSOCKET, SRT_SOCKGROUPDATA*, size_t*) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); }
SRT_SOCKOPT_CONFIG* srt_create_config() { return NULL; }
int srt_config_add(SRT_SOCKOPT_CONFIG*, SRT_SOCKOPT, const void*, int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); }
int srt_connect_group(SRTSOCKET, SRT_SOCKGROUPCONFIG[], int) { return srt::CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0); }
#endif
SRT_SOCKGROUPCONFIG srt_prepare_endpoint(const struct sockaddr* src, const struct sockaddr* dst, int namelen)
{
SRT_SOCKGROUPCONFIG data;
#if ENABLE_BONDING
data.errorcode = SRT_SUCCESS;
#else
data.errorcode = SRT_EINVOP;
#endif
data.id = -1;
data.token = -1;
data.weight = 0;
data.config = NULL;
if (src)
memcpy(&data.srcaddr, src, namelen);
else
{
memset(&data.srcaddr, 0, sizeof data.srcaddr);
// Still set the family according to the target address
data.srcaddr.ss_family = dst->sa_family;
}
memcpy(&data.peeraddr, dst, namelen);
return data;
}
void srt_delete_config(SRT_SOCKOPT_CONFIG* in)
{
delete in;
}
// Binding and connection management
int srt_bind(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::bind(u, name, namelen); }
int srt_bind_peerof(SRTSOCKET u, UDPSOCKET udpsock) { return CUDT::bind(u, udpsock); }
int srt_bind_acquire(SRTSOCKET u, UDPSOCKET udpsock) { return CUDT::bind(u, udpsock); }
int srt_listen(SRTSOCKET u, int backlog) { return CUDT::listen(u, backlog); }
SRTSOCKET srt_accept(SRTSOCKET u, struct sockaddr * addr, int * addrlen) { return CUDT::accept(u, addr, addrlen); }
int srt_connect(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::connect(u, name, namelen, 0); }
SRTSOCKET srt_accept_bond(const SRTSOCKET lsns[], int lsize, int64_t msTimeOut) { return CUDT::accept_bond(lsns, lsize, msTimeOut); }
int srt_connect(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::connect(u, name, namelen, SRT_SEQNO_NONE); }
int srt_connect_debug(SRTSOCKET u, const struct sockaddr * name, int namelen, int forced_isn) { return CUDT::connect(u, name, namelen, forced_isn); }
int srt_connect_bind(SRTSOCKET u,
const struct sockaddr* source,
const struct sockaddr* target, int target_len)
{
return CUDT::connect(u, source, target, target_len);
}
int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen,
const struct sockaddr* remote_name, int remote_namelen)
{
bool yes = 1;
CUDT::setsockopt(u, 0, UDT_RENDEZVOUS, &yes, sizeof yes);
CUDT::setsockopt(u, 0, SRTO_RENDEZVOUS, &yes, sizeof yes);
// Note: PORT is 16-bit and at the same location in both sockaddr_in and sockaddr_in6.
// Just as a safety precaution, check the structs.
if ( (local_name->sa_family != AF_INET && local_name->sa_family != AF_INET6)
|| local_name->sa_family != remote_name->sa_family)
return SRT_EINVPARAM;
return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0);
sockaddr_in* local_sin = (sockaddr_in*)local_name;
sockaddr_in* remote_sin = (sockaddr_in*)remote_name;
if (local_sin->sin_port != remote_sin->sin_port)
return SRT_EINVPARAM;
int st = srt_bind(u, local_name, local_namelen);
if ( st != 0 )
const int st = srt_bind(u, local_name, local_namelen);
if (st != 0)
return st;
return srt_connect(u, remote_name, remote_namelen);
@ -111,17 +172,17 @@ int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen
int srt_send(SRTSOCKET u, const char * buf, int len) { return CUDT::send(u, buf, len, 0); }
int srt_recv(SRTSOCKET u, char * buf, int len) { return CUDT::recv(u, buf, len, 0); }
int srt_sendmsg(SRTSOCKET u, const char * buf, int len, int ttl, int inorder) { return CUDT::sendmsg(u, buf, len, ttl, 0!= inorder); }
int srt_recvmsg(SRTSOCKET u, char * buf, int len) { uint64_t ign_srctime; return CUDT::recvmsg(u, buf, len, ign_srctime); }
int srt_recvmsg(SRTSOCKET u, char * buf, int len) { int64_t ign_srctime; return CUDT::recvmsg(u, buf, len, ign_srctime); }
int64_t srt_sendfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block)
{
if (!path || !offset )
{
return CUDT::setError(CUDTException(MJ_NOTSUP, MN_INVAL, 0));
return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0);
}
fstream ifs(path, ios::binary | ios::in);
if (!ifs)
{
return CUDT::setError(CUDTException(MJ_FILESYSTEM, MN_READFAIL, 0));
return CUDT::APIError(MJ_FILESYSTEM, MN_READFAIL, 0);
}
int64_t ret = CUDT::sendfile(u, ifs, *offset, size, block);
ifs.close();
@ -132,19 +193,29 @@ int64_t srt_recvfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t siz
{
if (!path || !offset )
{
return CUDT::setError(CUDTException(MJ_NOTSUP, MN_INVAL, 0));
return CUDT::APIError(MJ_NOTSUP, MN_INVAL, 0);
}
fstream ofs(path, ios::binary | ios::out);
if (!ofs)
{
return CUDT::setError(CUDTException(MJ_FILESYSTEM, MN_WRAVAIL, 0));
return CUDT::APIError(MJ_FILESYSTEM, MN_WRAVAIL, 0);
}
int64_t ret = CUDT::recvfile(u, ofs, *offset, size, block);
ofs.close();
return ret;
}
extern const SRT_MSGCTRL srt_msgctrl_default = { 0, -1, false, 0, 0, 0, 0 };
extern const SRT_MSGCTRL srt_msgctrl_default = {
0, // no flags set
SRT_MSGTTL_INF,
false, // not in order (matters for msg mode only)
PB_SUBSEQUENT,
0, // srctime: take "now" time
SRT_SEQNO_NONE,
SRT_MSGNO_NONE,
NULL, // grpdata not supplied
0 // idem
};
void srt_msgctrl_init(SRT_MSGCTRL* mctrl)
{
@ -155,17 +226,17 @@ int srt_sendmsg2(SRTSOCKET u, const char * buf, int len, SRT_MSGCTRL *mctrl)
{
// Allow NULL mctrl in the API, but not internally.
if (mctrl)
return CUDT::sendmsg2(u, buf, len, Ref(*mctrl));
return CUDT::sendmsg2(u, buf, len, (*mctrl));
SRT_MSGCTRL mignore = srt_msgctrl_default;
return CUDT::sendmsg2(u, buf, len, Ref(mignore));
return CUDT::sendmsg2(u, buf, len, (mignore));
}
int srt_recvmsg2(SRTSOCKET u, char * buf, int len, SRT_MSGCTRL *mctrl)
{
if (mctrl)
return CUDT::recvmsg2(u, buf, len, Ref(*mctrl));
return CUDT::recvmsg2(u, buf, len, (*mctrl));
SRT_MSGCTRL mignore = srt_msgctrl_default;
return CUDT::recvmsg2(u, buf, len, Ref(mignore));
return CUDT::recvmsg2(u, buf, len, (mignore));
}
const char* srt_getlasterror_str() { return UDT::getlasterror().getErrorMessage(); }
@ -179,8 +250,8 @@ int srt_getlasterror(int* loc_errno)
const char* srt_strerror(int code, int err)
{
static CUDTException e;
e = CUDTException(CodeMajor(code/1000), CodeMinor(code%1000), err);
static srt::CUDTException e;
e = srt::CUDTException(CodeMajor(code/1000), CodeMinor(code%1000), err);
return(e.getErrorMessage());
}
@ -198,6 +269,8 @@ SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u) { return SRT_SOCKSTATUS((int)CUDT::
// event mechanism
int srt_epoll_create() { return CUDT::epoll_create(); }
int srt_epoll_clear_usocks(int eit) { return CUDT::epoll_clear_usocks(eit); }
// You can use either SRT_EPOLL_* flags or EPOLL* flags from <sys/epoll.h>, both are the same. IN/OUT/ERR only.
// events == NULL accepted, in which case all flags are set.
int srt_epoll_add_usock(int eid, SRTSOCKET u, const int * events) { return CUDT::epoll_add_usock(eid, u, events); }
@ -302,17 +375,104 @@ int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes)
return CUDT::getsndbuffer(sock, blocks, bytes);
}
enum SRT_REJECT_REASON srt_getrejectreason(SRTSOCKET sock)
int srt_getrejectreason(SRTSOCKET sock)
{
return CUDT::rejectReason(sock);
}
int srt_setrejectreason(SRTSOCKET sock, int value)
{
return CUDT::rejectReason(sock, value);
}
int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq)
{
if (!hook)
return CUDT::setError(CUDTException(MJ_NOTSUP, MN_INVAL));
return CUDT::APIError(MJ_NOTSUP, MN_INVAL);
return CUDT::installAcceptHook(lsn, hook, opaq);
}
int srt_connect_callback(SRTSOCKET lsn, srt_connect_callback_fn* hook, void* opaq)
{
if (!hook)
return CUDT::APIError(MJ_NOTSUP, MN_INVAL);
return CUDT::installConnectHook(lsn, hook, opaq);
}
uint32_t srt_getversion()
{
return SrtVersion(SRT_VERSION_MAJOR, SRT_VERSION_MINOR, SRT_VERSION_PATCH);
}
int64_t srt_time_now()
{
return srt::sync::count_microseconds(srt::sync::steady_clock::now().time_since_epoch());
}
int64_t srt_connection_time(SRTSOCKET sock)
{
return CUDT::socketStartTime(sock);
}
int srt_clock_type()
{
return SRT_SYNC_CLOCK;
}
const char* const srt_rejection_reason_msg [] = {
"Unknown or erroneous",
"Error in system calls",
"Peer rejected connection",
"Resource allocation failure",
"Rogue peer or incorrect parameters",
"Listener's backlog exceeded",
"Internal Program Error",
"Socket is being closed",
"Peer version too old",
"Rendezvous-mode cookie collision",
"Incorrect passphrase",
"Password required or unexpected",
"MessageAPI/StreamAPI collision",
"Congestion controller type collision",
"Packet Filter settings error",
"Group settings collision",
"Connection timeout"
};
// Deprecated, available in SRT API.
extern const char* const srt_rejectreason_msg[] = {
srt_rejection_reason_msg[0],
srt_rejection_reason_msg[1],
srt_rejection_reason_msg[2],
srt_rejection_reason_msg[3],
srt_rejection_reason_msg[4],
srt_rejection_reason_msg[5],
srt_rejection_reason_msg[6],
srt_rejection_reason_msg[7],
srt_rejection_reason_msg[8],
srt_rejection_reason_msg[9],
srt_rejection_reason_msg[10],
srt_rejection_reason_msg[11],
srt_rejection_reason_msg[12],
srt_rejection_reason_msg[13],
srt_rejection_reason_msg[14],
srt_rejection_reason_msg[15],
srt_rejection_reason_msg[16]
};
const char* srt_rejectreason_str(int id)
{
if (id >= SRT_REJC_PREDEFINED)
{
return "Application-defined rejection reason";
}
static const size_t ra_size = Size(srt_rejection_reason_msg);
if (size_t(id) >= ra_size)
return srt_rejection_reason_msg[0];
return srt_rejection_reason_msg[id];
}
}

View file

@ -16,12 +16,14 @@ written by
// Prevents from misconfiguration through preprocessor.
#include "platform_sys.h"
#include <srt_compat.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#if defined(__unix__) && !defined(BSD)
#if defined(__unix__) && !defined(BSD) && !defined(SUNOS)
#include <features.h>
#endif

View file

@ -14,15 +14,15 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef HAISRT_COMPAT_H__
#define HAISRT_COMPAT_H__
#ifndef INC_SRT_COMPAT_H
#define INC_SRT_COMPAT_H
#include <stddef.h>
#include <time.h>
#ifndef SRT_API
#ifdef _WIN32
#ifndef __MINGW__
#ifndef __MINGW32__
#ifdef SRT_DYNAMIC
#ifdef SRT_EXPORTS
#define SRT_API __declspec(dllexport)
@ -78,6 +78,7 @@ SRT_API const char * SysStrError(int errnum, char * buf, size_t buflen);
#include <string>
#include <cstring>
inline std::string SysStrError(int errnum)
{
char buf[1024];
@ -93,7 +94,10 @@ inline struct tm SysLocalTime(time_t tt)
if (rr == 0)
return tms;
#else
tms = *localtime_r(&tt, &tms);
// Ignore the error, state that if something
// happened, you simply have a pre-cleared tms.
localtime_r(&tt, &tms);
#endif
return tms;
@ -102,4 +106,4 @@ inline struct tm SysLocalTime(time_t tt)
#endif // defined C++
#endif // HAISRT_COMPAT_H__
#endif // INC_SRT_COMPAT_H

221
trunk/3rdparty/srt-1-fit/srtcore/stats.h vendored Normal file
View file

@ -0,0 +1,221 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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/.
*
*/
#ifndef INC_SRT_STATS_H
#define INC_SRT_STATS_H
#include "platform_sys.h"
#include "packet.h"
namespace srt
{
namespace stats
{
class Packets
{
public:
Packets() : m_count(0) {}
Packets(uint32_t num) : m_count(num) {}
void reset()
{
m_count = 0;
}
Packets& operator+= (const Packets& other)
{
m_count += other.m_count;
return *this;
}
uint32_t count() const
{
return m_count;
}
private:
uint32_t m_count;
};
class BytesPackets
{
public:
BytesPackets()
: m_bytes(0)
, m_packets(0)
{}
BytesPackets(uint64_t bytes, uint32_t n = 1)
: m_bytes(bytes)
, m_packets(n)
{}
BytesPackets& operator+= (const BytesPackets& other)
{
m_bytes += other.m_bytes;
m_packets += other.m_packets;
return *this;
}
public:
void reset()
{
m_packets = 0;
m_bytes = 0;
}
void count(uint64_t bytes, size_t n = 1)
{
m_packets += (uint32_t) n;
m_bytes += bytes;
}
uint64_t bytes() const
{
return m_bytes;
}
uint32_t count() const
{
return m_packets;
}
uint64_t bytesWithHdr() const
{
return m_bytes + m_packets * CPacket::SRT_DATA_HDR_SIZE;
}
private:
uint64_t m_bytes;
uint32_t m_packets;
};
template <class METRIC_TYPE>
struct Metric
{
METRIC_TYPE trace;
METRIC_TYPE total;
void count(METRIC_TYPE val)
{
trace += val;
total += val;
}
void reset()
{
trace.reset();
total.reset();
}
void resetTrace()
{
trace.reset();
}
};
/// Sender-side statistics.
struct Sender
{
Metric<BytesPackets> sent;
Metric<BytesPackets> sentUnique;
Metric<BytesPackets> sentRetrans; // The number of data packets retransmitted by the sender.
Metric<Packets> lost; // The number of packets reported lost (including repeated reports) to the sender in NAKs.
Metric<BytesPackets> dropped; // The number of data packets dropped by the sender.
Metric<Packets> sentFilterExtra; // The number of packets generate by the packet filter and sent by the sender.
Metric<Packets> recvdAck; // The number of ACK packets received by the sender.
Metric<Packets> recvdNak; // The number of ACK packets received by the sender.
void reset()
{
sent.reset();
sentUnique.reset();
sentRetrans.reset();
lost.reset();
dropped.reset();
recvdAck.reset();
recvdNak.reset();
sentFilterExtra.reset();
}
void resetTrace()
{
sent.resetTrace();
sentUnique.resetTrace();
sentRetrans.resetTrace();
lost.resetTrace();
dropped.resetTrace();
recvdAck.resetTrace();
recvdNak.resetTrace();
sentFilterExtra.resetTrace();
}
};
/// Receiver-side statistics.
struct Receiver
{
Metric<BytesPackets> recvd;
Metric<BytesPackets> recvdUnique;
Metric<BytesPackets> recvdRetrans; // The number of retransmitted data packets received by the receiver.
Metric<BytesPackets> lost; // The number of packets detected by the receiver as lost.
Metric<BytesPackets> dropped; // The number of packets dropped by the receiver (as too-late to be delivered).
Metric<BytesPackets> recvdBelated; // The number of belated packets received (dropped as too late but eventually received).
Metric<BytesPackets> undecrypted; // The number of packets received by the receiver that failed to be decrypted.
Metric<Packets> recvdFilterExtra; // The number of filter packets (e.g. FEC) received by the receiver.
Metric<Packets> suppliedByFilter; // The number of lost packets got from the packet filter at the receiver side (e.g. loss recovered by FEC).
Metric<Packets> lossFilter; // The number of lost DATA packets not recovered by the packet filter at the receiver side.
Metric<Packets> sentAck; // The number of ACK packets sent by the receiver.
Metric<Packets> sentNak; // The number of NACK packets sent by the receiver.
void reset()
{
recvd.reset();
recvdUnique.reset();
recvdRetrans.reset();
lost.reset();
dropped.reset();
recvdBelated.reset();
undecrypted.reset();
recvdFilterExtra.reset();
suppliedByFilter.reset();
lossFilter.reset();
sentAck.reset();
sentNak.reset();
}
void resetTrace()
{
recvd.resetTrace();
recvdUnique.resetTrace();
recvdRetrans.resetTrace();
lost.resetTrace();
dropped.resetTrace();
recvdBelated.resetTrace();
undecrypted.resetTrace();
recvdFilterExtra.resetTrace();
suppliedByFilter.resetTrace();
lossFilter.resetTrace();
sentAck.resetTrace();
sentNak.resetTrace();
}
};
} // namespace stats
} // namespace srt
#endif // INC_SRT_STATS_H

View file

@ -0,0 +1,154 @@
/*
WARNING: Generated from ../scripts/generate-error-types.tcl
DO NOT MODIFY.
Copyright applies as per the generator script.
*/
#include <cstddef>
namespace srt
{
// MJ_SUCCESS 'Success'
const char* strerror_msgs_success [] = {
"Success", // MN_NONE = 0
""
};
// MJ_SETUP 'Connection setup failure'
const char* strerror_msgs_setup [] = {
"Connection setup failure", // MN_NONE = 0
"Connection setup failure: connection timed out", // MN_TIMEOUT = 1
"Connection setup failure: connection rejected", // MN_REJECTED = 2
"Connection setup failure: unable to create/configure SRT socket", // MN_NORES = 3
"Connection setup failure: aborted for security reasons", // MN_SECURITY = 4
"Connection setup failure: socket closed during operation", // MN_CLOSED = 5
""
};
// MJ_CONNECTION ''
const char* strerror_msgs_connection [] = {
"", // MN_NONE = 0
"Connection was broken", // MN_CONNLOST = 1
"Connection does not exist", // MN_NOCONN = 2
""
};
// MJ_SYSTEMRES 'System resource failure'
const char* strerror_msgs_systemres [] = {
"System resource failure", // MN_NONE = 0
"System resource failure: unable to create new threads", // MN_THREAD = 1
"System resource failure: unable to allocate buffers", // MN_MEMORY = 2
"System resource failure: unable to allocate a system object", // MN_OBJECT = 3
""
};
// MJ_FILESYSTEM 'File system failure'
const char* strerror_msgs_filesystem [] = {
"File system failure", // MN_NONE = 0
"File system failure: cannot seek read position", // MN_SEEKGFAIL = 1
"File system failure: failure in read", // MN_READFAIL = 2
"File system failure: cannot seek write position", // MN_SEEKPFAIL = 3
"File system failure: failure in write", // MN_WRITEFAIL = 4
""
};
// MJ_NOTSUP 'Operation not supported'
const char* strerror_msgs_notsup [] = {
"Operation not supported", // MN_NONE = 0
"Operation not supported: Cannot do this operation on a BOUND socket", // MN_ISBOUND = 1
"Operation not supported: Cannot do this operation on a CONNECTED socket", // MN_ISCONNECTED = 2
"Operation not supported: Bad parameters", // MN_INVAL = 3
"Operation not supported: Invalid socket ID", // MN_SIDINVAL = 4
"Operation not supported: Cannot do this operation on an UNBOUND socket", // MN_ISUNBOUND = 5
"Operation not supported: Socket is not in listening state", // MN_NOLISTEN = 6
"Operation not supported: Listen/accept is not supported in rendezous connection setup", // MN_ISRENDEZVOUS = 7
"Operation not supported: Cannot call connect on UNBOUND socket in rendezvous connection setup", // MN_ISRENDUNBOUND = 8
"Operation not supported: Incorrect use of Message API (sendmsg/recvmsg).", // MN_INVALMSGAPI = 9
"Operation not supported: Incorrect use of Buffer API (send/recv) or File API (sendfile/recvfile).", // MN_INVALBUFFERAPI = 10
"Operation not supported: Another socket is already listening on the same port", // MN_BUSY = 11
"Operation not supported: Message is too large to send (it must be less than the SRT send buffer size)", // MN_XSIZE = 12
"Operation not supported: Invalid epoll ID", // MN_EIDINVAL = 13
"Operation not supported: All sockets removed from epoll, waiting would deadlock", // MN_EEMPTY = 14
"Operation not supported: Another socket is bound to that port and is not reusable for requested settings", // MN_BUSYPORT = 15
""
};
// MJ_AGAIN 'Non-blocking call failure'
const char* strerror_msgs_again [] = {
"Non-blocking call failure", // MN_NONE = 0
"Non-blocking call failure: no buffer available for sending", // MN_WRAVAIL = 1
"Non-blocking call failure: no data available for reading", // MN_RDAVAIL = 2
"Non-blocking call failure: transmission timed out", // MN_XMTIMEOUT = 3
"Non-blocking call failure: early congestion notification", // MN_CONGESTION = 4
""
};
// MJ_PEERERROR 'The peer side has signaled an error'
const char* strerror_msgs_peererror [] = {
"The peer side has signaled an error", // MN_NONE = 0
""
};
const char** strerror_array_major [] = {
strerror_msgs_success, // MJ_SUCCESS = 0
strerror_msgs_setup, // MJ_SETUP = 1
strerror_msgs_connection, // MJ_CONNECTION = 2
strerror_msgs_systemres, // MJ_SYSTEMRES = 3
strerror_msgs_filesystem, // MJ_FILESYSTEM = 4
strerror_msgs_notsup, // MJ_NOTSUP = 5
strerror_msgs_again, // MJ_AGAIN = 6
strerror_msgs_peererror, // MJ_PEERERROR = 7
NULL
};
#define SRT_ARRAY_SIZE(ARR) sizeof(ARR) / sizeof(ARR[0])
const size_t strerror_array_sizes [] = {
SRT_ARRAY_SIZE(strerror_msgs_success) - 1,
SRT_ARRAY_SIZE(strerror_msgs_setup) - 1,
SRT_ARRAY_SIZE(strerror_msgs_connection) - 1,
SRT_ARRAY_SIZE(strerror_msgs_systemres) - 1,
SRT_ARRAY_SIZE(strerror_msgs_filesystem) - 1,
SRT_ARRAY_SIZE(strerror_msgs_notsup) - 1,
SRT_ARRAY_SIZE(strerror_msgs_again) - 1,
SRT_ARRAY_SIZE(strerror_msgs_peererror) - 1,
0
};
const char* strerror_get_message(size_t major, size_t minor)
{
static const char* const undefined = "UNDEFINED ERROR";
// Extract the major array
if (major >= sizeof(strerror_array_major)/sizeof(const char**))
{
return undefined;
}
const char** array = strerror_array_major[major];
const size_t size = strerror_array_sizes[major];
if (minor >= size)
{
return undefined;
}
return array[minor];
}
} // namespace srt

View file

@ -0,0 +1,355 @@
/*
* 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 =
floor((static_cast<int64_t>(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
}
catch (const CThreadException& e)
{
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
}

945
trunk/3rdparty/srt-1-fit/srtcore/sync.h vendored Normal file
View file

@ -0,0 +1,945 @@
/*
* 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 <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 definitons 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 occured 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 occured 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 occured 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

View file

@ -0,0 +1,108 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2020 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 <math.h>
#include <stdexcept>
#include "sync.h"
#include "srt_compat.h"
#include "common.h"
////////////////////////////////////////////////////////////////////////////////
//
// Clock frequency helpers
//
////////////////////////////////////////////////////////////////////////////////
namespace {
template <int val>
int pow10();
template <>
int pow10<10>()
{
return 1;
}
template <int val>
int pow10()
{
return 1 + pow10<val / 10>();
}
}
int srt::sync::clockSubsecondPrecision()
{
const int64_t ticks_per_sec = (srt::sync::steady_clock::period::den / srt::sync::steady_clock::period::num);
const int decimals = pow10<ticks_per_sec>();
return decimals;
}
////////////////////////////////////////////////////////////////////////////////
//
// SyncCond (based on stl chrono C++11)
//
////////////////////////////////////////////////////////////////////////////////
srt::sync::Condition::Condition() {}
srt::sync::Condition::~Condition() {}
void srt::sync::Condition::init() {}
void srt::sync::Condition::destroy() {}
void srt::sync::Condition::wait(UniqueLock& lock)
{
m_cv.wait(lock);
}
bool srt::sync::Condition::wait_for(UniqueLock& lock, const steady_clock::duration& rel_time)
{
// Another possible implementation is wait_until(steady_clock::now() + timeout);
return m_cv.wait_for(lock, rel_time) != std::cv_status::timeout;
}
bool srt::sync::Condition::wait_until(UniqueLock& lock, const steady_clock::time_point& timeout_time)
{
return m_cv.wait_until(lock, timeout_time) != std::cv_status::timeout;
}
void srt::sync::Condition::notify_one()
{
m_cv.notify_one();
}
void srt::sync::Condition::notify_all()
{
m_cv.notify_all();
}
////////////////////////////////////////////////////////////////////////////////
//
// CThreadError class - thread local storage error wrapper
//
////////////////////////////////////////////////////////////////////////////////
// Threal local error will be used by CUDTUnited
// with a static scope, therefore static thread_local
static thread_local srt::CUDTException s_thErr;
void srt::sync::SetThreadLocalError(const srt::CUDTException& e)
{
s_thErr = e;
}
srt::CUDTException& srt::sync::GetThreadLocalError()
{
return s_thErr;
}

View file

@ -0,0 +1,572 @@
/*
* 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 <math.h>
#include <stdexcept>
#include "sync.h"
#include "utilities.h"
#include "udt.h"
#include "srt.h"
#include "srt_compat.h"
#include "logging.h"
#include "common.h"
#if defined(_WIN32)
#include "win/wintime.h"
#include <sys/timeb.h>
#elif TARGET_OS_MAC
#include <mach/mach_time.h>
#endif
namespace srt_logging
{
extern Logger inlog;
}
using namespace srt_logging;
namespace srt
{
namespace sync
{
static void rdtsc(uint64_t& x)
{
#if SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_IA32_RDTSC
uint32_t lval, hval;
// asm volatile ("push %eax; push %ebx; push %ecx; push %edx");
// asm volatile ("xor %eax, %eax; cpuid");
asm volatile("rdtsc" : "=a"(lval), "=d"(hval));
// asm volatile ("pop %edx; pop %ecx; pop %ebx; pop %eax");
x = hval;
x = (x << 32) | lval;
#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_IA64_ITC
asm("mov %0=ar.itc" : "=r"(x)::"memory");
#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_AMD64_RDTSC
uint32_t lval, hval;
asm("rdtsc" : "=a"(lval), "=d"(hval));
x = hval;
x = (x << 32) | lval;
#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_WINQPC
// This function should not fail, because we checked the QPC
// when calling to QueryPerformanceFrequency. If it failed,
// the m_bUseMicroSecond was set to true.
QueryPerformanceCounter((LARGE_INTEGER*)&x);
#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_MACH_ABSTIME
x = mach_absolute_time();
#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_GETTIME_MONOTONIC
// get_cpu_frequency() returns 1 us accuracy in this case
timespec tm;
clock_gettime(CLOCK_MONOTONIC, &tm);
x = tm.tv_sec * uint64_t(1000000) + (tm.tv_nsec / 1000);
#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY
// use system call to read time clock for other archs
timeval t;
gettimeofday(&t, 0);
x = t.tv_sec * uint64_t(1000000) + t.tv_usec;
#else
#error Wrong SRT_SYNC_CLOCK
#endif
}
static int64_t get_cpu_frequency()
{
int64_t frequency = 1; // 1 tick per microsecond.
#if SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_WINQPC
LARGE_INTEGER ccf; // in counts per second
if (QueryPerformanceFrequency(&ccf))
{
frequency = ccf.QuadPart / 1000000; // counts per microsecond
if (frequency == 0)
{
LOGC(inlog.Warn, log << "Win QPC frequency of " << ccf.QuadPart
<< " counts/s is below the required 1 us accuracy. Please consider using C++11 timing (-DENABLE_STDCXX_SYNC=ON) instead.");
frequency = 1; // set back to 1 to avoid division by zero.
}
}
else
{
// Can't throw an exception, it won't be handled.
LOGC(inlog.Error, log << "IPE: QueryPerformanceFrequency failed with " << GetLastError());
}
#elif SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_MACH_ABSTIME
mach_timebase_info_data_t info;
mach_timebase_info(&info);
frequency = info.denom * int64_t(1000) / info.numer;
#elif SRT_SYNC_CLOCK >= SRT_SYNC_CLOCK_AMD64_RDTSC && SRT_SYNC_CLOCK <= SRT_SYNC_CLOCK_IA64_ITC
// SRT_SYNC_CLOCK_AMD64_RDTSC or SRT_SYNC_CLOCK_IA32_RDTSC or SRT_SYNC_CLOCK_IA64_ITC
uint64_t t1, t2;
rdtsc(t1);
timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 100000000;
nanosleep(&ts, NULL);
rdtsc(t2);
// CPU clocks per microsecond
frequency = int64_t(t2 - t1) / 100000;
#endif
return frequency;
}
static int count_subsecond_precision(int64_t ticks_per_us)
{
int signs = 6; // starting from 1 us
while (ticks_per_us /= 10) ++signs;
return signs;
}
const int64_t s_clock_ticks_per_us = get_cpu_frequency();
const int s_clock_subsecond_precision = count_subsecond_precision(s_clock_ticks_per_us);
int clockSubsecondPrecision() { return s_clock_subsecond_precision; }
} // namespace sync
} // namespace srt
////////////////////////////////////////////////////////////////////////////////
//
// Sync utilities section
//
////////////////////////////////////////////////////////////////////////////////
static timespec us_to_timespec(const uint64_t time_us)
{
timespec timeout;
timeout.tv_sec = time_us / 1000000;
timeout.tv_nsec = (time_us % 1000000) * 1000;
return timeout;
}
////////////////////////////////////////////////////////////////////////////////
//
// TimePoint section
//
////////////////////////////////////////////////////////////////////////////////
template <>
srt::sync::Duration<srt::sync::steady_clock> srt::sync::TimePoint<srt::sync::steady_clock>::time_since_epoch() const
{
return srt::sync::Duration<srt::sync::steady_clock>(m_timestamp);
}
srt::sync::TimePoint<srt::sync::steady_clock> srt::sync::steady_clock::now()
{
uint64_t x = 0;
rdtsc(x);
return TimePoint<steady_clock>(x);
}
int64_t srt::sync::count_microseconds(const steady_clock::duration& t)
{
return t.count() / s_clock_ticks_per_us;
}
int64_t srt::sync::count_milliseconds(const steady_clock::duration& t)
{
return t.count() / s_clock_ticks_per_us / 1000;
}
int64_t srt::sync::count_seconds(const steady_clock::duration& t)
{
return t.count() / s_clock_ticks_per_us / 1000000;
}
srt::sync::steady_clock::duration srt::sync::microseconds_from(int64_t t_us)
{
return steady_clock::duration(t_us * s_clock_ticks_per_us);
}
srt::sync::steady_clock::duration srt::sync::milliseconds_from(int64_t t_ms)
{
return steady_clock::duration((1000 * t_ms) * s_clock_ticks_per_us);
}
srt::sync::steady_clock::duration srt::sync::seconds_from(int64_t t_s)
{
return steady_clock::duration((1000000 * t_s) * s_clock_ticks_per_us);
}
srt::sync::Mutex::Mutex()
{
const int err = pthread_mutex_init(&m_mutex, 0);
if (err)
{
throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0);
}
}
srt::sync::Mutex::~Mutex()
{
pthread_mutex_destroy(&m_mutex);
}
int srt::sync::Mutex::lock()
{
return pthread_mutex_lock(&m_mutex);
}
int srt::sync::Mutex::unlock()
{
return pthread_mutex_unlock(&m_mutex);
}
bool srt::sync::Mutex::try_lock()
{
return (pthread_mutex_trylock(&m_mutex) == 0);
}
srt::sync::ScopedLock::ScopedLock(Mutex& m)
: m_mutex(m)
{
m_mutex.lock();
}
srt::sync::ScopedLock::~ScopedLock()
{
m_mutex.unlock();
}
srt::sync::UniqueLock::UniqueLock(Mutex& m)
: m_Mutex(m)
{
m_iLocked = m_Mutex.lock();
}
srt::sync::UniqueLock::~UniqueLock()
{
if (m_iLocked == 0)
{
unlock();
}
}
void srt::sync::UniqueLock::lock()
{
if (m_iLocked != -1)
throw CThreadException(MJ_SYSTEMRES, MN_THREAD, 0);
m_iLocked = m_Mutex.lock();
}
void srt::sync::UniqueLock::unlock()
{
if (m_iLocked != 0)
throw CThreadException(MJ_SYSTEMRES, MN_THREAD, 0);
m_Mutex.unlock();
m_iLocked = -1;
}
srt::sync::Mutex* srt::sync::UniqueLock::mutex()
{
return &m_Mutex;
}
////////////////////////////////////////////////////////////////////////////////
//
// Condition section (based on pthreads)
//
////////////////////////////////////////////////////////////////////////////////
namespace srt
{
namespace sync
{
Condition::Condition()
#ifdef _WIN32
: m_cv(PTHREAD_COND_INITIALIZER)
#endif
{}
Condition::~Condition() {}
void Condition::init()
{
pthread_condattr_t* attr = NULL;
#if SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_GETTIME_MONOTONIC
pthread_condattr_t CondAttribs;
pthread_condattr_init(&CondAttribs);
pthread_condattr_setclock(&CondAttribs, CLOCK_MONOTONIC);
attr = &CondAttribs;
#endif
const int res = pthread_cond_init(&m_cv, attr);
if (res != 0)
throw std::runtime_error("pthread_cond_init monotonic failed");
}
void Condition::destroy()
{
pthread_cond_destroy(&m_cv);
}
void Condition::wait(UniqueLock& lock)
{
pthread_cond_wait(&m_cv, &lock.mutex()->ref());
}
bool Condition::wait_for(UniqueLock& lock, const steady_clock::duration& rel_time)
{
timespec timeout;
#if SRT_SYNC_CLOCK == SRT_SYNC_CLOCK_GETTIME_MONOTONIC
clock_gettime(CLOCK_MONOTONIC, &timeout);
const uint64_t now_us = timeout.tv_sec * uint64_t(1000000) + (timeout.tv_nsec / 1000);
#else
timeval now;
gettimeofday(&now, 0);
const uint64_t now_us = now.tv_sec * uint64_t(1000000) + now.tv_usec;
#endif
timeout = us_to_timespec(now_us + count_microseconds(rel_time));
return pthread_cond_timedwait(&m_cv, &lock.mutex()->ref(), &timeout) != ETIMEDOUT;
}
bool Condition::wait_until(UniqueLock& lock, const steady_clock::time_point& timeout_time)
{
// This will work regardless as to which clock is in use. The time
// should be specified as steady_clock::time_point, so there's no
// question of the timer base.
const steady_clock::time_point now = steady_clock::now();
if (now >= timeout_time)
return false; // timeout
// wait_for() is used because it will be converted to pthread-frienly timeout_time inside.
return wait_for(lock, timeout_time - now);
}
void Condition::notify_one()
{
pthread_cond_signal(&m_cv);
}
void Condition::notify_all()
{
pthread_cond_broadcast(&m_cv);
}
}; // namespace sync
}; // namespace srt
////////////////////////////////////////////////////////////////////////////////
//
// CThread class
//
////////////////////////////////////////////////////////////////////////////////
srt::sync::CThread::CThread()
{
m_thread = pthread_t();
}
srt::sync::CThread::CThread(void *(*start_routine) (void *), void *arg)
{
create(start_routine, arg);
}
#if HAVE_FULL_CXX11
srt::sync::CThread& srt::sync::CThread::operator=(CThread&& other)
#else
srt::sync::CThread& srt::sync::CThread::operator=(CThread& other)
#endif
{
if (joinable())
{
// If the thread has already terminated, then
// pthread_join() returns immediately.
// But we have to check it has terminated before replacing it.
LOGC(inlog.Error, log << "IPE: Assigning to a thread that is not terminated!");
#ifndef DEBUG
#ifndef __ANDROID__
// In case of production build the hanging thread should be terminated
// to avoid hang ups and align with C++11 implementation.
// There is no pthread_cancel on Android. See #1476. This error should not normally
// happen, but if it happen, then detaching the thread.
pthread_cancel(m_thread);
#endif // __ANDROID__
#else
join();
#endif
}
// Move thread handler from other
m_thread = other.m_thread;
other.m_thread = pthread_t();
return *this;
}
#if !HAVE_FULL_CXX11
void srt::sync::CThread::create_thread(void *(*start_routine) (void *), void *arg)
{
SRT_ASSERT(!joinable());
create(start_routine, arg);
}
#endif
bool srt::sync::CThread::joinable() const
{
return !pthread_equal(m_thread, pthread_t());
}
void srt::sync::CThread::join()
{
void *retval;
const int ret SRT_ATR_UNUSED = pthread_join(m_thread, &retval);
if (ret != 0)
{
LOGC(inlog.Error, log << "pthread_join failed with " << ret);
}
#ifdef HEAVY_LOGGING
else
{
HLOGC(inlog.Debug, log << "pthread_join SUCCEEDED");
}
#endif
// After joining, joinable should be false
m_thread = pthread_t();
return;
}
void srt::sync::CThread::create(void *(*start_routine) (void *), void *arg)
{
const int st = pthread_create(&m_thread, NULL, start_routine, arg);
if (st != 0)
{
LOGC(inlog.Error, log << "pthread_create failed with " << st);
throw CThreadException(MJ_SYSTEMRES, MN_THREAD, 0);
}
}
////////////////////////////////////////////////////////////////////////////////
//
// CThreadError class - thread local storage error wrapper
//
////////////////////////////////////////////////////////////////////////////////
namespace srt {
namespace sync {
class CThreadError
{
public:
CThreadError()
{
pthread_key_create(&m_ThreadSpecKey, ThreadSpecKeyDestroy);
// This is a global object and as such it should be called in the
// main application thread or at worst in the thread that has first
// run `srt_startup()` function and so requested the SRT library to
// be dynamically linked. Most probably in this very thread the API
// errors will be reported, so preallocate the ThreadLocalSpecific
// object for this error description.
// This allows std::bac_alloc to crash the program during
// the initialization of the SRT library (likely it would be
// during the DL constructor, still way before any chance of
// doing any operations here). This will prevent SRT from running
// into trouble while trying to operate.
CUDTException* ne = new CUDTException();
pthread_setspecific(m_ThreadSpecKey, ne);
}
~CThreadError()
{
// Likely all objects should be deleted in all
// threads that have exited, but std::this_thread didn't exit
// yet :).
ThreadSpecKeyDestroy(pthread_getspecific(m_ThreadSpecKey));
pthread_key_delete(m_ThreadSpecKey);
}
void set(const CUDTException& e)
{
CUDTException* cur = get();
// If this returns NULL, it means that there was an unexpected
// memory allocation error. Simply ignore this request if so
// happened, and then when trying to get the error description
// the application will always get the memory allocation error.
// There's no point in doing anything else here; lack of memory
// must be prepared for prematurely, and that was already done.
if (!cur)
return;
*cur = e;
}
/*[[nullable]]*/ CUDTException* get()
{
if (!pthread_getspecific(m_ThreadSpecKey))
{
// This time if this can't be done due to memory allocation
// problems, just allow this value to be NULL, which during
// getting the error description will redirect to a memory
// allocation error.
// It would be nice to somehow ensure that this object is
// created in every thread of the application using SRT, but
// POSIX thread API doesn't contain any possibility to have
// a creation callback that would apply to every thread in
// the application (as it is for C++11 thread_local storage).
CUDTException* ne = new(std::nothrow) CUDTException();
pthread_setspecific(m_ThreadSpecKey, ne);
return ne;
}
return (CUDTException*)pthread_getspecific(m_ThreadSpecKey);
}
static void ThreadSpecKeyDestroy(void* e)
{
delete (CUDTException*)e;
}
private:
pthread_key_t m_ThreadSpecKey;
};
// Threal local error will be used by CUDTUnited
// that has a static scope
// This static makes this object file-private access so that
// the access is granted only for the accessor functions.
static CThreadError s_thErr;
void SetThreadLocalError(const CUDTException& e)
{
s_thErr.set(e);
}
CUDTException& GetThreadLocalError()
{
// In POSIX version we take into account the possibility
// of having an allocation error here. Therefore we need to
// allow thie value to return NULL and have some fallback
// for that case. The dynamic memory allocation failure should
// be the only case as to why it is unable to get the pointer
// to the error description.
static CUDTException resident_alloc_error (MJ_SYSTEMRES, MN_MEMORY);
CUDTException* curx = s_thErr.get();
if (!curx)
return resident_alloc_error;
return *curx;
}
} // namespace sync
} // namespace srt

View file

@ -1,11 +1,11 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2018 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/.
*
*
*/
/*****************************************************************************
@ -13,75 +13,212 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__THREADNAME_H
#define INC__THREADNAME_H
#ifndef INC_SRT_THREADNAME_H
#define INC_SRT_THREADNAME_H
#ifdef __linux__
// NOTE:
// HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H
// HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H
// HAVE_PTHREAD_GETNAME_NP
// HAVE_PTHREAD_GETNAME_NP
// Are detected and set in ../CMakeLists.txt.
// OS Availability of pthread_getname_np(..) and pthread_setname_np(..)::
// MacOS(10.6)
// iOS(3.2)
// AIX(7.1)
// FreeBSD(version?), OpenBSD(Version?)
// Linux-GLIBC(GLIBC-2.12).
// Linux-MUSL(MUSL-1.1.20 Partial Implementation. See below).
// MINGW-W64(4.0.6)
#include <sys/prctl.h>
#if defined(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) \
|| defined(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H)
#include <pthread_np.h>
#if defined(HAVE_PTHREAD_GETNAME_NP_IN_PTHREAD_NP_H) \
&& !defined(HAVE_PTHREAD_GETNAME_NP)
#define HAVE_PTHREAD_GETNAME_NP 1
#endif
#if defined(HAVE_PTHREAD_SETNAME_NP_IN_PTHREAD_NP_H) \
&& !defined(HAVE_PTHREAD_SETNAME_NP)
#define HAVE_PTHREAD_SETNAME_NP 1
#endif
#endif
#if (defined(HAVE_PTHREAD_GETNAME_NP) && defined(HAVE_PTHREAD_GETNAME_NP)) \
|| defined(__linux__)
// NOTE:
// Linux pthread_getname_np() and pthread_setname_np() became available
// in GLIBC-2.12 and later.
// Some Linux runtimes do not have pthread_getname_np(), but have
// pthread_setname_np(), for instance MUSL at least as of v1.1.20.
// So using the prctl() for Linux is more portable.
#if defined(__linux__)
#include <sys/prctl.h>
#endif
#include <pthread.h>
#endif
#include <cstdio>
#include <cstring>
#include <string>
#include "common.h"
#include "sync.h"
namespace srt {
class ThreadName
{
char old_name[128];
char new_name[128];
bool good;
public:
static const size_t BUFSIZE = 128;
#if (defined(HAVE_PTHREAD_GETNAME_NP) && defined(HAVE_PTHREAD_GETNAME_NP)) \
|| defined(__linux__)
static bool get(char* namebuf)
class ThreadNameImpl
{
return prctl(PR_GET_NAME, (unsigned long)namebuf, 0, 0) != -1;
}
public:
static const size_t BUFSIZE = 64;
static const bool DUMMY_IMPL = false;
static bool set(const char* name)
{
return prctl(PR_SET_NAME, (unsigned long)name, 0, 0) != -1;
}
ThreadName(const char* name)
{
if ( (good = get(old_name)) )
static bool get(char* namebuf)
{
snprintf(new_name, 127, "%s", name);
new_name[127] = 0;
prctl(PR_SET_NAME, (unsigned long)new_name, 0, 0);
#if defined(__linux__)
// since Linux 2.6.11. The buffer should allow space for up to 16
// bytes; the returned string will be null-terminated.
return prctl(PR_GET_NAME, (unsigned long)namebuf, 0, 0) != -1;
#elif defined(HAVE_PTHREAD_GETNAME_NP)
return pthread_getname_np(pthread_self(), namebuf, BUFSIZE) == 0;
#else
#error "unsupported platform"
#endif
}
}
~ThreadName()
{
if ( good )
prctl(PR_SET_NAME, (unsigned long)old_name, 0, 0);
}
};
static bool set(const char* name)
{
SRT_ASSERT(name != NULL);
#if defined(__linux__)
// The name can be up to 16 bytes long, including the terminating
// null byte. (If the length of the string, including the terminating
// null byte, exceeds 16 bytes, the string is silently truncated.)
return prctl(PR_SET_NAME, (unsigned long)name, 0, 0) != -1;
#elif defined(HAVE_PTHREAD_SETNAME_NP)
#if defined(__APPLE__)
return pthread_setname_np(name) == 0;
#else
return pthread_setname_np(pthread_self(), name) == 0;
#endif
#else
#error "unsupported platform"
#endif
}
explicit ThreadNameImpl(const std::string& name)
: reset(false)
{
tid = pthread_self();
if (!get(old_name))
return;
reset = set(name.c_str());
if (reset)
return;
// Try with a shorter name. 15 is the upper limit supported by Linux,
// other platforms should support a larger value. So 15 should works
// on all platforms.
const size_t max_len = 15;
if (name.size() > max_len)
reset = set(name.substr(0, max_len).c_str());
}
~ThreadNameImpl()
{
if (!reset)
return;
// ensure it's called on the right thread
if (tid == pthread_self())
set(old_name);
}
private:
ThreadNameImpl(ThreadNameImpl& other);
ThreadNameImpl& operator=(const ThreadNameImpl& other);
private:
bool reset;
pthread_t tid;
char old_name[BUFSIZE];
};
#else
// Fake class, which does nothing. You can also take a look how
// this works in other systems that are not supported here and add
// the support. This is a fallback for systems that do not support
// thread names.
class ThreadNameImpl
{
public:
static const bool DUMMY_IMPL = true;
static const size_t BUFSIZE = 64;
static bool get(char* output)
{
// The default implementation will simply try to get the thread ID
std::ostringstream bs;
bs << "T" << sync::this_thread::get_id();
size_t s = bs.str().copy(output, BUFSIZE - 1);
output[s] = '\0';
return true;
}
static bool set(const char*) { return false; }
ThreadNameImpl(const std::string&) {}
~ThreadNameImpl() // just to make it "non-trivially-destructible" for compatibility with normal version
{
}
};
#endif // platform dependent impl
// Why delegate to impl:
// 1. to make sure implementation on different platforms have the same interface.
// 2. it's simple to add some wrappers like get(const std::string &).
ThreadNameImpl impl;
class ThreadName
{
public:
static const bool DUMMY_IMPL = ThreadNameImpl::DUMMY_IMPL;
static const size_t BUFSIZE = ThreadNameImpl::BUFSIZE;
static bool get(char*) { return false; }
static bool set(const char*) { return false; }
/// @brief Print thread ID to the provided buffer.
/// The size of the destination buffer is assumed to be at least ThreadName::BUFSIZE.
/// @param [out] output destination buffer to get thread name
/// @return true on success, false on failure
static bool get(char* output) {
return ThreadNameImpl::get(output);
}
ThreadName(const char*)
{
}
~ThreadName() // just to make it "non-trivially-destructible" for compatibility with normal version
static bool get(std::string& name)
{
char buf[BUFSIZE];
bool ret = get(buf);
if (ret)
name = buf;
return ret;
}
static bool set(const std::string& name) { return ThreadNameImpl::set(name.c_str()); }
explicit ThreadName(const std::string& name)
: impl(name)
{
}
private:
ThreadName(const ThreadName&);
ThreadName(const char*);
ThreadName& operator=(const ThreadName& other);
};
} // namespace srt
#endif
#endif

View file

@ -0,0 +1,267 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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 "tsbpd_time.h"
#include "logging.h"
#include "logger_defs.h"
#include "packet.h"
using namespace srt_logging;
using namespace srt::sync;
namespace srt
{
#if SRT_DEBUG_TRACE_DRIFT
class drift_logger
{
typedef srt::sync::steady_clock steady_clock;
public:
drift_logger() {}
~drift_logger()
{
ScopedLock lck(m_mtx);
m_fout.close();
}
void trace(unsigned ackack_timestamp,
int rtt_us,
int64_t drift_sample,
int64_t drift,
int64_t overdrift,
const srt::sync::steady_clock::time_point& pkt_base,
const srt::sync::steady_clock::time_point& tsbpd_base)
{
using namespace srt::sync;
ScopedLock lck(m_mtx);
create_file();
// std::string str_tnow = srt::sync::FormatTime(steady_clock::now());
// str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [STDY]' part
std::string str_tbase = srt::sync::FormatTime(tsbpd_base);
str_tbase.resize(str_tbase.size() - 7); // remove trailing ' [STDY]' part
std::string str_pkt_base = srt::sync::FormatTime(pkt_base);
str_pkt_base.resize(str_pkt_base.size() - 7); // remove trailing ' [STDY]' part
// m_fout << str_tnow << ",";
m_fout << count_microseconds(steady_clock::now() - m_start_time) << ",";
m_fout << ackack_timestamp << ",";
m_fout << rtt_us << ",";
m_fout << drift_sample << ",";
m_fout << drift << ",";
m_fout << overdrift << ",";
m_fout << str_pkt_base << ",";
m_fout << str_tbase << "\n";
m_fout.flush();
}
private:
void print_header()
{
m_fout << "usElapsedStd,usAckAckTimestampStd,";
m_fout << "usRTTStd,usDriftSampleStd,usDriftStd,usOverdriftStd,tsPktBase,TSBPDBase\n";
}
void create_file()
{
if (m_fout.is_open())
return;
m_start_time = srt::sync::steady_clock::now();
std::string str_tnow = srt::sync::FormatTimeSys(m_start_time);
str_tnow.resize(str_tnow.size() - 7); // remove trailing ' [SYST]' part
while (str_tnow.find(':') != std::string::npos)
{
str_tnow.replace(str_tnow.find(':'), 1, 1, '_');
}
const std::string fname = "drift_trace_" + str_tnow + ".csv";
m_fout.open(fname, std::ofstream::out);
if (!m_fout)
std::cerr << "IPE: Failed to open " << fname << "!!!\n";
print_header();
}
private:
srt::sync::Mutex m_mtx;
std::ofstream m_fout;
srt::sync::steady_clock::time_point m_start_time;
};
drift_logger g_drift_logger;
#endif // SRT_DEBUG_TRACE_DRIFT
bool CTsbpdTime::addDriftSample(uint32_t usPktTimestamp, const time_point& tsPktArrival, int usRTTSample)
{
if (!m_bTsbPdMode)
return false;
ScopedLock lck(m_mtxRW);
// Remember the first RTT sample measured. Ideally we need RTT0 - the one from the handshaking phase,
// because TSBPD base is initialized there. But HS-based RTT is not yet implemented.
// Take the first one assuming it is close to RTT0.
if (m_iFirstRTT == -1)
{
m_iFirstRTT = usRTTSample;
}
// A change in network delay has to be taken into account. The only way to get some estimation of it
// is to estimate RTT change and assume that the change of the one way network delay is
// approximated by the half of the RTT change.
const duration tdRTTDelta = usRTTSample >= 0 ? microseconds_from((usRTTSample - m_iFirstRTT) / 2) : duration(0);
const time_point tsPktBaseTime = getPktTsbPdBaseTime(usPktTimestamp);
const steady_clock::duration tdDrift = tsPktArrival - tsPktBaseTime - tdRTTDelta;
const bool updated = m_DriftTracer.update(count_microseconds(tdDrift));
if (updated)
{
IF_HEAVY_LOGGING(const steady_clock::time_point oldbase = m_tsTsbPdTimeBase);
steady_clock::duration overdrift = microseconds_from(m_DriftTracer.overdrift());
m_tsTsbPdTimeBase += overdrift;
HLOGC(brlog.Debug,
log << "DRIFT=" << FormatDuration(tdDrift) << " AVG=" << (m_DriftTracer.drift() / 1000.0)
<< "ms, TB: " << FormatTime(oldbase) << " EXCESS: " << FormatDuration(overdrift)
<< " UPDATED TO: " << FormatTime(m_tsTsbPdTimeBase));
}
else
{
HLOGC(brlog.Debug,
log << "DRIFT=" << FormatDuration(tdDrift) << " TB REMAINS: " << FormatTime(m_tsTsbPdTimeBase));
}
#if SRT_DEBUG_TRACE_DRIFT
g_drift_logger.trace(usPktTimestamp,
usRTTSample,
count_microseconds(tdDrift),
m_DriftTracer.drift(),
m_DriftTracer.overdrift(),
tsPktBaseTime,
m_tsTsbPdTimeBase);
#endif
return updated;
}
void CTsbpdTime::setTsbPdMode(const steady_clock::time_point& timebase, bool wrap, duration delay)
{
m_bTsbPdMode = true;
m_bTsbPdWrapCheck = wrap;
// Timebase passed here comes is calculated as:
// Tnow - hspkt.m_iTimeStamp
// where hspkt is the packet with SRT_CMD_HSREQ message.
//
// This function is called in the HSREQ reception handler only.
m_tsTsbPdTimeBase = timebase;
m_tdTsbPdDelay = delay;
}
void CTsbpdTime::applyGroupTime(const steady_clock::time_point& timebase,
bool wrp,
uint32_t delay,
const steady_clock::duration& udrift)
{
// Same as setTsbPdMode, but predicted to be used for group members.
// This synchronizes the time from the INTERNAL TIMEBASE of an existing
// socket's internal timebase. This is required because the initial time
// base stays always the same, whereas the internal timebase undergoes
// adjustment as the 32-bit timestamps in the sockets wrap. The socket
// newly added to the group must get EXACTLY the same internal timebase
// or otherwise the TsbPd time calculation will ship different results
// on different member sockets.
m_bTsbPdMode = true;
m_tsTsbPdTimeBase = timebase;
m_bTsbPdWrapCheck = wrp;
m_tdTsbPdDelay = microseconds_from(delay);
m_DriftTracer.forceDrift(count_microseconds(udrift));
}
void CTsbpdTime::applyGroupDrift(const steady_clock::time_point& timebase,
bool wrp,
const steady_clock::duration& udrift)
{
// This is only when a drift was updated on one of the group members.
HLOGC(brlog.Debug,
log << "rcv-buffer: group synch uDRIFT: " << m_DriftTracer.drift() << " -> " << FormatDuration(udrift)
<< " TB: " << FormatTime(m_tsTsbPdTimeBase) << " -> " << FormatTime(timebase));
m_tsTsbPdTimeBase = timebase;
m_bTsbPdWrapCheck = wrp;
m_DriftTracer.forceDrift(count_microseconds(udrift));
}
CTsbpdTime::time_point CTsbpdTime::getTsbPdTimeBase(uint32_t timestamp_us) const
{
// A data packet within [TSBPD_WRAP_PERIOD; 2 * TSBPD_WRAP_PERIOD] would end TSBPD wrap-aware state.
// Some incoming control packets may not update the TSBPD base (calling updateTsbPdTimeBase(..)),
// but may come before a data packet with a timestamp in this range. Therefore the whole range should be tracked.
const int64_t carryover_us =
(m_bTsbPdWrapCheck && timestamp_us <= 2 * TSBPD_WRAP_PERIOD) ? int64_t(CPacket::MAX_TIMESTAMP) + 1 : 0;
return (m_tsTsbPdTimeBase + microseconds_from(carryover_us));
}
CTsbpdTime::time_point CTsbpdTime::getPktTsbPdTime(uint32_t usPktTimestamp) const
{
return getPktTsbPdBaseTime(usPktTimestamp) + m_tdTsbPdDelay + microseconds_from(m_DriftTracer.drift());
}
CTsbpdTime::time_point CTsbpdTime::getPktTsbPdBaseTime(uint32_t usPktTimestamp) const
{
return getTsbPdTimeBase(usPktTimestamp) + microseconds_from(usPktTimestamp);
}
void CTsbpdTime::updateTsbPdTimeBase(uint32_t usPktTimestamp)
{
if (m_bTsbPdWrapCheck)
{
// Wrap check period.
if ((usPktTimestamp >= TSBPD_WRAP_PERIOD) && (usPktTimestamp <= (TSBPD_WRAP_PERIOD * 2)))
{
/* Exiting wrap check period (if for packet delivery head) */
m_bTsbPdWrapCheck = false;
m_tsTsbPdTimeBase += microseconds_from(int64_t(CPacket::MAX_TIMESTAMP) + 1);
LOGC(tslog.Debug,
log << "tsbpd wrap period ends with ts=" << usPktTimestamp << " - NEW TIME BASE: "
<< FormatTime(m_tsTsbPdTimeBase) << " drift: " << m_DriftTracer.drift() << "us");
}
return;
}
// Check if timestamp is within the TSBPD_WRAP_PERIOD before reaching the MAX_TIMESTAMP.
if (usPktTimestamp > (CPacket::MAX_TIMESTAMP - TSBPD_WRAP_PERIOD))
{
// Approching wrap around point, start wrap check period (if for packet delivery head)
m_bTsbPdWrapCheck = true;
LOGC(tslog.Debug,
log << "tsbpd wrap period begins with ts=" << usPktTimestamp
<< " TIME BASE: " << FormatTime(m_tsTsbPdTimeBase) << " drift: " << m_DriftTracer.drift() << "us.");
}
}
void CTsbpdTime::getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift) const
{
ScopedLock lck(m_mtxRW);
w_tb = m_tsTsbPdTimeBase;
w_udrift = microseconds_from(m_DriftTracer.drift());
w_wrp = m_bTsbPdWrapCheck;
}
} // namespace srt

View file

@ -0,0 +1,163 @@
/*
* SRT - Secure, Reliable, Transport
* Copyright (c) 2021 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/.
*
*/
#ifndef INC_SRT_TSBPD_TIME_H
#define INC_SRT_TSBPD_TIME_H
#include "platform_sys.h"
#include "sync.h"
#include "utilities.h"
namespace srt
{
/// @brief TimeStamp-Based Packet Delivery Mode (TSBPD) time conversion logic.
/// Used by the receiver to calculate delivery time of data packets.
/// See SRT Internet Draft Section "Timestamp-Based Packet Delivery".
class CTsbpdTime
{
typedef srt::sync::steady_clock steady_clock;
typedef steady_clock::time_point time_point;
typedef steady_clock::duration duration;
typedef srt::sync::Mutex Mutex;
public:
CTsbpdTime()
: m_iFirstRTT(-1)
, m_bTsbPdMode(false)
, m_tdTsbPdDelay(0)
, m_bTsbPdWrapCheck(false)
{
}
/// Set TimeStamp-Based Packet Delivery Mode (receiver).
/// @param [in] timebase local time base (uSec) of packet time stamps including buffering delay.
/// @param [in] wrap wrapping period.
/// @param [in] delay negotiated TsbPD delay (buffering latency).
void setTsbPdMode(const time_point& timebase, bool wrap, duration delay);
/// @brief Check if TSBPD logic is enabled.
/// @return true if TSBPD is enabled.
bool isEnabled() const { return m_bTsbPdMode; }
/// @brief Apply new state derived from other members of a socket group.
/// @param timebase TSBPD base time.
/// @param wrp wrap period (enabled or not).
/// @param delay TSBPD delay.
/// @param udrift clock drift.
void applyGroupTime(const time_point& timebase, bool wrp, uint32_t delay, const duration& udrift);
/// @brief Apply new clock state (TSBPD base and drift) derived from other members of a socket group.
/// @param timebase TSBPD base time.
/// @param wrp state of the wrapping period (enabled or disabled).
/// @param udrift clock drift.
void applyGroupDrift(const time_point& timebase, bool wrp, const duration& udrift);
/// @brief Add new drift sample from an ACK-ACKACK pair.
/// ACKACK packets are sent immediately (except for UDP buffering).
/// Therefore their timestamp roughly corresponds to the time of sending
/// and can be used to estimate clock drift.
///
/// @param [in] pktTimestamp Timestamp of the arrived ACKACK packet.
/// @param [in] tsPktArrival packet arrival time.
/// @param [in] usRTTSample RTT sample from an ACK-ACKACK pair. If no sample, pass '-1'.
///
/// @return true if TSBPD base time has changed, false otherwise.
bool addDriftSample(uint32_t pktTimestamp, const time_point& tsPktArrival, int usRTTSample);
/// @brief Handle timestamp of data packet when 32-bit integer carryover is about to happen.
/// When packet timestamp approaches CPacket::MAX_TIMESTAMP, the TSBPD base time should be
/// shifted accordingly to correctly handle new packets with timestamps starting from zero.
/// @param usPktTimestamp timestamp field value of a data packet.
void updateTsbPdTimeBase(uint32_t usPktTimestamp);
/// @brief Get TSBPD base time adjusted for carryover, which occurs when
/// a packet's timestamp exceeds the UINT32_MAX and continues from zero.
/// @param [in] usPktTimestamp 32-bit value of packet timestamp field (microseconds).
///
/// @return TSBPD base time for a provided packet timestamp.
time_point getTsbPdTimeBase(uint32_t usPktTimestamp) const;
/// @brief Get packet TSBPD time without buffering delay and clock drift, which is
/// the target time for delivering the packet to an upstream application.
/// Essentially: getTsbPdTimeBase(usPktTimestamp) + usPktTimestamp
/// @param [in] usPktTimestamp 32-bit value of packet timestamp field (microseconds).
///
/// @return Packet TSBPD base time without buffering delay.
time_point getPktTsbPdBaseTime(uint32_t usPktTimestamp) const;
/// @brief Get packet TSBPD time with buffering delay and clock drift, which is
/// the target time for delivering the packet to an upstream application
/// (including drift and carryover effects, if any).
/// Essentially: getPktTsbPdBaseTime(usPktTimestamp) + m_tdTsbPdDelay + drift()
/// @param [in] usPktTimestamp 32-bit value of packet timestamp field (microseconds).
///
/// @return Packet TSBPD time with buffering delay.
time_point getPktTsbPdTime(uint32_t usPktTimestamp) const;
/// @brief Get current drift value.
/// @return current drift value.
int64_t drift() const { return m_DriftTracer.drift(); }
/// @brief Get current overdrift value.
/// @return current overdrift value.
int64_t overdrift() const { return m_DriftTracer.overdrift(); }
/// @brief Get internal state to apply to another member of a socket group.
/// @param w_tb TsbPd base time.
/// @param w_udrift drift value.
/// @param w_wrp wrap check.
void getInternalTimeBase(time_point& w_tb, bool& w_wrp, duration& w_udrift) const;
private:
int m_iFirstRTT; // First measured RTT sample.
bool m_bTsbPdMode; // Receiver buffering and TSBPD is active when true.
duration m_tdTsbPdDelay; // Negotiated buffering delay.
/// @brief Local time base for TsbPd.
/// @note m_tsTsbPdTimeBase is changed in the following cases:
/// 1. Initialized upon SRT_CMD_HSREQ packet as the difference with the current time:
/// = (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception.
/// 2. Shifted forward on timestamp overflow (@see CTsbpdTime::updateTsbPdTimeBase), when overflow
/// of the timestamp field value of a data packet is detected.
/// += CPacket::MAX_TIMESTAMP + 1
/// 3. Clock drift (@see CTsbpdTime::addDriftSample, executed exclusively
/// from ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE
/// once the value of average drift exceeds this value in either direction.
/// += (+/-)TSBPD_DRIFT_MAX_VALUE
///
/// @note The TSBPD base time is expected to hold the following condition:
/// (PACKET_TIMESTAMP + m_tsTsbPdTimeBase + drift) == NOW.
/// Then it can be used to estimate the origin time of a data packet, and calculate its delivery time
/// with buffering delay applied.
time_point m_tsTsbPdTimeBase;
/// @note Packet timestamps wrap around every 01h11m35s (32-bit in usec).
/// A wrap check period starts 30 seconds (TSBPD_WRAP_PERIOD) before the wrap point.
/// During the wrap check period, packet timestamps smaller than 30 seconds
/// are considered to have been wrapped around.
/// The wrap check period ends 30 seconds after the wrap point,
/// after which the TSBPD base time is adjusted.
bool m_bTsbPdWrapCheck; // true: check packet time stamp wraparound (overflow).
static const uint32_t TSBPD_WRAP_PERIOD = (30 * 1000000); // 30 seconds (in usec) for timestamp wrapping period.
/// Maximum clock drift (microseconds) above which TsbPD base time is already adjusted.
static const int TSBPD_DRIFT_MAX_VALUE = 5000;
/// Number of samples (ACKACK packets) on which to perform drift calculation and compensation.
static const int TSBPD_DRIFT_MAX_SAMPLES = 1000;
DriftTracer<TSBPD_DRIFT_MAX_SAMPLES, TSBPD_DRIFT_MAX_VALUE> m_DriftTracer;
/// Protect simultaneous change of state (read/write).
mutable Mutex m_mtxRW;
};
} // namespace srt
#endif // INC_SRT_TSBPD_TIME_H

View file

@ -64,16 +64,16 @@ modified by
* file doesn't contain _FUNCTIONS_ predicted to be used in C - see udtc.h
*/
#ifndef __UDT_H__
#define __UDT_H__
#ifndef INC_SRT_UDT_H
#define INC_SRT_UDT_H
#include "srt.h"
/*
* SRT_ENABLE_THREADCHECK (THIS IS SET IN MAKEFILE NOT HERE)
* SRT_ENABLE_THREADCHECK IS SET IN MAKEFILE, NOT HERE
*/
#if defined(SRT_ENABLE_THREADCHECK)
#include <threadcheck.h>
#include "threadcheck.h"
#else
#define THREAD_STATE_INIT(name)
#define THREAD_EXIT()
@ -82,13 +82,6 @@ modified by
#define INCREMENT_THREAD_ITERATIONS()
#endif
/* Obsolete way to define MINGW */
#ifndef __MINGW__
#if defined(__MINGW32__) || defined(__MINGW64__)
#define __MINGW__ 1
#endif
#endif
#ifdef __cplusplus
#include <fstream>
#include <set>
@ -96,10 +89,6 @@ modified by
#include <vector>
#endif
// Legacy/backward/deprecated
#define UDT_API SRT_API
////////////////////////////////////////////////////////////////////////////////
//if compiling on VC6.0 or pre-WindowsXP systems
@ -108,83 +97,6 @@ modified by
//if compiling with MinGW, it only works on XP or above
//use -D_WIN32_WINNT=0x0501
////////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
// This facility is used only for select() function.
// This is considered obsolete and the epoll() functionality rather should be used.
typedef std::set<SRTSOCKET> ud_set;
#define UD_CLR(u, uset) ((uset)->erase(u))
#define UD_ISSET(u, uset) ((uset)->find(u) != (uset)->end())
#define UD_SET(u, uset) ((uset)->insert(u))
#define UD_ZERO(uset) ((uset)->clear())
#endif
////////////////////////////////////////////////////////////////////////////////
// Legacy names
#define UDT_MSS SRTO_MSS
#define UDT_SNDSYN SRTO_SNDSYN
#define UDT_RCVSYN SRTO_RCVSYN
#define UDT_FC SRTO_FC
#define UDT_SNDBUF SRTO_SNDBUF
#define UDT_RCVBUF SRTO_RCVBUF
#define UDT_LINGER SRTO_LINGER
#define UDP_SNDBUF SRTO_UDP_SNDBUF
#define UDP_RCVBUF SRTO_UDP_RCVBUF
#define UDT_MAXMSG SRTO_MAXMSG
#define UDT_MSGTTL SRTO_MSGTTL
#define UDT_RENDEZVOUS SRTO_RENDEZVOUS
#define UDT_SNDTIMEO SRTO_SNDTIMEO
#define UDT_RCVTIMEO SRTO_RCVTIMEO
#define UDT_REUSEADDR SRTO_REUSEADDR
#define UDT_MAXBW SRTO_MAXBW
#define UDT_STATE SRTO_STATE
#define UDT_EVENT SRTO_EVENT
#define UDT_SNDDATA SRTO_SNDDATA
#define UDT_RCVDATA SRTO_RCVDATA
#define SRT_SENDER SRTO_SENDER
#define SRT_TSBPDMODE SRTO_TSBPDMODE
#define SRT_TSBPDDELAY SRTO_TSBPDDELAY
#define SRT_INPUTBW SRTO_INPUTBW
#define SRT_OHEADBW SRTO_OHEADBW
#define SRT_PASSPHRASE SRTO_PASSPHRASE
#define SRT_PBKEYLEN SRTO_PBKEYLEN
#define SRT_KMSTATE SRTO_KMSTATE
#define SRT_IPTTL SRTO_IPTTL
#define SRT_IPTOS SRTO_IPTOS
#define SRT_TLPKTDROP SRTO_TLPKTDROP
#define SRT_TSBPDMAXLAG SRTO_TSBPDMAXLAG
#define SRT_RCVNAKREPORT SRTO_NAKREPORT
#define SRT_CONNTIMEO SRTO_CONNTIMEO
#define SRT_SNDPBKEYLEN SRTO_SNDPBKEYLEN
#define SRT_RCVPBKEYLEN SRTO_RCVPBKEYLEN
#define SRT_SNDPEERKMSTATE SRTO_SNDPEERKMSTATE
#define SRT_RCVKMSTATE SRTO_RCVKMSTATE
#define UDT_EPOLL_OPT SRT_EPOLL_OPT
#define UDT_EPOLL_IN SRT_EPOLL_IN
#define UDT_EPOLL_OUT SRT_EPOLL_OUT
#define UDT_EPOLL_ERR SRT_EPOLL_ERR
/* Binary backward compatibility obsolete options */
#define SRT_NAKREPORT SRT_RCVNAKREPORT
#if !defined(SRT_DISABLE_LEGACY_UDTSTATUS)
#define UDTSTATUS SRT_SOCKSTATUS
#define INIT SRTS_INIT
#define OPENED SRTS_OPENED
#define LISTENING SRTS_LISTENING
#define CONNECTING SRTS_CONNECTING
#define CONNECTED SRTS_CONNECTED
#define BROKEN SRTS_BROKEN
#define CLOSING SRTS_CLOSING
#define CLOSED SRTS_CLOSED
#define NONEXIST SRTS_NONEXIST
#endif
////////////////////////////////////////////////////////////////////////////////
struct CPerfMon
@ -236,166 +148,75 @@ typedef SRTSOCKET UDTSOCKET; //legacy alias
#ifdef __cplusplus
// Class CUDTException exposed for C++ API.
// This is actually useless, unless you'd use a DIRECT C++ API,
// however there's no such API so far. The current C++ API for UDT/SRT
// is predicted to NEVER LET ANY EXCEPTION out of implementation,
// so it's useless to catch this exception anyway.
class UDT_API CUDTException
{
public:
CUDTException(CodeMajor major = MJ_SUCCESS, CodeMinor minor = MN_NONE, int err = -1);
CUDTException(const CUDTException& e);
~CUDTException();
/// Get the description of the exception.
/// @return Text message for the exception description.
const char* getErrorMessage();
/// Get the system errno for the exception.
/// @return errno.
int getErrorCode() const;
/// Get the system network errno for the exception.
/// @return errno.
int getErrno() const;
/// Clear the error code.
void clear();
private:
CodeMajor m_iMajor; // major exception categories
CodeMinor m_iMinor; // for specific error reasons
int m_iErrno; // errno returned by the system if there is any
std::string m_strMsg; // text error message
std::string m_strAPI; // the name of UDT function that returns the error
std::string m_strDebug; // debug information, set to the original place that causes the error
public: // Legacy Error Code
static const int EUNKNOWN = SRT_EUNKNOWN;
static const int SUCCESS = SRT_SUCCESS;
static const int ECONNSETUP = SRT_ECONNSETUP;
static const int ENOSERVER = SRT_ENOSERVER;
static const int ECONNREJ = SRT_ECONNREJ;
static const int ESOCKFAIL = SRT_ESOCKFAIL;
static const int ESECFAIL = SRT_ESECFAIL;
static const int ECONNFAIL = SRT_ECONNFAIL;
static const int ECONNLOST = SRT_ECONNLOST;
static const int ENOCONN = SRT_ENOCONN;
static const int ERESOURCE = SRT_ERESOURCE;
static const int ETHREAD = SRT_ETHREAD;
static const int ENOBUF = SRT_ENOBUF;
static const int EFILE = SRT_EFILE;
static const int EINVRDOFF = SRT_EINVRDOFF;
static const int ERDPERM = SRT_ERDPERM;
static const int EINVWROFF = SRT_EINVWROFF;
static const int EWRPERM = SRT_EWRPERM;
static const int EINVOP = SRT_EINVOP;
static const int EBOUNDSOCK = SRT_EBOUNDSOCK;
static const int ECONNSOCK = SRT_ECONNSOCK;
static const int EINVPARAM = SRT_EINVPARAM;
static const int EINVSOCK = SRT_EINVSOCK;
static const int EUNBOUNDSOCK = SRT_EUNBOUNDSOCK;
static const int ESTREAMILL = SRT_EINVALMSGAPI;
static const int EDGRAMILL = SRT_EINVALBUFFERAPI;
static const int ENOLISTEN = SRT_ENOLISTEN;
static const int ERDVNOSERV = SRT_ERDVNOSERV;
static const int ERDVUNBOUND = SRT_ERDVUNBOUND;
static const int EINVALMSGAPI = SRT_EINVALMSGAPI;
static const int EINVALBUFFERAPI = SRT_EINVALBUFFERAPI;
static const int EDUPLISTEN = SRT_EDUPLISTEN;
static const int ELARGEMSG = SRT_ELARGEMSG;
static const int EINVPOLLID = SRT_EINVPOLLID;
static const int EASYNCFAIL = SRT_EASYNCFAIL;
static const int EASYNCSND = SRT_EASYNCSND;
static const int EASYNCRCV = SRT_EASYNCRCV;
static const int ETIMEOUT = SRT_ETIMEOUT;
static const int ECONGEST = SRT_ECONGEST;
static const int EPEERERR = SRT_EPEERERR;
};
namespace srt { class CUDTException; }
namespace UDT
{
typedef CUDTException ERRORINFO;
//typedef UDT_SOCKOPT SOCKOPT;
typedef srt::CUDTException ERRORINFO;
typedef CPerfMon TRACEINFO;
typedef CBytePerfMon TRACEBSTATS;
typedef ud_set UDSET;
UDT_API extern const SRTSOCKET INVALID_SOCK;
// This facility is used only for select() function.
// This is considered obsolete and the epoll() functionality rather should be used.
typedef std::set<SRTSOCKET> UDSET;
#define UD_CLR(u, uset) ((uset)->erase(u))
#define UD_ISSET(u, uset) ((uset)->find(u) != (uset)->end())
#define UD_SET(u, uset) ((uset)->insert(u))
#define UD_ZERO(uset) ((uset)->clear())
SRT_API extern const SRTSOCKET INVALID_SOCK;
#undef ERROR
UDT_API extern const int ERROR;
SRT_API extern const int ERROR;
UDT_API int startup();
UDT_API int cleanup();
UDT_API UDTSOCKET socket(int af, int type, int protocol);
UDT_API int bind(UDTSOCKET u, const struct sockaddr* name, int namelen);
UDT_API int bind2(UDTSOCKET u, UDPSOCKET udpsock);
UDT_API int listen(UDTSOCKET u, int backlog);
UDT_API UDTSOCKET accept(UDTSOCKET u, struct sockaddr* addr, int* addrlen);
UDT_API int connect(UDTSOCKET u, const struct sockaddr* name, int namelen);
UDT_API int close(UDTSOCKET u);
UDT_API int getpeername(UDTSOCKET u, struct sockaddr* name, int* namelen);
UDT_API int getsockname(UDTSOCKET u, struct sockaddr* name, int* namelen);
UDT_API int getsockopt(UDTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen);
UDT_API int setsockopt(UDTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen);
UDT_API int send(UDTSOCKET u, const char* buf, int len, int flags);
UDT_API int recv(UDTSOCKET u, char* buf, int len, int flags);
SRT_API int startup();
SRT_API int cleanup();
SRT_API SRTSOCKET socket();
inline SRTSOCKET socket(int , int , int ) { return socket(); }
SRT_API int bind(SRTSOCKET u, const struct sockaddr* name, int namelen);
SRT_API int bind2(SRTSOCKET u, UDPSOCKET udpsock);
SRT_API int listen(SRTSOCKET u, int backlog);
SRT_API SRTSOCKET accept(SRTSOCKET u, struct sockaddr* addr, int* addrlen);
SRT_API int connect(SRTSOCKET u, const struct sockaddr* name, int namelen);
SRT_API int close(SRTSOCKET u);
SRT_API int getpeername(SRTSOCKET u, struct sockaddr* name, int* namelen);
SRT_API int getsockname(SRTSOCKET u, struct sockaddr* name, int* namelen);
SRT_API int getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen);
SRT_API int setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen);
SRT_API int send(SRTSOCKET u, const char* buf, int len, int flags);
SRT_API int recv(SRTSOCKET u, char* buf, int len, int flags);
UDT_API int sendmsg(UDTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false, uint64_t srctime = 0);
UDT_API int recvmsg(UDTSOCKET u, char* buf, int len, uint64_t& srctime);
UDT_API int recvmsg(UDTSOCKET u, char* buf, int len);
SRT_API int sendmsg(SRTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false, int64_t srctime = 0);
SRT_API int recvmsg(SRTSOCKET u, char* buf, int len, uint64_t& srctime);
SRT_API int recvmsg(SRTSOCKET u, char* buf, int len);
UDT_API int64_t sendfile(UDTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = 364000);
UDT_API int64_t recvfile(UDTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = 7280000);
UDT_API int64_t sendfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 364000);
UDT_API int64_t recvfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 7280000);
SRT_API int64_t sendfile(SRTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = 364000);
SRT_API int64_t recvfile(SRTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = 7280000);
SRT_API int64_t sendfile2(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 364000);
SRT_API int64_t recvfile2(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 7280000);
// select and selectEX are DEPRECATED; please use epoll.
UDT_API int select(int nfds, UDSET* readfds, UDSET* writefds, UDSET* exceptfds, const struct timeval* timeout);
UDT_API int selectEx(const std::vector<UDTSOCKET>& fds, std::vector<UDTSOCKET>* readfds,
std::vector<UDTSOCKET>* writefds, std::vector<UDTSOCKET>* exceptfds, int64_t msTimeOut);
SRT_API int select(int nfds, UDSET* readfds, UDSET* writefds, UDSET* exceptfds, const struct timeval* timeout);
SRT_API int selectEx(const std::vector<SRTSOCKET>& fds, std::vector<SRTSOCKET>* readfds,
std::vector<SRTSOCKET>* writefds, std::vector<SRTSOCKET>* exceptfds, int64_t msTimeOut);
UDT_API int epoll_create();
UDT_API int epoll_add_usock(int eid, UDTSOCKET u, const int* events = NULL);
UDT_API int epoll_add_ssock(int eid, SYSSOCKET s, const int* events = NULL);
UDT_API int epoll_remove_usock(int eid, UDTSOCKET u);
UDT_API int epoll_remove_ssock(int eid, SYSSOCKET s);
UDT_API int epoll_update_usock(int eid, UDTSOCKET u, const int* events = NULL);
UDT_API int epoll_update_ssock(int eid, SYSSOCKET s, const int* events = NULL);
UDT_API int epoll_wait(int eid, std::set<UDTSOCKET>* readfds, std::set<UDTSOCKET>* writefds, int64_t msTimeOut,
SRT_API int epoll_create();
SRT_API int epoll_add_usock(int eid, SRTSOCKET u, const int* events = NULL);
SRT_API int epoll_add_ssock(int eid, SYSSOCKET s, const int* events = NULL);
SRT_API int epoll_remove_usock(int eid, SRTSOCKET u);
SRT_API int epoll_remove_ssock(int eid, SYSSOCKET s);
SRT_API int epoll_update_usock(int eid, SRTSOCKET u, const int* events = NULL);
SRT_API int epoll_update_ssock(int eid, SYSSOCKET s, const int* events = NULL);
SRT_API int epoll_wait(int eid, std::set<SRTSOCKET>* readfds, std::set<SRTSOCKET>* writefds, int64_t msTimeOut,
std::set<SYSSOCKET>* lrfds = NULL, std::set<SYSSOCKET>* wrfds = NULL);
UDT_API int epoll_wait2(int eid, UDTSOCKET* readfds, int* rnum, UDTSOCKET* writefds, int* wnum, int64_t msTimeOut,
SRT_API int epoll_wait2(int eid, SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum, int64_t msTimeOut,
SYSSOCKET* lrfds = NULL, int* lrnum = NULL, SYSSOCKET* lwfds = NULL, int* lwnum = NULL);
UDT_API int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
UDT_API int epoll_release(int eid);
UDT_API ERRORINFO& getlasterror();
UDT_API int getlasterror_code();
UDT_API const char* getlasterror_desc();
UDT_API int bstats(UDTSOCKET u, TRACEBSTATS* perf, bool clear = true);
UDT_API SRT_SOCKSTATUS getsockstate(UDTSOCKET u);
// This is a C++ SRT API extension. This is not a part of legacy UDT API.
UDT_API void setloglevel(srt_logging::LogLevel::type ll);
UDT_API void addlogfa(srt_logging::LogFA fa);
UDT_API void dellogfa(srt_logging::LogFA fa);
UDT_API void resetlogfa(std::set<srt_logging::LogFA> fas);
UDT_API void resetlogfa(const int* fara, size_t fara_size);
UDT_API void setlogstream(std::ostream& stream);
UDT_API void setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler);
UDT_API void setlogflags(int flags);
UDT_API bool setstreamid(UDTSOCKET u, const std::string& sid);
UDT_API std::string getstreamid(UDTSOCKET u);
SRT_API int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
SRT_API int epoll_release(int eid);
SRT_API ERRORINFO& getlasterror();
SRT_API int getlasterror_code();
SRT_API const char* getlasterror_desc();
SRT_API int bstats(SRTSOCKET u, SRT_TRACEBSTATS* perf, bool clear = true);
SRT_API SRT_SOCKSTATUS getsockstate(SRTSOCKET u);
} // namespace UDT
@ -405,7 +226,47 @@ UDT_API std::string getstreamid(UDTSOCKET u);
// own logger FA objects, or create their own. The object of this type
// is required to initialize the logger FA object.
namespace srt_logging { struct LogConfig; }
UDT_API extern srt_logging::LogConfig srt_logger_config;
SRT_API extern srt_logging::LogConfig srt_logger_config;
namespace srt
{
// This is a C++ SRT API extension. This is not a part of legacy UDT API.
SRT_API void setloglevel(srt_logging::LogLevel::type ll);
SRT_API void addlogfa(srt_logging::LogFA fa);
SRT_API void dellogfa(srt_logging::LogFA fa);
SRT_API void resetlogfa(std::set<srt_logging::LogFA> fas);
SRT_API void resetlogfa(const int* fara, size_t fara_size);
SRT_API void setlogstream(std::ostream& stream);
SRT_API void setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler);
SRT_API void setlogflags(int flags);
SRT_API bool setstreamid(SRTSOCKET u, const std::string& sid);
SRT_API std::string getstreamid(SRTSOCKET u);
// Namespace alias
namespace logging {
using namespace srt_logging;
}
} // namespace srt
// Planned deprecated removal: rel1.6.0
// There's also no portable way possible to enforce a deprecation
// compiler warning, so leaving as is.
namespace UDT
{
// Backward-compatible aliases, just for a case someone was using it.
using srt::setloglevel;
using srt::addlogfa;
using srt::dellogfa;
using srt::resetlogfa;
using srt::setlogstream;
using srt::setloghandler;
using srt::setlogflags;
using srt::setstreamid;
using srt::getstreamid;
}
#endif /* __cplusplus */

405
trunk/3rdparty/srt-1-fit/srtcore/utilities.h vendored Executable file → Normal file
View file

@ -13,72 +13,14 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__SRT_UTILITIES_H
#define INC__SRT_UTILITIES_H
#ifdef __GNUG__
#define ATR_UNUSED __attribute__((unused))
#define ATR_DEPRECATED __attribute__((deprecated))
#else
#define ATR_UNUSED
#define ATR_DEPRECATED
#endif
#if defined(__cplusplus) && __cplusplus > 199711L
#define HAVE_CXX11 1
// For gcc 4.7, claim C++11 is supported, as long as experimental C++0x is on,
// however it's only the "most required C++11 support".
#if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 7 // 4.7 only!
#define ATR_NOEXCEPT
#define ATR_CONSTEXPR
#define ATR_OVERRIDE
#define ATR_FINAL
#else
#define HAVE_FULL_CXX11 1
#define ATR_NOEXCEPT noexcept
#define ATR_CONSTEXPR constexpr
#define ATR_OVERRIDE override
#define ATR_FINAL final
#endif
// Microsoft Visual Studio supports C++11, but not fully,
// and still did not change the value of __cplusplus. Treat
// this special way.
// _MSC_VER == 1800 means Microsoft Visual Studio 2013.
#elif defined(_MSC_VER) && _MSC_VER >= 1800
#define HAVE_CXX11 1
#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026
#define HAVE_FULL_CXX11 1
#define ATR_NOEXCEPT noexcept
#define ATR_CONSTEXPR constexpr
#define ATR_OVERRIDE override
#define ATR_FINAL final
#else
#define ATR_NOEXCEPT
#define ATR_CONSTEXPR
#define ATR_OVERRIDE
#define ATR_FINAL
#endif
#else
#define HAVE_CXX11 0
#define ATR_NOEXCEPT // throw() - bad idea
#define ATR_CONSTEXPR
#define ATR_OVERRIDE
#define ATR_FINAL
#endif
#if !HAVE_CXX11 && defined(REQUIRE_CXX11) && REQUIRE_CXX11 == 1
#error "The currently compiled application required C++11, but your compiler doesn't support it."
#endif
#ifndef INC_SRT_UTILITIES_H
#define INC_SRT_UTILITIES_H
// Windows warning disabler
#define _CRT_SECURE_NO_WARNINGS 1
#include "platform_sys.h"
#include "srt_attr_defs.h" // defines HAVE_CXX11
// Happens that these are defined, undefine them in advance
#undef min
@ -88,10 +30,11 @@ written by
#include <algorithm>
#include <bitset>
#include <map>
#include <vector>
#include <functional>
#include <memory>
#include <sstream>
#include <iomanip>
#include <sstream>
#if HAVE_CXX11
#include <type_traits>
@ -100,6 +43,7 @@ written by
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <stdexcept>
// -------------- UTILITIES ------------------------
@ -113,7 +57,7 @@ written by
#endif
#if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__)
#if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__) || defined(__GLIBC__)
# include <endian.h>
@ -171,7 +115,7 @@ written by
# include <sys/endian.h>
#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
# include <sys/endian.h>
@ -196,6 +140,46 @@ written by
# define le64toh(x) letoh64(x)
#endif
#elif defined(SUNOS)
// SunOS/Solaris
#include <sys/byteorder.h>
#include <sys/isa_defs.h>
#define __LITTLE_ENDIAN 1234
#define __BIG_ENDIAN 4321
# if defined(_BIG_ENDIAN)
#define __BYTE_ORDER __BIG_ENDIAN
#define be64toh(x) (x)
#define be32toh(x) (x)
#define be16toh(x) (x)
#define le16toh(x) ((uint16_t)BSWAP_16(x))
#define le32toh(x) BSWAP_32(x)
#define le64toh(x) BSWAP_64(x)
#define htobe16(x) (x)
#define htole16(x) ((uint16_t)BSWAP_16(x))
#define htobe32(x) (x)
#define htole32(x) BSWAP_32(x)
#define htobe64(x) (x)
#define htole64(x) BSWAP_64(x)
# else
#define __BYTE_ORDER __LITTLE_ENDIAN
#define be64toh(x) BSWAP_64(x)
#define be32toh(x) ntohl(x)
#define be16toh(x) ntohs(x)
#define le16toh(x) (x)
#define le32toh(x) (x)
#define le64toh(x) (x)
#define htobe16(x) htons(x)
#define htole16(x) (x)
#define htobe32(x) htonl(x)
#define htole32(x) (x)
#define htobe64(x) BSWAP_64(x)
#define htole64(x) (x)
# endif
#elif defined(__WINDOWS__)
# include <winsock2.h>
@ -317,7 +301,7 @@ template<size_t R>
struct BitsetMask<R, R, true>
{
static const bool correct = true;
static const uint32_t value = 1 << R;
static const uint32_t value = 1u << R;
};
// This is a trap for a case that BitsetMask::correct in the master template definition
@ -426,6 +410,88 @@ struct DynamicStruct
};
/// Fixed-size array template class.
namespace srt {
template <class T>
class FixedArray
{
public:
FixedArray(size_t size)
: m_size(size)
, m_entries(new T[size])
{
}
~FixedArray()
{
delete [] m_entries;
}
public:
const T& operator[](size_t index) const
{
if (index >= m_size)
raise_expection(index);
return m_entries[index];
}
T& operator[](size_t index)
{
if (index >= m_size)
raise_expection(index);
return m_entries[index];
}
const T& operator[](int index) const
{
if (index < 0 || static_cast<size_t>(index) >= m_size)
raise_expection(index);
return m_entries[index];
}
T& operator[](int index)
{
if (index < 0 || static_cast<size_t>(index) >= m_size)
raise_expection(index);
return m_entries[index];
}
size_t size() const { return m_size; }
typedef T* iterator;
typedef const T* const_iterator;
iterator begin() { return m_entries; }
iterator end() { return m_entries + m_size; }
const_iterator cbegin() const { return m_entries; }
const_iterator cend() const { return m_entries + m_size; }
T* data() { return m_entries; }
private:
FixedArray(const FixedArray<T>& );
FixedArray<T>& operator=(const FixedArray<T>&);
void raise_expection(int i) const
{
std::stringstream ss;
ss << "Index " << i << "out of range";
throw std::runtime_error(ss.str());
}
private:
size_t m_size;
T* const m_entries;
};
} // namespace srt
// ------------------------------------------------------------
@ -435,47 +501,25 @@ inline bool IsSet(int32_t bitset, int32_t flagset)
return (bitset & flagset) == flagset;
}
// Homecooked version of ref_t. It's a copy of std::reference_wrapper
// voided of unwanted properties and renamed to ref_t.
#if HAVE_CXX11
#include <functional>
#endif
template<typename Type>
class ref_t
// std::addressof in C++11,
// needs to be provided for C++03
template <class RefType>
inline RefType* AddressOf(RefType& r)
{
Type* m_data;
return (RefType*)(&(unsigned char&)(r));
}
public:
typedef Type type;
template <class T>
struct explicit_t
{
T inobject;
explicit_t(const T& uo): inobject(uo) {}
#if HAVE_CXX11
explicit ref_t(Type& __indata)
: m_data(std::addressof(__indata))
{ }
#else
explicit ref_t(Type& __indata)
: m_data((Type*)(&(char&)(__indata)))
{ }
#endif
operator T() const { return inobject; }
ref_t(const ref_t<Type>& inref)
: m_data(inref.m_data)
{ }
#if HAVE_CXX11
ref_t(const std::reference_wrapper<Type>& i): m_data(std::addressof(i.get())) {}
#endif
Type& operator*() { return *m_data; }
Type& get() const
{ return *m_data; }
Type operator->() const
{ return *m_data; }
private:
template <class X>
explicit_t(const X& another);
};
// This is required for Printable function if you have a container of pairs,
@ -492,15 +536,6 @@ namespace srt_pair_op
#if HAVE_CXX11
// This alias was created so that 'Ref' (not 'ref') is used everywhere.
// Normally the C++11 'ref' fits perfectly here, however in C++03 mode
// it would have to be newly created. This would then cause a conflict
// between C++03 SRT and C++11 applications as well as between C++ standard
// library and SRT when SRT is compiled in C++11 mode (as it happens on
// Darwin/clang).
template <class In>
inline auto Ref(In& i) -> decltype(std::ref(i)) { return std::ref(i); }
template <class In>
inline auto Move(In& i) -> decltype(std::move(i)) { return std::move(i); }
@ -530,8 +565,6 @@ inline std::string Sprint(Args&&... args)
template <class T>
using UniquePtr = std::unique_ptr<T>;
// Some utilities borrowed from tumux, as this is using options
// similar way.
template <class Container, class Value = typename Container::value_type, typename... Args> inline
std::string Printable(const Container& in, Value /*pseudoargument*/, Args&&... args)
{
@ -577,12 +610,6 @@ auto map_getp(const Map& m, const Key& key) -> typename Map::mapped_type const*
#else
template <class Type>
ref_t<Type> Ref(Type& arg)
{
return ref_t<Type>(arg);
}
// The unique_ptr requires C++11, and the rvalue-reference feature,
// so here we're simulate the behavior using the old std::auto_ptr.
@ -610,14 +637,14 @@ public:
// All constructor declarations must be repeated.
// "Constructor delegation" is also only C++11 feature.
explicit UniquePtr(element_type* __p = 0) throw() : Base(__p) {}
UniquePtr(UniquePtr& __a) throw() : Base(__a) { }
template<typename _Tp1>
UniquePtr(UniquePtr<_Tp1>& __a) throw() : Base(__a) {}
explicit UniquePtr(element_type* p = 0) throw() : Base(p) {}
UniquePtr(UniquePtr& a) throw() : Base(a) { }
template<typename Type1>
UniquePtr(UniquePtr<Type1>& a) throw() : Base(a) {}
UniquePtr& operator=(UniquePtr& __a) throw() { return Base::operator=(__a); }
template<typename _Tp1>
UniquePtr& operator=(UniquePtr<_Tp1>& __a) throw() { return Base::operator=(__a); }
UniquePtr& operator=(UniquePtr& a) throw() { return Base::operator=(a); }
template<typename Type1>
UniquePtr& operator=(UniquePtr<Type1>& a) throw() { return Base::operator=(a); }
// Good, now we need to add some parts of the API of unique_ptr.
@ -630,7 +657,15 @@ public:
operator bool () { return 0!= get(); }
};
// A primitive one-argument version of Printable
// A primitive one-argument versions of Sprint and Printable
template <class Arg1>
inline std::string Sprint(const Arg1& arg)
{
std::ostringstream sout;
sout << arg;
return sout.str();
}
template <class Container> inline
std::string Printable(const Container& in)
{
@ -675,6 +710,44 @@ typename Map::mapped_type const* map_getp(const Map& m, const Key& key)
#endif
// Printable with prefix added for every element.
// Useful when printing a container of sockets or sequence numbers.
template <class Container> inline
std::string PrintableMod(const Container& in, const std::string& prefix)
{
using namespace srt_pair_op;
typedef typename Container::value_type Value;
std::ostringstream os;
os << "[ ";
for (typename Container::const_iterator y = in.begin(); y != in.end(); ++y)
os << prefix << Value(*y) << " ";
os << "]";
return os.str();
}
template<typename InputIterator, typename OutputIterator, typename TransFunction>
inline void FilterIf(InputIterator bg, InputIterator nd,
OutputIterator out, TransFunction fn)
{
for (InputIterator i = bg; i != nd; ++i)
{
std::pair<typename TransFunction::result_type, bool> result = fn(*i);
if (!result.second)
continue;
*out++ = result.first;
}
}
template <class Value, class ArgValue>
inline void insert_uniq(std::vector<Value>& v, const ArgValue& val)
{
typename std::vector<Value>::iterator i = std::find(v.begin(), v.end(), val);
if (i != v.end())
return;
v.push_back(val);
}
template <class Signature>
struct CallbackHolder
{
@ -696,7 +769,8 @@ struct CallbackHolder
// Casting function-to-function, however, should not. Unfortunately
// newer compilers disallow that, too (when a signature differs), but
// then they should better use the C++11 way, much more reliable and safer.
void* (*testfn)(void*) ATR_UNUSED = (void*(*)(void*))f;
void* (*testfn)(void*) = (void*(*)(void*))f;
(void)(testfn);
#endif
opaque = o;
fn = f;
@ -767,11 +841,13 @@ public:
m_qDriftSum += driftval;
++m_uDriftSpan;
// I moved it here to calculate accumulated overdrift.
if (CLEAR_ON_UPDATE)
m_qOverdrift = 0;
if (m_uDriftSpan < MAX_SPAN)
return false;
if (CLEAR_ON_UPDATE)
m_qOverdrift = 0;
// Calculate the median of all drift values.
// In most cases, the divisor should be == MAX_SPAN.
@ -799,6 +875,12 @@ public:
return true;
}
// For group overrides
void forceDrift(int64_t driftval)
{
m_qDrift = driftval;
}
// These values can be read at any time, however if you want
// to depend on the fact that they have been changed lately,
// you have to check the return value from update().
@ -869,15 +951,15 @@ struct MapProxy
}
};
/// Print some hash-based stamp of the first 16 bytes in the buffer
inline std::string BufferStamp(const char* mem, size_t size)
{
using namespace std;
char spread[16];
int n = 16-size;
if (n > 0)
memset(spread+16-n, 0, n);
memcpy(spread, mem, min(size_t(16), size));
if (size < 16)
memset((spread + size), 0, 16 - size);
memcpy((spread), mem, min(size_t(16), size));
// Now prepare 4 cells for uint32_t.
union
@ -885,7 +967,7 @@ inline std::string BufferStamp(const char* mem, size_t size)
uint32_t sum;
char cells[4];
};
memset(cells, 0, 4);
memset((cells), 0, 4);
for (size_t x = 0; x < 4; ++x)
for (size_t y = 0; y < 4; ++y)
@ -894,9 +976,7 @@ inline std::string BufferStamp(const char* mem, size_t size)
}
// Convert to hex string
ostringstream os;
os << hex << uppercase << setfill('0') << setw(8) << sum;
return os.str();
@ -963,7 +1043,56 @@ ATR_CONSTEXPR size_t Size(const V (&)[N]) ATR_NOEXCEPT { return N; }
template <size_t DEPRLEN, typename ValueType>
inline ValueType avg_iir(ValueType old_value, ValueType new_value)
{
return (old_value*(DEPRLEN-1) + new_value)/DEPRLEN;
return (old_value * (DEPRLEN - 1) + new_value) / DEPRLEN;
}
template <size_t DEPRLEN, typename ValueType>
inline ValueType avg_iir_w(ValueType old_value, ValueType new_value, size_t new_val_weight)
{
return (old_value * (DEPRLEN - new_val_weight) + new_value * new_val_weight) / DEPRLEN;
}
// Property accessor definitions
//
// "Property" is a special method that accesses given field.
// This relies only on a convention, which is the following:
//
// V x = object.prop(); <-- get the property's value
// object.prop(x); <-- set the property a value
//
// Properties might be also chained when setting:
//
// object.prop1(v1).prop2(v2).prop3(v3);
//
// Properties may be defined various even very complicated
// ways, which is simply providing a method with body. In order
// to define a property simplest possible way, that is, refer
// directly to the field that keeps it, here are the following macros:
//
// Prefix: SRTU_PROPERTY_
// Followed by:
// - access type: RO, WO, RW, RR, RRW
// - chain flag: optional _CHAIN
// Where access type is:
// - RO - read only. Defines reader accessor. The accessor method will be const.
// - RR - read reference. The accessor isn't const to allow reference passthrough.
// - WO - write only. Defines writer accessor.
// - RW - combines RO and WO.
// - RRW - combines RR and WO.
//
// The _CHAIN marker is optional for macros providing writable accessors
// for properties. The difference is that while simple write accessors return
// void, the chaining accessors return the reference to the object for which
// the write accessor was called so that you can call the next accessor (or
// any other method as well) for the result.
#define SRTU_PROPERTY_RR(type, name, field) type name() { return field; }
#define SRTU_PROPERTY_RO(type, name, field) type name() const { return field; }
#define SRTU_PROPERTY_WO(type, name, field) void set_##name(type arg) { field = arg; }
#define SRTU_PROPERTY_WO_CHAIN(otype, type, name, field) otype& set_##name(type arg) { field = arg; return *this; }
#define SRTU_PROPERTY_RW(type, name, field) SRTU_PROPERTY_RO(type, name, field); SRTU_PROPERTY_WO(type, name, field)
#define SRTU_PROPERTY_RRW(type, name, field) SRTU_PROPERTY_RR(type, name, field); SRTU_PROPERTY_WO(type, name, field)
#define SRTU_PROPERTY_RW_CHAIN(otype, type, name, field) SRTU_PROPERTY_RO(type, name, field); SRTU_PROPERTY_WO_CHAIN(otype, type, name, field)
#define SRTU_PROPERTY_RRW_CHAIN(otype, type, name, field) SRTU_PROPERTY_RR(type, name, field); SRTU_PROPERTY_WO_CHAIN(otype, type, name, field)
#endif

View file

@ -13,8 +13,8 @@ written by
Haivision Systems Inc.
*****************************************************************************/
#ifndef INC__SRT_VERSION_H
#define INC__SRT_VERSION_H
#ifndef INC_SRT_VERSION_H
#define INC_SRT_VERSION_H
// To construct version value
#define SRT_MAKE_VERSION(major, minor, patch) \
@ -24,11 +24,11 @@ written by
#define SRT_VERSION_MAJOR @SRT_VERSION_MAJOR@
#define SRT_VERSION_MINOR @SRT_VERSION_MINOR@
#define SRT_VERSION_PATCH @SRT_VERSION_PATCH@
#cmakedefine SRT_VERSION_BUILD @APPVEYOR_BUILD_NUMBER_STRING@
#cmakedefine SRT_VERSION_BUILD @CI_BUILD_NUMBER_STRING@
#define SRT_VERSION_STRING "@SRT_VERSION@"
#define SRT_VERSION_VALUE \
SRT_MAKE_VERSION_VALUE( \
SRT_VERSION_MAJOR, SRT_VERSION_MINOR, SRT_VERSION_PATCH )
#endif // INC__SRT_VERSION_H
#endif // INC_SRT_VERSION_H

View file

@ -50,6 +50,8 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#include "platform_sys.h"
#include <cmath>
#include <cstring>
#include "common.h"
@ -57,7 +59,10 @@ modified by
#include <algorithm>
using namespace std;
using namespace srt::sync;
namespace srt
{
namespace ACKWindowTools
{
@ -65,7 +70,7 @@ void store(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t s
{
r_aSeq[r_iHead].iACKSeqNo = seq;
r_aSeq[r_iHead].iACK = ack;
r_aSeq[r_iHead].TimeStamp = CTimer::getTime();
r_aSeq[r_iHead].tsTimeStamp = steady_clock::now();
r_iHead = (r_iHead + 1) % size;
@ -74,27 +79,26 @@ void store(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t s
r_iTail = (r_iTail + 1) % size;
}
int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack)
int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack, const steady_clock::time_point& currtime)
{
// Head has not exceeded the physical boundary of the window
if (r_iHead >= r_iTail)
{
// Head has not exceeded the physical boundary of the window
for (int i = r_iTail, n = r_iHead; i < n; ++ i)
{
// looking for indentical ACK Seq. No.
// Looking for an identical ACK Seq. No.
if (seq == r_aSeq[i].iACKSeqNo)
{
// return the Data ACK it carried
// Return the Data ACK it carried
r_ack = r_aSeq[i].iACK;
// calculate RTT
int rtt = int(CTimer::getTime() - r_aSeq[i].TimeStamp);
// Calculate RTT estimate
const int rtt = count_microseconds(currtime - r_aSeq[i].tsTimeStamp);
if (i + 1 == r_iHead)
{
r_iTail = r_iHead = 0;
r_aSeq[0].iACKSeqNo = -1;
r_aSeq[0].iACKSeqNo = SRT_SEQNO_NONE;
}
else
r_iTail = (i + 1) % size;
@ -103,22 +107,22 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3
}
}
// Bad input, the ACK node has been overwritten
// The record about ACK is not found in the buffer, RTT can not be calculated
return -1;
}
// Head has exceeded the physical window boundary, so it is behind tail
for (int j = r_iTail, n = r_iHead + size; j < n; ++ j)
{
// looking for indentical ACK seq. no.
// Looking for an identical ACK Seq. No.
if (seq == r_aSeq[j % size].iACKSeqNo)
{
// return Data ACK
// Return the Data ACK it carried
j %= size;
r_ack = r_aSeq[j].iACK;
// calculate RTT
int rtt = int(CTimer::getTime() - r_aSeq[j].TimeStamp);
// Calculate RTT estimate
const int rtt = count_microseconds(currtime - r_aSeq[j].tsTimeStamp);
if (j == r_iHead)
{
@ -132,14 +136,16 @@ int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int3
}
}
// bad input, the ACK node has been overwritten
// The record about ACK is not found in the buffer, RTT can not be calculated
return -1;
}
}
} // namespace AckTools
} // namespace srt
////////////////////////////////////////////////////////////////////////////////
void CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize)
void srt::CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize)
{
for (size_t i = 0; i < asize; ++ i)
r_pktWindow[i] = 1000000; //1 sec -> 1 pkt/sec
@ -148,11 +154,11 @@ void CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_probeW
r_probeWindow[k] = 1000; //1 msec -> 1000 pkts/sec
for (size_t i = 0; i < asize; ++ i)
r_bytesWindow[i] = CPacket::SRT_MAX_PAYLOAD_SIZE; //based on 1 pkt/sec set in r_pktWindow[i]
r_bytesWindow[i] = srt::CPacket::SRT_MAX_PAYLOAD_SIZE; //based on 1 pkt/sec set in r_pktWindow[i]
}
int CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, const int* abytes, size_t asize, int& bytesps)
int srt::CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, const int* abytes, size_t asize, int& bytesps)
{
// get median value, but cannot change the original value order in the window
std::copy(window, window + asize, replica);
@ -185,7 +191,7 @@ int CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, cons
// claculate speed, or return 0 if not enough valid value
if (count > (asize >> 1))
{
bytes += (CPacket::SRT_DATA_HDR_SIZE * count); //Add protocol headers to bytes received
bytes += (srt::CPacket::SRT_DATA_HDR_SIZE * count); //Add protocol headers to bytes received
bytesps = (unsigned long)ceil(1000000.0 / (double(sum) / double(bytes)));
return (int)ceil(1000000.0 / (sum / count));
}
@ -196,7 +202,7 @@ int CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, cons
}
}
int CPktTimeWindowTools::getBandwidth_in(const int* window, int* replica, size_t psize)
int srt::CPktTimeWindowTools::getBandwidth_in(const int* window, int* replica, size_t psize)
{
// This calculation does more-less the following:
//

View file

@ -50,28 +50,31 @@ modified by
Haivision Systems Inc.
*****************************************************************************/
#ifndef __UDT_WINDOW_H__
#define __UDT_WINDOW_H__
#ifndef INC_SRT_WINDOW_H
#define INC_SRT_WINDOW_H
#ifndef _WIN32
#include <sys/time.h>
#include <time.h>
#endif
#include "udt.h"
#include "packet.h"
#include "udt.h"
namespace srt
{
namespace ACKWindowTools
{
struct Seq
{
int32_t iACKSeqNo; // Seq. No. for the ACK packet
int32_t iACK; // Data Seq. No. carried by the ACK packet
uint64_t TimeStamp; // The timestamp when the ACK was sent
int32_t iACKSeqNo; // Seq. No. of the ACK packet
int32_t iACK; // Data packet Seq. No. carried by the ACK packet
sync::steady_clock::time_point tsTimeStamp; // The timestamp when the ACK was sent
};
void store(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t ack);
int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack);
int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack, const sync::steady_clock::time_point& currtime);
}
template <size_t SIZE>
@ -83,28 +86,30 @@ public:
m_iHead(0),
m_iTail(0)
{
m_aSeq[0].iACKSeqNo = -1;
m_aSeq[0].iACKSeqNo = SRT_SEQNO_NONE;
}
~CACKWindow() {}
/// Write an ACK record into the window.
/// @param [in] seq ACK seq. no.
/// @param [in] ack DATA ACK no.
/// @param [in] seq Seq. No. of the ACK packet
/// @param [in] ack Data packet Seq. No. carried by the ACK packet
void store(int32_t seq, int32_t ack)
{
return ACKWindowTools::store(m_aSeq, SIZE, m_iHead, m_iTail, seq, ack);
}
/// Search the ACK-2 "seq" in the window, find out the DATA "ack" and caluclate RTT .
/// @param [in] seq ACK-2 seq. no.
/// @param [out] ack the DATA ACK no. that matches the ACK-2 no.
/// @return RTT.
/// Search the ACKACK "seq" in the window, find out the data packet "ack"
/// and calculate RTT estimate based on the ACK/ACKACK pair
/// @param [in] seq Seq. No. of the ACK packet carried within ACKACK
/// @param [out] ack Acknowledged data packet Seq. No. from the ACK packet that matches the ACKACK
/// @param [in] currtime The timestamp of ACKACK packet reception by the receiver
/// @return RTT
int acknowledge(int32_t seq, int32_t& r_ack)
int acknowledge(int32_t seq, int32_t& r_ack, const sync::steady_clock::time_point& currtime)
{
return ACKWindowTools::acknowledge(m_aSeq, SIZE, m_iHead, m_iTail, seq, r_ack);
return ACKWindowTools::acknowledge(m_aSeq, SIZE, m_iHead, m_iTail, seq, r_ack, currtime);
}
private:
@ -112,7 +117,7 @@ private:
typedef ACKWindowTools::Seq Seq;
Seq m_aSeq[SIZE];
int m_iHead; // Pointer to the lastest ACK record
int m_iHead; // Pointer to the latest ACK record
int m_iTail; // Pointer to the oldest ACK record
private:
@ -143,24 +148,22 @@ public:
m_iProbeWindowPtr(0),
m_iLastSentTime(0),
m_iMinPktSndInt(1000000),
m_LastArrTime(),
m_CurrArrTime(),
m_ProbeTime(),
m_Probe1Sequence(-1)
m_tsLastArrTime(sync::steady_clock::now()),
m_tsCurrArrTime(),
m_tsProbeTime(),
m_Probe1Sequence(SRT_SEQNO_NONE)
{
pthread_mutex_init(&m_lockPktWindow, NULL);
pthread_mutex_init(&m_lockProbeWindow, NULL);
m_LastArrTime = CTimer::getTime();
// Exception: up to CUDT ctor
sync::setupMutex(m_lockPktWindow, "PktWindow");
sync::setupMutex(m_lockProbeWindow, "ProbeWindow");
CPktTimeWindowTools::initializeWindowArrays(m_aPktWindow, m_aProbeWindow, m_aBytesWindow, ASIZE, PSIZE);
}
~CPktTimeWindow()
{
pthread_mutex_destroy(&m_lockPktWindow);
pthread_mutex_destroy(&m_lockProbeWindow);
}
public:
/// read the minimum packet sending interval.
/// @return minimum packet sending interval (microseconds).
@ -169,19 +172,19 @@ public:
/// Calculate the packets arrival speed.
/// @return Packet arrival speed (packets per second).
int getPktRcvSpeed(ref_t<int> bytesps) const
int getPktRcvSpeed(int& w_bytesps) const
{
// Lock access to the packet Window
CGuard cg(m_lockPktWindow);
sync::ScopedLock cg(m_lockPktWindow);
int pktReplica[ASIZE]; // packet information window (inter-packet time)
return getPktRcvSpeed_in(m_aPktWindow, pktReplica, m_aBytesWindow, ASIZE, *bytesps);
return getPktRcvSpeed_in(m_aPktWindow, pktReplica, m_aBytesWindow, ASIZE, (w_bytesps));
}
int getPktRcvSpeed() const
{
int bytesps;
return getPktRcvSpeed(Ref(bytesps));
return getPktRcvSpeed((bytesps));
}
/// Estimate the bandwidth.
@ -190,7 +193,7 @@ public:
int getBandwidth() const
{
// Lock access to the packet Window
CGuard cg(m_lockProbeWindow);
sync::ScopedLock cg(m_lockProbeWindow);
int probeReplica[PSIZE];
return getBandwidth_in(m_aProbeWindow, probeReplica, PSIZE);
@ -213,12 +216,12 @@ public:
void onPktArrival(int pktsz = 0)
{
CGuard cg(m_lockPktWindow);
sync::ScopedLock cg(m_lockPktWindow);
m_CurrArrTime = CTimer::getTime();
m_tsCurrArrTime = sync::steady_clock::now();
// record the packet interval between the current and the last one
m_aPktWindow[m_iPktWindowPtr] = int(m_CurrArrTime - m_LastArrTime);
m_aPktWindow[m_iPktWindowPtr] = (int) sync::count_microseconds(m_tsCurrArrTime - m_tsLastArrTime);
m_aBytesWindow[m_iPktWindowPtr] = pktsz;
// the window is logically circular
@ -227,7 +230,7 @@ public:
m_iPktWindowPtr = 0;
// remember last packet arrival time
m_LastArrTime = m_CurrArrTime;
m_tsLastArrTime = m_tsCurrArrTime;
}
/// Shortcut to test a packet for possible probe 1 or 2
@ -259,11 +262,11 @@ public:
// Reset the starting probe into "undefined", when
// a packet has come as retransmitted before the
// measurement at arrival of 17th could be taken.
m_Probe1Sequence = -1;
m_Probe1Sequence = SRT_SEQNO_NONE;
return;
}
m_ProbeTime = CTimer::getTime();
m_tsProbeTime = sync::steady_clock::now();
m_Probe1Sequence = pkt.m_iSeqNo; // Record the sequence where 16th packet probe was taken
}
@ -279,26 +282,26 @@ public:
// expected packet pair, behave as if the 17th packet was lost.
// no start point yet (or was reset) OR not very next packet
if (m_Probe1Sequence == -1 || CSeqNo::incseq(m_Probe1Sequence) != pkt.m_iSeqNo)
if (m_Probe1Sequence == SRT_SEQNO_NONE || CSeqNo::incseq(m_Probe1Sequence) != pkt.m_iSeqNo)
return;
// Grab the current time before trying to acquire
// a mutex. This might add extra delay and therefore
// screw up the measurement.
const uint64_t now = CTimer::getTime();
const sync::steady_clock::time_point now = sync::steady_clock::now();
// Lock access to the packet Window
CGuard cg(m_lockProbeWindow);
sync::ScopedLock cg(m_lockProbeWindow);
m_CurrArrTime = now;
m_tsCurrArrTime = now;
// Reset the starting probe to prevent checking if the
// measurement was already taken.
m_Probe1Sequence = -1;
m_Probe1Sequence = SRT_SEQNO_NONE;
// record the probing packets interval
// Adjust the time for what a complete packet would have take
const int64_t timediff = m_CurrArrTime - m_ProbeTime;
const int64_t timediff = sync::count_microseconds(m_tsCurrArrTime - m_tsProbeTime);
const int64_t timediff_times_pl_size = timediff * CPacket::SRT_MAX_PAYLOAD_SIZE;
// Let's take it simpler than it is coded here:
@ -312,11 +315,11 @@ public:
// the ETH+IP+UDP+SRT header part elliminates the constant packet delivery time influence.
//
const size_t pktsz = pkt.getLength();
m_aProbeWindow[m_iProbeWindowPtr] = pktsz ? timediff_times_pl_size / pktsz : int(timediff);
m_aProbeWindow[m_iProbeWindowPtr] = pktsz ? int(timediff_times_pl_size / pktsz) : int(timediff);
// OLD CODE BEFORE BSTATS:
// record the probing packets interval
// m_aProbeWindow[m_iProbeWindowPtr] = int(m_CurrArrTime - m_ProbeTime);
// m_aProbeWindow[m_iProbeWindowPtr] = int(m_tsCurrArrTime - m_tsProbeTime);
// the window is logically circular
++ m_iProbeWindowPtr;
@ -324,29 +327,29 @@ public:
m_iProbeWindowPtr = 0;
}
private:
int m_aPktWindow[ASIZE]; // packet information window (inter-packet time)
int m_aBytesWindow[ASIZE]; //
int m_iPktWindowPtr; // position pointer of the packet info. window.
mutable pthread_mutex_t m_lockPktWindow; // used to synchronize access to the packet window
int m_aPktWindow[ASIZE]; // Packet information window (inter-packet time)
int m_aBytesWindow[ASIZE];
int m_iPktWindowPtr; // Position pointer of the packet info. window
mutable sync::Mutex m_lockPktWindow; // Used to synchronize access to the packet window
int m_aProbeWindow[PSIZE]; // record inter-packet time for probing packet pairs
int m_iProbeWindowPtr; // position pointer to the probing window
mutable pthread_mutex_t m_lockProbeWindow; // used to synchronize access to the probe window
int m_aProbeWindow[PSIZE]; // Record inter-packet time for probing packet pairs
int m_iProbeWindowPtr; // Position pointer to the probing window
mutable sync::Mutex m_lockProbeWindow; // Used to synchronize access to the probe window
int m_iLastSentTime; // last packet sending time
int m_iMinPktSndInt; // Minimum packet sending interval
int m_iLastSentTime; // Last packet sending time
int m_iMinPktSndInt; // Minimum packet sending interval
uint64_t m_LastArrTime; // last packet arrival time
uint64_t m_CurrArrTime; // current packet arrival time
uint64_t m_ProbeTime; // arrival time of the first probing packet
int32_t m_Probe1Sequence; // sequence number for which the arrival time was notified
sync::steady_clock::time_point m_tsLastArrTime; // Last packet arrival time
sync::steady_clock::time_point m_tsCurrArrTime; // Current packet arrival time
sync::steady_clock::time_point m_tsProbeTime; // Arrival time of the first probing packet
int32_t m_Probe1Sequence; // Sequence number for which the arrival time was notified
private:
CPktTimeWindow(const CPktTimeWindow&);
CPktTimeWindow &operator=(const CPktTimeWindow&);
};
} // namespace srt
#endif