static void s_write_outgoing_frames()

in source/h2_connection.c [631:739]


static void s_write_outgoing_frames(struct aws_h2_connection *connection, bool first_try) {
    AWS_PRECONDITION(aws_channel_thread_is_callers_thread(connection->base.channel_slot->channel));
    AWS_PRECONDITION(connection->thread_data.is_outgoing_frames_task_active);

    struct aws_channel_slot *channel_slot = connection->base.channel_slot;
    struct aws_linked_list *outgoing_frames_queue = &connection->thread_data.outgoing_frames_queue;
    struct aws_linked_list *outgoing_streams_list = &connection->thread_data.outgoing_streams_list;

    if (connection->thread_data.is_writing_stopped) {
        return;
    }

    /* Determine whether there's work to do, and end task immediately if there's not.
     * Note that we stop writing DATA frames if the channel is trying to shut down */
    bool has_control_frames = !aws_linked_list_empty(outgoing_frames_queue);
    bool has_data_frames = !aws_linked_list_empty(outgoing_streams_list);
    bool may_write_data_frames = (connection->thread_data.window_size_peer > AWS_H2_MIN_WINDOW_SIZE) &&
                                 !connection->thread_data.channel_shutdown_waiting_for_goaway_to_be_written;
    bool will_write = has_control_frames || (has_data_frames && may_write_data_frames);

    if (!will_write) {
        if (!first_try) {
            CONNECTION_LOGF(
                TRACE,
                connection,
                "Outgoing frames task stopped. has_control_frames:%d has_data_frames:%d may_write_data_frames:%d",
                has_control_frames,
                has_data_frames,
                may_write_data_frames);
        }

        connection->thread_data.is_outgoing_frames_task_active = false;

        if (connection->thread_data.channel_shutdown_waiting_for_goaway_to_be_written) {
            s_finish_shutdown(connection);
        }

        return;
    }

    if (first_try) {
        CONNECTION_LOG(TRACE, connection, "Starting outgoing frames task");
    }

    /* Acquire aws_io_message, that we will attempt to fill up */
    struct aws_io_message *msg = aws_channel_slot_acquire_max_message_for_write(channel_slot);
    if (AWS_UNLIKELY(!msg)) {
        CONNECTION_LOG(ERROR, connection, "Failed to acquire message from pool, closing connection.");
        goto error;
    }

    /* Set up callback so we can send another message when this one completes */
    msg->on_completion = s_on_channel_write_complete;
    msg->user_data = connection;

    CONNECTION_LOGF(
        TRACE,
        connection,
        "Outgoing frames task acquired message with %zu bytes available",
        msg->message_data.capacity - msg->message_data.len);

    /* Write as many frames from outgoing_frames_queue as possible. */
    if (s_encode_outgoing_frames_queue(connection, &msg->message_data)) {
        goto error;
    }

    /* If outgoing_frames_queue emptied, and connection is running normally,
     * then write as many DATA frames from outgoing_streams_list as possible. */
    if (aws_linked_list_empty(outgoing_frames_queue) && may_write_data_frames) {
        if (s_encode_data_from_outgoing_streams(connection, &msg->message_data)) {
            goto error;
        }
    }

    if (msg->message_data.len) {
        /* Write message to channel.
         * outgoing_frames_task will resume when message completes. */
        CONNECTION_LOGF(TRACE, connection, "Outgoing frames task sending message of size %zu", msg->message_data.len);

        if (aws_channel_slot_send_message(channel_slot, msg, AWS_CHANNEL_DIR_WRITE)) {
            CONNECTION_LOGF(
                ERROR,
                connection,
                "Failed to send channel message: %s. Closing connection.",
                aws_error_name(aws_last_error()));

            goto error;
        }
    } else {
        /* Message is empty, warn that no work is being done and reschedule the task to try again next tick.
         * It's likely that body isn't ready, so body streaming function has no data to write yet.
         * If this scenario turns out to be common we should implement a "pause" feature. */
        CONNECTION_LOG(WARN, connection, "Outgoing frames task sent no data, will try again next tick.");

        aws_mem_release(msg->allocator, msg);

        aws_channel_schedule_task_now(channel_slot->channel, &connection->outgoing_frames_task);
    }
    return;

error:;
    int error_code = aws_last_error();

    if (msg) {
        aws_mem_release(msg->allocator, msg);
    }

    aws_h2_connection_shutdown_due_to_write_err(connection, error_code);
}