[PATCH] Tests: improved timeout handling on Windows

Maxim Dounin mdounin at mdounin.ru
Wed Jan 28 21:13:54 UTC 2026


# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1769631451 -10800
#      Wed Jan 28 23:17:31 2026 +0300
# Node ID cfa6c3971b24c0fcf1dcc8708454cc9348906dde
# Parent  9bf525096ac997f9b8da66443377977da167838f
Tests: improved timeout handling on Windows.

On Windows, the eval + alarm model does not work (see perlport(1) for alarm,
as well as 1678:d0025a0dead7 and 1705:99a9b8b50f21 for previous related
fixes).  Most notably, trying to rely on it for timeout handling around
accept() in the main test process results in infinite hangs on Windows
if something goes wrong and the timeout is indeed needed.

A similar issue in grpc.t was fixed in 1705:99a9b8b50f21, though other
similar constructs, originally introduced in 542:e7e3ced702f5, remained
in the code.  At least grpc_ssl.t was seen hanging indefinitely after
an SSL handshake failure with the "certificate is not yet valid" error
due to the time adjusted backwards during the test.

With this change, all eval + alarm constructs around accept() calls are
replaced with IO::Select can_read() calls (with appropriate timeouts)
to check if there is a connection to accept.  Code in grpc.t, which was
previously fixed in 1705:99a9b8b50f21, was slightly adjusted to match
other tests.

diff --git a/fastcgi_request_buffering.t b/fastcgi_request_buffering.t
--- a/fastcgi_request_buffering.t
+++ b/fastcgi_request_buffering.t
@@ -12,6 +12,8 @@ use warnings;
 use strict;
 
 use Test::More;
+
+use IO::Select;
 use Socket qw/ CRLF /;
 
 BEGIN { use FindBin; chdir($FindBin::Bin); }
@@ -233,23 +235,14 @@ EOF
 
 	$s = http($r, start => 1);
 
