mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2026-02-16 02:21:07 +00:00
Use SO_RCVTIMEO to avoid the overhead of an extra syscall per packet
This commit is contained in:
@@ -151,9 +151,19 @@ static void ReceiveThreadProc(void* context) {
|
||||
PRTP_PACKET rtp;
|
||||
PQUEUED_AUDIO_PACKET packet;
|
||||
int queueStatus;
|
||||
int useSelect;
|
||||
|
||||
packet = NULL;
|
||||
|
||||
if (setNonFatalRecvTimeoutMs(rtpSocket, UDP_RECV_POLL_TIMEOUT_MS) < 0) {
|
||||
// SO_RCVTIMEO failed, so use select() to wait
|
||||
useSelect = 1;
|
||||
}
|
||||
else {
|
||||
// SO_RCVTIMEO timeout set for recv()
|
||||
useSelect = 0;
|
||||
}
|
||||
|
||||
while (!PltIsThreadInterrupted(&receiveThread)) {
|
||||
if (packet == NULL) {
|
||||
packet = (PQUEUED_AUDIO_PACKET)malloc(sizeof(*packet));
|
||||
@@ -164,7 +174,7 @@ static void ReceiveThreadProc(void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
packet->size = recvUdpSocket(rtpSocket, &packet->data[0], MAX_PACKET_SIZE);
|
||||
packet->size = recvUdpSocket(rtpSocket, &packet->data[0], MAX_PACKET_SIZE, useSelect);
|
||||
if (packet->size < 0) {
|
||||
Limelog("Audio Receive: recvUdpSocket() failed: %d\n", (int)LastSocketError());
|
||||
ListenerCallbacks.connectionTerminated(LastSocketError());
|
||||
|
||||
@@ -34,6 +34,8 @@ extern int HighQualitySurroundEnabled;
|
||||
#define isBefore24(x, y) (U24((x) - (y)) > (UINT24_MAX/2))
|
||||
#define isBefore32(x, y) (U32((x) - (y)) > (UINT32_MAX/2))
|
||||
|
||||
#define UDP_RECV_POLL_TIMEOUT_MS 100
|
||||
|
||||
int serviceEnetHost(ENetHost* client, ENetEvent* event, enet_uint32 timeoutMs);
|
||||
int extractVersionQuadFromString(const char* string, int* quad);
|
||||
int isReferenceFrameInvalidationEnabled(void);
|
||||
|
||||
@@ -30,6 +30,22 @@ void shutdownTcpSocket(SOCKET s) {
|
||||
shutdown(s, SHUT_RDWR);
|
||||
}
|
||||
|
||||
int setNonFatalRecvTimeoutMs(SOCKET s, int timeoutMs) {
|
||||
#if defined(LC_WINDOWS)
|
||||
// Windows says that SO_RCVTIMEO puts the socket
|
||||
// into an indeterminate state, so we won't use
|
||||
// it for non-fatal socket operations.
|
||||
return -1;
|
||||
#else
|
||||
struct timeval val;
|
||||
|
||||
val.tv_sec = 0;
|
||||
val.tv_usec = timeoutMs * 1000;
|
||||
|
||||
return setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&val, sizeof(val));
|
||||
#endif
|
||||
}
|
||||
|
||||
void setRecvTimeout(SOCKET s, int timeoutSec) {
|
||||
#if defined(LC_WINDOWS)
|
||||
int val = timeoutSec * 1000;
|
||||
@@ -44,26 +60,40 @@ void setRecvTimeout(SOCKET s, int timeoutSec) {
|
||||
}
|
||||
}
|
||||
|
||||
int recvUdpSocket(SOCKET s, char* buffer, int size) {
|
||||
int recvUdpSocket(SOCKET s, char* buffer, int size, int useSelect) {
|
||||
fd_set readfds;
|
||||
int err;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(s, &readfds);
|
||||
|
||||
// Wait up to 100 ms for the socket to be readable
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 100 * 1000;
|
||||
|
||||
err = select((int)(s) + 1, &readfds, NULL, NULL, &tv);
|
||||
if (err <= 0) {
|
||||
// Return if an error or timeout occurs
|
||||
if (useSelect) {
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(s, &readfds);
|
||||
|
||||
// Wait up to 100 ms for the socket to be readable
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = UDP_RECV_POLL_TIMEOUT_MS * 1000;
|
||||
|
||||
err = select((int)(s) + 1, &readfds, NULL, NULL, &tv);
|
||||
if (err <= 0) {
|
||||
// Return if an error or timeout occurs
|
||||
return err;
|
||||
}
|
||||
|
||||
// This won't block since the socket is readable
|
||||
return (int)recv(s, buffer, size, 0);
|
||||
}
|
||||
else {
|
||||
// The caller has already configured a timeout on this
|
||||
// socket via SO_RCVTIMEO, so we can avoid a syscall
|
||||
// for each packet.
|
||||
err = (int)recv(s, buffer, size, 0);
|
||||
if (err < 0 && LastSocketError() == EWOULDBLOCK) {
|
||||
// Return 0 for timeout
|
||||
return 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// This won't block since the socket is readable
|
||||
return (int)recv(s, buffer, size, 0);
|
||||
}
|
||||
|
||||
void closeSocket(SOCKET s) {
|
||||
@@ -207,11 +237,7 @@ SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen,
|
||||
// select() timed out
|
||||
Limelog("select() timed out after %d seconds\n", timeoutSec);
|
||||
closeSocket(s);
|
||||
#if defined(LC_WINDOWS)
|
||||
SetLastSocketError(WSAEWOULDBLOCK);
|
||||
#else
|
||||
SetLastSocketError(EWOULDBLOCK);
|
||||
#endif
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
else if (FD_ISSET(s, &writefds) || FD_ISSET(s, &exceptfds)) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#define LastSocketError() WSAGetLastError()
|
||||
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
|
||||
typedef int SOCK_RET;
|
||||
typedef int SOCKADDR_LEN;
|
||||
@@ -67,7 +68,8 @@ void addrToUrlSafeString(struct sockaddr_storage* addr, char* string);
|
||||
SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, unsigned short port, int timeoutSec);
|
||||
SOCKET bindUdpSocket(int addrfamily, int bufferSize);
|
||||
int enableNoDelay(SOCKET s);
|
||||
int recvUdpSocket(SOCKET s, char* buffer, int size);
|
||||
int recvUdpSocket(SOCKET s, char* buffer, int size, int useSelect);
|
||||
void shutdownTcpSocket(SOCKET s);
|
||||
int setNonFatalRecvTimeoutMs(SOCKET s, int timeoutMs);
|
||||
void setRecvTimeout(SOCKET s, int timeoutSec);
|
||||
void closeSocket(SOCKET s);
|
||||
|
||||
@@ -65,12 +65,22 @@ static void ReceiveThreadProc(void* context) {
|
||||
int bufferSize, receiveSize;
|
||||
char* buffer;
|
||||
int queueStatus;
|
||||
int useSelect;
|
||||
PRTPFEC_QUEUE_ENTRY queueEntry;
|
||||
|
||||
receiveSize = StreamConfig.packetSize + MAX_RTP_HEADER_SIZE;
|
||||
bufferSize = receiveSize + sizeof(RTPFEC_QUEUE_ENTRY);
|
||||
buffer = NULL;
|
||||
|
||||
if (setNonFatalRecvTimeoutMs(rtpSocket, UDP_RECV_POLL_TIMEOUT_MS) < 0) {
|
||||
// SO_RCVTIMEO failed, so use select() to wait
|
||||
useSelect = 1;
|
||||
}
|
||||
else {
|
||||
// SO_RCVTIMEO timeout set for recv()
|
||||
useSelect = 0;
|
||||
}
|
||||
|
||||
while (!PltIsThreadInterrupted(&receiveThread)) {
|
||||
PRTP_PACKET packet;
|
||||
|
||||
@@ -83,7 +93,7 @@ static void ReceiveThreadProc(void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
err = recvUdpSocket(rtpSocket, buffer, receiveSize);
|
||||
err = recvUdpSocket(rtpSocket, buffer, receiveSize, useSelect);
|
||||
if (err < 0) {
|
||||
Limelog("Video Receive: recvUdpSocket() failed: %d\n", (int)LastSocketError());
|
||||
ListenerCallbacks.connectionTerminated(LastSocketError());
|
||||
|
||||
Reference in New Issue
Block a user