Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package aws-c-http for openSUSE:Factory checked in at 2024-08-09 16:14:43 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/aws-c-http (Old) and /work/SRC/openSUSE:Factory/.aws-c-http.new.7232 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "aws-c-http" Fri Aug 9 16:14:43 2024 rev:6 rq:1192574 version:0.8.6 Changes: -------- --- /work/SRC/openSUSE:Factory/aws-c-http/aws-c-http.changes 2024-08-01 22:06:48.498354445 +0200 +++ /work/SRC/openSUSE:Factory/.aws-c-http.new.7232/aws-c-http.changes 2024-08-09 16:15:12.292286136 +0200 @@ -1,0 +2,7 @@ +Wed Aug 7 13:24:54 UTC 2024 - John Paul Adrian Glaubitz <adrian.glaubitz@suse.com> + +- Update to version 0.8.6 + * Connection Manager Acquisition Timeout by @waahm7 in (#479) + * Support MaxPendingConnectionAcquisitions by @waahm7 in (#481) + +------------------------------------------------------------------- Old: ---- v0.8.5.tar.gz New: ---- v0.8.6.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ aws-c-http.spec ++++++ --- /var/tmp/diff_new_pack.j8sbL3/_old 2024-08-09 16:15:12.896311348 +0200 +++ /var/tmp/diff_new_pack.j8sbL3/_new 2024-08-09 16:15:12.896311348 +0200 @@ -19,7 +19,7 @@ %define library_version 1.0.0 %define library_soversion 1_0_0 Name: aws-c-http -Version: 0.8.5 +Version: 0.8.6 Release: 0 Summary: C99 implementation of the HTTP/1.1 and HTTP/2 specifications License: Apache-2.0 ++++++ v0.8.5.tar.gz -> v0.8.6.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.8.5/include/aws/http/connection_manager.h new/aws-c-http-0.8.6/include/aws/http/connection_manager.h --- old/aws-c-http-0.8.5/include/aws/http/connection_manager.h 2024-07-29 22:12:51.000000000 +0200 +++ new/aws-c-http-0.8.6/include/aws/http/connection_manager.h 2024-08-06 00:51:26.000000000 +0200 @@ -126,6 +126,20 @@ uint64_t max_connection_idle_in_milliseconds; /** + * If set to a non-zero value, aws_http_connection_manager_acquire_connection() calls + * will give up after waiting this long for a connection from the pool, + * failing with error AWS_ERROR_HTTP_CONNECTION_MANAGER_ACQUISITION_TIMEOUT. + */ + uint64_t connection_acquisition_timeout_ms; + + /* + * If set to a non-zero value, aws_http_connection_manager_acquire_connection() calls will fail with + * AWS_ERROR_HTTP_CONNECTION_MANAGER_MAX_PENDING_ACQUISITIONS_EXCEEDED if there are already pending acquisitions + * equal to `max_pending_connection_acquisitions`. + */ + uint64_t max_pending_connection_acquisitions; + + /** * THIS IS AN EXPERIMENTAL AND UNSTABLE API * (Optional) * An array of network interface names. The manager will distribute the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.8.5/include/aws/http/http.h new/aws-c-http-0.8.6/include/aws/http/http.h --- old/aws-c-http-0.8.5/include/aws/http/http.h 2024-07-29 22:12:51.000000000 +0200 +++ new/aws-c-http-0.8.6/include/aws/http/http.h 2024-08-06 00:51:26.000000000 +0200 @@ -60,6 +60,8 @@ AWS_ERROR_HTTP_MANUAL_WRITE_NOT_ENABLED, AWS_ERROR_HTTP_MANUAL_WRITE_HAS_COMPLETED, AWS_ERROR_HTTP_RESPONSE_FIRST_BYTE_TIMEOUT, + AWS_ERROR_HTTP_CONNECTION_MANAGER_ACQUISITION_TIMEOUT, + AWS_ERROR_HTTP_CONNECTION_MANAGER_MAX_PENDING_ACQUISITIONS_EXCEEDED, AWS_ERROR_HTTP_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_HTTP_PACKAGE_ID) }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.8.5/source/connection_manager.c new/aws-c-http-0.8.6/source/connection_manager.c --- old/aws-c-http-0.8.5/source/connection_manager.c 2024-07-29 22:12:51.000000000 +0200 +++ new/aws-c-http-0.8.6/source/connection_manager.c 2024-08-06 00:51:26.000000000 +0200 @@ -193,7 +193,10 @@ struct aws_linked_list idle_connections; /* - * The set of all incomplete connection acquisition requests + * The set of all incomplete connection acquisition requests. + * This must be a FIFO list. When connections are requested by the user, they are added to the back. When we need to + * complete the acquisition, we pop from the front. In this way, the list is always sorted from the oldest (in terms + * of timeout timestamp) to the newest and we can cull it similar to idle_connections. */ struct aws_linked_list pending_acquisitions; @@ -286,6 +289,10 @@ */ uint64_t max_connection_idle_in_milliseconds; + uint64_t connection_acquisition_timeout_ms; + + uint64_t max_pending_connection_acquisitions; + /* * Task to cull idle connections. This task is run periodically on the cull_event_loop if a non-zero * culling time interval is specified. @@ -392,6 +399,7 @@ struct aws_http_connection *connection; int error_code; struct aws_channel_task acquisition_task; + uint64_t timeout_timestamp; }; static void s_connection_acquisition_task( @@ -758,26 +766,17 @@ } static void s_cull_task(struct aws_task *task, void *arg, enum aws_task_status status); -static void s_schedule_connection_culling(struct aws_http_connection_manager *manager) { - if (manager->max_connection_idle_in_milliseconds == 0) { - return; - } - - if (manager->cull_task == NULL) { - manager->cull_task = aws_mem_calloc(manager->allocator, 1, sizeof(struct aws_task)); - aws_task_init(manager->cull_task, s_cull_task, manager, "cull_idle_connections"); - /* For the task to properly run and cancel, we need to keep manager alive */ - aws_ref_count_acquire(&manager->internal_ref_count); - } - if (manager->cull_event_loop == NULL) { - manager->cull_event_loop = aws_event_loop_group_get_next_loop(manager->bootstrap->event_loop_group); +/* + * Calculates the next timestamp the idle connections should be culled. Manager lock must be held somewhere in the call + * stack. Returns UINT64_MAX if max_connection_idle_in_milliseconds is not set. + */ +static uint64_t s_calculate_idle_connection_cull_task_time_synced(struct aws_http_connection_manager *manager) { + if (manager->max_connection_idle_in_milliseconds == 0) { + return UINT64_MAX; } - AWS_FATAL_ASSERT(manager->cull_event_loop != NULL); - uint64_t cull_task_time = 0; - aws_mutex_lock(&manager->lock); const struct aws_linked_list_node *end = aws_linked_list_end(&manager->idle_connections); struct aws_linked_list_node *oldest_node = aws_linked_list_begin(&manager->idle_connections); if (oldest_node != end) { @@ -799,11 +798,68 @@ now + aws_timestamp_convert( manager->max_connection_idle_in_milliseconds, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL); } - aws_mutex_unlock(&manager->lock); - aws_event_loop_schedule_task_future(manager->cull_event_loop, manager->cull_task, cull_task_time); + return cull_task_time; +} + +/* + * Calculates the next timestamp the pending acquisitions should be culled. Manager lock must be held somewhere in the + * call stack. Returns UINT64_MAX if connection_acquisition_timeout_ms is not set. + */ +static uint64_t s_calculate_pending_acquisition_cull_task_time_synced(struct aws_http_connection_manager *manager) { + if (manager->connection_acquisition_timeout_ms == 0) { + return UINT64_MAX; + } + + uint64_t cull_task_time = 0; + + const struct aws_linked_list_node *end = aws_linked_list_end(&manager->pending_acquisitions); + struct aws_linked_list_node *oldest_node = aws_linked_list_begin(&manager->pending_acquisitions); + if (oldest_node != end) { + /* + * front of the list has the closest cull time + */ + struct aws_http_connection_acquisition *oldest_pending_acquire = + AWS_CONTAINER_OF(oldest_node, struct aws_http_connection_acquisition, node); + cull_task_time = oldest_pending_acquire->timeout_timestamp; + } else { + /* + * There are no acquisition in the list, so the absolute minimum anything could be culled is the full + * culling interval from now. + */ + uint64_t now = 0; + manager->system_vtable->aws_high_res_clock_get_ticks(&now); + cull_task_time = + now + aws_timestamp_convert( + manager->connection_acquisition_timeout_ms, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL); + } + return cull_task_time; +} + +static void s_schedule_culling(struct aws_http_connection_manager *manager) { + if (manager->max_connection_idle_in_milliseconds == 0 && manager->connection_acquisition_timeout_ms == 0) { + return; + } + + if (manager->cull_task == NULL) { + manager->cull_task = aws_mem_calloc(manager->allocator, 1, sizeof(struct aws_task)); + aws_task_init(manager->cull_task, s_cull_task, manager, "cull_idle_connections"); + /* For the task to properly run and cancel, we need to keep manager alive */ + aws_ref_count_acquire(&manager->internal_ref_count); + } + + if (manager->cull_event_loop == NULL) { + manager->cull_event_loop = aws_event_loop_group_get_next_loop(manager->bootstrap->event_loop_group); + } + AWS_FATAL_ASSERT(manager->cull_event_loop != NULL); + + aws_mutex_lock(&manager->lock); + uint64_t idle_cull_time = s_calculate_idle_connection_cull_task_time_synced(manager); + uint64_t acquisition_cull_time = s_calculate_pending_acquisition_cull_task_time_synced(manager); + aws_mutex_unlock(&manager->lock); - return; + aws_event_loop_schedule_task_future( + manager->cull_event_loop, manager->cull_task, aws_min_u64(idle_cull_time, acquisition_cull_time)); } struct aws_http_connection_manager *aws_http_connection_manager_new( @@ -900,6 +956,9 @@ manager->shutdown_complete_user_data = options->shutdown_complete_user_data; manager->enable_read_back_pressure = options->enable_read_back_pressure; manager->max_connection_idle_in_milliseconds = options->max_connection_idle_in_milliseconds; + manager->connection_acquisition_timeout_ms = options->connection_acquisition_timeout_ms; + manager->max_pending_connection_acquisitions = options->max_pending_connection_acquisitions; + if (options->proxy_ev_settings) { manager->proxy_ev_settings = *options->proxy_ev_settings; } @@ -938,7 +997,7 @@ } /* NOTHING can fail after here */ - s_schedule_connection_culling(manager); + s_schedule_culling(manager); AWS_LOGF_INFO(AWS_LS_HTTP_CONNECTION_MANAGER, "id=%p: Successfully created", (void *)manager); @@ -1227,6 +1286,22 @@ request->user_data = user_data; request->manager = manager; + if (manager->connection_acquisition_timeout_ms) { + uint64_t acquire_start_timestamp = 0; + if (manager->system_vtable->aws_high_res_clock_get_ticks(&acquire_start_timestamp) == AWS_OP_SUCCESS) { + request->timeout_timestamp = + acquire_start_timestamp + + aws_timestamp_convert( + manager->connection_acquisition_timeout_ms, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL); + } else { + AWS_LOGF_WARN( + AWS_LS_HTTP_CONNECTION_MANAGER, + "id=%p: Failed to get current timestamp using aws_high_res_clock_get_ticks function. Ignoring the " + "connection_acquisition_timeout_ms value. ", + (void *)manager); + } + } + struct aws_connection_management_transaction work; s_aws_connection_management_transaction_init(&work, manager); @@ -1235,8 +1310,14 @@ /* It's a use after free crime, we don't want to handle */ AWS_FATAL_ASSERT(manager->state == AWS_HCMST_READY); - aws_linked_list_push_back(&manager->pending_acquisitions, &request->node); - ++manager->pending_acquisition_count; + if (manager->max_pending_connection_acquisitions == 0 || + manager->pending_acquisition_count < manager->max_pending_connection_acquisitions) { + aws_linked_list_push_back(&manager->pending_acquisitions, &request->node); + ++manager->pending_acquisition_count; + } else { + request->error_code = AWS_ERROR_HTTP_CONNECTION_MANAGER_MAX_PENDING_ACQUISITIONS_EXCEEDED; + aws_linked_list_push_back(&work.completions, &request->node); + } s_aws_http_connection_manager_build_transaction(&work); @@ -1545,10 +1626,11 @@ s_aws_http_connection_manager_execute_transaction(&work); } -static void s_cull_idle_connections(struct aws_http_connection_manager *manager) { - AWS_LOGF_INFO(AWS_LS_HTTP_CONNECTION_MANAGER, "id=%p: culling idle connections", (void *)manager); +static void s_cull_task_impl(struct aws_http_connection_manager *manager) { + AWS_LOGF_INFO( + AWS_LS_HTTP_CONNECTION_MANAGER, "id=%p: culling idle connections and pending acquisitions", (void *)manager); - if (manager == NULL || manager->max_connection_idle_in_milliseconds == 0) { + if (manager == NULL) { return; } @@ -1564,26 +1646,53 @@ /* Only if we're not shutting down */ if (manager->state == AWS_HCMST_READY) { - const struct aws_linked_list_node *end = aws_linked_list_end(&manager->idle_connections); - struct aws_linked_list_node *current_node = aws_linked_list_begin(&manager->idle_connections); - while (current_node != end) { - struct aws_linked_list_node *node = current_node; - struct aws_idle_connection *current_idle_connection = - AWS_CONTAINER_OF(node, struct aws_idle_connection, node); - if (current_idle_connection->cull_timestamp > now) { - break; + /* cull idle connections */ + if (manager->max_connection_idle_in_milliseconds != 0) { + const struct aws_linked_list_node *idle_connections_end = aws_linked_list_end(&manager->idle_connections); + struct aws_linked_list_node *idle_connections_current = aws_linked_list_begin(&manager->idle_connections); + while (idle_connections_current != idle_connections_end) { + struct aws_linked_list_node *node = idle_connections_current; + struct aws_idle_connection *current_idle_connection = + AWS_CONTAINER_OF(node, struct aws_idle_connection, node); + if (current_idle_connection->cull_timestamp > now) { + break; + } + + idle_connections_current = aws_linked_list_next(idle_connections_current); + aws_linked_list_remove(node); + aws_linked_list_push_back(&work.connections_to_release, node); + --manager->idle_connection_count; + + AWS_LOGF_DEBUG( + AWS_LS_HTTP_CONNECTION_MANAGER, + "id=%p: culling idle connection (%p)", + (void *)manager, + (void *)current_idle_connection->connection); } + } - current_node = aws_linked_list_next(current_node); - aws_linked_list_remove(node); - aws_linked_list_push_back(&work.connections_to_release, node); - --manager->idle_connection_count; - - AWS_LOGF_DEBUG( - AWS_LS_HTTP_CONNECTION_MANAGER, - "id=%p: culling idle connection (%p)", - (void *)manager, - (void *)current_idle_connection->connection); + /* cull pending acquisitions */ + if (manager->connection_acquisition_timeout_ms != 0) { + const struct aws_linked_list_node *pending_acquisitions_end = + aws_linked_list_end(&manager->pending_acquisitions); + struct aws_linked_list_node *pending_acquisitions_current = + aws_linked_list_begin(&manager->pending_acquisitions); + while (pending_acquisitions_current != pending_acquisitions_end) { + struct aws_linked_list_node *node = pending_acquisitions_current; + struct aws_http_connection_acquisition *current_pending_acquire = + AWS_CONTAINER_OF(node, struct aws_http_connection_acquisition, node); + if (current_pending_acquire->timeout_timestamp > now) { + break; + } + + pending_acquisitions_current = aws_linked_list_next(pending_acquisitions_current); + s_aws_http_connection_manager_move_front_acquisition( + manager, NULL, AWS_ERROR_HTTP_CONNECTION_MANAGER_ACQUISITION_TIMEOUT, &work.completions); + AWS_LOGF_DEBUG( + AWS_LS_HTTP_CONNECTION_MANAGER, + "id=%p: Failing pending acquisition due to timeout", + (void *)manager); + } } } @@ -1601,10 +1710,9 @@ } struct aws_http_connection_manager *manager = arg; + s_cull_task_impl(manager); - s_cull_idle_connections(manager); - - s_schedule_connection_culling(manager); + s_schedule_culling(manager); } void aws_http_connection_manager_fetch_metrics( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.8.5/source/http.c new/aws-c-http-0.8.6/source/http.c --- old/aws-c-http-0.8.5/source/http.c 2024-07-29 22:12:51.000000000 +0200 +++ new/aws-c-http-0.8.6/source/http.c 2024-08-06 00:51:26.000000000 +0200 @@ -151,6 +151,12 @@ AWS_DEFINE_ERROR_INFO_HTTP( AWS_ERROR_HTTP_RESPONSE_FIRST_BYTE_TIMEOUT, "The server does not begin responding within the configuration after a request is fully sent."), + AWS_DEFINE_ERROR_INFO_HTTP( + AWS_ERROR_HTTP_CONNECTION_MANAGER_ACQUISITION_TIMEOUT, + "Connection Manager failed to acquire a connection within the defined timeout."), + AWS_DEFINE_ERROR_INFO_HTTP( + AWS_ERROR_HTTP_CONNECTION_MANAGER_MAX_PENDING_ACQUISITIONS_EXCEEDED, + "Max pending acquisitions reached"), }; /* clang-format on */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.8.5/tests/CMakeLists.txt new/aws-c-http-0.8.6/tests/CMakeLists.txt --- old/aws-c-http-0.8.5/tests/CMakeLists.txt 2024-07-29 22:12:51.000000000 +0200 +++ new/aws-c-http-0.8.6/tests/CMakeLists.txt 2024-08-06 00:51:26.000000000 +0200 @@ -519,6 +519,7 @@ # unit tests where connections are mocked add_net_test_case(test_connection_manager_setup_shutdown) add_net_test_case(test_connection_manager_acquire_release_mix_synchronous) +add_net_test_case(test_connection_manager_acquisition_timeout) add_net_test_case(test_connection_manager_connect_callback_failure) add_net_test_case(test_connection_manager_connect_immediate_failure) add_net_test_case(test_connection_manager_proxy_setup_shutdown) @@ -539,6 +540,7 @@ add_net_test_case(test_connection_manager_acquire_release) add_net_test_case(test_connection_manager_close_and_release) add_net_test_case(test_connection_manager_acquire_release_mix) +add_net_test_case(test_connection_manager_max_pending_acquisitions) # Integration test that requires proxy envrionment in us-east-1 region. # TODO: test the server name validation properly diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-http-0.8.5/tests/test_connection_manager.c new/aws-c-http-0.8.6/tests/test_connection_manager.c --- old/aws-c-http-0.8.5/tests/test_connection_manager.c 2024-07-29 22:12:51.000000000 +0200 +++ new/aws-c-http-0.8.6/tests/test_connection_manager.c 2024-08-06 00:51:26.000000000 +0200 @@ -53,6 +53,9 @@ struct aws_tls_connection_options *env_configured_tls; size_t max_connections; uint64_t max_connection_idle_in_ms; + uint64_t connection_acquisition_timeout_ms; + uint64_t max_pending_connection_acquisitions; + uint64_t starting_mock_time; bool http2; struct aws_http2_setting *initial_settings_array; @@ -82,6 +85,8 @@ struct aws_condition_variable signal; struct aws_array_list connections; + /* aws_array_list<uint32> of error codes */ + struct aws_array_list connection_errors_list; size_t connection_errors; size_t connection_releases; @@ -154,6 +159,9 @@ ASSERT_SUCCESS( aws_array_list_init_dynamic(&tester->connections, tester->allocator, 10, sizeof(struct aws_http_connection *))); + ASSERT_SUCCESS( + aws_array_list_init_dynamic(&tester->connection_errors_list, tester->allocator, 10, sizeof(uint32_t))); + aws_mutex_init(&tester->mock_time_lock); s_tester_set_mock_time(options->starting_mock_time); @@ -220,6 +228,8 @@ .shutdown_complete_user_data = tester, .shutdown_complete_callback = s_cm_tester_on_cm_shutdown_complete, .max_connection_idle_in_milliseconds = options->max_connection_idle_in_ms, + .connection_acquisition_timeout_ms = options->connection_acquisition_timeout_ms, + .max_pending_connection_acquisitions = options->max_pending_connection_acquisitions, .http2_prior_knowledge = !options->use_tls && options->http2, .initial_settings_array = options->initial_settings_array, .num_initial_settings = options->num_initial_settings, @@ -343,6 +353,7 @@ AWS_FATAL_ASSERT(aws_mutex_lock(&tester->lock) == AWS_OP_SUCCESS); if (connection == NULL) { + aws_array_list_push_back(&tester->connection_errors_list, &error_code); ++tester->connection_errors; } else { aws_array_list_push_back(&tester->connections, &connection); @@ -408,6 +419,7 @@ ASSERT_SUCCESS(s_release_connections(aws_array_list_length(&tester->connections), false)); aws_array_list_clean_up(&tester->connections); + aws_array_list_clean_up(&tester->connection_errors_list); for (size_t i = 0; i < aws_array_list_length(&tester->mock_connections); ++i) { struct mock_connection *mock = NULL; @@ -733,6 +745,36 @@ } AWS_TEST_CASE(test_connection_manager_acquire_release_mix, s_test_connection_manager_acquire_release_mix); +static int s_test_connection_manager_max_pending_acquisitions(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + size_t num_connections = 2; + size_t num_pending_connections = 3; + struct cm_tester_options options = { + .allocator = allocator, + .max_connections = num_connections, + .max_pending_connection_acquisitions = num_connections, + }; + + ASSERT_SUCCESS(s_cm_tester_init(&options)); + + s_acquire_connections(num_connections + num_pending_connections); + + ASSERT_SUCCESS(s_wait_on_connection_reply_count(num_connections + num_pending_connections)); + ASSERT_UINT_EQUALS(num_pending_connections, s_tester.connection_errors); + for (size_t i = 0; i < num_pending_connections; i++) { + uint32_t error_code; + aws_array_list_get_at(&s_tester.connection_errors_list, &error_code, i); + ASSERT_UINT_EQUALS(AWS_ERROR_HTTP_CONNECTION_MANAGER_MAX_PENDING_ACQUISITIONS_EXCEEDED, error_code); + } + ASSERT_SUCCESS(s_release_connections(num_connections, false)); + + ASSERT_SUCCESS(s_cm_tester_clean_up()); + + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_connection_manager_max_pending_acquisitions, s_test_connection_manager_max_pending_acquisitions); + static int s_aws_http_connection_manager_create_connection_sync_mock( const struct aws_http_client_connection_options *options) { struct cm_tester *tester = &s_tester; @@ -890,8 +932,6 @@ ASSERT_SUCCESS(s_release_connections(1, false)); } - ASSERT_SUCCESS(s_wait_on_connection_reply_count(15)); - for (size_t i = 15; i < 20; ++i) { ASSERT_SUCCESS(s_release_connections(1, false)); @@ -908,6 +948,46 @@ test_connection_manager_acquire_release_mix_synchronous, s_test_connection_manager_acquire_release_mix_synchronous); +static int s_test_connection_manager_acquisition_timeout(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + size_t num_connections = 2; + size_t num_pending_connections = 3; + struct cm_tester_options options = { + .allocator = allocator, + .max_connections = num_connections, + .mock_table = &s_synchronous_mocks, + .connection_acquisition_timeout_ms = 1000, + .starting_mock_time = 0, + }; + + ASSERT_SUCCESS(s_cm_tester_init(&options)); + + s_add_mock_connections(num_connections, AWS_NCRT_SUCCESS, true); + s_acquire_connections(num_connections + num_pending_connections); + + /* advance fake time enough to cause the acquire connections to timeout, also sleep for real to give the cull task + * a chance to run in the real event loop + */ + uint64_t one_sec_in_nanos = aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); + s_tester_set_mock_time(2 * one_sec_in_nanos); + aws_thread_current_sleep(2 * one_sec_in_nanos); + + ASSERT_SUCCESS(s_wait_on_connection_reply_count(num_connections + num_pending_connections)); + ASSERT_UINT_EQUALS(num_pending_connections, s_tester.connection_errors); + for (size_t i = 0; i < num_pending_connections; i++) { + uint32_t error_code; + aws_array_list_get_at(&s_tester.connection_errors_list, &error_code, i); + ASSERT_UINT_EQUALS(AWS_ERROR_HTTP_CONNECTION_MANAGER_ACQUISITION_TIMEOUT, error_code); + } + ASSERT_SUCCESS(s_release_connections(num_connections, false)); + + ASSERT_SUCCESS(s_cm_tester_clean_up()); + + return AWS_OP_SUCCESS; +} +AWS_TEST_CASE(test_connection_manager_acquisition_timeout, s_test_connection_manager_acquisition_timeout); + static int s_test_connection_manager_connect_callback_failure(struct aws_allocator *allocator, void *ctx) { (void)ctx;