1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22 #include "uv.h"
23 #include "task.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h> /* memset */
28
29 #ifdef __POSIX__
30 #include <pthread.h>
31 #endif
32
33 struct getaddrinfo_req {
34 uv_thread_t thread_id;
35 unsigned int counter;
36 uv_loop_t* loop;
37 uv_getaddrinfo_t handle;
38 };
39
40
41 struct fs_req {
42 uv_thread_t thread_id;
43 unsigned int counter;
44 uv_loop_t* loop;
45 uv_fs_t handle;
46 };
47
48
49 struct test_thread {
50 uv_thread_t thread_id;
51 int thread_called;
52 };
53
54 static void getaddrinfo_do(struct getaddrinfo_req* req);
55 static void getaddrinfo_cb(uv_getaddrinfo_t* handle,
56 int status,
57 struct addrinfo* res);
58 static void fs_do(struct fs_req* req);
59 static void fs_cb(uv_fs_t* handle);
60
61 static int thread_called;
62 static uv_key_t tls_key;
63
64
getaddrinfo_do(struct getaddrinfo_req * req)65 static void getaddrinfo_do(struct getaddrinfo_req* req) {
66 int r;
67
68 r = uv_getaddrinfo(req->loop,
69 &req->handle,
70 getaddrinfo_cb,
71 "localhost",
72 NULL,
73 NULL);
74 ASSERT(r == 0);
75 }
76
77
getaddrinfo_cb(uv_getaddrinfo_t * handle,int status,struct addrinfo * res)78 static void getaddrinfo_cb(uv_getaddrinfo_t* handle,
79 int status,
80 struct addrinfo* res) {
81 /* TODO(gengjiawen): Fix test on QEMU. */
82 #if defined(__QEMU__)
83 RETURN_SKIP("Test does not currently work in QEMU");
84 #endif
85 struct getaddrinfo_req* req;
86
87 ASSERT(status == 0);
88
89 req = container_of(handle, struct getaddrinfo_req, handle);
90 uv_freeaddrinfo(res);
91
92 if (--req->counter)
93 getaddrinfo_do(req);
94 }
95
96
fs_do(struct fs_req * req)97 static void fs_do(struct fs_req* req) {
98 int r;
99
100 r = uv_fs_stat(req->loop, &req->handle, ".", fs_cb);
101 ASSERT(r == 0);
102 }
103
104
fs_cb(uv_fs_t * handle)105 static void fs_cb(uv_fs_t* handle) {
106 struct fs_req* req = container_of(handle, struct fs_req, handle);
107
108 uv_fs_req_cleanup(handle);
109
110 if (--req->counter)
111 fs_do(req);
112 }
113
114
do_work(void * arg)115 static void do_work(void* arg) {
116 struct getaddrinfo_req getaddrinfo_reqs[4];
117 struct fs_req fs_reqs[4];
118 uv_loop_t loop;
119 size_t i;
120 struct test_thread* thread = arg;
121
122 ASSERT(0 == uv_loop_init(&loop));
123
124 for (i = 0; i < ARRAY_SIZE(getaddrinfo_reqs); i++) {
125 struct getaddrinfo_req* req = getaddrinfo_reqs + i;
126 req->counter = 4;
127 req->loop = &loop;
128 getaddrinfo_do(req);
129 }
130
131 for (i = 0; i < ARRAY_SIZE(fs_reqs); i++) {
132 struct fs_req* req = fs_reqs + i;
133 req->counter = 4;
134 req->loop = &loop;
135 fs_do(req);
136 }
137
138 ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT));
139 ASSERT(0 == uv_loop_close(&loop));
140 thread->thread_called = 1;
141 }
142
143
thread_entry(void * arg)144 static void thread_entry(void* arg) {
145 ASSERT(arg == (void *) 42);
146 thread_called++;
147 }
148
149
TEST_IMPL(thread_create)150 TEST_IMPL(thread_create) {
151 uv_thread_t tid;
152 int r;
153
154 r = uv_thread_create(&tid, thread_entry, (void *) 42);
155 ASSERT(r == 0);
156
157 r = uv_thread_join(&tid);
158 ASSERT(r == 0);
159
160 ASSERT(thread_called == 1);
161
162 return 0;
163 }
164
165
166 /* Hilariously bad test name. Run a lot of tasks in the thread pool and verify
167 * that each "finished" callback is run in its originating thread.
168 */
TEST_IMPL(threadpool_multiple_event_loops)169 TEST_IMPL(threadpool_multiple_event_loops) {
170 /* TODO(gengjiawen): Fix test on QEMU. */
171 #if defined(__QEMU__)
172 RETURN_SKIP("Test does not currently work in QEMU");
173 #endif
174
175 struct test_thread threads[8];
176 size_t i;
177 int r;
178
179 memset(threads, 0, sizeof(threads));
180
181 for (i = 0; i < ARRAY_SIZE(threads); i++) {
182 r = uv_thread_create(&threads[i].thread_id, do_work, &threads[i]);
183 ASSERT(r == 0);
184 }
185
186 for (i = 0; i < ARRAY_SIZE(threads); i++) {
187 r = uv_thread_join(&threads[i].thread_id);
188 ASSERT(r == 0);
189 ASSERT(threads[i].thread_called == 1);
190 }
191
192 return 0;
193 }
194
195
tls_thread(void * arg)196 static void tls_thread(void* arg) {
197 ASSERT_NULL(uv_key_get(&tls_key));
198 uv_key_set(&tls_key, arg);
199 ASSERT(arg == uv_key_get(&tls_key));
200 uv_key_set(&tls_key, NULL);
201 ASSERT_NULL(uv_key_get(&tls_key));
202 }
203
204
TEST_IMPL(thread_local_storage)205 TEST_IMPL(thread_local_storage) {
206 char name[] = "main";
207 uv_thread_t threads[2];
208 ASSERT(0 == uv_key_create(&tls_key));
209 ASSERT_NULL(uv_key_get(&tls_key));
210 uv_key_set(&tls_key, name);
211 ASSERT(name == uv_key_get(&tls_key));
212 ASSERT(0 == uv_thread_create(threads + 0, tls_thread, threads + 0));
213 ASSERT(0 == uv_thread_create(threads + 1, tls_thread, threads + 1));
214 ASSERT(0 == uv_thread_join(threads + 0));
215 ASSERT(0 == uv_thread_join(threads + 1));
216 uv_key_delete(&tls_key);
217 return 0;
218 }
219
220
thread_check_stack(void * arg)221 static void thread_check_stack(void* arg) {
222 #if defined(__APPLE__)
223 size_t expected;
224 expected = arg == NULL ? 0 : ((uv_thread_options_t*)arg)->stack_size;
225 /* 512 kB is the default stack size of threads other than the main thread
226 * on MacOS. */
227 if (expected == 0)
228 expected = 512 * 1024;
229 ASSERT(pthread_get_stacksize_np(pthread_self()) >= expected);
230 #elif defined(__linux__) && defined(__GLIBC__)
231 size_t expected;
232 struct rlimit lim;
233 size_t stack_size;
234 pthread_attr_t attr;
235 ASSERT(0 == getrlimit(RLIMIT_STACK, &lim));
236 if (lim.rlim_cur == RLIM_INFINITY)
237 lim.rlim_cur = 2 << 20; /* glibc default. */
238 ASSERT(0 == pthread_getattr_np(pthread_self(), &attr));
239 ASSERT(0 == pthread_attr_getstacksize(&attr, &stack_size));
240 expected = arg == NULL ? 0 : ((uv_thread_options_t*)arg)->stack_size;
241 if (expected == 0)
242 expected = (size_t)lim.rlim_cur;
243 ASSERT(stack_size >= expected);
244 ASSERT(0 == pthread_attr_destroy(&attr));
245 #endif
246 }
247
248
TEST_IMPL(thread_stack_size)249 TEST_IMPL(thread_stack_size) {
250 uv_thread_t thread;
251 ASSERT(0 == uv_thread_create(&thread, thread_check_stack, NULL));
252 ASSERT(0 == uv_thread_join(&thread));
253 return 0;
254 }
255
TEST_IMPL(thread_stack_size_explicit)256 TEST_IMPL(thread_stack_size_explicit) {
257 uv_thread_t thread;
258 uv_thread_options_t options;
259
260 options.flags = UV_THREAD_HAS_STACK_SIZE;
261 options.stack_size = 1024 * 1024;
262 ASSERT(0 == uv_thread_create_ex(&thread, &options,
263 thread_check_stack, &options));
264 ASSERT(0 == uv_thread_join(&thread));
265
266 options.stack_size = 8 * 1024 * 1024; /* larger than most default os sizes */
267 ASSERT(0 == uv_thread_create_ex(&thread, &options,
268 thread_check_stack, &options));
269 ASSERT(0 == uv_thread_join(&thread));
270
271 options.stack_size = 0;
272 ASSERT(0 == uv_thread_create_ex(&thread, &options,
273 thread_check_stack, &options));
274 ASSERT(0 == uv_thread_join(&thread));
275
276 options.stack_size = 42;
277 ASSERT(0 == uv_thread_create_ex(&thread, &options,
278 thread_check_stack, &options));
279 ASSERT(0 == uv_thread_join(&thread));
280
281 #ifdef PTHREAD_STACK_MIN
282 options.stack_size = PTHREAD_STACK_MIN - 42; /* unaligned size */
283 ASSERT(0 == uv_thread_create_ex(&thread, &options,
284 thread_check_stack, &options));
285 ASSERT(0 == uv_thread_join(&thread));
286
287 options.stack_size = PTHREAD_STACK_MIN / 2 - 42; /* unaligned size */
288 ASSERT(0 == uv_thread_create_ex(&thread, &options,
289 thread_check_stack, &options));
290 ASSERT(0 == uv_thread_join(&thread));
291 #endif
292
293 /* unaligned size, should be larger than PTHREAD_STACK_MIN */
294 options.stack_size = 1234567;
295 ASSERT(0 == uv_thread_create_ex(&thread, &options,
296 thread_check_stack, &options));
297 ASSERT(0 == uv_thread_join(&thread));
298
299 return 0;
300 }
301