1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <errno.h>
6 #include <inttypes.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <threads.h>
10 
11 #include <zircon/syscalls.h>
12 #include <zircon/threads.h>
13 #include <unittest/unittest.h>
14 
15 static volatile int threads_done[7];
16 
thread_entry(void * arg)17 static int thread_entry(void* arg) {
18     int thread_number = (int)(intptr_t)arg;
19     errno = thread_number;
20     unittest_printf("thread %d sleeping for .1 seconds\n", thread_number);
21     zx_nanosleep(zx_deadline_after(ZX_MSEC(100)));
22     EXPECT_EQ(errno, thread_number, "errno changed by someone!");
23     threads_done[thread_number] = 1;
24     return thread_number;
25 }
26 
c11_thread_test(void)27 bool c11_thread_test(void) {
28     BEGIN_TEST;
29 
30     thrd_t thread;
31     int return_value = 99;
32 
33     unittest_printf("Welcome to thread test!\n");
34 
35     memset((void*)threads_done, 0, sizeof(threads_done));
36     for (int i = 0; i != 4; ++i) {
37         int return_value = 99;
38         int ret = thrd_create_with_name(&thread, thread_entry, (void*)(intptr_t)i, "c11 thread test");
39         ASSERT_EQ(ret, thrd_success, "Error while creating thread");
40 
41         ret = thrd_join(thread, &return_value);
42         ASSERT_EQ(ret, thrd_success, "Error while thread join");
43         ASSERT_EQ(return_value, i, "Incorrect return from thread");
44     }
45 
46     unittest_printf("Attempting to create thread with a null name. This should succeed\n");
47     int ret = thrd_create_with_name(&thread, thread_entry, (void*)(intptr_t)4, NULL);
48     ASSERT_EQ(ret, thrd_success, "Error returned from thread creation");
49     zx_handle_t handle = thrd_get_zx_handle(thread);
50     ASSERT_NE(handle, ZX_HANDLE_INVALID, "got invalid thread handle");
51     // Prove this is a valid handle by duplicating it.
52     zx_handle_t dup_handle;
53     zx_status_t status = zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, &dup_handle);
54     ASSERT_EQ(status, 0, "failed to duplicate thread handle");
55 
56     ret = thrd_join(thread, &return_value);
57     ASSERT_EQ(ret, thrd_success, "Error while thread join");
58     ASSERT_EQ(zx_handle_close(dup_handle), ZX_OK, "failed to close duplicate handle");
59     ASSERT_EQ(return_value, 4, "Incorrect return from thread");
60 
61     ret = thrd_create_with_name(&thread, thread_entry, (void*)(intptr_t)5, NULL);
62     ASSERT_EQ(ret, thrd_success, "Error returned from thread creation");
63     ret = thrd_detach(thread);
64     ASSERT_EQ(ret, thrd_success, "Error while thread detach");
65 
66     while (!threads_done[5])
67         zx_nanosleep(zx_deadline_after(ZX_MSEC(100)));
68 
69     thread_entry((void*)(intptr_t)6);
70     ASSERT_TRUE(threads_done[6], "All threads should have completed");
71 
72     END_TEST;
73 }
74 
long_name_succeeds(void)75 bool long_name_succeeds(void) {
76     BEGIN_TEST;
77 
78     // Creating a thread with a super long name should succeed.
79     static const char long_name[] =
80         "0123456789012345678901234567890123456789"
81         "0123456789012345678901234567890123456789";
82     ASSERT_GT(strlen(long_name), (size_t)ZX_MAX_NAME_LEN-1,
83               "too short to truncate");
84 
85     thrd_t thread;
86     int ret = thrd_create_with_name(
87         &thread, thread_entry, (void*)(intptr_t)0, long_name);
88     ASSERT_EQ(ret, thrd_success, "long name should have succeeded");
89 
90     // Clean up.
91     int return_value;
92     EXPECT_EQ(thrd_join(thread, &return_value), thrd_success, "");
93     END_TEST;
94 }
95 
detach_thrd(void * arg)96 static int detach_thrd(void* arg) {
97     BEGIN_HELPER;
98     thrd_t* thrd = (thrd_t*) arg;
99     EXPECT_EQ(thrd_detach(*thrd), 0, "");
100     free(thrd);
101     END_HELPER;
102 }
103 
detach_self_test(void)104 bool detach_self_test(void) {
105     BEGIN_TEST;
106 
107     for (size_t i = 0; i < 1000; i++) {
108         thrd_t* thrd = calloc(sizeof(thrd_t), 1);
109         ASSERT_NONNULL(thrd, "");
110         ASSERT_EQ(thrd_create(thrd, detach_thrd, thrd), 0, "");
111     }
112 
113     END_TEST;
114 }
115 
116 BEGIN_TEST_CASE(c11_thread_tests)
RUN_TEST(c11_thread_test)117 RUN_TEST(c11_thread_test)
118 RUN_TEST(long_name_succeeds)
119 RUN_TEST(detach_self_test)
120 END_TEST_CASE(c11_thread_tests)
121 
122 #ifndef BUILD_COMBINED_TESTS
123 int main(int argc, char** argv) {
124     return unittest_run_all_tests(argc, argv) ? 0 : -1;
125 }
126 #endif
127