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
This commit is contained in:
Spoike 2021-05-09 13:02:23 +00:00
parent a13e5cfbcf
commit 5e38a007c7
4 changed files with 75 additions and 34 deletions

View file

@ -3221,6 +3221,7 @@ static qboolean QDECL SHA1File_Close (struct vfsfile_s *file)
hashfile_t *f = (hashfile_t*)file; hashfile_t *f = (hashfile_t*)file;
if (!VFS_CLOSE(f->f)) if (!VFS_CLOSE(f->f))
f->fail = true; //something went wrong. f->fail = true; //something went wrong.
f->f = NULL;
f->hashfunc->terminate(digest, &f->ctx); f->hashfunc->terminate(digest, &f->ctx);
if (f->fail) if (f->fail)
@ -3434,10 +3435,9 @@ static void PM_StartADownload(void)
#ifdef AVAIL_XZDEC #ifdef AVAIL_XZDEC
case EXTRACT_XZ: case EXTRACT_XZ:
{ {
vfsfile_t *raw; vfsfile_t *raw = FS_OpenVFS(temp, "wb", temproot);
raw = FS_OpenVFS(temp, "wb", temproot); tmpfile = raw?FS_XZ_DecompressWriteFilter(raw):NULL;
tmpfile = FS_XZ_DecompressWriteFilter(raw); if (!tmpfile && raw)
if (!tmpfile)
VFS_CLOSE(raw); VFS_CLOSE(raw);
} }
break; break;
@ -3445,10 +3445,9 @@ static void PM_StartADownload(void)
#ifdef AVAIL_GZDEC #ifdef AVAIL_GZDEC
case EXTRACT_GZ: case EXTRACT_GZ:
{ {
vfsfile_t *raw; vfsfile_t *raw = FS_OpenVFS(temp, "wb", temproot);
raw = FS_OpenVFS(temp, "wb", temproot); tmpfile = raw?FS_GZ_WriteFilter(raw, true, false):NULL;
tmpfile = FS_GZ_WriteFilter(raw, true, false); if (!tmpfile && raw)
if (!tmpfile)
VFS_CLOSE(raw); VFS_CLOSE(raw);
} }
break; break;
@ -3474,6 +3473,7 @@ static void PM_StartADownload(void)
char syspath[MAX_OSPATH]; char syspath[MAX_OSPATH];
FS_NativePath(temp, temproot, syspath, sizeof(syspath)); FS_NativePath(temp, temproot, syspath, sizeof(syspath));
Con_Printf("Unable to write %s. Fix permissions before trying to download %s\n", syspath, p->name); 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) if (p->download)
{ {

View file

@ -8859,6 +8859,42 @@ void NET_Shutdown (void)
#ifdef HAVE_TCP #ifdef HAVE_TCP
#ifdef HAVE_EPOLL
#include <poll.h>
#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 { typedef struct {
vfsfile_t funcs; vfsfile_t funcs;
@ -8879,16 +8915,7 @@ int QDECL VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestorea
if (tf->conpending) if (tf->conpending)
{ {
fd_set wr; if (VFSTCP_IsStillConnecting(tf->sock))
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))
return 0; return 0;
tf->conpending = false; tf->conpending = false;
} }
@ -8976,16 +9003,7 @@ int QDECL VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int byt
if (tf->conpending) if (tf->conpending)
{ {
fd_set fdw, fdx; if (VFSTCP_IsStillConnecting(tf->sock))
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))
return 0; return 0;
tf->conpending = false; tf->conpending = false;
} }

View file

@ -532,6 +532,9 @@ static int VFSError_To_HTTP(int vfserr)
} }
} }
#ifdef HAVE_EPOLL
#include <poll.h>
#endif
static qboolean HTTP_DL_Work(struct dl_download *dl) static qboolean HTTP_DL_Work(struct dl_download *dl)
{ {
struct http_dl_ctx_s *con = dl->ctx; 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 we're running in a thread, wait for some actual activity instead of busylooping like an moron.
if (dl->threadctx) 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; struct timeval timeout;
fd_set rdset, wrset; fd_set rdset, wrset;
FD_ZERO(&wrset); FD_ZERO(&wrset);
@ -560,6 +573,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl)
else else
select(con->sock+1, &rdset, NULL, NULL, &timeout); //wake when we can read. 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. //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 #endif

View file

@ -215,26 +215,35 @@ void Cluster_Run(cluster_t *cluster, qboolean dowait)
if (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); FD_ZERO(&socketset);
m = 0; m = 0;
if (cluster->qwdsocket[0] != INVALID_SOCKET) if (cluster->qwdsocket[0] != INVALID_SOCKET)
{
if (cluster->qwdsocket[0] < FD_SETSIZE)
{ {
FD_SET(cluster->qwdsocket[0], &socketset); FD_SET(cluster->qwdsocket[0], &socketset);
if (cluster->qwdsocket[0] >= m) if (cluster->qwdsocket[0] >= m)
m = cluster->qwdsocket[0]+1; m = cluster->qwdsocket[0]+1;
} }
}
if (cluster->qwdsocket[1] != INVALID_SOCKET) if (cluster->qwdsocket[1] != INVALID_SOCKET)
{
if (cluster->qwdsocket[1] < FD_SETSIZE)
{ {
FD_SET(cluster->qwdsocket[1], &socketset); FD_SET(cluster->qwdsocket[1], &socketset);
if (cluster->qwdsocket[1] >= m) if (cluster->qwdsocket[1] >= m)
m = cluster->qwdsocket[1]+1; m = cluster->qwdsocket[1]+1;
} }
}
for (sv = cluster->servers; sv; sv = sv->next) for (sv = cluster->servers; sv; sv = sv->next)
{ {
if (sv->usequakeworldprotocols && sv->sourcesock != INVALID_SOCKET) if (sv->usequakeworldprotocols && sv->sourcesock != INVALID_SOCKET)
{ {
if (sv->sourcesock >= FD_SETSIZE)
continue; //panic...
FD_SET(sv->sourcesock, &socketset); FD_SET(sv->sourcesock, &socketset);
if (sv->sourcesock >= m) if (sv->sourcesock >= m)
m = sv->sourcesock+1; m = sv->sourcesock+1;