@@ -1533,7 +1533,7 @@ const TestOptions = struct {
15331533 request_target : []const u8 = undefined ,
15341534 request_headers : []std.http.Header = undefined ,
15351535 test_server_runtime_uri : ? []u8 = null ,
1536- server_ready : bool = false ,
1536+ server_ready : std.Thread.Semaphore = .{} ,
15371537 requests_processed : usize = 0 ,
15381538
15391539 const Self = @This ();
@@ -1588,11 +1588,18 @@ const TestOptions = struct {
15881588 return error .HeaderOrValueNotFound ;
15891589 }
15901590 fn waitForReady (self : * Self ) ! void {
1591- // Set 1 minute timeout...this is way longer than necessary
1592- var remaining_iters : isize = std .time .ns_per_min / 100 ;
1593- while (! self .server_ready and remaining_iters > 0 ) : (remaining_iters -= 1 )
1594- std .time .sleep (100 );
1595- if (! self .server_ready ) return error .TestServerTimeoutWaitingForReady ;
1591+ // Set 10s timeout...this is way longer than necessary
1592+ log .debug ("waiting for ready" , .{});
1593+ try self .server_ready .timedWait (1000 * std .time .ns_per_ms );
1594+ // var deadline = std.Thread.Futex.Deadline.init(1000 * std.time.ns_per_ms);
1595+ // if (self.futex_word.load(.acquire) != 0) return;
1596+ // log.debug("futex zero", .{});
1597+ // // note that this seems backwards from the documentation...
1598+ // deadline.wait(self.futex_word, 1) catch {
1599+ // log.err("futex value {d}", .{self.futex_word.load(.acquire)});
1600+ // return error.TestServerTimeoutWaitingForReady;
1601+ // };
1602+ log .debug ("the wait is over!" , .{});
15961603 }
15971604};
15981605
@@ -1620,9 +1627,9 @@ fn threadMain(options: *TestOptions) !void {
16201627 // var aa = arena.allocator();
16211628 // We're in control of all requests/responses, so this flag will tell us
16221629 // when it's time to shut down
1623- defer options .server_ready = true ; // In case remaining_requests = 0, we don't want to wait forever
1624- while ( options .server_remaining_requests > 0 ) {
1625- options .server_remaining_requests -= 1 ;
1630+ if ( options .server_remaining_requests = = 0 )
1631+ options .server_ready . post (); // This will cause the wait for server to return
1632+ while ( options . server_remaining_requests > 0 ) : ( options .server_remaining_requests -= 1 ) {
16261633 processRequest (options , & http_server ) catch | e | {
16271634 log .err ("Unexpected error processing request: {any}" , .{e });
16281635 if (@errorReturnTrace ()) | trace | {
@@ -1633,12 +1640,13 @@ fn threadMain(options: *TestOptions) !void {
16331640}
16341641
16351642fn processRequest (options : * TestOptions , net_server : * std.net.Server ) ! void {
1636- options .server_ready = true ;
1637- errdefer options .server_ready = false ;
16381643 log .debug (
16391644 "tid {d} (server): server waiting to accept. requests remaining: {d}" ,
1640- .{ std .Thread .getCurrentId (), options .server_remaining_requests + 1 },
1645+ .{ std .Thread .getCurrentId (), options .server_remaining_requests },
16411646 );
1647+ // options.futex_word.store(1, .release);
1648+ // errdefer options.futex_word.store(0, .release);
1649+ options .server_ready .post ();
16421650 var connection = try net_server .accept ();
16431651 defer connection .stream .close ();
16441652 var read_buffer : [1024 * 16 ]u8 = undefined ;
@@ -1657,8 +1665,6 @@ fn processRequest(options: *TestOptions, net_server: *std.net.Server) !void {
16571665}
16581666
16591667fn serveRequest (options : * TestOptions , request : * std.http.Server.Request ) ! void {
1660- options .server_ready = false ;
1661-
16621668 options .requests_processed += 1 ;
16631669 options .request_body = try (try request .reader ()).readAllAlloc (options .allocator , std .math .maxInt (usize ));
16641670 options .request_method = request .head .method ;
@@ -1728,7 +1734,8 @@ const TestSetup = struct {
17281734 // Not sure why we're getting sprayed here, but we have an arena allocator, and this
17291735 // is testing, so yolo
17301736 awshttp .endpoint_override = self .request_options .test_server_runtime_uri ;
1731- log .debug ("endpoint override set to {?s}" , .{awshttp .endpoint_override });
1737+ if (awshttp .endpoint_override == null ) return error .TestSetupStartFailure ;
1738+ std .log .debug ("endpoint override set to {?s}" , .{awshttp .endpoint_override });
17321739 self .creds = aws_auth .Credentials .init (
17331740 self .allocator ,
17341741 try self .allocator .dupe (u8 , "ACCESS" ),
@@ -1746,6 +1753,27 @@ const TestSetup = struct {
17461753 }
17471754
17481755 fn stop (self : * Self ) void {
1756+ if (self .request_options .server_remaining_requests > 0 )
1757+ if (test_error_log_enabled )
1758+ std .log .err (
1759+ "Test server has {d} request(s) remaining to issue! Draining" ,
1760+ .{self .request_options .server_remaining_requests },
1761+ )
1762+ else
1763+ std .log .info (
1764+ "Test server has {d} request(s) remaining to issue! Draining" ,
1765+ .{self .request_options .server_remaining_requests },
1766+ );
1767+
1768+ var rr = self .request_options .server_remaining_requests ;
1769+ while (rr > 0 ) : (rr -= 1 ) {
1770+ std .log .debug ("rr: {d}" , .{self .request_options .server_remaining_requests });
1771+ // We need to drain all remaining requests, otherwise the server
1772+ // will hang indefinitely
1773+ var client = std.http.Client { .allocator = self .allocator };
1774+ defer client .deinit ();
1775+ _ = client .fetch (.{ .location = .{ .url = self .request_options .test_server_runtime_uri .? } }) catch unreachable ;
1776+ }
17491777 self .server_thread .join ();
17501778 }
17511779
@@ -2391,3 +2419,33 @@ test "json_1_1: ECR timestamps" {
23912419 // try std.testing.expectEqual(@as(i64, 1.73859841557E9), call.response.authorization_data.?[0].expires_at.?);
23922420 try std .testing .expectEqual (@as (f128 , 1.7385984915E9 ), call .response .authorization_data .? [0 ].expires_at .? );
23932421}
2422+ var test_error_log_enabled = true ;
2423+ test "test server timeout works" {
2424+ // const old = std.testing.log_level;
2425+ // defer std.testing.log_level = old;
2426+ // std.testing.log_level = .debug;
2427+ // defer std.testing.log_level = old;
2428+ // std.testing.log_level = .debug;
2429+ test_error_log_enabled = false ;
2430+ defer test_error_log_enabled = true ;
2431+ std .log .debug ("test start" , .{});
2432+ const allocator = std .testing .allocator ;
2433+ var test_harness = TestSetup .init (.{
2434+ .allocator = allocator ,
2435+ .server_response =
2436+ \\{}
2437+ ,
2438+ .server_response_headers = &.{
2439+ .{ .name = "Content-Type" , .value = "application/json" },
2440+ .{ .name = "x-amzn-RequestId" , .value = "QBI72OUIN8U9M9AG6PCSADJL4JVV4KQNSO5AEMVJF66Q9ASUAAJG" },
2441+ },
2442+ });
2443+ defer test_harness .deinit ();
2444+ defer test_harness .creds .deinit (); // Usually this gets done during the call,
2445+ // but we're purposely not making a call
2446+ // here, so we have to deinit() manually
2447+ _ = try test_harness .start ();
2448+ std .log .debug ("harness started" , .{});
2449+ test_harness .stop ();
2450+ std .log .debug ("test complete" , .{});
2451+ }
0 commit comments