in source/connection_manager.c [995:1118]
static void s_aws_http_connection_manager_execute_transaction(struct aws_connection_management_transaction *work) {
struct aws_http_connection_manager *manager = work->manager;
bool should_destroy = work->should_destroy_manager;
int representative_error = 0;
size_t new_connection_failures = 0;
/*
* Step 1 - Logging
*/
s_aws_http_connection_manager_log_snapshot(manager, &work->snapshot);
/*
* Step 2 - Perform any requested connection releases
*/
while (!aws_linked_list_empty(&work->connections_to_release)) {
struct aws_linked_list_node *node = aws_linked_list_pop_back(&work->connections_to_release);
struct aws_idle_connection *idle_connection = AWS_CONTAINER_OF(node, struct aws_idle_connection, node);
AWS_LOGF_INFO(
AWS_LS_HTTP_CONNECTION_MANAGER,
"id=%p: Releasing connection (id=%p)",
(void *)manager,
(void *)idle_connection->connection);
manager->system_vtable->release_connection(idle_connection->connection);
aws_mem_release(idle_connection->allocator, idle_connection);
}
if (work->connection_to_release) {
AWS_LOGF_INFO(
AWS_LS_HTTP_CONNECTION_MANAGER,
"id=%p: Releasing connection (id=%p)",
(void *)manager,
(void *)work->connection_to_release);
manager->system_vtable->release_connection(work->connection_to_release);
}
/*
* Step 3 - Make new connections
*/
struct aws_array_list errors;
AWS_ZERO_STRUCT(errors);
/* Even if we can't init this array, we still need to invoke error callbacks properly */
bool push_errors = false;
if (work->new_connections > 0) {
AWS_LOGF_INFO(
AWS_LS_HTTP_CONNECTION_MANAGER,
"id=%p: Requesting %zu new connections from http",
(void *)manager,
work->new_connections);
push_errors = aws_array_list_init_dynamic(&errors, work->allocator, work->new_connections, sizeof(int)) ==
AWS_ERROR_SUCCESS;
}
for (size_t i = 0; i < work->new_connections; ++i) {
if (s_aws_http_connection_manager_new_connection(manager)) {
++new_connection_failures;
representative_error = aws_last_error();
if (push_errors) {
AWS_FATAL_ASSERT(aws_array_list_push_back(&errors, &representative_error) == AWS_OP_SUCCESS);
}
}
}
if (new_connection_failures > 0) {
/*
* We failed and aren't going to receive a callback, but the current state assumes we will receive
* a callback. So we need to re-lock and update the state ourselves.
*/
aws_mutex_lock(&manager->lock);
AWS_FATAL_ASSERT(manager->pending_connects_count >= new_connection_failures);
manager->pending_connects_count -= new_connection_failures;
/*
* Rather than failing one acquisition for each connection failure, if there's at least one
* connection failure, we instead fail all excess acquisitions, since there's no pending
* connect that will necessarily resolve them.
*
* Try to correspond an error with the acquisition failure, but as a fallback just use the
* representative error.
*/
size_t i = 0;
while (manager->pending_acquisition_count > manager->pending_connects_count) {
int error = representative_error;
if (i < aws_array_list_length(&errors)) {
aws_array_list_get_at(&errors, &error, i);
}
AWS_LOGF_DEBUG(
AWS_LS_HTTP_CONNECTION_MANAGER,
"id=%p: Failing excess connection acquisition with error code %d",
(void *)manager,
(int)error);
s_aws_http_connection_manager_move_front_acquisition(manager, NULL, error, &work->completions);
++i;
}
should_destroy = s_aws_http_connection_manager_should_destroy(manager);
aws_mutex_unlock(&manager->lock);
}
/*
* Step 4 - Perform acquisition callbacks
*/
s_aws_http_connection_manager_complete_acquisitions(&work->completions, work->allocator);
aws_array_list_clean_up(&errors);
/*
* Step 5 - destroy the manager if necessary
*/
if (should_destroy) {
s_aws_http_connection_manager_begin_destroy(manager);
}
/*
* Step 6 - Clean up work. Do this here rather than at the end of every caller.
*/
s_aws_connection_management_transaction_clean_up(work);
}