Skip to content

Commit 71c6ab7

Browse files
authored
Best Effort Thread Pinning to CPU_ID (#1096)
1 parent 42119c5 commit 71c6ab7

File tree

3 files changed

+51
-3
lines changed

3 files changed

+51
-3
lines changed

source/posix/thread.c

+16-3
Original file line numberDiff line numberDiff line change
@@ -296,9 +296,9 @@ int aws_thread_launch(
296296
attr_return = pthread_attr_setaffinity_np(attributes_ptr, sizeof(cpuset), &cpuset);
297297

298298
if (attr_return) {
299-
AWS_LOGF_ERROR(
299+
AWS_LOGF_WARN(
300300
AWS_LS_COMMON_THREAD,
301-
"id=%p: pthread_attr_setaffinity_np() failed with %d.",
301+
"id=%p: pthread_attr_setaffinity_np() failed with %d. Continuing without cpu affinity",
302302
(void *)thread,
303303
attr_return);
304304
goto cleanup;
@@ -382,7 +382,20 @@ int aws_thread_launch(
382382

383383
if (attr_return) {
384384
s_thread_wrapper_destroy(wrapper);
385-
385+
if (options && options->cpu_id >= 0) {
386+
/*
387+
* `pthread_create` can fail with an `EINVAL` error or `EDEADLK` on freebasd if the `cpu_id` is
388+
* restricted/invalid. Since the pinning to a particular `cpu_id` is supposed to be best-effort, try to
389+
* launch a thread again without pinning to a specific cpu_id.
390+
*/
391+
AWS_LOGF_INFO(
392+
AWS_LS_COMMON_THREAD,
393+
"id=%p: Attempting to launch the thread again without pinning to a cpu_id",
394+
(void *)thread);
395+
struct aws_thread_options new_options = *options;
396+
new_options.cpu_id = -1;
397+
return aws_thread_launch(thread, func, arg, &new_options);
398+
}
386399
switch (attr_return) {
387400
case EINVAL:
388401
return aws_raise_error(AWS_ERROR_THREAD_INVALID_SETTINGS);

tests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ add_test_case(aws_load_error_strings_test)
4040
add_test_case(aws_assume_compiles_test)
4141

4242
add_test_case(thread_creation_join_test)
43+
add_test_case(thread_creation_join_invalid_cpu_id_test)
4344
add_test_case(thread_atexit_test)
4445
add_test_case(test_managed_thread_join)
4546
add_test_case(test_managed_thread_join_timeout)

tests/thread_test.c

+34
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,40 @@ static int s_test_thread_creation_join_fn(struct aws_allocator *allocator, void
7070

7171
AWS_TEST_CASE(thread_creation_join_test, s_test_thread_creation_join_fn)
7272

73+
static int s_test_thread_creation_join_invalid_cpu_id_fn(struct aws_allocator *allocator, void *ctx) {
74+
(void)ctx;
75+
aws_common_library_init(allocator);
76+
struct thread_test_data test_data = {.allocator = allocator};
77+
78+
struct aws_thread thread;
79+
aws_thread_init(&thread, allocator);
80+
81+
struct aws_thread_options thread_options = *aws_default_thread_options();
82+
/* invalid cpu_id. Ensure that the cpu_id is best-effort based. */
83+
thread_options.cpu_id = 4096;
84+
85+
ASSERT_SUCCESS(
86+
aws_thread_launch(&thread, s_thread_fn, (void *)&test_data, &thread_options), "thread creation failed");
87+
ASSERT_INT_EQUALS(
88+
AWS_THREAD_JOINABLE, aws_thread_get_detach_state(&thread), "thread state should have returned JOINABLE");
89+
ASSERT_SUCCESS(aws_thread_join(&thread), "thread join failed");
90+
ASSERT_TRUE(
91+
aws_thread_thread_id_equal(test_data.thread_id, aws_thread_get_id(&thread)),
92+
"get_thread_id should have returned the same id as the thread calling current_thread_id");
93+
ASSERT_INT_EQUALS(
94+
AWS_THREAD_JOIN_COMPLETED,
95+
aws_thread_get_detach_state(&thread),
96+
"thread state should have returned JOIN_COMPLETED");
97+
98+
aws_string_destroy(test_data.thread_name);
99+
aws_thread_clean_up(&thread);
100+
aws_common_library_clean_up();
101+
102+
return 0;
103+
}
104+
105+
AWS_TEST_CASE(thread_creation_join_invalid_cpu_id_test, s_test_thread_creation_join_invalid_cpu_id_fn)
106+
73107
static uint32_t s_atexit_call_count = 0;
74108
static void s_thread_atexit_fn(void *user_data) {
75109
(void)user_data;

0 commit comments

Comments
 (0)