This is the mail archive of the cygwin@sources.redhat.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]

1.1.4 select and poll false positive past 32 descriptors


Hello,

When invoked in a way that may pend they fail
to update any result data past descriptors 0-31.
This typically means you get false positive bits.

NT 4.0 + cygwin1.dll 1.1.4 as built 2000-08-03.

It looks from the sources like the intent was to
support a large variable number of descriptors,
not (as may have been once) a fixed maximum.
If that's not so, the rest of this might not apply.

Sample output of test program with 40 sockets
plus three for the stdin, stdout, and stderr fds:

Press Enter to run the test
recv_fd_max = 42
read_fds before select that pends:
f9 ff ff ff ff 07 00 00
read_fds after select that pends:
01 00 00 00 ff 07 00 00
read_fds before select that polls:
f9 ff ff ff ff 07 00 00
read_fds after select that polls:
01 00 00 00 00 00 00 00

The pending select set the first read_fds word
but didn't clear the second 32-bit word. If I use
a no-time-timeout I get expected read_fds bits.
Nothing is talking to the sockets so they're 0s.

To return descriptors as ready for reading that
are not really ready will hang programs that do
reads based on select saying it's OK to do so.

A workaround for the problem is to use both of
these selects, one to pend and one to get bits.
I've tested up to around 200 this way, using a
larger FD_SETSIZE when compiling of course.

The idea for polling comes from select.cc code
which goes down a different path for that case.
I also ran under strace and saw that a pended
operation bases its returned size on a value n
which is always small, e.g. 1 or 2 and not the
expected value to copy out. That value should
be based on the maximum file descriptor given
by the user, but not work too hard beyond that.

A possible fix would be to change the loop that
does the counting to count n less conditionally.
Just a thought. I don't understand all the code.

By the way, an attempt to wait on a large poll
array suffers a similar fate -- a scan for active
elements finds the one with file descriptor 32
active when it shouldn't be. Probably a leftover
POLLIN came back on all the high descriptors.

Test program for select:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <unistd.h>

#define SOCKET_MAX 40

main() {
    int count;
    int recv_fd, recv_fd_max;
    int status;
    struct sockaddr_in addr;
    struct protoent *proto_ptr;
    fd_set recv_fds;
    fd_set zero_fds;
    fd_set read_fds, write_fds, except_fds;
    struct timeval tv;

    recv_fd_max = 0;
    FD_ZERO(&recv_fds);
    FD_ZERO(&zero_fds);

    addr.sin_family = PF_INET;
    proto_ptr = getprotobyname("udp");
    if (proto_ptr == 0) {
	printf("Could not find protocol number for udp\n");
	exit(1);
    }

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(0);
    for (count = 0; count < SOCKET_MAX; count++) {
	recv_fd = socket(AF_INET, SOCK_DGRAM, proto_ptr->p_proto);
	status = bind(recv_fd, (struct sockaddr *) &addr,
	    sizeof(struct sockaddr));
	if (status == -1) {
	    perror("Could not bind");
	    exit(1);
	}
	FD_SET(recv_fd, &recv_fds);
	if (recv_fd > recv_fd_max) {
	    recv_fd_max = recv_fd;
	}
    }

    FD_SET(0, &recv_fds);
    printf("Press Enter to run the test\n");

    memcpy(&read_fds, &recv_fds, sizeof(fd_set));
    memcpy(&write_fds, &zero_fds, sizeof(fd_set));
    memcpy(&except_fds, &zero_fds, sizeof(fd_set));

    printf("recv_fd_max = %d\n", recv_fd_max);
    print_fds("read_fds before select that pends:\n", &read_fds);

    status = select(recv_fd_max + 1,
	    &read_fds, &write_fds, &except_fds, NULL);
    if (status == -1) {
	printf("Select failed\n");
	exit(1);
    }

    print_fds("read_fds after select that pends:\n", &read_fds);

    memcpy(&read_fds, &recv_fds, sizeof(fd_set));
    memcpy(&write_fds, &zero_fds, sizeof(fd_set));
    memcpy(&except_fds, &zero_fds, sizeof(fd_set));

    print_fds("read_fds before select that polls:\n", &read_fds);

    tv.tv_sec = 0;
    tv.tv_usec = 0;
    status = select(recv_fd_max + 1,
	    &read_fds, &write_fds, &except_fds, &tv);
    if (status == -1) {
	printf("Select failed\n");
	exit(1);
    }

    print_fds("read_fds after select that polls:\n", &read_fds);
}

print_fds(label, fds)
    char *label;
    fd_set *fds;
{
    int count;

    printf("%s", label);
    for (count = 0; count < sizeof(fd_set); count++) {
	printf("%02x ", * ((unsigned char *) fds + count));
    }
    printf("\n");
}

Thank you!

Regards,
Ted

--
Want to unsubscribe from this list?
Send a message to cygwin-unsubscribe@sourceware.cygnus.com


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