Expand BSD sockets poll tests and remove legacy-poll code (#7414)
* Move integ test to corredt place and re-enable tests * Depend on `poll.h` (removes support for non-POSIX systems) * Only build tests for each arch * Move back to unit tests (poll is mocked) * Better error message for valgrind not found * Simplify dependency injection for BSD sockets poll test * Improve test readability for BSD net poll * Split out 2-in-1 test for `isAnyAddr` * Stub out sleep function * Improve coverage for pollSocket * Use gmock ON_CALL instead of manual mock * Remove unused function signature * Use conventional deps struct instead of std functional * Add test for socket data FD set to -1 * Add assertation for adding unblock pipe * Use older style array alloc * Less precision around `getNetworkDataForThread` value * Use `ssize_t` for `ignore` * Remove unused var * Update ChangeLog
This commit is contained in:
parent
cdb6dcd597
commit
b02077550c
9 changed files with 301 additions and 283 deletions
|
|
@ -60,6 +60,7 @@ Enhancements:
|
|||
- #7407 Implement safer memory use, improve dev env, fixed GUI bugs
|
||||
- #7412 Reduce GUI compile time by building a GUI library
|
||||
- #7413 Improve UI design and reduce over-use of `#ifdef`
|
||||
- #7414 Expand BSD sockets poll tests and remove legacy-poll code
|
||||
|
||||
# 1.14.6
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ macro(configure_unix_libs)
|
|||
check_function_exists(getpwuid_r HAVE_GETPWUID_R)
|
||||
check_function_exists(gmtime_r HAVE_GMTIME_R)
|
||||
check_function_exists(nanosleep HAVE_NANOSLEEP)
|
||||
check_function_exists(poll HAVE_POLL)
|
||||
check_function_exists(sigwait HAVE_POSIX_SIGWAIT)
|
||||
check_function_exists(strftime HAVE_STRFTIME)
|
||||
check_function_exists(vsnprintf HAVE_VSNPRINTF)
|
||||
|
|
|
|||
|
|
@ -49,9 +49,6 @@
|
|||
/* Define to 1 if you have the <ostream> header file. */
|
||||
#cmakedefine HAVE_OSTREAM ${HAVE_OSTREAM}
|
||||
|
||||
/* Define if you have the `poll` function. */
|
||||
#cmakedefine HAVE_POLL ${HAVE_POLL}
|
||||
|
||||
/* Define if you have a POSIX `sigwait` function. */
|
||||
#cmakedefine HAVE_POSIX_SIGWAIT ${HAVE_POSIX_SIGWAIT}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ env.ensure_in_venv(__file__)
|
|||
|
||||
import argparse, os, sys
|
||||
import lib.cmd_utils as cmd_utils
|
||||
import lib.colors as colors
|
||||
|
||||
valgrind_bin = "valgrind"
|
||||
|
||||
|
||||
def main():
|
||||
|
|
@ -39,7 +42,11 @@ def main():
|
|||
command = [binary]
|
||||
|
||||
if args.valgrind:
|
||||
command = ["valgrind"] + command
|
||||
if not cmd_utils.has_command(valgrind_bin):
|
||||
print(f"{colors.ERROR_TEXT} {valgrind_bin} not found")
|
||||
sys.exit(1)
|
||||
|
||||
command = [valgrind_bin] + command
|
||||
|
||||
result = cmd_utils.run(command, print_cmd=True, check=False)
|
||||
if not args.ignore_return_code:
|
||||
|
|
|
|||
|
|
@ -22,18 +22,20 @@
|
|||
#include "arch/unix/ArchMultithreadPosix.h"
|
||||
#include "arch/unix/XArchUnix.h"
|
||||
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#if !defined(TCP_NODELAY)
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
#include <arpa/inet.h>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if !defined(TCP_NODELAY)
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
|
||||
#if !HAVE_INET_ATON
|
||||
#include <stdio.h>
|
||||
|
|
@ -44,8 +46,11 @@ static const int s_family[] = {
|
|||
PF_INET,
|
||||
PF_INET6,
|
||||
};
|
||||
|
||||
static const int s_type[] = {SOCK_DGRAM, SOCK_STREAM};
|
||||
|
||||
ArchNetworkBSD::Deps ArchNetworkBSD::s_deps;
|
||||
|
||||
#if !HAVE_INET_ATON
|
||||
// parse dotted quad addresses. we don't bother with the weird BSD'ism
|
||||
// of handling octal and hex and partial forms.
|
||||
|
|
@ -67,12 +72,35 @@ static in_addr_t inet_aton(const char *cp, struct in_addr *inp) {
|
|||
#endif
|
||||
|
||||
//
|
||||
// ArchNetworkBSD
|
||||
// ArchNetworkBSD::Deps
|
||||
//
|
||||
|
||||
ArchNetworkBSD::Connectors ArchNetworkBSD::s_connectors;
|
||||
void ArchNetworkBSD::Deps::sleep(double seconds) {
|
||||
//
|
||||
ARCH->sleep(seconds);
|
||||
}
|
||||
|
||||
ArchNetworkBSD::ArchNetworkBSD() = default;
|
||||
int ArchNetworkBSD::Deps::poll(struct pollfd *fds, nfds_t nfds, int timeout) {
|
||||
return ::poll(fds, nfds, timeout);
|
||||
}
|
||||
|
||||
std::shared_ptr<struct pollfd[]> ArchNetworkBSD::Deps::makePollFD(nfds_t n) {
|
||||
// C++20 supports std::make_shared<struct pollfd[]>(n) but this is not
|
||||
// implemented on the compiler that comes with Ubuntu 22 and a few other
|
||||
// distros, so use the manual new and delete until we drop those distros.
|
||||
return std::shared_ptr<struct pollfd[]>(
|
||||
new struct pollfd[n], std::default_delete<struct pollfd[]>());
|
||||
}
|
||||
|
||||
ssize_t ArchNetworkBSD::Deps::read(int fd, void *buf, size_t len) {
|
||||
return ::read(fd, buf, len);
|
||||
}
|
||||
|
||||
void ArchNetworkBSD::Deps::testCancelThread() { ARCH->testCancelThread(); }
|
||||
|
||||
//
|
||||
// ArchNetworkBSD
|
||||
//
|
||||
|
||||
ArchNetworkBSD::~ArchNetworkBSD() {
|
||||
if (m_mutex)
|
||||
|
|
@ -240,21 +268,20 @@ bool ArchNetworkBSD::connectSocket(ArchSocket s, ArchNetAddress addr) {
|
|||
return true;
|
||||
}
|
||||
|
||||
#if HAVE_POLL
|
||||
|
||||
int ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout) {
|
||||
assert(pe != NULL || num == 0);
|
||||
|
||||
// return if nothing to do
|
||||
if (num == 0) {
|
||||
if (timeout > 0.0) {
|
||||
ARCH->sleep(timeout);
|
||||
m_deps.sleep(timeout);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// allocate space for translated query
|
||||
auto *pfd = new struct pollfd[1 + num];
|
||||
auto pfdPtr = m_deps.makePollFD(1 + num);
|
||||
auto *pfd = pfdPtr.get();
|
||||
|
||||
// translate query
|
||||
for (int i = 0; i < num; ++i) {
|
||||
|
|
@ -272,7 +299,9 @@ int ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout) {
|
|||
// add the unblock pipe
|
||||
const int *unblockPipe = getUnblockPipe();
|
||||
if (unblockPipe != nullptr) {
|
||||
assert(n < (1 + num));
|
||||
pfd[n].fd = unblockPipe[0];
|
||||
|
||||
pfd[n].events = POLLIN;
|
||||
++n;
|
||||
}
|
||||
|
|
@ -281,16 +310,14 @@ int ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout) {
|
|||
int t = (timeout < 0.0) ? -1 : static_cast<int>(1000.0 * timeout);
|
||||
|
||||
// do the poll
|
||||
n = s_connectors.poll_impl(pfd, n, t);
|
||||
n = m_deps.poll(pfd, n, t);
|
||||
|
||||
// reset the unblock pipe
|
||||
if (n > 0 && unblockPipe != nullptr && (pfd[num].revents & POLLIN) != 0) {
|
||||
// the unblock event was signalled. flush the pipe.
|
||||
char dummy[100];
|
||||
int ignore;
|
||||
|
||||
do {
|
||||
ignore = read(unblockPipe[0], dummy, sizeof(dummy));
|
||||
m_deps.read(unblockPipe[0], dummy, sizeof(dummy));
|
||||
} while (errno != EAGAIN);
|
||||
|
||||
// don't count this unblock pipe in return value
|
||||
|
|
@ -301,13 +328,11 @@ int ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout) {
|
|||
if (n == -1) {
|
||||
if (errno == EINTR) {
|
||||
// interrupted system call
|
||||
ARCH->testCancelThread();
|
||||
delete[] pfd;
|
||||
m_deps.testCancelThread();
|
||||
return 0;
|
||||
}
|
||||
delete[] pfd;
|
||||
throwError(errno);
|
||||
return -1;
|
||||
return -1; // unreachable
|
||||
}
|
||||
|
||||
// translate back
|
||||
|
|
@ -327,131 +352,9 @@ int ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout) {
|
|||
}
|
||||
}
|
||||
|
||||
delete[] pfd;
|
||||
return n;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout) {
|
||||
int i, n;
|
||||
|
||||
// prepare sets for select
|
||||
n = 0;
|
||||
fd_set readSet, writeSet, errSet;
|
||||
fd_set *readSetP = NULL;
|
||||
fd_set *writeSetP = NULL;
|
||||
fd_set *errSetP = NULL;
|
||||
FD_ZERO(&readSet);
|
||||
FD_ZERO(&writeSet);
|
||||
FD_ZERO(&errSet);
|
||||
for (i = 0; i < num; ++i) {
|
||||
// reset return flags
|
||||
pe[i].m_revents = 0;
|
||||
|
||||
// set invalid flag if socket is bogus then go to next socket
|
||||
if (pe[i].m_socket == NULL) {
|
||||
pe[i].m_revents |= kPOLLNVAL;
|
||||
continue;
|
||||
}
|
||||
|
||||
int fdi = pe[i].m_socket->m_fd;
|
||||
if (pe[i].m_events & kPOLLIN) {
|
||||
FD_SET(pe[i].m_socket->m_fd, &readSet);
|
||||
readSetP = &readSet;
|
||||
if (fdi > n) {
|
||||
n = fdi;
|
||||
}
|
||||
}
|
||||
if (pe[i].m_events & kPOLLOUT) {
|
||||
FD_SET(pe[i].m_socket->m_fd, &writeSet);
|
||||
writeSetP = &writeSet;
|
||||
if (fdi > n) {
|
||||
n = fdi;
|
||||
}
|
||||
}
|
||||
if (true) {
|
||||
FD_SET(pe[i].m_socket->m_fd, &errSet);
|
||||
errSetP = &errSet;
|
||||
if (fdi > n) {
|
||||
n = fdi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add the unblock pipe
|
||||
const int *unblockPipe = getUnblockPipe();
|
||||
if (unblockPipe != NULL) {
|
||||
FD_SET(unblockPipe[0], &readSet);
|
||||
readSetP = &readSet;
|
||||
if (unblockPipe[0] > n) {
|
||||
n = unblockPipe[0];
|
||||
}
|
||||
}
|
||||
|
||||
// if there are no sockets then don't block forever
|
||||
if (n == 0 && timeout < 0.0) {
|
||||
timeout = 0.0;
|
||||
}
|
||||
|
||||
// prepare timeout for select
|
||||
struct timeval timeout2;
|
||||
struct timeval *timeout2P;
|
||||
if (timeout < 0.0) {
|
||||
timeout2P = NULL;
|
||||
} else {
|
||||
timeout2P = &timeout2;
|
||||
timeout2.tv_sec = static_cast<int>(timeout);
|
||||
timeout2.tv_usec = static_cast<int>(1.0e+6 * (timeout - timeout2.tv_sec));
|
||||
}
|
||||
|
||||
// do the select
|
||||
n = select(
|
||||
(SELECT_TYPE_ARG1)n + 1, SELECT_TYPE_ARG234 readSetP,
|
||||
SELECT_TYPE_ARG234 writeSetP, SELECT_TYPE_ARG234 errSetP,
|
||||
SELECT_TYPE_ARG5 timeout2P);
|
||||
|
||||
// reset the unblock pipe
|
||||
if (n > 0 && unblockPipe != NULL && FD_ISSET(unblockPipe[0], &readSet)) {
|
||||
// the unblock event was signalled. flush the pipe.
|
||||
char dummy[100];
|
||||
do {
|
||||
read(unblockPipe[0], dummy, sizeof(dummy));
|
||||
} while (errno != EAGAIN);
|
||||
}
|
||||
|
||||
// handle results
|
||||
if (n == -1) {
|
||||
if (errno == EINTR) {
|
||||
// interrupted system call
|
||||
ARCH->testCancelThread();
|
||||
return 0;
|
||||
}
|
||||
throwError(errno);
|
||||
}
|
||||
n = 0;
|
||||
for (i = 0; i < num; ++i) {
|
||||
if (pe[i].m_socket != NULL) {
|
||||
if (FD_ISSET(pe[i].m_socket->m_fd, &readSet)) {
|
||||
pe[i].m_revents |= kPOLLIN;
|
||||
}
|
||||
if (FD_ISSET(pe[i].m_socket->m_fd, &writeSet)) {
|
||||
pe[i].m_revents |= kPOLLOUT;
|
||||
}
|
||||
if (FD_ISSET(pe[i].m_socket->m_fd, &errSet)) {
|
||||
pe[i].m_revents |= kPOLLERR;
|
||||
}
|
||||
}
|
||||
if (pe[i].m_revents != 0) {
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void ArchNetworkBSD::unblockPollSocket(ArchThread thread) {
|
||||
const int *unblockPipe = getUnblockPipeForThread(thread);
|
||||
if (unblockPipe != nullptr) {
|
||||
|
|
|
|||
|
|
@ -20,10 +20,12 @@
|
|||
|
||||
#include "arch/IArchMultithread.h"
|
||||
#include "arch/IArchNetwork.h"
|
||||
#include <memory>
|
||||
|
||||
#if HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#else
|
||||
|
|
@ -40,25 +42,16 @@ struct sockaddr_storage {
|
|||
typedef int socklen_t;
|
||||
#endif
|
||||
|
||||
#if HAVE_POLL
|
||||
#include <poll.h>
|
||||
#else
|
||||
#if HAVE_SYS_SELECT_H
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
#if HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define ARCH_NETWORK ArchNetworkBSD
|
||||
#define TYPED_ADDR(type_, addr_) (reinterpret_cast<type_ *>(&addr_->m_addr))
|
||||
|
||||
// old systems may use char* for [gs]etsockopt()'s optval argument.
|
||||
// this should be void on modern systems but char is forwards
|
||||
// compatible so we always use it.
|
||||
typedef char optval_t;
|
||||
|
||||
#define ARCH_NETWORK ArchNetworkBSD
|
||||
#define TYPED_ADDR(type_, addr_) (reinterpret_cast<type_ *>(&addr_->m_addr))
|
||||
|
||||
class ArchSocketImpl {
|
||||
public:
|
||||
int m_fd;
|
||||
|
|
@ -77,57 +70,55 @@ public:
|
|||
//! Berkeley (BSD) sockets implementation of IArchNetwork
|
||||
class ArchNetworkBSD : public IArchNetwork {
|
||||
public:
|
||||
ArchNetworkBSD();
|
||||
struct Deps {
|
||||
virtual ~Deps() = default;
|
||||
virtual void sleep(double);
|
||||
virtual int poll(struct pollfd *, nfds_t, int);
|
||||
virtual std::shared_ptr<struct pollfd[]> makePollFD(nfds_t);
|
||||
virtual ssize_t read(int, void *, size_t);
|
||||
virtual void testCancelThread();
|
||||
};
|
||||
|
||||
explicit ArchNetworkBSD() : m_deps(s_deps) {}
|
||||
explicit ArchNetworkBSD(Deps &deps) : m_deps(deps) {}
|
||||
ArchNetworkBSD(ArchNetworkBSD const &) = delete;
|
||||
ArchNetworkBSD(ArchNetworkBSD &&) = delete;
|
||||
virtual ~ArchNetworkBSD();
|
||||
~ArchNetworkBSD() override;
|
||||
|
||||
ArchNetworkBSD &operator=(ArchNetworkBSD const &) = delete;
|
||||
ArchNetworkBSD &operator=(ArchNetworkBSD &&) = delete;
|
||||
|
||||
virtual void init();
|
||||
void init() override;
|
||||
|
||||
// IArchNetwork overrides
|
||||
virtual ArchSocket newSocket(EAddressFamily, ESocketType);
|
||||
virtual ArchSocket copySocket(ArchSocket s);
|
||||
virtual void closeSocket(ArchSocket s);
|
||||
virtual void closeSocketForRead(ArchSocket s);
|
||||
virtual void closeSocketForWrite(ArchSocket s);
|
||||
virtual void bindSocket(ArchSocket s, ArchNetAddress addr);
|
||||
virtual void listenOnSocket(ArchSocket s);
|
||||
virtual ArchSocket acceptSocket(ArchSocket s, ArchNetAddress *addr);
|
||||
virtual bool connectSocket(ArchSocket s, ArchNetAddress name);
|
||||
virtual int pollSocket(PollEntry[], int num, double timeout);
|
||||
virtual void unblockPollSocket(ArchThread thread);
|
||||
virtual size_t readSocket(ArchSocket s, void *buf, size_t len);
|
||||
virtual size_t writeSocket(ArchSocket s, const void *buf, size_t len);
|
||||
virtual void throwErrorOnSocket(ArchSocket);
|
||||
virtual bool setNoDelayOnSocket(ArchSocket, bool noDelay);
|
||||
virtual bool setReuseAddrOnSocket(ArchSocket, bool reuse);
|
||||
virtual std::string getHostName();
|
||||
virtual ArchNetAddress newAnyAddr(EAddressFamily);
|
||||
virtual ArchNetAddress copyAddr(ArchNetAddress);
|
||||
virtual std::vector<ArchNetAddress> nameToAddr(const std::string &);
|
||||
virtual void closeAddr(ArchNetAddress);
|
||||
virtual std::string addrToName(ArchNetAddress);
|
||||
virtual std::string addrToString(ArchNetAddress);
|
||||
virtual EAddressFamily getAddrFamily(ArchNetAddress);
|
||||
virtual void setAddrPort(ArchNetAddress, int port);
|
||||
virtual int getAddrPort(ArchNetAddress);
|
||||
virtual bool isAnyAddr(ArchNetAddress);
|
||||
virtual bool isEqualAddr(ArchNetAddress, ArchNetAddress);
|
||||
|
||||
struct Connectors {
|
||||
#if HAVE_POLL
|
||||
int (*poll_impl)(struct pollfd *, nfds_t, int);
|
||||
#endif // HAVE_POLL
|
||||
Connectors() {
|
||||
#if HAVE_POLL
|
||||
poll_impl = poll;
|
||||
#endif // HAVE_POLL
|
||||
}
|
||||
};
|
||||
static Connectors s_connectors;
|
||||
ArchSocket newSocket(EAddressFamily, ESocketType) override;
|
||||
ArchSocket copySocket(ArchSocket s) override;
|
||||
void closeSocket(ArchSocket s) override;
|
||||
void closeSocketForRead(ArchSocket s) override;
|
||||
void closeSocketForWrite(ArchSocket s) override;
|
||||
void bindSocket(ArchSocket s, ArchNetAddress addr) override;
|
||||
void listenOnSocket(ArchSocket s) override;
|
||||
ArchSocket acceptSocket(ArchSocket s, ArchNetAddress *addr) override;
|
||||
bool connectSocket(ArchSocket s, ArchNetAddress name) override;
|
||||
int pollSocket(PollEntry[], int num, double timeout) override;
|
||||
void unblockPollSocket(ArchThread thread) override;
|
||||
size_t readSocket(ArchSocket s, void *buf, size_t len) override;
|
||||
size_t writeSocket(ArchSocket s, const void *buf, size_t len) override;
|
||||
void throwErrorOnSocket(ArchSocket) override;
|
||||
bool setNoDelayOnSocket(ArchSocket, bool noDelay) override;
|
||||
bool setReuseAddrOnSocket(ArchSocket, bool reuse) override;
|
||||
std::string getHostName() override;
|
||||
ArchNetAddress newAnyAddr(EAddressFamily) override;
|
||||
ArchNetAddress copyAddr(ArchNetAddress) override;
|
||||
std::vector<ArchNetAddress> nameToAddr(const std::string &) override;
|
||||
void closeAddr(ArchNetAddress) override;
|
||||
std::string addrToName(ArchNetAddress) override;
|
||||
std::string addrToString(ArchNetAddress) override;
|
||||
EAddressFamily getAddrFamily(ArchNetAddress) override;
|
||||
void setAddrPort(ArchNetAddress, int port) override;
|
||||
int getAddrPort(ArchNetAddress) override;
|
||||
bool isAnyAddr(ArchNetAddress) override;
|
||||
bool isEqualAddr(ArchNetAddress, ArchNetAddress) override;
|
||||
|
||||
private:
|
||||
const int *getUnblockPipe();
|
||||
|
|
@ -138,4 +129,6 @@ private:
|
|||
|
||||
private:
|
||||
ArchMutex m_mutex{};
|
||||
Deps &m_deps;
|
||||
static Deps s_deps;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,19 +27,8 @@
|
|||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#if HAVE_POLL
|
||||
|
||||
#include <poll.h>
|
||||
#else
|
||||
#if HAVE_SYS_SELECT_H
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
#if HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#if HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//
|
||||
// EventQueueTimer
|
||||
|
|
@ -112,7 +101,6 @@ void XWindowsEventQueueBuffer::waitForEvent(double dtimeout) {
|
|||
|
||||
// use poll() to wait for a message from the X server or for timeout.
|
||||
// this is a good deal more efficient than polling and sleeping.
|
||||
#if HAVE_POLL
|
||||
struct pollfd pfds[2];
|
||||
pfds[0].fd = ConnectionNumber(m_display);
|
||||
pfds[0].events = POLLIN;
|
||||
|
|
@ -121,29 +109,7 @@ void XWindowsEventQueueBuffer::waitForEvent(double dtimeout) {
|
|||
int timeout = (dtimeout < 0.0) ? -1 : static_cast<int>(1000.0 * dtimeout);
|
||||
int remaining = timeout;
|
||||
int retval = 0;
|
||||
#else
|
||||
struct timeval timeout;
|
||||
struct timeval *timeoutPtr;
|
||||
if (dtimeout < 0.0) {
|
||||
timeoutPtr = NULL;
|
||||
} else {
|
||||
timeout.tv_sec = static_cast<int>(dtimeout);
|
||||
timeout.tv_usec = static_cast<int>(1.0e+6 * (dtimeout - timeout.tv_sec));
|
||||
timeoutPtr = &timeout;
|
||||
}
|
||||
|
||||
// initialize file descriptor sets
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(ConnectionNumber(m_display), &rfds);
|
||||
FD_SET(m_pipefd[0], &rfds);
|
||||
int nfds;
|
||||
if (ConnectionNumber(m_display) > m_pipefd[0]) {
|
||||
nfds = ConnectionNumber(m_display) + 1;
|
||||
} else {
|
||||
nfds = m_pipefd[0] + 1;
|
||||
}
|
||||
#endif
|
||||
// It's possible that the X server has queued events locally
|
||||
// in xlib's event buffer and not pushed on to the fd. Hence we
|
||||
// can't simply monitor the fd as we may never be woken up.
|
||||
|
|
@ -159,7 +125,7 @@ void XWindowsEventQueueBuffer::waitForEvent(double dtimeout) {
|
|||
|
||||
while (((dtimeout < 0.0) || (remaining > 0)) &&
|
||||
getPendingCountLocked() == 0 && retval == 0) {
|
||||
#if HAVE_POLL
|
||||
|
||||
retval = poll(pfds, 2, TIMEOUT_DELAY); // 16ms = 60hz, but we make it > to
|
||||
// play nicely with the cpu
|
||||
if (pfds[1].revents & POLLIN) {
|
||||
|
|
@ -170,14 +136,6 @@ void XWindowsEventQueueBuffer::waitForEvent(double dtimeout) {
|
|||
// todo: handle read response
|
||||
}
|
||||
}
|
||||
#else
|
||||
retval = select(
|
||||
nfds, SELECT_TYPE_ARG234 & rfds, SELECT_TYPE_ARG234 NULL,
|
||||
SELECT_TYPE_ARG234 NULL, SELECT_TYPE_ARG5 TIMEOUT_DELAY);
|
||||
if (FD_SET(m_pipefd[0], &rfds)) {
|
||||
read(m_pipefd[0], buf, 15);
|
||||
}
|
||||
#endif
|
||||
remaining -= TIMEOUT_DELAY;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,11 +68,12 @@ macro(set_sources)
|
|||
list(APPEND sources ${CMAKE_BINARY_DIR}/src/version.rc)
|
||||
endif()
|
||||
|
||||
append_platform_sources()
|
||||
replace_platform_sources()
|
||||
replace_arch_sources()
|
||||
|
||||
endmacro()
|
||||
|
||||
macro(append_platform_sources)
|
||||
macro(replace_platform_sources)
|
||||
|
||||
set(platform_dir ${CMAKE_CURRENT_SOURCE_DIR}/platform)
|
||||
|
||||
|
|
@ -99,6 +100,30 @@ macro(append_platform_sources)
|
|||
|
||||
endmacro()
|
||||
|
||||
macro(replace_arch_sources)
|
||||
|
||||
set(arch_dir ${CMAKE_CURRENT_SOURCE_DIR}/arch)
|
||||
|
||||
# Remove arch files so that specific arch files can be added later.
|
||||
# This is a bit weird, but it's simpler to include everything, remove all
|
||||
# arch files, then only include the archs we need.
|
||||
file(GLOB_RECURSE all_arch_files ${arch_dir}/*)
|
||||
list(REMOVE_ITEM headers ${all_arch_files})
|
||||
list(REMOVE_ITEM sources ${all_arch_files})
|
||||
|
||||
if(WIN32)
|
||||
file(GLOB arch_sources ${arch_dir}/win32/*.cpp)
|
||||
file(GLOB arch_headers ${arch_dir}/win32/*.h)
|
||||
elseif(UNIX)
|
||||
file(GLOB arch_sources ${arch_dir}/unix/*.cpp)
|
||||
file(GLOB arch_headers ${arch_dir}/unix/*.h)
|
||||
endif()
|
||||
|
||||
list(APPEND sources ${arch_sources})
|
||||
list(APPEND headers ${arch_headers})
|
||||
|
||||
endmacro()
|
||||
|
||||
macro(config_test_deps)
|
||||
|
||||
set(gtest_dir ${gtest_base_dir}/googletest)
|
||||
|
|
|
|||
|
|
@ -15,53 +15,188 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// TODO: mock out sockets on integ tests masquerading as unit tests
|
||||
#if 0
|
||||
|
||||
#ifndef _WIN32
|
||||
#include "lib/arch/XArch.h"
|
||||
#include "arch/IArchNetwork.h"
|
||||
#include "lib/arch/unix/ArchNetworkBSD.h"
|
||||
|
||||
#include <array>
|
||||
#include "lib/arch/XArch.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
TEST(ArchNetworkBSDTests, pollSocket_errs_EACCES) {
|
||||
ArchNetworkBSD networkBSD;
|
||||
ArchNetworkBSD::s_connectors.poll_impl = [](struct pollfd *, nfds_t, int) {
|
||||
using ::testing::_;
|
||||
using ::testing::NiceMock;
|
||||
using PollEntries = std::vector<IArchNetwork::PollEntry>;
|
||||
using PollFD = struct pollfd[];
|
||||
|
||||
struct MockDeps : public ArchNetworkBSD::Deps {
|
||||
MockDeps() {
|
||||
ON_CALL(*this, makePollFD(_)).WillByDefault([this](nfds_t n) {
|
||||
m_pollFD = ArchNetworkBSD::Deps::makePollFD(n);
|
||||
return m_pollFD;
|
||||
});
|
||||
}
|
||||
|
||||
MOCK_METHOD(void, sleep, (double), (override));
|
||||
MOCK_METHOD(int, poll, (struct pollfd *, nfds_t, int), (override));
|
||||
MOCK_METHOD(std::shared_ptr<PollFD>, makePollFD, (nfds_t), (override));
|
||||
MOCK_METHOD(ssize_t, read, (int, void *, size_t), (override));
|
||||
MOCK_METHOD(void, testCancelThread, (), (override));
|
||||
|
||||
std::shared_ptr<PollFD> m_pollFD;
|
||||
};
|
||||
|
||||
TEST(ArchNetworkBSDTests, pollSocket_zeroEntries_callsSleep) {
|
||||
MockDeps deps;
|
||||
ArchNetworkBSD networkBSD(deps);
|
||||
|
||||
EXPECT_CALL(deps, sleep(1)).Times(1);
|
||||
auto result = networkBSD.pollSocket(nullptr, 0, 1);
|
||||
|
||||
EXPECT_EQ(result, 0);
|
||||
}
|
||||
|
||||
TEST(ArchNetworkBSDTests, pollSocket_mockAccessError_throws) {
|
||||
NiceMock<MockDeps> deps;
|
||||
ON_CALL(deps, poll(_, _, _)).WillByDefault([]() {
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
});
|
||||
ArchNetworkBSD networkBSD(deps);
|
||||
PollEntries entries{{nullptr, 0, 0}};
|
||||
|
||||
const auto f = [&] {
|
||||
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
|
||||
};
|
||||
try {
|
||||
std::array<IArchNetwork::PollEntry, 2> pe{{nullptr, 0, 0}};
|
||||
networkBSD.pollSocket(pe.data(), pe.size(), 1);
|
||||
FAIL() << "Expected to throw";
|
||||
} catch (XArchNetworkAccess const &err) {
|
||||
EXPECT_STREQ(err.what(), "Permission denied");
|
||||
} catch (std::runtime_error const &baseerr) {
|
||||
FAIL() << "Expected to throw XArchNetworkAccess but got " << baseerr.what();
|
||||
}
|
||||
|
||||
EXPECT_THROW({ f(); }, XArchNetworkAccess);
|
||||
}
|
||||
|
||||
TEST(ArchNetworkBSDTests, isAnyAddr_IP6) {
|
||||
ArchNetworkBSD networkBSD;
|
||||
TEST(ArchNetworkBSDTests, pollSocket_pfdHasRevents_copiedToEntries) {
|
||||
NiceMock<MockDeps> deps;
|
||||
ON_CALL(deps, poll(_, _, _)).WillByDefault([](auto pfd, auto, auto) {
|
||||
pfd[0].revents = POLLIN | POLLOUT | POLLERR | POLLNVAL;
|
||||
return 0;
|
||||
});
|
||||
ArchNetworkBSD networkBSD(deps);
|
||||
PollEntries entries{{nullptr, 0, 0}};
|
||||
|
||||
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
|
||||
|
||||
const auto expect = IArchNetwork::kPOLLIN | IArchNetwork::kPOLLOUT |
|
||||
IArchNetwork::kPOLLERR | IArchNetwork::kPOLLNVAL;
|
||||
EXPECT_EQ(entries[0].m_revents, expect);
|
||||
}
|
||||
|
||||
TEST(ArchNetworkBSDTests, pollSocket_nullSocket_fdIsNegativeOne) {
|
||||
NiceMock<MockDeps> deps;
|
||||
ArchNetworkBSD networkBSD(deps);
|
||||
PollEntries entries{{nullptr, 0, 0}};
|
||||
|
||||
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
|
||||
|
||||
EXPECT_EQ(deps.m_pollFD[0].fd, -1);
|
||||
}
|
||||
|
||||
TEST(ArchNetworkBSDTests, pollSocket_socketSet_fdWasSet) {
|
||||
NiceMock<MockDeps> deps;
|
||||
ArchNetworkBSD networkBSD(deps);
|
||||
ArchSocketImpl socket{1, 0};
|
||||
PollEntries entries{{&socket, 0, 0}};
|
||||
|
||||
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
|
||||
|
||||
EXPECT_EQ(deps.m_pollFD[0].fd, 1);
|
||||
}
|
||||
|
||||
TEST(ArchNetworkBSDTests, pollSocket_eventHasPollInBit_bitWasSet) {
|
||||
NiceMock<MockDeps> deps;
|
||||
ArchNetworkBSD networkBSD(deps);
|
||||
ArchSocketImpl socket{1, 0};
|
||||
PollEntries entries{{&socket, IArchNetwork::kPOLLIN, 0}};
|
||||
|
||||
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
|
||||
|
||||
EXPECT_EQ(deps.m_pollFD[0].events, POLLIN);
|
||||
}
|
||||
|
||||
TEST(ArchNetworkBSDTests, pollSocket_eventHasPollOutBit_bitWasSet) {
|
||||
NiceMock<MockDeps> deps;
|
||||
ArchNetworkBSD networkBSD(deps);
|
||||
ArchSocketImpl socket{1, 0};
|
||||
PollEntries entries{{&socket, IArchNetwork::kPOLLOUT, 0}};
|
||||
|
||||
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
|
||||
|
||||
EXPECT_EQ(deps.m_pollFD[0].events, POLLOUT);
|
||||
}
|
||||
|
||||
TEST(ArchNetworkBSDTests, pollSocket_nullSocket_unblockPipeAppended) {
|
||||
NiceMock<MockDeps> deps;
|
||||
ArchNetworkBSD networkBSD(deps);
|
||||
PollEntries entries{{nullptr, 0, 0}};
|
||||
|
||||
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
|
||||
|
||||
// interesting: unblock pipe fd comes from `getNetworkDataForThread` which
|
||||
// seems to differ depending on linux distro.
|
||||
EXPECT_GT(deps.m_pollFD[1].fd, -1);
|
||||
}
|
||||
|
||||
TEST(ArchNetworkBSDTests, pollSocket_unblockPipeReventsError_readCalled) {
|
||||
const auto unblockPipeIndex = 1;
|
||||
NiceMock<MockDeps> deps;
|
||||
ON_CALL(deps, poll(_, _, _)).WillByDefault([](auto pfd, auto, auto) {
|
||||
pfd[unblockPipeIndex].revents = POLLIN;
|
||||
return 1;
|
||||
});
|
||||
ON_CALL(deps, read(_, _, _)).WillByDefault([]() {
|
||||
errno = EAGAIN;
|
||||
return 0;
|
||||
});
|
||||
ArchNetworkBSD networkBSD(deps);
|
||||
PollEntries entries{{nullptr, 0, 0}};
|
||||
|
||||
EXPECT_CALL(deps, read(_, _, _)).Times(1);
|
||||
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
|
||||
}
|
||||
|
||||
TEST(ArchNetworkBSDTests, pollSocket_interruptSystemCall_testCancelThread) {
|
||||
NiceMock<MockDeps> deps;
|
||||
ON_CALL(deps, poll(_, _, _)).WillByDefault([]() {
|
||||
errno = EINTR;
|
||||
return -1;
|
||||
});
|
||||
ArchNetworkBSD networkBSD(deps);
|
||||
PollEntries entries{{nullptr, 0, 0}};
|
||||
|
||||
EXPECT_CALL(deps, testCancelThread()).Times(1);
|
||||
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
|
||||
}
|
||||
|
||||
TEST(ArchNetworkBSDTests, isAnyAddr_goodAddress_returnsTrue) {
|
||||
MockDeps deps;
|
||||
ArchNetworkBSD networkBSD(deps);
|
||||
std::unique_ptr<ArchNetAddressImpl> addr;
|
||||
addr.reset(networkBSD.newAnyAddr(IArchNetwork::kINET6));
|
||||
EXPECT_TRUE(networkBSD.isAnyAddr(addr.get()));
|
||||
|
||||
auto scratch = (char *)&addr->m_addr;
|
||||
scratch[2] = 'b';
|
||||
scratch[3] = 'a';
|
||||
scratch[4] = 'd';
|
||||
scratch[5] = 'a';
|
||||
scratch[6] = 'd';
|
||||
scratch[7] = 'd';
|
||||
scratch[8] = 'r';
|
||||
EXPECT_FALSE(networkBSD.isAnyAddr(addr.get()));
|
||||
auto result = networkBSD.isAnyAddr(addr.get());
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
}
|
||||
|
||||
#endif // #ifdnef _WIN32
|
||||
TEST(ArchNetworkBSDTests, isAnyAddr_badAddress_returnsFalse) {
|
||||
MockDeps deps;
|
||||
ArchNetworkBSD networkBSD(deps);
|
||||
std::unique_ptr<ArchNetAddressImpl> addr;
|
||||
addr.reset(networkBSD.newAnyAddr(IArchNetwork::kINET6));
|
||||
auto scratch = (char *)&addr->m_addr;
|
||||
std::string badAddr = "badaddr";
|
||||
std::ranges::copy(badAddr, scratch + 2);
|
||||
|
||||
#endif
|
||||
auto result = networkBSD.isAnyAddr(addr.get());
|
||||
|
||||
EXPECT_FALSE(result);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue