Implemented UDP socket packet send pacing, WebRTC now makes use of this new feature.

This commit is contained in:
Thulinma 2023-06-15 12:34:25 +02:00
parent a1232d56af
commit 2a8f2f75d3
8 changed files with 83 additions and 12 deletions

View file

@ -1608,6 +1608,7 @@ int Socket::Server::getSocket(){
/// If both fail, prints an DLVL_FAIL debug message.
/// \param nonblock Whether the socket should be nonblocking.
Socket::UDPConnection::UDPConnection(bool nonblock){
lastPace = 0;
boundPort = 0;
family = AF_INET6;
sock = socket(AF_INET6, SOCK_DGRAM, 0);
@ -1680,6 +1681,7 @@ void Socket::UDPConnection::checkRecvBuf(){
/// Copies a UDP socket, re-allocating local copies of any needed structures.
/// The data/data_size/data_len variables are *not* copied over.
Socket::UDPConnection::UDPConnection(const UDPConnection &o){
lastPace = 0;
boundPort = 0;
family = AF_INET6;
sock = socket(AF_INET6, SOCK_DGRAM, 0);
@ -1892,6 +1894,53 @@ void Socket::UDPConnection::SendNow(const char *sdata, size_t len){
}
}
void Socket::UDPConnection::sendPaced(const char *sdata, size_t len){
if (!paceQueue.size() && (!lastPace || Util::getMicros(lastPace) > 10000)){
SendNow(sdata, len);
lastPace = Util::getMicros();
}else{
paceQueue.push_back(Util::ResizeablePointer());
paceQueue.back().assign(sdata, len);
// Try to send a packet, if time allows
//sendPaced(0);
}
}
/// Spends uSendWindow microseconds either sending paced packets or sleeping, whichever is more appropriate
void Socket::UDPConnection::sendPaced(uint64_t uSendWindow){
uint64_t currPace = Util::getMicros();
do{
uint64_t uTime = Util::getMicros();
uint64_t sleepTime = uTime - currPace;
if (sleepTime > uSendWindow){
sleepTime = 0;
}else{
sleepTime = uSendWindow - sleepTime;
}
uint64_t paceWait = uTime - lastPace;
size_t qSize = paceQueue.size();
// If the queue is complete, wait out the remainder of the time
if (!qSize){
Util::usleep(sleepTime);
return;
}
// Otherwise, target clearing the queue in 25ms at most.
uint64_t targetTime = 25000 / qSize;
// If this slows us to below 1 packet per 5ms, go that speed instead.
if (targetTime > 5000){targetTime = 5000;}
// If the wait is over, send now.
if (paceWait >= targetTime){
SendNow(*paceQueue.begin(), paceQueue.begin()->size());
paceQueue.pop_front();
lastPace = uTime;
continue;
}
// Otherwise, wait for the smaller of remaining wait time or remaining send window time.
if (targetTime - paceWait < sleepTime){sleepTime = targetTime - paceWait;}
Util::usleep(sleepTime);
}while(Util::getMicros(currPace) < uSendWindow);
}
std::string Socket::UDPConnection::getBoundAddress(){
std::string boundaddr;
uint32_t boundport;

View file

@ -206,6 +206,8 @@ namespace Socket{
std::string boundAddr, boundMulti;
int boundPort;
void checkRecvBuf();
std::deque<Util::ResizeablePointer> paceQueue;
uint64_t lastPace;
public:
Util::ResizeablePointer data;
@ -228,6 +230,8 @@ namespace Socket{
void SendNow(const std::string &data);
void SendNow(const char *data);
void SendNow(const char *data, size_t len);
void sendPaced(const char * data, size_t len);
void sendPaced(uint64_t uSendWindow);
void setSocketFamily(int AF_TYPE);
};
}// namespace Socket

View file

@ -55,6 +55,20 @@ void Util::sleep(int64_t ms){
nanosleep(&T, 0);
}
/// Sleeps for roughly the indicated amount of microseconds.
/// Will not sleep if ms is negative.
/// Will not sleep for longer than 0.1 seconds (100000us).
/// Can be interrupted early by a signal, no guarantee of minimum sleep time.
/// Can be slightly off depending on OS accuracy.
void Util::usleep(int64_t us){
if (us < 0){return;}
if (us > 100000){us = 100000;}
struct timespec T;
T.tv_sec = 0;
T.tv_nsec = 1000 * us;
nanosleep(&T, 0);
}
uint64_t Util::getNTP(){
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);

View file

@ -8,6 +8,7 @@
namespace Util{
void wait(int64_t ms); ///< Sleeps for the indicated amount of milliseconds or longer.
void sleep(int64_t ms); ///< Sleeps for roughly the indicated amount of milliseconds.
void usleep(int64_t us); ///< Sleeps for roughly the indicated amount of microseconds.
uint64_t getMS(); ///< Gets the current time in milliseconds.
uint64_t bootSecs(); ///< Gets the current system uptime in seconds.
uint64_t unixMS(); ///< Gets the current Unix time in milliseconds.