commit 8a2d9073e959356808ce1685822b839d880e6498 Author: Florian Weimer Date: Fri Sep 14 17:27:35 2012 +0200 Replace most calls to select(2) with poll(2) select(2) limits the number of file descriptor in a process to FD_SETSIZE (typically 1023). Process creation and certain socket operations fail because they call select(2) on a file descriptor outside the FD_SETSIZE range. The remaining select(2) calls are used for timeouts only, or are in the traditional event loop. The glib-based event loop does not use select(2), so this should be sufficient. This change adds a poll emulation for VxWorks, which only offers select(2). Change-Id: I9b0cf5bec81da70b29c501c62d14fb57df87fa61 diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index e159bf8..bb8a3ae 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -134,13 +134,6 @@ static void qt_sa_sigchld_handler(int signum) oldAction(signum); } -static inline void add_fd(int &nfds, int fd, fd_set *fdset) -{ - FD_SET(fd, fdset); - if ((fd) > nfds) - nfds = fd; -} - struct QProcessInfo { QProcess *process; int deathPipe; @@ -235,9 +228,9 @@ QProcessManager::~QProcessManager() void QProcessManager::run() { forever { - fd_set readset; - FD_ZERO(&readset); - FD_SET(qt_qprocess_deadChild_pipe[0], &readset); + pollfd fd; + fd.fd = qt_qprocess_deadChild_pipe[0]; + fd.events = POLLIN; #if defined (QPROCESS_DEBUG) qDebug() << "QProcessManager::run() waiting for children to die"; @@ -246,8 +239,8 @@ void QProcessManager::run() // block forever, or until activity is detected on the dead child // pipe. the only other peers are the SIGCHLD signal handler, and the // QProcessManager destructor. - int nselect = select(qt_qprocess_deadChild_pipe[0] + 1, &readset, 0, 0, 0); - if (nselect < 0) { + int ret = qt_safe_poll(&fd, 1, -1, /* retry_eintr */ false); + if (ret < 0) { if (errno == EINTR) continue; break; @@ -996,17 +989,6 @@ void QProcessPrivate::killProcess() ::kill(pid_t(pid), SIGKILL); } -static int select_msecs(int nfds, fd_set *fdread, fd_set *fdwrite, int timeout) -{ - if (timeout < 0) - return qt_safe_select(nfds, fdread, fdwrite, 0, 0); - - struct timeval tv; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - return qt_safe_select(nfds, fdread, fdwrite, 0, &tv); -} - /* Returns the difference between msecs and elapsed. If msecs is -1, however, -1 is returned. @@ -1029,10 +1011,10 @@ bool QProcessPrivate::waitForStarted(int msecs) childStartedPipe[0]); #endif - fd_set fds; - FD_ZERO(&fds); - FD_SET(childStartedPipe[0], &fds); - if (select_msecs(childStartedPipe[0] + 1, &fds, 0, msecs) == 0) { + pollfd fd; + fd.fd = childStartedPipe[0]; + fd.events = POLLIN; + if (qt_safe_poll(&fd, 1, msecs) == 0) { processError = QProcess::Timedout; q->setErrorString(QProcess::tr("Process operation timed out")); #if defined (QPROCESS_DEBUG) @@ -1048,6 +1030,47 @@ bool QProcessPrivate::waitForStarted(int msecs) return startedEmitted; } +class QProcessFDSet { + pollfd fds[5]; + + static size_t size() + { + return sizeof(fds)/sizeof(fds[0]); + } + +public: + QProcessFDSet(QProcessPrivate &proc) + { + for (size_t i = 0; i < size(); ++i) { + fds[i].fd = -1; + fds[i].events = POLLIN; + } + death().fd = proc.deathPipe[0]; + + if (proc.processState == QProcess::Starting) + started().fd = proc.childStartedPipe[0]; + + stdout().fd = proc.stdoutChannel.pipe[0]; + stderr().fd = proc.stderrChannel.pipe[0]; + + if (!proc.writeBuffer.isEmpty()) { + stdin().fd = proc.stdinChannel.pipe[1]; + stdin().events = POLLOUT; + } + } + + int poll(int timeout) + { + return qt_safe_poll(fds, size(), timeout); + } + + pollfd &death() { return fds[0]; } + pollfd &started() { return fds[1]; } + pollfd &stdout() { return fds[2]; } + pollfd &stderr() { return fds[3]; } + pollfd &stdin() { return fds[4]; } +}; + bool QProcessPrivate::waitForReadyRead(int msecs) { Q_Q(QProcess); @@ -1059,28 +1082,9 @@ bool QProcessPrivate::waitForReadyRead(int msecs) stopWatch.start(); forever { - fd_set fdread; - fd_set fdwrite; - - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - - int nfds = deathPipe[0]; - FD_SET(deathPipe[0], &fdread); - - if (processState == QProcess::Starting) - add_fd(nfds, childStartedPipe[0], &fdread); - - if (stdoutChannel.pipe[0] != -1) - add_fd(nfds, stdoutChannel.pipe[0], &fdread); - if (stderrChannel.pipe[0] != -1) - add_fd(nfds, stderrChannel.pipe[0], &fdread); - - if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1) - add_fd(nfds, stdinChannel.pipe[1], &fdwrite); - + QProcessFDSet fdset(*this); int timeout = qt_timeout_value(msecs, stopWatch.elapsed()); - int ret = select_msecs(nfds + 1, &fdread, &fdwrite, timeout); + int ret = fdset.poll(timeout); if (ret < 0) { break; } @@ -1090,18 +1094,18 @@ bool QProcessPrivate::waitForReadyRead(int msecs) return false; } - if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { + if (qt_readable(fdset.started())) { if (!_q_startupNotification()) return false; } bool readyReadEmitted = false; - if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) { + if (qt_readable(fdset.stdout())) { bool canRead = _q_canReadStandardOutput(); if (processChannel == QProcess::StandardOutput && canRead) readyReadEmitted = true; } - if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) { + if (qt_readable(fdset.stderr())) { bool canRead = _q_canReadStandardError(); if (processChannel == QProcess::StandardError && canRead) readyReadEmitted = true; @@ -1109,13 +1113,13 @@ bool QProcessPrivate::waitForReadyRead(int msecs) if (readyReadEmitted) return true; - if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) + if (qt_writable(fdset.stdin())) _q_canWrite(); - if (deathPipe[0] == -1 || FD_ISSET(deathPipe[0], &fdread)) { + if (qt_readable(fdset.death())) { if (_q_processDied()) return false; - } + } } return false; } @@ -1131,29 +1135,9 @@ bool QProcessPrivate::waitForBytesWritten(int msecs) stopWatch.start(); while (!writeBuffer.isEmpty()) { - fd_set fdread; - fd_set fdwrite; - - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - - int nfds = deathPipe[0]; - FD_SET(deathPipe[0], &fdread); - - if (processState == QProcess::Starting) - add_fd(nfds, childStartedPipe[0], &fdread); - - if (stdoutChannel.pipe[0] != -1) - add_fd(nfds, stdoutChannel.pipe[0], &fdread); - if (stderrChannel.pipe[0] != -1) - add_fd(nfds, stderrChannel.pipe[0], &fdread); - - - if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1) - add_fd(nfds, stdinChannel.pipe[1], &fdwrite); - + QProcessFDSet fdset(*this); int timeout = qt_timeout_value(msecs, stopWatch.elapsed()); - int ret = select_msecs(nfds + 1, &fdread, &fdwrite, timeout); + int ret = fdset.poll(timeout); if (ret < 0) { break; } @@ -1164,24 +1148,24 @@ bool QProcessPrivate::waitForBytesWritten(int msecs) return false; } - if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { + if (qt_readable(fdset.started())) { if (!_q_startupNotification()) return false; } - if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) + if (qt_writable(fdset.stdin())) return _q_canWrite(); - if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) + if (qt_readable(fdset.stdout())) _q_canReadStandardOutput(); - if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) + if (qt_readable(fdset.stderr())) _q_canReadStandardError(); - if (deathPipe[0] == -1 || FD_ISSET(deathPipe[0], &fdread)) { - if (_q_processDied()) - return false; - } + if (qt_readable(fdset.death())) { + if (_q_processDied()) + return false; + } } return false; @@ -1198,29 +1182,9 @@ bool QProcessPrivate::waitForFinished(int msecs) stopWatch.start(); forever { - fd_set fdread; - fd_set fdwrite; - int nfds = -1; - - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - - if (processState == QProcess::Starting) - add_fd(nfds, childStartedPipe[0], &fdread); - - if (stdoutChannel.pipe[0] != -1) - add_fd(nfds, stdoutChannel.pipe[0], &fdread); - if (stderrChannel.pipe[0] != -1) - add_fd(nfds, stderrChannel.pipe[0], &fdread); - - if (processState == QProcess::Running) - add_fd(nfds, deathPipe[0], &fdread); - - if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1) - add_fd(nfds, stdinChannel.pipe[1], &fdwrite); - + QProcessFDSet fdset(*this); int timeout = qt_timeout_value(msecs, stopWatch.elapsed()); - int ret = select_msecs(nfds + 1, &fdread, &fdwrite, timeout); + int ret = fdset.poll(timeout); if (ret < 0) { break; } @@ -1230,20 +1194,20 @@ bool QProcessPrivate::waitForFinished(int msecs) return false; } - if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { + if (qt_readable(fdset.started())) { if (!_q_startupNotification()) return false; } - if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) + if (qt_writable(fdset.stdin())) _q_canWrite(); - if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) + if (qt_readable(fdset.stdout())) _q_canReadStandardOutput(); - if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) + if (qt_readable(fdset.stderr())) _q_canReadStandardError(); - if (deathPipe[0] == -1 || FD_ISSET(deathPipe[0], &fdread)) { + if (qt_readable(fdset.death())) { if (_q_processDied()) return true; } @@ -1253,10 +1217,10 @@ bool QProcessPrivate::waitForFinished(int msecs) bool QProcessPrivate::waitForWrite(int msecs) { - fd_set fdwrite; - FD_ZERO(&fdwrite); - FD_SET(stdinChannel.pipe[1], &fdwrite); - return select_msecs(stdinChannel.pipe[1] + 1, 0, &fdwrite, msecs < 0 ? 0 : msecs) == 1; + pollfd fd; + fd.fd = stdinChannel.pipe[1]; + fd.events = POLLIN; + return qt_safe_poll(&fd, 1, msecs); } void QProcessPrivate::findExitCode() diff --git a/src/corelib/kernel/qcore_unix.cpp b/src/corelib/kernel/qcore_unix.cpp index cc54798..ca178bb 100644 --- a/src/corelib/kernel/qcore_unix.cpp +++ b/src/corelib/kernel/qcore_unix.cpp @@ -103,4 +103,165 @@ int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, } } +#ifndef Q_OS_VXWORKS + +int qt_safe_poll(struct pollfd *fds, int nfds, int timeout_ms, bool retry_eintr) +{ + if (nfds == 0) + return 0; + if (nfds < 0) { + errno = EINVAL; + return -1; + } + + // Retry on ret == 0 if the deadline has not yet passed because + // Linux can return early from the syscall, without setting EINTR. + if (timeout_ms < 0) { + forever { + int ret = ::poll(fds, nfds, -1); + if (ret > 0) + return ret; + if (retry_eintr) { + if (ret == 0 || ret == -1 && errno == EINTR) { + continue; + } else { + return -1; + } + } + if (ret == 0) { + errno = EINTR; + return -1; + } + return ret; + } + } + + timeval previous = qt_gettime(); + timeval deadline = previous; + deadline.tv_sec += timeout_ms / 1000; + deadline.tv_usec += (timeout_ms % 1000) * 1000; + if (deadline.tv_usec >= 1000000) { + ++deadline.tv_sec; + deadline.tv_usec -= 1000000; + } + int remaining = timeout_ms; + + forever { + int ret = ::poll(fds, nfds, remaining); + if (ret > 0) + return ret; + timeval now = qt_gettime(); + if ((now.tv_sec > deadline.tv_sec // past deadline + || (now.tv_sec == deadline.tv_sec + && now.tv_usec >= deadline.tv_usec)) + || (now.tv_sec < previous.tv_sec // time warp + || (now.tv_sec == previous.tv_sec + && now.tv_usec < previous.tv_usec)) + || (ret < 0 && (errno != EINTR || !retry_eintr))) // other error + return ret; + if (ret == 0 && !retry_eintr) { + errno = EINTR; + return -1; + } + remaining = (deadline.tv_sec - now.tv_sec) * 1000 + + (deadline.tv_usec - now.tv_usec) / 1000; + previous = now; + } +} + +#else + +// Poll emulation for VxWorks. + +static int mark_bad_descriptors(pollfd *fds, int nfds) +{ + fd_set r; + FD_ZERO(&r); + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + int ret = 0; + + // Check each descriptor invidually for badness. + for (int i = 0; i < nfds; ++i) { + pollfd &fd(fds[i]); + if (fd.fd >= 0) { + FD_SET(fd.fd, &r); + int ret = qt_safe_select(fd.fd + 1, &r, NULL, NULL, &tv); + FD_CLR(fd.fd, &r); + if (ret < 0 && errno == EBADF) { + fd.revents = POLLNVAL; + ++ret; + } + } + } + Q_ASSERT(ret > 0); + return ret; +} + +int qt_safe_poll(pollfd *fds, int nfds, int timeout, bool retry_eintr) +{ + fd_set r, w; + FD_ZERO(&r); + FD_ZERO(&w); + int maxfd = -1; + + // Extract the watched descriptors. + for (int i = 0; i < nfds; ++i) { + pollfd &fd(fds[i]); + if (fd.fd >= 0 && fd.fd < FD_SETSIZE) { + if (fd.events & POLLIN) { + FD_SET(fd.fd, &r); + if (fd.fd > maxfd) + maxfd = fd.fd; + } + if (fd.events & POLLOUT) { + FD_SET(fd.fd, &w); + if (fd.fd > maxfd) + maxfd = fd.fd; + } + } + } + + // If timeout is negative, wait indefinitely for activity. + timeval tv; + timeval *ptv; + if (timeout >= 0) { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + ptv = &tv; + } else + ptv = NULL; + + int ret; + if (retry_eintr) + ret = qt_safe_select(maxfd + 1, &r, &w, NULL, ptv); + else + ret = ::select(maxfd + 1, &r, &w, NULL, ptv); + if (ret < 0 && errno == EBADF) { + return mark_bad_descriptors(fds, nfds); + } + if (ret <= 0) + return ret; + + // Set the revents flags. + ret = 0; + for (int i = 0; i < nfds; ++i) { + pollfd &fd(fds[i]); + fd.revents = 0; + if (fd.fd >= 0 && fd.fd < FD_SETSIZE) { + if ((fd.events & POLLIN) && FD_ISSET(fd.fd, &r)) + fd.revents |= POLLIN; + if ((fd.events & POLLOUT) && FD_ISSET(fd.fd, &w)) + fd.revents |= POLLOUT; + if (fd.revents) + ++ret; + } + } + Q_ASSERT(ret > 0); + return ret; +} + +#endif // Q_OS_VXWORKS + QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h index 6342b03..f7f4767 100644 --- a/src/corelib/kernel/qcore_unix_p.h +++ b/src/corelib/kernel/qcore_unix_p.h @@ -71,6 +71,8 @@ #if defined(Q_OS_VXWORKS) # include +#else +# include #endif struct sockaddr; @@ -341,6 +343,36 @@ void qt_nanosleep(timespec amount); Q_CORE_EXPORT int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, const struct timeval *tv); +#ifdef Q_OS_VXWORKS +// Poll emulation for VxWorks. Provided by on other systems. + +struct pollfd { + int fd; + short events; + short revents; +}; + +#define POLLIN 1 +#define POLLOUT 2 +#define POLLERR 4 +#define POLLHUP 8 +#define POLLNVAL 16 +#endif + +inline bool qt_readable(const pollfd &fd) +{ + return fd.fd >= 0 && (fd.revents & (POLLIN | POLLHUP | POLLERR | POLLNVAL)) != 0; +} + +inline bool qt_writable(const pollfd &fd) +{ + return fd.fd >= 0 && (fd.revents & (POLLOUT | POLLHUP | POLLERR | POLLNVAL)) != 0; +} + +// Deprecated due to FD_SETSIZE limitation, use qt_safe_poll instead. +Q_CORE_EXPORT int qt_safe_poll(pollfd *fds, int nfds, int timeout, + bool retry_eintr = true); + // according to X/OPEN we have to define semun ourselves // we use prefix as on some systems sem.h will have it struct semid_ds; diff --git a/src/network/socket/qlocalserver_unix.cpp b/src/network/socket/qlocalserver_unix.cpp index 2bcf1ac..efb8128 100644 --- a/src/network/socket/qlocalserver_unix.cpp +++ b/src/network/socket/qlocalserver_unix.cpp @@ -293,16 +293,11 @@ void QLocalServerPrivate::_q_onNewConnection() void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut) { - fd_set readfds; - FD_ZERO(&readfds); - FD_SET(listenSocket, &readfds); + struct pollfd fd; + fd.fd = listenSocket; + fd.events = POLLIN; - timeval timeout; - timeout.tv_sec = msec / 1000; - timeout.tv_usec = (msec % 1000) * 1000; - - int result = -1; - result = qt_safe_select(listenSocket + 1, &readfds, 0, 0, (msec == -1) ? 0 : &timeout); + int result = qt_safe_poll(&fd, 1, msec); if (-1 == result) { setError(QLatin1String("QLocalServer::waitForNewConnection")); closeServer(); diff --git a/src/network/socket/qlocalsocket_unix.cpp b/src/network/socket/qlocalsocket_unix.cpp index 49eb71a..c598c2b 100644 --- a/src/network/socket/qlocalsocket_unix.cpp +++ b/src/network/socket/qlocalsocket_unix.cpp @@ -56,10 +56,6 @@ #include #include -#ifdef Q_OS_VXWORKS -# include -#endif - #define QT_CONNECT_TIMEOUT 30000 QT_BEGIN_NAMESPACE @@ -524,25 +520,16 @@ bool QLocalSocket::waitForConnected(int msec) if (state() != ConnectingState) return (state() == ConnectedState); - fd_set fds; - FD_ZERO(&fds); - FD_SET(d->connectingSocket, &fds); - - timeval timeout; - timeout.tv_sec = msec / 1000; - timeout.tv_usec = (msec % 1000) * 1000; + pollfd fd; + fd.fd = d->connectingSocket; + fd.events = POLLIN | POLLOUT; - // timeout can not be 0 or else select will return an error. - if (0 == msec) - timeout.tv_usec = 1000; - - int result = -1; - // on Linux timeout will be updated by select, but _not_ on other systems. + int result; QElapsedTimer timer; + int remaining = msec > 0 ? msec : 1000; timer.start(); - while (state() == ConnectingState - && (-1 == msec || timer.elapsed() < msec)) { - result = ::select(d->connectingSocket + 1, &fds, 0, 0, &timeout); + while (state() == ConnectingState) { + result = qt_safe_poll(&fd, 1, remaining, /* retry_eintr */ false); if (-1 == result && errno != EINTR) { d->errorOccurred( QLocalSocket::UnknownSocketError, QLatin1String("QLocalSocket::waitForConnected")); @@ -550,6 +537,11 @@ bool QLocalSocket::waitForConnected(int msec) } if (result > 0) d->_q_connectToSocket(); + if (msec >= 0) { + remaining = timer.elapsed() - msec; + if (remaining < 0) + break; + } } return (state() == ConnectedState); diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 4f3408b..a1bb298 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -1122,48 +1122,40 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize) int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) const { - fd_set fds; - FD_ZERO(&fds); - FD_SET(socketDescriptor, &fds); - - struct timeval tv; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - - int retval; - if (selectForRead) - retval = qt_safe_select(socketDescriptor + 1, &fds, 0, 0, timeout < 0 ? 0 : &tv); - else - retval = qt_safe_select(socketDescriptor + 1, 0, &fds, 0, timeout < 0 ? 0 : &tv); - - return retval; + struct pollfd fd; + fd.fd = socketDescriptor; + if (selectForRead) { + fd.events = POLLIN; + } else { + fd.events = POLLOUT; + } + return qt_safe_poll(&fd, 1, timeout); } int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite, bool *selectForRead, bool *selectForWrite) const { - fd_set fdread; - FD_ZERO(&fdread); + struct pollfd fd; + fd.fd = socketDescriptor; if (checkRead) - FD_SET(socketDescriptor, &fdread); - - fd_set fdwrite; - FD_ZERO(&fdwrite); + fd.events = POLLIN; + else + fd.events = 0; if (checkWrite) - FD_SET(socketDescriptor, &fdwrite); - - struct timeval tv; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - - int ret; - ret = qt_safe_select(socketDescriptor + 1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv); - + fd.events |= POLLOUT; + int ret = qt_safe_poll(&fd, 1, timeout); if (ret <= 0) - return ret; - *selectForRead = FD_ISSET(socketDescriptor, &fdread); - *selectForWrite = FD_ISSET(socketDescriptor, &fdwrite); - + return ret; + bool r = (fd.revents & (POLLIN | POLLHUP | POLLERR)) != 0; + bool w = (fd.revents & (POLLOUT | POLLHUP | POLLERR)) != 0; + // Emulate the return value from select(2). + ret = 0; + if (r) + ++ret; + if (w) + ++ret; + *selectForRead = r; + *selectForWrite = w; return ret; }