The winsock select() implementation doesn't support all empty 'fd_set's.
Jan Dubois [Tue, 13 Oct 2009 23:46:58 +0000 (16:46 -0700)]
The code already contained a workaround for the special case

    select(undef, undef, undef, $sleep);

but didn't handle the case when actual bit vectors were passed in
that didn't have any bits set.

Fixes http://rt.perl.org/rt3/Public/Bug/Display.html?id=54544

t/op/sselect.t
win32/win32sck.c

index 0f877b1..332b7c0 100644 (file)
@@ -7,7 +7,7 @@ BEGIN {
 
 require 'test.pl';
 
-plan (9);
+plan (11);
 
 my $blank = "";
 eval {select undef, $blank, $blank, 0};
@@ -30,3 +30,14 @@ eval {select $blank, "a", $blank, 0};
 like ($@, qr/^Modification of a read-only value attempted/);
 eval {select $blank, $blank, "a", 0};
 like ($@, qr/^Modification of a read-only value attempted/);
+
+my $sleep = 3;
+my $t = time;
+select(undef, undef, undef, $sleep);
+ok(time-$t >= $sleep, "$sleep seconds have passed");
+
+my $empty = "";
+vec($empty,0,1) = 0;
+$t = time;
+select($empty, undef, undef, $sleep);
+ok(time-$t >= $sleep, "$sleep seconds have passed");
index 8ff0196..dd46bb3 100644 (file)
@@ -261,18 +261,8 @@ win32_select(int nfds, Perl_fd_set* rd, Perl_fd_set* wr, Perl_fd_set* ex, const
 #ifdef USE_SOCKETS_AS_HANDLES
     int i, fd, save_errno = errno;
     FD_SET nrd, nwr, nex;
+    bool just_sleep = TRUE;
 
-    /* winsock seems incapable of dealing with all three null fd_sets,
-     * so do the (millisecond) sleep as a special case
-     */
-    if (!(rd || wr || ex)) {
-       if (timeout)
-           Sleep(timeout->tv_sec  * 1000 +
-                 timeout->tv_usec / 1000);     /* do the best we can */
-       else
-           Sleep(UINT_MAX);
-       return 0;
-    }
     StartSockets();
 
     FD_ZERO(&nrd);
@@ -282,17 +272,32 @@ win32_select(int nfds, Perl_fd_set* rd, Perl_fd_set* wr, Perl_fd_set* ex, const
        if (rd && PERL_FD_ISSET(i,rd)) {
            fd = TO_SOCKET(i);
            FD_SET((unsigned)fd, &nrd);
+            just_sleep = FALSE;
        }
        if (wr && PERL_FD_ISSET(i,wr)) {
            fd = TO_SOCKET(i);
            FD_SET((unsigned)fd, &nwr);
+            just_sleep = FALSE;
        }
        if (ex && PERL_FD_ISSET(i,ex)) {
            fd = TO_SOCKET(i);
            FD_SET((unsigned)fd, &nex);
+            just_sleep = FALSE;
        }
     }
 
+    /* winsock seems incapable of dealing with all three fd_sets being empty,
+     * so do the (millisecond) sleep as a special case
+     */
+    if (just_sleep) {
+       if (timeout)
+           Sleep(timeout->tv_sec  * 1000 +
+                 timeout->tv_usec / 1000);     /* do the best we can */
+       else
+           Sleep(UINT_MAX);
+       return 0;
+    }
+
     errno = save_errno;
     SOCKET_TEST_ERROR(r = select(nfds, &nrd, &nwr, &nex, timeout));
     save_errno = errno;