⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 poller_test.cc

📁 实现了poll/epoll/devpoll等C++封装
💻 CC
字号:
/*-------------------------------------------------------------------------- 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 <fcntl.h> #include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/socket.h>#include <arpa/inet.h>#include <sys/socket.h>/*---------------------------------------------------------------------- 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<<bitnum) {	case POLLIN: return "POLLIN";	case POLLPRI: return "POLLPRI";	case POLLOUT: return "POLLOUT";	case POLLERR: return "POLLERR";	case POLLHUP: return "POLLHUP";	case POLLNVAL: return "POLLNVAL";	default: return "unknown";	}	return "bug";}template<class T>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<EVENTBITS; i++) {			if (eventflags & (1 << i)) {				m_eventCounts[i]++;				DPRINT(("notifyPollEvent: m_eventCounts[%d %s] = %d\n", i, eventbitname(i), m_eventCounts[i]));			}		}		(void) fd;		return 0;	}	void clear() {		DPRINT(("clear: clearing m_eventCounts\n"));		for (int i=0; i<EVENTBITS; i++) 			m_eventCounts[i] = 0;	}	/// The main function for this test.	void unitTest()	{		testWakeUp();		testCaching();		testRejection("127.0.0.1", 2222);		// To fully test nonblocking connections, must also test with a *remote*		// computer that will reject the connection.		//testRejection("192.168.123.254", 2222);		testMondo();	}	/// My original big hairy test.	void testMondo()	{		CHECK(0, m_p.init());		CHECK(0, m_p.setSignum(SIGRTMIN));		clear();		// 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]));		// Add a client to watch the standard input.  We'll delete it shortly.		CHECK(0, m_p.add(0, this, POLLIN));		// 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));		// Delete the client registered for standard input; this used to trigger a bug in Poller.		CHECK(0, m_p.del(0));		/* For sigio and friends, assume that fd isn't really ready, and		 * clear sigio's spurious readiness notification, as if we		 * had tried to do I/O and gotten an EWOULDBLOCK.		 * Err, except for POLLOUT on the writing end of the pipe, of course.		 */		m_p.clearReadiness(m_filedes[0], POLLIN|POLLPRI);		m_p.clearReadiness(m_filedes[1], POLLIN);		// 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));		CHECK(0, m_eventCounts[log2(POLLIN)]);		CHECK(1, m_eventCounts[log2(POLLOUT)]);		CHECK(0, m_eventCounts[log2(POLLPRI)]);		CHECK(0, m_eventCounts[log2(POLLERR)]);		CHECK(0, m_eventCounts[log2(POLLHUP)]);		CHECK(0, m_eventCounts[log2(POLLNVAL)]);		DPRINT(("unitTest: same thing, but mask off all events first\n"));		clear();		CHECK(0, m_p.andMask(m_filedes[0],0));		CHECK(0, m_p.andMask(m_filedes[1],0));		CHECK(EWOULDBLOCK, m_p.waitAndDispatchEvents(0));		CHECK(0, m_eventCounts[log2(POLLOUT)]);		DPRINT(("unitTest: make sure andMask can't set bits\n"));		CHECK(0, m_p.andMask(m_filedes[0],~0));		CHECK(0, m_p.andMask(m_filedes[1],~0));		CHECK(EWOULDBLOCK, m_p.waitAndDispatchEvents(0));		CHECK(0, m_eventCounts[log2(POLLOUT)]);		DPRINT(("unitTest: Flush cached events\n"));		CHECK(EWOULDBLOCK, m_p.waitAndDispatchEvents(100));		clear();		CHECK(EWOULDBLOCK, m_p.waitAndDispatchEvents(100));		CHECK(0, m_eventCounts[log2(POLLIN)]);		CHECK(0, m_eventCounts[log2(POLLPRI)]);		CHECK(0, m_eventCounts[log2(POLLOUT)]);		CHECK(0, m_eventCounts[log2(POLLERR)]);		CHECK(0, m_eventCounts[log2(POLLHUP)]);		CHECK(0, m_eventCounts[log2(POLLNVAL)]);		DPRINT(("unitTest: write to fd %d until full, make sure readiness goes off\n", m_filedes[1]));		CHECK(0, m_p.setMask(m_filedes[0],POLLIN|POLLPRI));		CHECK(0, m_p.setMask(m_filedes[1],POLLOUT));		while (write(m_filedes[1], "hi", 2) == 2)			;		CHECK(EWOULDBLOCK, errno);		/* We got an EWOULDBLOCK, which is exactly when we are		 * required to clear our readiness notification 		 */		m_p.clearReadiness(m_filedes[1], POLLOUT);		clear();		CHECK(0, m_p.waitAndDispatchEvents(1000));		// FIXME: fails with Poller_sigio		// After we write to the write end of the pipe, the write end is no longer ready,		// and the read end is ready for reading.		CHECK(1, m_eventCounts[log2(POLLIN)]);		CHECK(0, m_eventCounts[log2(POLLPRI)]);		CHECK(0, m_eventCounts[log2(POLLOUT)]);		CHECK(0, m_eventCounts[log2(POLLERR)]);		CHECK(0, m_eventCounts[log2(POLLHUP)]);		CHECK(0, m_eventCounts[log2(POLLNVAL)]);		// Check for bug reported by Jeon:		// Deleting a client who has events waiting and then calling getNextEvent		// returns the deleted file descriptor and a NULL client.		// At this point, we know that m_filedes[0] is readable.		CHECK(0, m_p.waitForEvents(0));		CHECK(0, m_p.del(m_filedes[0]));		Poller::PollEvent e;		CHECK(EWOULDBLOCK, m_p.getNextEvent( &e ));		m_p.shutdown();	}	/// Just test rejected connections.	void testRejection(const char *theHost, int thePort)	{		CHECK(0, m_p.init());		CHECK(0, m_p.setSignum(SIGRTMIN));		DPRINT(("Connect to a port that will refuse connections, verify error reported.\n"));		// Requires theHost = a nonlocal IP adr, thePort = port where that host will refuse a connection		// i.e. this test assumes that if you do `telnet theHost thePort`, you get a quick rejection		int sock = socket (AF_INET, SOCK_STREAM, 0);		CHECKNE(sock, -1);		CHECK(0, setNonblocking(sock));		struct sockaddr_in sin;		sin.sin_family=AF_INET;		sin.sin_port=htons(thePort);		sin.sin_addr.s_addr=inet_addr(theHost);		DPRINT(("connecting to host %s port %d, expect rejection\n", theHost, thePort));		CHECK(-1, connect (sock, reinterpret_cast<struct sockaddr *>(&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<Poller_poll> ptp;	ptp.unitTest();		DPRINT(("select test\n"));	Poller_test<Poller_select> pts;	pts.unitTest();	#if HAVE_KQUEUE	DPRINT(("kqueue test\n"));	Poller_test<Poller_kqueue> ptk;	ptk.unitTest();#endif#if HAVE_DEVPOLL	DPRINT(("devpoll test\n"));	Poller_test<Poller_devpoll> ptd;	ptd.unitTest();#endif#if HAVE_F_SETSIG	DPRINT(("sigio test\n"));	Poller_test<Poller_sigio> ptr;	ptr.unitTest();#endif#if HAVE_F_SETSIG && HAVE_F_SETAUXFL	DPRINT(("sigfd test\n"));	Poller_test<Poller_sigfd> ptf;	ptf.unitTest();#endif	unlink("Poller_test.log");	printf("Poller_test: test passed\n");	exit(0);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -