Skip to content

Commit 9e5564e

Browse files
authored
Fix refcounting bug in h2_connection.c (#236)
- aws_stream_activate() should be incrementing stream refcount, not the function that moves it to a thread. - renamed the function that moves it to a thread to reduce confusion. - reduce number of times we load atomic new_stream_error_code. - moved atomics out of `synced_data` to reduce confusion
1 parent d383e95 commit 9e5564e

File tree

3 files changed

+30
-22
lines changed

3 files changed

+30
-22
lines changed

include/aws/http/private/h2_connection.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,6 @@ struct aws_h2_connection {
9999

100100
/* Any thread may touch this data, but the lock must be held (unless it's an atomic) */
101101
struct {
102-
/* For checking status from outside the event-loop thread. */
103-
struct aws_atomic_var is_open;
104-
105-
/* If non-zero, reason to immediately reject new streams. (ex: closing) */
106-
struct aws_atomic_var new_stream_error_code;
107-
108102
struct aws_mutex lock;
109103

110104
/* New `aws_h2_stream *` that haven't moved to `thread_data` yet */
@@ -113,6 +107,14 @@ struct aws_h2_connection {
113107
bool is_cross_thread_work_task_scheduled;
114108

115109
} synced_data;
110+
111+
struct {
112+
/* For checking status from outside the event-loop thread. */
113+
struct aws_atomic_var is_open;
114+
115+
/* If non-zero, reason to immediately reject new streams. (ex: closing) */
116+
struct aws_atomic_var new_stream_error_code;
117+
} atomic;
116118
};
117119

118120
/**

source/h1_connection.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ int aws_h1_stream_activate(struct aws_http_stream *stream) {
455455
return AWS_OP_ERR;
456456
}
457457

458-
/* activate one more time now that the connection can actually run the stream. */
458+
/* connection keeps activated stream alive until stream completes */
459459
aws_atomic_fetch_add(&stream->refcount, 1);
460460

461461
if (should_schedule_task) {

source/h2_connection.c

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ static void s_stop(
183183

184184
/* Even if we're not scheduling shutdown just yet (ex: sent final request but waiting to read final response)
185185
* we don't consider the connection "open" anymore so user can't create more streams */
186-
aws_atomic_store_int(&connection->synced_data.new_stream_error_code, AWS_ERROR_HTTP_CONNECTION_CLOSED);
187-
aws_atomic_store_int(&connection->synced_data.is_open, 0);
186+
aws_atomic_store_int(&connection->atomic.new_stream_error_code, AWS_ERROR_HTTP_CONNECTION_CLOSED);
187+
aws_atomic_store_int(&connection->atomic.is_open, 0);
188188

189189
if (schedule_shutdown) {
190190
AWS_LOGF_INFO(
@@ -242,8 +242,8 @@ static struct aws_h2_connection *s_connection_new(
242242
/* 1 refcount for user */
243243
aws_atomic_init_int(&connection->base.refcount, 1);
244244

245-
aws_atomic_init_int(&connection->synced_data.is_open, 1);
246-
aws_atomic_init_int(&connection->synced_data.new_stream_error_code, 0);
245+
aws_atomic_init_int(&connection->atomic.is_open, 1);
246+
aws_atomic_init_int(&connection->atomic.new_stream_error_code, 0);
247247
aws_linked_list_init(&connection->synced_data.pending_stream_list);
248248

249249
aws_linked_list_init(&connection->thread_data.outgoing_streams_list);
@@ -1257,7 +1257,7 @@ struct aws_h2err s_decoder_on_goaway_begin(
12571257
return aws_h2err_from_h2_code(AWS_H2_ERR_PROTOCOL_ERROR);
12581258
}
12591259
/* stop sending any new stream and making new request */
1260-
aws_atomic_store_int(&connection->synced_data.new_stream_error_code, AWS_ERROR_HTTP_GOAWAY_RECEIVED);
1260+
aws_atomic_store_int(&connection->atomic.new_stream_error_code, AWS_ERROR_HTTP_GOAWAY_RECEIVED);
12611261
connection->thread_data.goaway_received_last_stream_id = last_stream;
12621262
CONNECTION_LOGF(
12631263
DEBUG,
@@ -1569,10 +1569,12 @@ int aws_h2_connection_send_rst_and_close_reserved_stream(
15691569
}
15701570

15711571
/* Move stream into "active" datastructures and notify stream that it can send frames now */
1572-
static void s_activate_stream(struct aws_h2_connection *connection, struct aws_h2_stream *stream) {
1572+
static void s_move_stream_to_thread(
1573+
struct aws_h2_connection *connection,
1574+
struct aws_h2_stream *stream,
1575+
int new_stream_error_code) {
15731576
AWS_PRECONDITION(aws_channel_thread_is_callers_thread(connection->base.channel_slot->channel));
15741577

1575-
int new_stream_error_code = (int)aws_atomic_load_int(&connection->synced_data.new_stream_error_code);
15761578
if (new_stream_error_code) {
15771579
aws_raise_error(new_stream_error_code);
15781580
AWS_H2_STREAM_LOGF(
@@ -1601,8 +1603,6 @@ static void s_activate_stream(struct aws_h2_connection *connection, struct aws_h
16011603
goto error;
16021604
}
16031605

1604-
aws_atomic_fetch_add(&stream->base.refcount, 1);
1605-
16061606
if (has_outgoing_data) {
16071607
aws_linked_list_push_back(&connection->thread_data.outgoing_streams_list, &stream->node);
16081608
}
@@ -1635,10 +1635,13 @@ static void s_cross_thread_work_task(struct aws_channel_task *task, void *arg, e
16351635
} /* END CRITICAL SECTION */
16361636

16371637
/* Process new pending_streams */
1638-
while (!aws_linked_list_empty(&pending_streams)) {
1639-
struct aws_linked_list_node *node = aws_linked_list_pop_front(&pending_streams);
1640-
struct aws_h2_stream *stream = AWS_CONTAINER_OF(node, struct aws_h2_stream, node);
1641-
s_activate_stream(connection, stream);
1638+
if (!aws_linked_list_empty(&pending_streams)) {
1639+
int new_stream_error_code = (int)aws_atomic_load_int(&connection->atomic.new_stream_error_code);
1640+
do {
1641+
struct aws_linked_list_node *node = aws_linked_list_pop_front(&pending_streams);
1642+
struct aws_h2_stream *stream = AWS_CONTAINER_OF(node, struct aws_h2_stream, node);
1643+
s_move_stream_to_thread(connection, stream, new_stream_error_code);
1644+
} while (!aws_linked_list_empty(&pending_streams));
16421645
}
16431646

16441647
/* #TODO: process stuff from other API calls (ex: window-updates) */
@@ -1680,6 +1683,9 @@ int aws_h2_stream_activate(struct aws_http_stream *stream) {
16801683
return AWS_OP_ERR;
16811684
}
16821685

1686+
/* connection keeps activated stream alive until stream completes */
1687+
aws_atomic_fetch_add(&stream->refcount, 1);
1688+
16831689
if (!was_cross_thread_work_scheduled) {
16841690
CONNECTION_LOG(TRACE, connection, "Scheduling cross-thread work task");
16851691
aws_channel_schedule_task_now(connection->base.channel_slot->channel, &connection->cross_thread_work_task);
@@ -1708,7 +1714,7 @@ static struct aws_http_stream *s_connection_make_request(
17081714
return NULL;
17091715
}
17101716

1711-
int new_stream_error_code = (int)aws_atomic_load_int(&connection->synced_data.new_stream_error_code);
1717+
int new_stream_error_code = (int)aws_atomic_load_int(&connection->atomic.new_stream_error_code);
17121718
if (new_stream_error_code) {
17131719
aws_raise_error(new_stream_error_code);
17141720
CONNECTION_LOGF(
@@ -1731,7 +1737,7 @@ static struct aws_http_stream *s_connection_make_request(
17311737

17321738
static bool s_connection_is_open(const struct aws_http_connection *connection_base) {
17331739
struct aws_h2_connection *connection = AWS_CONTAINER_OF(connection_base, struct aws_h2_connection, base);
1734-
bool is_open = aws_atomic_load_int(&connection->synced_data.is_open);
1740+
bool is_open = aws_atomic_load_int(&connection->atomic.is_open);
17351741
return is_open;
17361742
}
17371743

0 commit comments

Comments
 (0)