@@ -60,6 +60,7 @@ static size_t s_handler_message_overhead(struct aws_channel_handler *handler);
6060static void s_handler_destroy (struct aws_channel_handler * handler );
6161static struct aws_http_stream * s_new_client_request_stream (const struct aws_http_request_options * options );
6262static void s_connection_close (struct aws_http_connection * connection_base );
63+ static bool s_connection_is_open (const struct aws_http_connection * connection_base );
6364static void s_stream_destroy (struct aws_http_stream * stream_base );
6465static void s_stream_update_window (struct aws_http_stream * stream , size_t increment_size );
6566static int s_decoder_on_request (
@@ -86,6 +87,7 @@ static struct aws_http_connection_vtable s_connection_vtable = {
8687
8788 .new_client_request_stream = s_new_client_request_stream ,
8889 .close = s_connection_close ,
90+ .is_open = s_connection_is_open ,
8991};
9092
9193static const struct aws_http_stream_vtable s_stream_vtable = {
@@ -189,32 +191,41 @@ struct h1_stream {
189191
190192/**
191193 * Internal function for shutting down the connection.
192- * If connection is already shutting down, this call has no effect.
194+ * This function can be called multiple times, from on-thread or off.
195+ * This function is always run once on-thread during channel shutdown.
193196 */
194197static void s_shutdown_connection (struct h1_connection * connection , int error_code ) {
195- assert (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
198+ bool on_thread = aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel );
199+ if (on_thread ) {
200+ /* If thread_data already knew about shutdown, then no more work to do here. */
201+ if (connection -> thread_data .is_shutting_down ) {
202+ return ;
203+ }
196204
197- if (!connection -> thread_data .is_shutting_down ) {
205+ connection -> thread_data .is_shutting_down = true;
206+ connection -> thread_data .shutdown_error_code = error_code ;
207+ }
208+
209+ bool was_shutdown_known ;
210+ { /* BEGIN CRITICAL SECTION */
211+ int err = aws_mutex_lock (& connection -> synced_data .lock );
212+ AWS_FATAL_ASSERT (!err );
213+
214+ was_shutdown_known = connection -> synced_data .is_shutting_down ;
215+ connection -> synced_data .is_shutting_down = true;
216+
217+ err = aws_mutex_unlock (& connection -> synced_data .lock );
218+ AWS_FATAL_ASSERT (!err );
219+ } /* END CRITICAL SECTION */
220+
221+ if (!was_shutdown_known ) {
198222 AWS_LOGF_INFO (
199223 AWS_LS_HTTP_CONNECTION ,
200224 "id=%p: Connection shutting down with error code %d (%s)." ,
201225 (void * )& connection -> base ,
202226 error_code ,
203227 aws_error_name (error_code ));
204228
205- { /* BEGIN CRITICAL SECTION */
206- int err = aws_mutex_lock (& connection -> synced_data .lock );
207- AWS_FATAL_ASSERT (!err );
208-
209- connection -> synced_data .is_shutting_down = true;
210-
211- err = aws_mutex_unlock (& connection -> synced_data .lock );
212- AWS_FATAL_ASSERT (!err );
213- } /* END CRITICAL SECTION */
214-
215- connection -> thread_data .is_shutting_down = true;
216- connection -> thread_data .shutdown_error_code = error_code ;
217-
218229 /* Delay the call to aws_channel_shutdown().
219230 * This ensures that a user calling aws_http_connection_close() won't have completion callbacks
220231 * firing before aws_http_connection_close() has even returned. */
@@ -234,18 +245,27 @@ static void s_shutdown_delay_task(struct aws_channel_task *task, void *arg, enum
234245
235246/**
236247 * Public function for closing connection.
237- * If connection is already shutting down, this call has no effect.
238248 */
239249static void s_connection_close (struct aws_http_connection * connection_base ) {
240250 struct h1_connection * connection = AWS_CONTAINER_OF (connection_base , struct h1_connection , base );
251+ s_shutdown_connection (connection , AWS_ERROR_SUCCESS );
252+ }
241253
242- if (aws_channel_thread_is_callers_thread (connection_base -> channel_slot -> channel )) {
243- /* Invoke internal function so connection ceases work immediately */
244- s_shutdown_connection (connection , AWS_ERROR_SUCCESS );
245- } else {
246- /* Not on thread, so tell channel to shut down, which will result in connection shutting down. */
247- aws_channel_shutdown (connection_base -> channel_slot -> channel , AWS_ERROR_SUCCESS );
248- }
254+ static bool s_connection_is_open (const struct aws_http_connection * connection_base ) {
255+ struct h1_connection * connection = AWS_CONTAINER_OF (connection_base , struct h1_connection , base );
256+ bool is_shutting_down ;
257+
258+ { /* BEGIN CRITICAL SECTION */
259+ int err = aws_mutex_lock (& connection -> synced_data .lock );
260+ AWS_FATAL_ASSERT (!err );
261+
262+ is_shutting_down = connection -> synced_data .is_shutting_down ;
263+
264+ err = aws_mutex_unlock (& connection -> synced_data .lock );
265+ AWS_FATAL_ASSERT (!err );
266+ } /* END CRITICAL SECTION */
267+
268+ return !is_shutting_down ;
249269}
250270
251271/**
0 commit comments