/*-------------------------------------------------------------------------- Copyright 1999,2000, Dan Kegel http://www.kegel.com/ See the file COPYING (Also freely licensed to Disappearing, Inc. under a separate license which allows them to do absolutely anything they want with it, without regard to the GPL.) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --------------------------------------------------------------------------*/ #include "Poller_poll.h" #include "Poller_select.h" #include "Poller_kqueue.h" #include "Poller_devpoll.h" #include "Poller_sigio.h" #include "Poller_sigfd.h" #include "dprint.h" #include "CHECK.h" #include #include #include #include #include #include #include /*---------------------------------------------------------------------- Portable function to set a socket into nonblocking mode. ----------------------------------------------------------------------*/ static int setNonblocking(int fd) { int flags; /* Caution: O_NONBLOCK is defined but broken on SunOS 4.1.x & AIX 3.2.5. */ #if defined(O_NONBLOCK) if (-1 == (flags = fcntl(fd, F_GETFL, 0))) flags = 0; return fcntl(fd, F_SETFL, flags | O_NONBLOCK); #else flags = 1; return ioctl(fd, FIOBIO, &flags); #endif } // handy little function used to count number of zeroes to right of given bit // converts a bit mask like POLLIN into an index into m_eventCounts[]. static int log2(int val) { int i; for (i=0; i<16; i++) if (val == (1 << i)) return i; return -1; } static const char *eventbitname(int bitnum) { switch (1< class Poller_test : Poller::Client { private: /// The object being tested. T m_p; /// The two file descriptors we use to test Poller. int m_filedes[2]; /// Number of bits in struct pollfd's events field we care about. static const int EVENTBITS = 8; /// Number of times each bit in struct pollfd's events field has been set. int m_eventCounts[EVENTBITS]; public: /// Just increments counts in m_eventCounts int notifyPollEvent(Poller::PollEvent *event) { int i; int fd = event->fd; short eventflags = event->revents; DPRINT(("notifyPollEvent: fd %d, eventflags %x\n", fd, eventflags)); for (i=0; i(&sin), sizeof (sin))); CHECK(errno, EINPROGRESS); /* Don't add the socket to the Poller until after you've * called connect(). Otherwise, it's ambiguous what writability * means; unconnected sockets are always 'writable', but once you * call connect(), it won't become writable again until the connect * completes or fails. * Be careful to only ask for POLLOUT + POLLERR until connection * completes; other events would just be confusing. */ m_p.add(sock, this, POLLOUT|POLLERR); clear(); CHECK(0, m_p.waitAndDispatchEvents(1000)); // we expect rejection, so the error count should be 1 // however, select doesn't tell us about errors, //CHECK(1, m_eventCounts[log2(POLLERR)]); // so we have to do I/O to tell whether it failed CHECK(true, m_eventCounts[log2(POLLOUT)]||m_eventCounts[log2(POLLERR)]); // nonblocking connect completion signalled by POLLOUT; see Stevens UNP vol 1 p. 410 /* check to see if connect succeeded */ int connecterr = -1; socklen_t len = sizeof(connecterr); if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&connecterr, &len) < 0) { int xerr = errno; EDPRINT(("can't getsockopt SO_ERROR fd %d fails, errno %d\n", sock, xerr)); CHECK(0, 1); } DPRINT(("got connection errno %d\n", connecterr)); if (connecterr == EINPROGRESS) { DPRINT(("Hmm, kernel says we're still connecting...\n")); /* after every EWOULDBLOCK/EINPROGRESS, must clear readiness */ m_p.clearReadiness(sock, POLLOUT); /* This would happen if we had a spurious writability readiness * event. Normally we'd just continue in the event loop until * next event. I haven't coded that here out of laziness. */ CHECK(0, 1); } else if (connecterr) { DPRINT(("connection failed as expected\n")); } else { DPRINT(("connection succeeded. Are you sure you edited the source and specified a remote computer that will reject the connection??\n")); CHECK(0, 1); } CHECK(0, m_p.del(sock)); close(sock); m_p.shutdown(); } /// Just test wakeUp. void testWakeUp() { CHECK(0, m_p.init()); CHECK(0, m_p.setSignum(SIGRTMIN)); DPRINT(("testWakeUp: Set up a wakeup pipe\n")); CHECK(0, m_p.initWakeUpPipe()); /* Clear the cached readiness bits */ m_p.waitAndDispatchEvents(0); DPRINT(("testWakeUp: Verify that waitForEvents() will block at moment\n")); CHECK(EWOULDBLOCK, m_p.waitAndDispatchEvents(0)); DPRINT(("testWakeUp: Verify that wakeUp will keep waitForEvents() from blocking\n")); CHECK(0, m_p.wakeUp()); int err = m_p.waitAndDispatchEvents(1000); if (err != 0) { fprintf(stderr, "Your kernel might not support SIGIO with pipes...\n" "see http://www.cs.helsinki.fi/linux/linux-kernel/2002-13/0191.html\n"); } CHECK(0, err); m_p.shutdown(); } /// Just test state caching. void testCaching(void) { DPRINT(("testCaching\n")); CHECK(0, m_p.init()); CHECK(0, m_p.setSignum(SIGRTMIN)); // Create a pipe so we have file descriptors to watch with Poller. // We use a pipe rather than a file or socket because it's easy to // generate readiness events by writing to one end of the pipe. CHECK(0, pipe(m_filedes)); CHECK(0, setNonblocking(m_filedes[0])); CHECK(0, setNonblocking(m_filedes[1])); // Watch our 2 test file descriptors, and count their readiness events. // The same client watches both file descriptors. CHECK(0, m_p.add(m_filedes[1], this, POLLOUT)); CHECK(0, m_p.add(m_filedes[0], this, POLLIN|POLLPRI)); for (int i=0; i<3; i++) { DPRINT(("testCaching: loop iter %d\n", i)); // Initially, the only thing that's ready is the writing end of the pipe // Verify we get notified that the expected number of fd's are readable/writable clear(); CHECK(0, m_p.waitAndDispatchEvents(0)); /* well, these should be 0 and 1, but Poller is allowed to give us spurious * wakeups for anything we're registered for, and it does initially for Poller_sigio. * Makes this hard to test, eh? */ #if 0 CHECK(0, m_eventCounts[log2(POLLIN)]); CHECK(1, m_eventCounts[log2(POLLOUT)]); #endif CHECK(0, m_eventCounts[log2(POLLPRI)]); CHECK(0, m_eventCounts[log2(POLLERR)]); CHECK(0, m_eventCounts[log2(POLLHUP)]); CHECK(0, m_eventCounts[log2(POLLNVAL)]); } CHECK(0, m_p.del(m_filedes[0])); CHECK(0, m_p.del(m_filedes[1])); close(m_filedes[0]); close(m_filedes[1]); m_p.shutdown(); } }; int main(int argc, char **argv) { (void) argc; (void) argv; // send all DPRINTs to logfile FILE *log = fopen("Poller_test.log", "w"); CHECKNE(0,log); DPRINT_SETFP(log); // setlinebuf(log) setvbuf(log, (char *)NULL, _IOLBF, 0); DPRINT_ENABLE(true); DPRINT(("poll test\n")); Poller_test ptp; ptp.unitTest(); DPRINT(("select test\n")); Poller_test pts; pts.unitTest(); #if HAVE_KQUEUE DPRINT(("kqueue test\n")); Poller_test ptk; ptk.unitTest(); #endif #if HAVE_DEVPOLL DPRINT(("devpoll test\n")); Poller_test ptd; ptd.unitTest(); #endif #if HAVE_F_SETSIG DPRINT(("sigio test\n")); Poller_test ptr; ptr.unitTest(); #endif #if HAVE_F_SETSIG && HAVE_F_SETAUXFL DPRINT(("sigfd test\n")); Poller_test ptf; ptf.unitTest(); #endif unlink("Poller_test.log"); printf("Poller_test: test passed\n"); exit(0); }