From 5e38a007c7b4b86166ae5edb400dbd04af59eb64 Mon Sep 17 00:00:00 2001 From: Spoike Date: Sun, 9 May 2021 13:02:23 +0000 Subject: [PATCH] Switch to poll instead of select, to avoid crashes from select's arbitrary fd limit. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5848 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/m_download.c | 16 +++++------ engine/common/net_wins.c | 58 +++++++++++++++++++++++++------------- engine/http/httpclient.c | 14 +++++++++ fteqtv/control.c | 21 ++++++++++---- 4 files changed, 75 insertions(+), 34 deletions(-) diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 583ae0cd..4113d2c1 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -3221,6 +3221,7 @@ static qboolean QDECL SHA1File_Close (struct vfsfile_s *file) hashfile_t *f = (hashfile_t*)file; if (!VFS_CLOSE(f->f)) f->fail = true; //something went wrong. + f->f = NULL; f->hashfunc->terminate(digest, &f->ctx); if (f->fail) @@ -3434,10 +3435,9 @@ static void PM_StartADownload(void) #ifdef AVAIL_XZDEC case EXTRACT_XZ: { - vfsfile_t *raw; - raw = FS_OpenVFS(temp, "wb", temproot); - tmpfile = FS_XZ_DecompressWriteFilter(raw); - if (!tmpfile) + vfsfile_t *raw = FS_OpenVFS(temp, "wb", temproot); + tmpfile = raw?FS_XZ_DecompressWriteFilter(raw):NULL; + if (!tmpfile && raw) VFS_CLOSE(raw); } break; @@ -3445,10 +3445,9 @@ static void PM_StartADownload(void) #ifdef AVAIL_GZDEC case EXTRACT_GZ: { - vfsfile_t *raw; - raw = FS_OpenVFS(temp, "wb", temproot); - tmpfile = FS_GZ_WriteFilter(raw, true, false); - if (!tmpfile) + vfsfile_t *raw = FS_OpenVFS(temp, "wb", temproot); + tmpfile = raw?FS_GZ_WriteFilter(raw, true, false):NULL; + if (!tmpfile && raw) VFS_CLOSE(raw); } break; @@ -3474,6 +3473,7 @@ static void PM_StartADownload(void) char syspath[MAX_OSPATH]; FS_NativePath(temp, temproot, syspath, sizeof(syspath)); Con_Printf("Unable to write %s. Fix permissions before trying to download %s\n", syspath, p->name); + p->trymirrors = 0; //don't bother trying other mirrors if we can't write the file or understand its type. } if (p->download) { diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 3b7495b4..6523b376 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -8859,6 +8859,42 @@ void NET_Shutdown (void) #ifdef HAVE_TCP +#ifdef HAVE_EPOLL +#include +#endif +static qboolean VFSTCP_IsStillConnecting(SOCKET sock) +{ +#ifdef HAVE_EPOLL + //poll has no arbitrary fd limit. use it instead of select where possible. + struct pollfd ourfd[1]; + ourfd[0].fd = sock; + ourfd[0].events = POLLOUT; + ourfd[0].revents = 0; + if (!poll(ourfd, countof(ourfd), 0)) + return true; //no events yet. +#else + //okay on windows where sock+1 is ignored, has issues when lots of other fds are already open (for any reason). + fd_set fdw, fdx; + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + FD_ZERO(&fdw); + FD_SET(sock, &fdw); + FD_ZERO(&fdx); + FD_SET(sock, &fdx); + //check if we can actually write to it yet, without generating weird errors... + if (!select((int)sock+1, NULL, &fdw, &fdx, &timeout)) + return true; +#endif + + //if we get here then its writable(read: connected) or failed. + +// int error = NET_ENOTCONN; +// socklen_t sz = sizeof(error); +// if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &sz)) +// error = NET_ENOTCONN; + return false; +} typedef struct { vfsfile_t funcs; @@ -8879,16 +8915,7 @@ int QDECL VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestorea if (tf->conpending) { - fd_set wr; - fd_set ex; - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - FD_ZERO(&wr); - FD_SET(tf->sock, &wr); - FD_ZERO(&ex); - FD_SET(tf->sock, &ex); - if (!select((int)tf->sock+1, NULL, &wr, &ex, &timeout)) + if (VFSTCP_IsStillConnecting(tf->sock)) return 0; tf->conpending = false; } @@ -8976,16 +9003,7 @@ int QDECL VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int byt if (tf->conpending) { - fd_set fdw, fdx; - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - FD_ZERO(&fdw); - FD_SET(tf->sock, &fdw); - FD_ZERO(&fdx); - FD_SET(tf->sock, &fdx); - //check if we can actually write to it yet, without generating weird errors... - if (!select((int)tf->sock+1, NULL, &fdw, &fdx, &timeout)) + if (VFSTCP_IsStillConnecting(tf->sock)) return 0; tf->conpending = false; } diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index 81a65091..74632804 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -532,6 +532,9 @@ static int VFSError_To_HTTP(int vfserr) } } +#ifdef HAVE_EPOLL +#include +#endif static qboolean HTTP_DL_Work(struct dl_download *dl) { struct http_dl_ctx_s *con = dl->ctx; @@ -547,6 +550,16 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) //if we're running in a thread, wait for some actual activity instead of busylooping like an moron. if (dl->threadctx) { +#ifdef HAVE_EPOLL + struct pollfd fd; + fd.fd = con->sock; + fd.events = POLLIN; + fd.revents = 0; + if (con->state == HC_REQUESTING) + fd.events |= POLLOUT; + poll(&fd, 1, 0.1*1000); //wake up when we can read OR write + //note that https should wake up more often, but we don't want to wake up because we *can* write when we're reading without any need to write. +#else struct timeval timeout; fd_set rdset, wrset; FD_ZERO(&wrset); @@ -560,6 +573,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) else select(con->sock+1, &rdset, NULL, NULL, &timeout); //wake when we can read. //note that https should wake up more often, but we don't want to wake up because we *can* write when we're reading without any need to write. +#endif } #endif diff --git a/fteqtv/control.c b/fteqtv/control.c index 49d995b9..ea40a939 100644 --- a/fteqtv/control.c +++ b/fteqtv/control.c @@ -215,26 +215,35 @@ void Cluster_Run(cluster_t *cluster, qboolean dowait) if (dowait) { + //FIXME: use poll or epoll to work around FD_SETSIZE limits, though we're mostly only doing this for the sleeping. FD_ZERO(&socketset); m = 0; if (cluster->qwdsocket[0] != INVALID_SOCKET) { - FD_SET(cluster->qwdsocket[0], &socketset); - if (cluster->qwdsocket[0] >= m) - m = cluster->qwdsocket[0]+1; + if (cluster->qwdsocket[0] < FD_SETSIZE) + { + FD_SET(cluster->qwdsocket[0], &socketset); + if (cluster->qwdsocket[0] >= m) + m = cluster->qwdsocket[0]+1; + } } if (cluster->qwdsocket[1] != INVALID_SOCKET) { - FD_SET(cluster->qwdsocket[1], &socketset); - if (cluster->qwdsocket[1] >= m) - m = cluster->qwdsocket[1]+1; + if (cluster->qwdsocket[1] < FD_SETSIZE) + { + FD_SET(cluster->qwdsocket[1], &socketset); + if (cluster->qwdsocket[1] >= m) + m = cluster->qwdsocket[1]+1; + } } for (sv = cluster->servers; sv; sv = sv->next) { if (sv->usequakeworldprotocols && sv->sourcesock != INVALID_SOCKET) { + if (sv->sourcesock >= FD_SETSIZE) + continue; //panic... FD_SET(sv->sourcesock, &socketset); if (sv->sourcesock >= m) m = sv->sourcesock+1;