From 11e29ff7eb173bcce3365d0673f4850f45052dc9 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 16 Oct 2002 22:01:50 +0000 Subject: [PATCH] Added support for using select() instead of poll(). --- acinclude.m4 | 15 ++ configure.in | 2 + lib/net/CNetwork.cpp | 316 ++++++++++++++++++------------- lib/net/CNetwork.h | 76 +++++--- lib/net/CTCPSocket.cpp | 3 +- lib/platform/CXWindowsScreen.cpp | 46 ++++- 6 files changed, 301 insertions(+), 157 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 685209d4..155c47ea 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -75,6 +75,21 @@ AC_DEFUN([ACX_CHECK_CXX_STDLIB], [ fi ])dnl ACX_CHECK_CXX_STDLIB +AC_DEFUN([ACX_CHECK_POLL], [ + AC_MSG_CHECKING([for poll]) + AC_TRY_LINK([#include ], + [struct pollfd ufds[] = { 0, POLLIN, 0 }; poll(ufds, 1, 10);], + acx_poll_ok=yes, acx_poll_ok=no) + AC_MSG_RESULT($acx_poll_ok) + if test x"$acx_poll_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_POLL,1,[Define if you have the `poll' function.]),[$1]) + : + else + acx_poll_ok=no + $2 + fi +])dnl ACX_CHECK_POLL + dnl The following macros are from http://www.gnu.org/software/ac-archive/ dnl which distributes them under the following license: dnl diff --git a/configure.in b/configure.in index ea56066b..56346272 100644 --- a/configure.in +++ b/configure.in @@ -72,6 +72,8 @@ AC_FUNC_STRFTIME AC_CHECK_FUNCS(gmtime_r) AC_CHECK_FUNCS(getpwuid_r) AC_CHECK_FUNCS(vsnprintf) +AC_FUNC_SELECT_ARGTYPES +ACX_CHECK_POLL dnl use AC_REPLACE_FUNCS() for stuff in string.h dnl checks for system services diff --git a/lib/net/CNetwork.cpp b/lib/net/CNetwork.cpp index 918095c9..c844503f 100644 --- a/lib/net/CNetwork.cpp +++ b/lib/net/CNetwork.cpp @@ -51,6 +51,7 @@ struct protoent FAR * (PASCAL FAR *CNetwork::getprotobyname)(const char FAR * na int (PASCAL FAR *CNetwork::getsockerror)(void); int (PASCAL FAR *CNetwork::gethosterror)(void); int (PASCAL FAR *CNetwork::setblocking)(CNetwork::Socket s, bool blocking); +int (PASCAL FAR *CNetwork::setnodelay)(CNetwork::Socket s, bool blocking); #if WINDOWS_LIKE @@ -226,79 +227,11 @@ CNetwork::init2( read = read2; write = write2; setblocking = setblocking2; + setnodelay = setnodelay2; s_networkModule = module; } -int PASCAL FAR -CNetwork::poll2(PollEntry fd[], int nfds, int timeout) -{ - int i; - - // prepare sets for select - 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 < nfds; ++i) { - if (fd[i].events & kPOLLIN) { - FD_SET(fd[i].fd, &readSet); - readSetP = &readSet; - } - if (fd[i].events & kPOLLOUT) { - FD_SET(fd[i].fd, &writeSet); - writeSetP = &writeSet; - } - if (true) { - FD_SET(fd[i].fd, &errSet); - errSetP = &errSet; - } - } - - // prepare timeout for select - struct timeval timeout2; - struct timeval* timeout2P; - if (timeout < 0) { - timeout2P = NULL; - } - else { - timeout2P = &timeout2; - timeout2.tv_sec = timeout / 1000; - timeout2.tv_usec = 1000 * (timeout % 1000); - } - - // do the select. note that winsock ignores the first argument. - int n = select(0, readSetP, writeSetP, errSetP, timeout2P); - - // handle results - if (n == Error) { - return Error; - } - if (n == 0) { - return 0; - } - n = 0; - for (i = 0; i < nfds; ++i) { - fd[i].revents = 0; - if (FD_ISSET(fd[i].fd, &readSet)) { - fd[i].revents |= kPOLLIN; - } - if (FD_ISSET(fd[i].fd, &writeSet)) { - fd[i].revents |= kPOLLOUT; - } - if (FD_ISSET(fd[i].fd, &errSet)) { - fd[i].revents |= kPOLLERR; - } - if (fd[i].revents != 0) { - ++n; - } - } - return n; -} - ssize_t PASCAL FAR CNetwork::read2(Socket s, void FAR* buf, size_t len) { @@ -318,17 +251,39 @@ CNetwork::setblocking2(CNetwork::Socket s, bool blocking) return ioctl(s, FIONBIO, &flag); } +int PASCAL FAR +CNetwork::setnodelay2(CNetwork::Socket s, bool nodelay) +{ + BOOL flag = nodelay ? 1 : 0; + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); +} + #endif #if UNIX_LIKE -#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif #include #include +#include #include +#if !defined(TCP_NODELAY) || !defined(SOL_TCP) +# include +#endif // FIXME -- use reentrant versions of non-reentrant functions +const int CNetwork::Error = -1; +const CNetwork::Socket CNetwork::Null = -1; + #define setfunc(var, name, type) var = (type)::name UInt32 @@ -355,30 +310,73 @@ CNetwork::swapntohs(UInt16 v) return ntohs(v); } -static -int -myerrno() +void +CNetwork::init() +{ + setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); + setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); + setfunc(close, close, int (PASCAL FAR *)(Socket s)); + setfunc(connect, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); + setfunc(ioctl, ioctl, int (PASCAL FAR *)(Socket s, int cmd, void FAR *)); + setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); + setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); + setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); + setfunc(inet_ntoa, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); + setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); +#if HAVE_POLL + setfunc(poll, poll, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); +#else + setfunc(poll, CNetwork::poll2, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); +#endif + setfunc(read, read, ssize_t (PASCAL FAR *)(CNetwork::Socket s, void FAR * buf, size_t len)); + setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); + setfunc(recvfrom, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); + setfunc(send, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); + setfunc(sendto, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); + setfunc(setsockopt, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); + setfunc(shutdown, shutdown, int (PASCAL FAR *)(Socket s, int how)); + setfunc(socket, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); + setfunc(write, write, ssize_t (PASCAL FAR *)(CNetwork::Socket s, const void FAR * buf, size_t len)); + setfunc(gethostbyaddr, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); + setfunc(gethostbyname, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); + setfunc(getservbyport, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); + setfunc(getservbyname, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); + setfunc(getprotobynumber, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); + setfunc(getprotobyname, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); + gethostname = gethostname2; + getsockerror = getsockerror2; + gethosterror = gethosterror2; + setblocking = setblocking2; + setnodelay = setnodelay2; +} + +void +CNetwork::cleanup() +{ + // do nothing +} + +int PASCAL FAR +CNetwork::gethostname2(char* name, int namelen) +{ + return ::gethostname(name, namelen); +} + +int PASCAL FAR +CNetwork::getsockerror2(void) { return errno; } -static -int -myherrno() +int PASCAL FAR +CNetwork::gethosterror2(void) { return h_errno; } -static -int -mygethostname(char* name, int namelen) -{ - return gethostname(name, namelen); -} - -static -int -mysetblocking(CNetwork::Socket s, bool blocking) +int PASCAL FAR +CNetwork::setblocking2(CNetwork::Socket s, bool blocking) { int mode = fcntl(s, F_GETFL, 0); if (mode == -1) { @@ -396,49 +394,111 @@ mysetblocking(CNetwork::Socket s, bool blocking) return 0; } -const int CNetwork::Error = -1; -const CNetwork::Socket CNetwork::Null = -1; - -void -CNetwork::init() +int PASCAL FAR +CNetwork::setnodelay2(CNetwork::Socket s, bool nodelay) { - setfunc(accept, accept, Socket (PASCAL FAR *)(Socket s, Address FAR *addr, AddressLength FAR *addrlen)); - setfunc(bind, bind, int (PASCAL FAR *)(Socket s, const Address FAR *addr, AddressLength namelen)); - setfunc(close, close, int (PASCAL FAR *)(Socket s)); - setfunc(connect, connect, int (PASCAL FAR *)(Socket s, const Address FAR *name, AddressLength namelen)); - setfunc(ioctl, ioctl, int (PASCAL FAR *)(Socket s, int cmd, void FAR *)); - setfunc(getpeername, getpeername, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); - setfunc(getsockname, getsockname, int (PASCAL FAR *)(Socket s, Address FAR *name, AddressLength FAR * namelen)); - setfunc(getsockopt, getsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, void FAR * optval, AddressLength FAR *optlen)); - setfunc(inet_addr, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp)); - setfunc(inet_ntoa, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in)); - setfunc(listen, listen, int (PASCAL FAR *)(Socket s, int backlog)); - setfunc(poll, poll, int (PASCAL FAR *)(CNetwork::PollEntry fds[], int nfds, int timeout)); - setfunc(read, read, ssize_t (PASCAL FAR *)(CNetwork::Socket s, void FAR * buf, size_t len)); - setfunc(recv, recv, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags)); - setfunc(recvfrom, recvfrom, ssize_t (PASCAL FAR *)(Socket s, void FAR * buf, size_t len, int flags, Address FAR *from, AddressLength FAR * fromlen)); - setfunc(send, send, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags)); - setfunc(sendto, sendto, ssize_t (PASCAL FAR *)(Socket s, const void FAR * buf, size_t len, int flags, const Address FAR *to, AddressLength tolen)); - setfunc(setsockopt, setsockopt, int (PASCAL FAR *)(Socket s, int level, int optname, const void FAR * optval, AddressLength optlen)); - setfunc(shutdown, shutdown, int (PASCAL FAR *)(Socket s, int how)); - setfunc(socket, socket, Socket (PASCAL FAR *)(int af, int type, int protocol)); - setfunc(write, write, ssize_t (PASCAL FAR *)(CNetwork::Socket s, const void FAR * buf, size_t len)); - setfunc(gethostbyaddr, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type)); - setfunc(gethostbyname, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); - setfunc(gethostname, mygethostname, int (PASCAL FAR *)(char FAR * name, int namelen)); - setfunc(getservbyport, getservbyport, struct servent FAR * (PASCAL FAR *)(int port, const char FAR * proto)); - setfunc(getservbyname, getservbyname, struct servent FAR * (PASCAL FAR *)(const char FAR * name, const char FAR * proto)); - setfunc(getprotobynumber, getprotobynumber, struct protoent FAR * (PASCAL FAR *)(int proto)); - setfunc(getprotobyname, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name)); - setfunc(getsockerror, myerrno, int (PASCAL FAR *)(void)); - setfunc(gethosterror, myherrno, int (PASCAL FAR *)(void)); - setfunc(setblocking, mysetblocking, int (PASCAL FAR *)(Socket, bool)); -} - -void -CNetwork::cleanup() -{ - // do nothing + int flag = nodelay ? 1 : 0; + setsockopt(s, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); +} + +#endif + +#if WINDOWS_LIKE || !HAVE_POLL + +#if HAVE_SYS_SELECT_H +# include +#endif +#if HAVE_SYS_TIME_H +# include +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif + +int PASCAL FAR +CNetwork::poll2(PollEntry fd[], int nfds, int timeout) +{ + int i; + + // prepare sets for select + int 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 < nfds; ++i) { + if (fd[i].events & kPOLLIN) { + FD_SET(fd[i].fd, &readSet); + readSetP = &readSet; + if (fd[i].fd > n) { + n = fd[i].fd; + } + } + if (fd[i].events & kPOLLOUT) { + FD_SET(fd[i].fd, &writeSet); + writeSetP = &writeSet; + if (fd[i].fd > n) { + n = fd[i].fd; + } + } + if (true) { + FD_SET(fd[i].fd, &errSet); + errSetP = &errSet; + if (fd[i].fd > n) { + n = fd[i].fd; + } + } + } + + // prepare timeout for select + struct timeval timeout2; + struct timeval* timeout2P; + if (timeout < 0) { + timeout2P = NULL; + } + else { + timeout2P = &timeout2; + timeout2.tv_sec = timeout / 1000; + timeout2.tv_usec = 1000 * (timeout % 1000); + } + + // do the select. note that winsock ignores the first argument. + n = select((SELECT_TYPE_ARG1) n + 1, + SELECT_TYPE_ARG234 readSetP, + SELECT_TYPE_ARG234 writeSetP, + SELECT_TYPE_ARG234 errSetP, + SELECT_TYPE_ARG5 timeout2P); + + // handle results + if (n == Error) { + return Error; + } + if (n == 0) { + return 0; + } + n = 0; + for (i = 0; i < nfds; ++i) { + fd[i].revents = 0; + if (FD_ISSET(fd[i].fd, &readSet)) { + fd[i].revents |= kPOLLIN; + } + if (FD_ISSET(fd[i].fd, &writeSet)) { + fd[i].revents |= kPOLLOUT; + } + if (FD_ISSET(fd[i].fd, &errSet)) { + fd[i].revents |= kPOLLERR; + } + if (fd[i].revents != 0) { + ++n; + } + } + return n; } #endif diff --git a/lib/net/CNetwork.h b/lib/net/CNetwork.h index 96650a99..7b3ef64c 100644 --- a/lib/net/CNetwork.h +++ b/lib/net/CNetwork.h @@ -17,15 +17,22 @@ #include "BasicTypes.h" +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif +#if HAVE_POLL +# include +#endif + #if WINDOWS_LIKE // declare no functions in winsock2 # define INCL_WINSOCK_API_PROTOTYPES 0 # define INCL_WINSOCK_API_TYPEDEFS 0 # include typedef int ssize_t; -# if !defined(SOL_TCP) -# define SOL_TCP IPPROTO_TCP -# endif #else # undef FAR # undef PASCAL @@ -34,15 +41,8 @@ typedef int ssize_t; #endif #if UNIX_LIKE -# include -# include -# include -# include -# include -# include -# if !defined(TCP_NODELAY) || !defined(SOL_TCP) -# include -# endif +# include +# include #endif //! Networking functions @@ -53,7 +53,13 @@ public: typedef SOCKET Socket; typedef struct sockaddr Address; typedef int AddressLength; - typedef BOOL TCPNoDelayType; +#elif UNIX_LIKE + typedef int Socket; + typedef struct sockaddr Address; + typedef socklen_t AddressLength; +#endif + +#if WINDOWS_LIKE || !HAVE_POLL class PollEntry { public: Socket fd; @@ -61,21 +67,17 @@ public: short revents; }; enum { - kPOLLIN = 1, - kPOLLOUT = 2, - kPOLLERR = 4, + kPOLLIN = 1, + kPOLLOUT = 2, + kPOLLERR = 4, kPOLLNVAL = 8 }; -#elif UNIX_LIKE - typedef int Socket; - typedef struct sockaddr Address; - typedef socklen_t AddressLength; +#else typedef struct pollfd PollEntry; - typedef int TCPNoDelayType; enum { - kPOLLIN = POLLIN, - kPOLLOUT = POLLOUT, - kPOLLERR = POLLERR, + kPOLLIN = POLLIN, + kPOLLOUT = POLLOUT, + kPOLLERR = POLLERR, kPOLLNVAL = POLLNVAL }; #endif @@ -183,17 +185,39 @@ public: //! Set socket to (non-)blocking operation static int (PASCAL FAR *setblocking)(CNetwork::Socket s, bool blocking); -#if WINDOWS_LIKE + //! Turn Nagle algorithm on or off on socket + /*! + Set socket to send messages immediately (true) or to collect small + messages into one packet (false). + */ + static int (PASCAL FAR *setnodelay)(CNetwork::Socket s, bool nodelay); + private: +#if WINDOWS_LIKE +#define SELECT_TYPE_ARG1 int +#define SELECT_TYPE_ARG234 (fd_set *) +#define SELECT_TYPE_ARG5 (struct timeval *) static void init2(HMODULE); - static int PASCAL FAR poll2(PollEntry[], int nfds, int timeout); static ssize_t PASCAL FAR read2(Socket s, void FAR * buf, size_t len); static ssize_t PASCAL FAR write2(Socket s, const void FAR * buf, size_t len); static int PASCAL FAR setblocking2(CNetwork::Socket s, bool blocking); + static int PASCAL FAR setnodelay2(CNetwork::Socket s, bool nodelay); static int (PASCAL FAR *WSACleanup)(void); static int (PASCAL FAR *__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *); static int (PASCAL FAR *select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); #endif + +#if UNIX_LIKE + static int PASCAL FAR gethostname2(char FAR * name, int namelen); + static int PASCAL FAR getsockerror2(void); + static int PASCAL FAR gethosterror2(void); + static int PASCAL FAR setblocking2(CNetwork::Socket s, bool blocking); + static int PASCAL FAR setnodelay2(CNetwork::Socket s, bool nodelay); +#endif + +#if WINDOWS_LIKE || !HAVE_POLL + static int PASCAL FAR poll2(PollEntry[], int nfds, int timeout); +#endif }; #endif diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index aa8dd065..a9937537 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -208,8 +208,7 @@ CTCPSocket::init() // turn off Nagle algorithm. we send lots of very short messages // that should be sent without (much) delay. for example, the // mouse motion messages are much less useful if they're delayed. - CNetwork::TCPNoDelayType flag = 1; - CNetwork::setsockopt(m_fd, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); + CNetwork::setnodelay(m_fd, true); } void diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 920c98de..5cf8fe80 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -32,7 +32,22 @@ # include #endif #if UNIX_LIKE -#include +# if HAVE_POLL +# include +# else +# if HAVE_SYS_SELECT_H +# include +# endif +# if HAVE_SYS_TIME_H +# include +# endif +# if HAVE_SYS_TYPES_H +# include +# endif +# if HAVE_UNISTD_H +# include +# endif +# endif #endif // @@ -240,19 +255,48 @@ CXWindowsScreen::mainLoop() // 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[1]; pfds[0].fd = ConnectionNumber(m_display); pfds[0].events = POLLIN; +#endif while (!m_stop) { // compute timeout to next timer +#if HAVE_POLL int timeout = (m_timers.empty() ? -1 : static_cast(1000.0 * m_timers.top())); +#else + struct timeval timeout; + struct timeval* timeoutPtr; + if (m_timers.empty()) { + timeoutPtr = NULL; + } + else { + timeout.tv_sec = static_cast(m_timers.top()); + timeout.tv_usec = static_cast(1.0e+6 * + (m_timers.top() - timeout.tv_sec)); + timeoutPtr = &timeout; + } + + // initialize file descriptor sets + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(ConnectionNumber(m_display), &rfds); +#endif // wait for message from X server or for timeout. also check // if the thread has been cancelled. poll() should return -1 // with EINTR when the thread is cancelled. m_mutex.unlock(); +#if HAVE_POLL poll(pfds, 1, timeout); +#else + select(ConnectionNumber(m_display) + 1, + SELECT_TYPE_ARG234 &rfds, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG5 timeoutPtr); +#endif CThread::testCancel(); m_mutex.lock();