This is the mail archive of the cygwin@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

poll crashes with > 1200 fds


It looks like poll() is implemented on top of select() in cygwin.
This is fine unless you try to use it in a program with
more than 1024 fd's or so.  Here's the result of running a test app
that goes beyond that limit:

Program received signal SIGSEGV, Segmentation fault.
0x6106fe3a in select () from /usr/bin/cygwin1.dll
(gdb) bt
#0  0x6106fe3a in select () from /usr/bin/cygwin1.dll
#1  0x61610790 in ?? ()
#2  0x6106ca1d in select () from /usr/bin/cygwin1.dll
#3  0x6106c223 in select () from /usr/bin/cygwin1.dll
#4  0x61059e7d in poll () from /usr/bin/cygwin1.dll
#5  0x004012d3 in count_connects (ipadr=16777343, portnum=80, want=1200) at pollbug.c:120
#6  0x0040175d in main (argc=2, argv=0xa0418c8) at pollbug.c:211

And here's the test code.  To reproduce the bug, just run with argument 1500 or so.
- Dan

/*--------------------------------------------------------------------------
 Copyright 1999,2000, Dan Kegel http://www.kegel.com/
 See the file COPYING

 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
--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------
 Program to measure limits on network sockets, file descriptors, etc.
--------------------------------------------------------------------------*/

#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <time.h>
#include <unistd.h>

#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#endif

#define MAX_TEST 100000

int fds[MAX_TEST];

/*----------------------------------------------------------------------
 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 and 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
}

static int count_connects(int ipadr, int portnum, int want)
{
	int i;
	int maxfd;
	int nopen;
	struct pollfd pfds[MAX_TEST];
	int nok;
	int nrej;
	time_t end;

#ifdef VERBOSE
	printf("Starting connections until we can't start any more; will print what stops us to stderr.\n");
#endif
	for (i = 0; i < want; i++) {
		struct sockaddr_in srv_addr;
		int err;

		fds[i] = socket(AF_INET, SOCK_STREAM, 0);
		if (fds[i] < 0) {
#ifdef VERBOSE
			perror("socket");
#endif
			break;
		}
		setNonblocking(fds[i]);	/* else connect will wait until resources avail */

		memset((char *) &srv_addr, 0, sizeof(srv_addr));
		srv_addr.sin_family = AF_INET;
		srv_addr.sin_addr.s_addr = ipadr;
		srv_addr.sin_port = htons(portnum);

		err = connect(fds[i], (struct sockaddr *) &srv_addr, sizeof(srv_addr));
		if ((err != 0) && (errno != EINPROGRESS)) {
#ifdef VERBOSE
			perror("connect");
#endif
			break;
		}
		
		pfds[i].fd = fds[i];
		pfds[i].events = POLLOUT;
	}
	maxfd = i;
#ifdef VERBOSE
	printf("%d connections in progress...\n", maxfd);
#endif

	/* Now sit until they all connect or fail, or until five seconds
	 * pass, whichever comes first.
	 */
	end = time(0) + 6;
	for (nrej=nok=0, nopen=maxfd; nopen; ) {
		int nready;
		if ((end - time(0)) < 0)
			break;
		nready = poll(pfds, maxfd, 1000);
		if (nready < 0) {
			perror("poll");
			exit(1);
		}
		for (i=0; i<maxfd; i++) {
			if ((pfds[i].fd == -1) || !pfds[i].revents)
				continue;

			/* question: if a connect fails, will it show up first
			 * as a POLLERR, POLLHUP, or POLLOUT?
			 */
			if (pfds[i].revents & (POLLHUP|POLLERR)) {
				/* connect failed? */
				close(pfds[i].fd);
				nrej++;
			} else if (pfds[i].revents & POLLOUT) {
				/* check to see if connect succeeded */
				int connecterr = -1;
				socklen_t len = sizeof(connecterr);
				if (getsockopt(pfds[i].fd, SOL_SOCKET, SO_ERROR, (char *)&connecterr, &len) < 0) {
					perror("getsockopt");
					exit(1);
				}
				if (!connecterr) {
					nok++;
					/* keep socket open */
				} else if (connecterr ==  ECONNREFUSED) {
					close(pfds[i].fd);
					nrej++;
				}
			} else {
				printf("bad poll result: pfds[%d].fd %d, .revents %x\n", i, pfds[i].fd, pfds[i].revents);
				exit(1);
			}
			pfds[i].fd = -1;
			nopen--;
		}
	}

	/* Close 'em (might close a few extra, but that's ok) */
	for (i = 0; i < maxfd; i++)
		close(fds[i]);

#ifdef VERBOSE
	printf("%d connections accepted, %d rejected, %d pending\n", nok, nrej, nopen);
#endif
	return nok + nrej + nopen;
}

int main(int argc, char **argv)
{
	const char *ipadrstr;
	int ipadr;
	int portnum;
	int want;

	if (argc < 2) {
		printf("Usage: %s num_wanted [ipadr portnum]\n\
Tries to open the specified number of sockets in various ways.\n\
", argv[0]);
		exit(1);
	}
	want = atoi(argv[1]);
	if (want < 1 || want > MAX_TEST) {
		printf("Sorry, want must be between 1 and %d\n", MAX_TEST);
		exit(1);
	}

	if (argc < 3)
		ipadrstr = "127.0.0.1";
	else
		ipadrstr = argv[2];
	ipadr = inet_addr(ipadrstr);
	if (ipadr == -1) {
		printf("Bad ip address %s.\n", ipadrstr);
		exit(1);
	}

	if (argc < 4)
		portnum = 80;
	else {
		const char *portnumstr;
		portnumstr = argv[3];
		portnum = atoi(portnumstr);
		if ((portnum <= 0) || (portnum > 65535)) {
			printf("Bad port number '%s'.  Must be number between 1 and 65535.\n", portnumstr);
			exit(1);
		}
	}

	printf("Can start %d nonblocking connect()'s\n", count_connects(ipadr, portnum, want));
	return 0;
}

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]