|
11 | 11 | #include <aws/common/clock.h>
|
12 | 12 | #include <aws/common/condition_variable.h>
|
13 | 13 | #include <aws/common/log_writer.h>
|
| 14 | +#include <aws/common/logging.h> |
14 | 15 | #include <aws/common/string.h>
|
15 | 16 | #include <aws/common/thread.h>
|
16 | 17 | #include <aws/common/uuid.h>
|
@@ -42,12 +43,14 @@ struct tester_options {
|
42 | 43 | char *server_alpn_list;
|
43 | 44 | char *client_alpn_list;
|
44 | 45 | bool no_connection; /* don't connect server to client */
|
| 46 | + bool pin_event_loop; |
45 | 47 | };
|
46 | 48 |
|
47 | 49 | /* Singleton used by tests in this file */
|
48 | 50 | struct tester {
|
49 | 51 | struct aws_allocator *alloc;
|
50 |
| - struct aws_event_loop_group *event_loop_group; |
| 52 | + struct aws_event_loop_group *server_event_loop_group; |
| 53 | + struct aws_event_loop_group *client_event_loop_group; |
51 | 54 | struct aws_host_resolver *host_resolver;
|
52 | 55 | struct aws_server_bootstrap *server_bootstrap;
|
53 | 56 | struct aws_http_server *server;
|
@@ -239,7 +242,7 @@ static void s_client_connection_options_init_tester(
|
239 | 242 | struct aws_http_client_connection_options *client_options,
|
240 | 243 | struct tester *tester) {
|
241 | 244 | struct aws_client_bootstrap_options bootstrap_options = {
|
242 |
| - .event_loop_group = tester->event_loop_group, |
| 245 | + .event_loop_group = tester->client_event_loop_group, |
243 | 246 | .host_resolver = tester->host_resolver,
|
244 | 247 | };
|
245 | 248 | tester->client_bootstrap = aws_client_bootstrap_new(tester->alloc, &bootstrap_options);
|
@@ -298,15 +301,37 @@ static int s_tester_init(struct tester *tester, const struct tester_options *opt
|
298 | 301 | ASSERT_SUCCESS(aws_mutex_init(&tester->wait_lock));
|
299 | 302 | ASSERT_SUCCESS(aws_condition_variable_init(&tester->wait_cvar));
|
300 | 303 |
|
301 |
| - tester->event_loop_group = aws_event_loop_group_new_default(tester->alloc, 1, NULL); |
| 304 | + /* |
| 305 | + * The current http testing framework has several issues that hinder testing event loop pinning: |
| 306 | + * (1) Server shutdown can crash with memory corruption if the server uses an event loop group with more than one |
| 307 | + * thread |
| 308 | + * (2) s_tester_wait mixes results from both client and server and once you unlink them out of the same, single- |
| 309 | + * threaded event loop, the test assumptions start breaking due to different serializations of io events. |
| 310 | + * |
| 311 | + * This leads to a self-defeating situation: in order to test event loop pinning we need event loop groups with |
| 312 | + * many threads, but as soon as we use one, existing tests start breaking. |
| 313 | + * |
| 314 | + * Event loop pinning is a critical blocker for an upcoming release, so rather than trying to figure out the |
| 315 | + * underlying race condition within the http testing framework (I suspect it's socket listener related), we |
| 316 | + * instead add some complexity to the testing framework such that |
| 317 | + * (1) Existing tests continue to use a single event loop group with one thread |
| 318 | + * (2) The event loop pinning test uses two event loop groups, the server elg with a single thread and the |
| 319 | + * client elg with many threads to actually test pinning. |
| 320 | + */ |
| 321 | + tester->server_event_loop_group = aws_event_loop_group_new_default(tester->alloc, 1, NULL); |
| 322 | + if (options->pin_event_loop) { |
| 323 | + tester->client_event_loop_group = aws_event_loop_group_new_default(tester->alloc, 16, NULL); |
| 324 | + } else { |
| 325 | + tester->client_event_loop_group = aws_event_loop_group_acquire(tester->server_event_loop_group); |
| 326 | + } |
302 | 327 |
|
303 | 328 | struct aws_host_resolver_default_options resolver_options = {
|
304 |
| - .el_group = tester->event_loop_group, |
| 329 | + .el_group = tester->client_event_loop_group, |
305 | 330 | .max_entries = 8,
|
306 | 331 | };
|
307 | 332 |
|
308 | 333 | tester->host_resolver = aws_host_resolver_new_default(tester->alloc, &resolver_options);
|
309 |
| - tester->server_bootstrap = aws_server_bootstrap_new(tester->alloc, tester->event_loop_group); |
| 334 | + tester->server_bootstrap = aws_server_bootstrap_new(tester->alloc, tester->server_event_loop_group); |
310 | 335 | ASSERT_NOT_NULL(tester->server_bootstrap);
|
311 | 336 |
|
312 | 337 | struct aws_socket_options socket_options = {
|
@@ -360,6 +385,11 @@ static int s_tester_init(struct tester *tester, const struct tester_options *opt
|
360 | 385 | aws_byte_cursor_from_c_str("localhost")));
|
361 | 386 | client_options.tls_options = &tester->client_tls_connection_options;
|
362 | 387 | }
|
| 388 | + |
| 389 | + if (options->pin_event_loop) { |
| 390 | + client_options.requested_event_loop = aws_event_loop_group_get_next_loop(tester->client_event_loop_group); |
| 391 | + } |
| 392 | + |
363 | 393 | tester->client_options = client_options;
|
364 | 394 |
|
365 | 395 | tester->server_connection_num = 0;
|
@@ -395,7 +425,8 @@ static int s_tester_clean_up(struct tester *tester) {
|
395 | 425 | aws_server_bootstrap_release(tester->server_bootstrap);
|
396 | 426 | aws_client_bootstrap_release(tester->client_bootstrap);
|
397 | 427 | aws_host_resolver_release(tester->host_resolver);
|
398 |
| - aws_event_loop_group_release(tester->event_loop_group); |
| 428 | + aws_event_loop_group_release(tester->client_event_loop_group); |
| 429 | + aws_event_loop_group_release(tester->server_event_loop_group); |
399 | 430 |
|
400 | 431 | aws_http_library_clean_up();
|
401 | 432 | aws_mutex_clean_up(&tester->wait_lock);
|
@@ -645,7 +676,7 @@ static int s_test_connection_customized_alpn(struct aws_allocator *allocator, vo
|
645 | 676 | }
|
646 | 677 | AWS_TEST_CASE(connection_customized_alpn, s_test_connection_customized_alpn);
|
647 | 678 |
|
648 |
| -static int s_test_connection_customized_alpn_error_with_unknow_return_string( |
| 679 | +static int s_test_connection_customized_alpn_error_with_unknown_return_string( |
649 | 680 | struct aws_allocator *allocator,
|
650 | 681 | void *ctx) {
|
651 | 682 | (void)ctx;
|
@@ -702,8 +733,8 @@ static int s_test_connection_customized_alpn_error_with_unknow_return_string(
|
702 | 733 | return AWS_OP_SUCCESS;
|
703 | 734 | }
|
704 | 735 | AWS_TEST_CASE(
|
705 |
| - connection_customized_alpn_error_with_unknow_return_string, |
706 |
| - s_test_connection_customized_alpn_error_with_unknow_return_string); |
| 736 | + connection_customized_alpn_error_with_unknown_return_string, |
| 737 | + s_test_connection_customized_alpn_error_with_unknown_return_string); |
707 | 738 |
|
708 | 739 | static int s_test_connection_destroy_server_with_connection_existing(struct aws_allocator *allocator, void *ctx) {
|
709 | 740 | (void)ctx;
|
@@ -856,7 +887,7 @@ static int s_test_connection_server_shutting_down_new_connection_setup_fail(
|
856 | 887 |
|
857 | 888 | /* get the first eventloop of tester, which will be the eventloop for server listener socket, block the listener
|
858 | 889 | * socket */
|
859 |
| - struct aws_event_loop *server_eventloop = aws_event_loop_group_get_loop_at(tester.event_loop_group, 0); |
| 890 | + struct aws_event_loop *server_eventloop = aws_event_loop_group_get_loop_at(tester.server_event_loop_group, 0); |
860 | 891 | struct aws_task *server_block_task = aws_mem_acquire(allocator, sizeof(struct aws_task));
|
861 | 892 | aws_task_init(server_block_task, s_block_task, &tester, "wait_a_bit");
|
862 | 893 | aws_event_loop_schedule_task_now(server_eventloop, server_block_task);
|
@@ -916,3 +947,27 @@ static int s_test_connection_server_shutting_down_new_connection_setup_fail(
|
916 | 947 | AWS_TEST_CASE(
|
917 | 948 | connection_server_shutting_down_new_connection_setup_fail,
|
918 | 949 | s_test_connection_server_shutting_down_new_connection_setup_fail);
|
| 950 | + |
| 951 | +static int s_test_connection_setup_shutdown_pinned_event_loop(struct aws_allocator *allocator, void *ctx) { |
| 952 | + (void)ctx; |
| 953 | + struct tester_options options = { |
| 954 | + .alloc = allocator, |
| 955 | + .pin_event_loop = true, |
| 956 | + }; |
| 957 | + struct tester tester; |
| 958 | + ASSERT_SUCCESS(s_tester_init(&tester, &options)); |
| 959 | + |
| 960 | + for (int i = 0; i < tester.client_connection_num; i++) { |
| 961 | + struct aws_http_connection *connection = tester.client_connections[i]; |
| 962 | + ASSERT_PTR_EQUALS( |
| 963 | + tester.client_options.requested_event_loop, aws_channel_get_event_loop(connection->channel_slot->channel)); |
| 964 | + } |
| 965 | + |
| 966 | + release_all_client_connections(&tester); |
| 967 | + release_all_server_connections(&tester); |
| 968 | + ASSERT_SUCCESS(s_tester_wait(&tester, s_tester_connection_shutdown_pred)); |
| 969 | + |
| 970 | + ASSERT_SUCCESS(s_tester_clean_up(&tester)); |
| 971 | + return AWS_OP_SUCCESS; |
| 972 | +} |
| 973 | +AWS_TEST_CASE(connection_setup_shutdown_pinned_event_loop, s_test_connection_setup_shutdown_pinned_event_loop); |
0 commit comments