-	eval {
-		local $SIG{ALRM} = sub { die "timeout\n" };
-		local $SIG{PIPE} = sub { die "sigpipe\n" };
-		alarm(5);
-
-		$client = $server->accept();
-
-		log2c("(new connection $client)");
-
-		alarm(0);
-	};
-	alarm(0);
-	if ($@) {
-		log_in("died: $@");
+	if (!IO::Select->new($server)->can_read(5)) {
+		log2c("timeout");
 		return undef;
 	}
 
+	$client = $server->accept();
+	log2c("(new connection $client)");
+
 	$client->sysread(my $buf, 1024);
 	log2i($buf);
 
diff --git a/fastcgi_request_buffering_chunked.t b/fastcgi_request_buffering_chunked.t
--- a/fastcgi_request_buffering_chunked.t
+++ b/fastcgi_request_buffering_chunked.t
@@ -13,6 +13,8 @@ use warnings;
 use strict;
 
 use Test::More;
+
+use IO::Select;
 use Socket qw/ CRLF /;
 
 BEGIN { use FindBin; chdir($FindBin::Bin); }
@@ -262,23 +264,14 @@ EOF
 
 	$s = http($r, start => 1);
 
-	eval {
-		local $SIG{ALRM} = sub { die "timeout\n" };
-		local $SIG{PIPE} = sub { die "sigpipe\n" };
-		alarm(5);
-
-		$client = $server->accept();
-
-		log2c("(new connection $client)");
-
-		alarm(0);
-	};
-	alarm(0);
-	if ($@) {
-		log_in("died: $@");
+	if (!IO::Select->new($server)->can_read(5)) {
+		log2c("timeout");
 		return undef;
 	}
 
+	$client = $server->accept();
+	log2c("(new connection $client)");
+
 	$client->sysread(my $buf, 1024);
 	log2i($buf);
 
diff --git a/grpc.t b/grpc.t
--- a/grpc.t
+++ b/grpc.t
@@ -715,16 +715,14 @@ sub grpc {
 			{ name => 'te', value => 'trailers', mode => 2 }]});
 
 		if (!$extra{reuse}) {
-			if (IO::Select->new($server)->can_read(5)) {
-				$client = $server->accept();
-
-			} else {
-				log_in("timeout");
+			if (!IO::Select->new($server)->can_read(5)) {
+				log2c("timeout");
 				# connection could be unexpectedly reused
 				goto reused if $client;
 				return undef;
 			}
 
+			$client = $server->accept();
 			log2c("(new connection $client)");
 			$n++;
 
diff --git a/grpc_request_buffering.t b/grpc_request_buffering.t
--- a/grpc_request_buffering.t
+++ b/grpc_request_buffering.t
@@ -133,20 +133,12 @@ sub grpc {
 			{ name => 'content-length', value => length($body) }]});
 
 		if (!$extra{reuse}) {
-			eval {
-				local $SIG{ALRM} = sub { die "timeout\n" };
-				alarm(5);
-
-				$client = $server->accept() or return;
-
-				alarm(0);
-			};
-			alarm(0);
-			if ($@) {
-				log_in("died: $@");
+			if (!IO::Select->new($server)->can_read(5)) {
+				log2c("timeout");
 				return undef;
 			}
 
+			$client = $server->accept() or return;
 			log2c("(new connection $client)");
 
 			$client->sysread(my $buf, 24) == 24 or return; # preface
diff --git a/grpc_ssl.t b/grpc_ssl.t
--- a/grpc_ssl.t
+++ b/grpc_ssl.t
@@ -259,20 +259,12 @@ sub grpc {
 			{ name => 'te', value => 'trailers', mode => 2 }]});
 
 		if (!$extra{reuse}) {
-			eval {
-				local $SIG{ALRM} = sub { die "timeout\n" };
-				alarm(5);
-
-				$client = $server->accept() or return;
-
-				alarm(0);
-			};
-			alarm(0);
-			if ($@) {
-				log_in("died: $@");
+			if (!IO::Select->new($server)->can_read(5)) {
+				log2c("timeout");
 				return undef;
 			}
-
+	
+			$client = $server->accept() or return;
 			log2c("(new connection $client)");
 
 			$client->sysread(my $buf, 24) == 24 or return; # preface
diff --git a/proxy_request_buffering.t b/proxy_request_buffering.t
--- a/proxy_request_buffering.t
+++ b/proxy_request_buffering.t
@@ -12,6 +12,8 @@ use warnings;
 use strict;
 
 use Test::More;
+
+use IO::Select;
 use Socket qw/ CRLF /;
 
 BEGIN { use FindBin; chdir($FindBin::Bin); }
@@ -200,23 +202,14 @@ EOF
 
 	$s = http($r, start => 1);
 
-	eval {
-		local $SIG{ALRM} = sub { die "timeout\n" };
-		local $SIG{PIPE} = sub { die "sigpipe\n" };
-		alarm(5);
-
-		$client = $server->accept();
-
-		log2c("(new connection $client)");
-
-		alarm(0);
-	};
-	alarm(0);
-	if ($@) {
-		log_in("died: $@");
+	if (!IO::Select->new($server)->can_read(5)) {
+		log2c("timeout");
 		return undef;
 	}
 
+	$client = $server->accept();
+	log2c("(new connection $client)");
+
 	$client->sysread(my $buf, 1024);
 	log2i($buf);
 
diff --git a/proxy_request_buffering_chunked.t b/proxy_request_buffering_chunked.t
--- a/proxy_request_buffering_chunked.t
+++ b/proxy_request_buffering_chunked.t
@@ -12,6 +12,8 @@ use warnings;
 use strict;
 
 use Test::More;
+
+use IO::Select;
 use Socket qw/ CRLF /;
 
 BEGIN { use FindBin; chdir($FindBin::Bin); }
@@ -235,23 +237,14 @@ EOF
 
 	$s = http($r, start => 1);
 
-	eval {
-		local $SIG{ALRM} = sub { die "timeout\n" };
-		local $SIG{PIPE} = sub { die "sigpipe\n" };
-		alarm(5);
-
-		$client = $server->accept();
-
-		log2c("(new connection $client)");
-
-		alarm(0);
-	};
-	alarm(0);
-	if ($@) {
-		log_in("died: $@");
+	if (!IO::Select->new($server)->can_read(5)) {
+		log2c("timeout");
 		return undef;
 	}
 
+	$client = $server->accept();
+	log2c("(new connection $client)");
+
 	$client->sysread(my $buf, 1024);
 	log2i($buf);
 
diff --git a/proxy_request_buffering_ssl.t b/proxy_request_buffering_ssl.t
--- a/proxy_request_buffering_ssl.t
+++ b/proxy_request_buffering_ssl.t
@@ -12,6 +12,8 @@ use warnings;
 use strict;
 
 use Test::More;
+
+use IO::Select;
 use Socket qw/ CRLF /;
 
 BEGIN { use FindBin; chdir($FindBin::Bin); }
@@ -221,23 +223,14 @@ EOF
 
 	$s = http($r, start => 1);
 
-	eval {
-		local $SIG{ALRM} = sub { die "timeout\n" };
-		local $SIG{PIPE} = sub { die "sigpipe\n" };
-		alarm(5);
-
-		$client = $server->accept();
-
-		log2c("(new connection $client)");
-
-		alarm(0);
-	};
-	alarm(0);
-	if ($@) {
-		log_in("died: $@");
+	if (!IO::Select->new($server)->can_read(5)) {
+		log2c("timeout");
 		return undef;
 	}
 
+	$client = $server->accept();
+	log2c("(new connection $client)");
+
 	$client->sysread(my $buf, 1024);
 	log2i($buf);
 



More information about the nginx-devel mailing list