]>
Commit | Line | Data |
---|---|---|
964d3577 A |
1 | #include <assert.h> |
2 | #include <pthread.h> | |
3 | #include <stdint.h> | |
4 | #include <stdio.h> | |
5 | #include <unistd.h> | |
a0619f9c | 6 | #include <stdlib.h> |
964d3577 | 7 | #include <mach/mach.h> |
a0619f9c | 8 | #include <libkern/OSAtomicQueue.h> |
964d3577 | 9 | |
a0619f9c | 10 | #include "darwintest_defaults.h" |
2546420a | 11 | |
964d3577 A |
12 | #define WAITTIME (100 * 1000) |
13 | ||
14 | static inline void* | |
15 | test(void) | |
16 | { | |
17 | static uintptr_t idx; | |
964d3577 A |
18 | return (void*)idx; |
19 | } | |
20 | ||
21 | static void * | |
22 | thread(void *param) | |
23 | { | |
24 | usleep(WAITTIME); | |
25 | return param; | |
26 | } | |
27 | ||
2546420a | 28 | /* |
964d3577 A |
29 | static void * |
30 | thread1(void *param) | |
31 | { | |
32 | int res; | |
33 | pthread_t p = param; | |
34 | ||
35 | usleep(WAITTIME); | |
36 | res = pthread_join(p, NULL); | |
37 | assert(res == 0); | |
964d3577 A |
38 | return 0; |
39 | } | |
2546420a | 40 | */ |
964d3577 | 41 | |
2546420a A |
42 | T_DECL(join, "pthread_join", |
43 | T_META_ALL_VALID_ARCHS(YES)) | |
964d3577 A |
44 | { |
45 | int res; | |
46 | kern_return_t kr; | |
47 | pthread_t p = NULL; | |
48 | void *param, *value; | |
49 | ||
50 | param = test(); | |
51 | res = pthread_create(&p, NULL, thread, param); | |
2546420a | 52 | T_ASSERT_POSIX_ZERO(res, "pthread_create"); |
964d3577 A |
53 | value = NULL; |
54 | res = pthread_join(p, &value); | |
2546420a A |
55 | T_ASSERT_POSIX_ZERO(res, "pthread_join"); |
56 | T_ASSERT_EQ_PTR(param, value, "early join value"); | |
964d3577 A |
57 | |
58 | param = test(); | |
59 | res = pthread_create(&p, NULL, thread, param); | |
2546420a | 60 | T_ASSERT_POSIX_ZERO(res, "pthread_create"); |
964d3577 A |
61 | usleep(3 * WAITTIME); |
62 | value = NULL; | |
63 | res = pthread_join(p, &value); | |
2546420a A |
64 | T_ASSERT_POSIX_ZERO(res, "pthread_join"); |
65 | T_ASSERT_EQ_PTR(param, value, "late join value"); | |
964d3577 A |
66 | |
67 | param = test(); | |
68 | res = pthread_create_suspended_np(&p, NULL, thread, param); | |
2546420a | 69 | T_ASSERT_POSIX_ZERO(res, "pthread_create_suspended_np"); |
964d3577 | 70 | kr = thread_resume(pthread_mach_thread_np(p)); |
2546420a | 71 | T_ASSERT_EQ_INT(kr, 0, "thread_resume"); |
964d3577 A |
72 | value = NULL; |
73 | res = pthread_join(p, &value); | |
2546420a A |
74 | T_ASSERT_POSIX_ZERO(res, "pthread_join"); |
75 | T_ASSERT_EQ_PTR(param, value, "suspended early join value"); | |
964d3577 A |
76 | |
77 | param = test(); | |
78 | res = pthread_create_suspended_np(&p, NULL, thread, param); | |
2546420a | 79 | T_ASSERT_POSIX_ZERO(res, "pthread_create_suspended_np"); |
964d3577 | 80 | kr = thread_resume(pthread_mach_thread_np(p)); |
2546420a | 81 | T_ASSERT_EQ_INT(kr, 0, "thread_resume"); |
964d3577 A |
82 | usleep(3 * WAITTIME); |
83 | value = NULL; | |
84 | res = pthread_join(p, &value); | |
2546420a A |
85 | T_ASSERT_POSIX_ZERO(res, "pthread_join"); |
86 | T_ASSERT_EQ_PTR(param, value, "suspended late join value"); | |
964d3577 | 87 | |
2546420a A |
88 | // This test is supposed to test joining on the main thread. It's not |
89 | // clear how to express this with libdarwintest for now. | |
90 | /* | |
964d3577 A |
91 | test(); |
92 | param = pthread_self(); | |
93 | res = pthread_create_suspended_np(&p, NULL, thread1, param); | |
2546420a | 94 | T_ASSERT_POSIX_ZERO(res, "pthread_create_suspended_np"); |
964d3577 | 95 | res = pthread_detach(p); |
2546420a | 96 | T_ASSERT_POSIX_ZERO(res, "pthread_detach"); |
964d3577 | 97 | kr = thread_resume(pthread_mach_thread_np(p)); |
2546420a | 98 | T_ASSERT_EQ_INT(kr, 0, "thread_resume"); |
964d3577 | 99 | pthread_exit(0); |
2546420a A |
100 | */ |
101 | } | |
102 | ||
103 | static void * | |
104 | thread_stub(__unused void *arg) | |
105 | { | |
106 | return NULL; | |
964d3577 A |
107 | } |
108 | ||
2546420a A |
109 | T_DECL(pthread_join_stress, "pthread_join in a loop") |
110 | { | |
111 | for (int i = 0; i < 1000; i++) { | |
112 | pthread_t th[16]; | |
113 | for (int j = 0; j < i%16; j++){ | |
114 | T_QUIET; T_ASSERT_POSIX_SUCCESS(pthread_create(&th[j], NULL, thread_stub, NULL), NULL); | |
115 | } | |
116 | for (int j = i%16; j >= 0; j--){ | |
117 | T_QUIET; T_ASSERT_POSIX_SUCCESS(pthread_join(th[j], NULL), NULL); | |
118 | } | |
119 | } | |
120 | T_PASS("Success!"); | |
121 | } | |
a0619f9c A |
122 | |
123 | static pthread_mutex_t join3way_mutex = PTHREAD_MUTEX_INITIALIZER; | |
124 | static pthread_cond_t join3way_cond = PTHREAD_COND_INITIALIZER; | |
125 | static OSQueueHead join3way_queue = OS_ATOMIC_QUEUE_INIT; | |
126 | ||
127 | struct join3way_item { | |
128 | pthread_t th; | |
129 | struct join3way_item *next; | |
130 | }; | |
131 | ||
132 | static void * | |
133 | join3way_joiner(__unused void *arg) | |
134 | { | |
135 | pthread_mutex_lock(&join3way_mutex); | |
136 | while (1) { | |
137 | pthread_cond_wait(&join3way_cond, &join3way_mutex); | |
138 | struct join3way_item *item = OSAtomicDequeue(&join3way_queue, | |
139 | offsetof(struct join3way_item, next)); | |
140 | pthread_join(item->th, NULL); | |
141 | free(item); | |
142 | } | |
143 | return NULL; | |
144 | } | |
145 | ||
146 | static void * | |
147 | join3way_child(__unused void *arg) | |
148 | { | |
149 | struct join3way_item *item = malloc(sizeof(struct join3way_item)); | |
150 | item->th = pthread_self(); | |
151 | item->next = NULL; | |
152 | OSAtomicEnqueue(&join3way_queue, item, | |
153 | offsetof(struct join3way_item, next)); | |
154 | pthread_cond_signal(&join3way_cond); | |
155 | return NULL; | |
156 | } | |
157 | ||
158 | static void * | |
159 | join3way_creator(__unused void *arg) | |
160 | { | |
161 | pthread_attr_t attr; | |
162 | T_QUIET; T_ASSERT_POSIX_ZERO(pthread_attr_init(&attr), "pthread_attr_init"); | |
163 | T_ASSERT_POSIX_ZERO(pthread_attr_set_qos_class_np(&attr, | |
164 | QOS_CLASS_USER_INTERACTIVE, 0), "pthread_attr_set_qos_class_np (child)"); | |
165 | ||
166 | int n = 1000; | |
167 | while (--n > 0) { | |
168 | pthread_t t; | |
169 | T_QUIET; T_ASSERT_POSIX_SUCCESS(pthread_create(&t, &attr, | |
170 | join3way_child, NULL), "create thread"); | |
171 | } | |
172 | T_ASSERT_EQ_INT(n, 0, "created all child threads"); | |
173 | return NULL; | |
174 | } | |
175 | ||
176 | T_DECL(pthread_join_3way, "pthread_join from non-parent with priority inversion") | |
177 | { | |
178 | pthread_attr_t joinerattr; | |
179 | T_QUIET; T_ASSERT_POSIX_ZERO(pthread_attr_init(&joinerattr), | |
180 | "pthread_attr_init"); | |
181 | T_ASSERT_POSIX_ZERO(pthread_attr_set_qos_class_np(&joinerattr, | |
182 | QOS_CLASS_USER_INTERACTIVE, 0), "pthread_attr_set_qos_class_np"); | |
183 | ||
184 | pthread_t joiner; | |
185 | T_ASSERT_POSIX_SUCCESS(pthread_create(&joiner, &joinerattr, join3way_joiner, | |
186 | NULL), "create joiner thread"); | |
187 | ||
188 | pthread_attr_t creatorattr; | |
189 | T_QUIET; T_ASSERT_POSIX_ZERO(pthread_attr_init(&creatorattr), | |
190 | "pthread_attr_init"); | |
191 | T_ASSERT_POSIX_ZERO(pthread_attr_set_qos_class_np(&joinerattr, | |
192 | QOS_CLASS_BACKGROUND, 0), "pthread_attr_set_qos_class_np (creator)"); | |
193 | ||
194 | pthread_t creator; | |
195 | T_ASSERT_POSIX_SUCCESS(pthread_create(&creator, &creatorattr, | |
196 | join3way_creator, NULL), "create creator thread"); | |
197 | ||
198 | pthread_join(creator, NULL); | |
199 | } |